LLM(大语言模子)——Springboot集成文心一言、讯飞星火、通义千问、智谱 ...

打印 上一主题 下一主题

主题 537|帖子 537|积分 1611

目录
引言
代码完整地点
入参
 出参
Controller
Service
Service实现类
 模子Service
 入参转换类
文心一言实现类
讯飞星火实现类
 通义千问实现类
智谱清言实现类


引言

   本文将介绍怎样利用Java语言,结合Spring Boot框架,集成国内热门大模子API,包括文心一言、讯飞星火、通义千问、智谱清言。
  在开始前,请确保您已经按照各模子官网的指引,完成了相应的资源申请和设置。这些资源是调用大模子API的必要凭据,务必妥善保管。接下来,我们将通过具体的代码示例和步调说明,引导您完成Spring Boot项目和大模子API集成。
  代码完整地点

https://github.com/wangsilingwsl/model-integrate.git
入参

  1. package com.wsl.model.llm.api.dto;
  2. import com.alibaba.fastjson.JSONObject;
  3. import io.swagger.annotations.ApiModelProperty;
  4. import lombok.Data;
  5. import javax.validation.constraints.NotNull;
  6. import java.io.Serializable;
  7. import java.util.List;
  8. /**
  9. * 聊天请求 DTO
  10. *
  11. * @author wsl
  12. * @date 2024/2/20
  13. */
  14. @Data
  15. public class ChatRequestDTO implements Serializable {
  16.     private static final long serialVersionUID = 1L;
  17.     @ApiModelProperty(value = "聊天上下文信息", notes = "(1)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
  18.             "(2)成员数目必须为奇数\n" +
  19.             "(3)示例中message中的role值分别为user、assistant;奇数位message中的role值为user;偶数位值为assistant",
  20.             example = "[{"role":"user","content":"你好"},{"role":"assistant","content":"需要什么帮助"},{"role":"user","content":"自我介绍下"}]")
  21.     @NotNull(message = "聊天上下文信息不能为空")
  22.     private List<MessageDTO> messages;
  23.     @ApiModelProperty(value = "模型人设", notes = "主要用于人设设定,例如,你是xxx公司制作的AI助手,最大20000字符", example = "你是一名天气助手,需要提供天气查询服务")
  24.     private String system;
  25.     @ApiModelProperty(value = "请求参数", notes = "请求参数", example = "{"key":"value"}")
  26.     private JSONObject params;
  27. }
复制代码

  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.AllArgsConstructor;
  4. import lombok.Data;
  5. import java.io.Serializable;
  6. /**
  7. * 消息 DTO
  8. *
  9. * @author wsl
  10. * @date 2024/2/20
  11. */
  12. @Data
  13. @AllArgsConstructor
  14. public class MessageDTO implements Serializable {
  15.     private static final long serialVersionUID = 1L;
  16.     @ApiModelProperty(value = "角色", notes = "说明: user-用户, assistant-助手", example = "user")
  17.     private String role;
  18.     @ApiModelProperty(value = "消息内容", notes = "说明: 消息内容", example = "你好")
  19.     private String content;
  20. }
复制代码
 出参

  1. package com.wsl.model.llm.api.vo;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import java.io.Serializable;
  5. /**
  6. * 聊天响应 VO
  7. *
  8. * @author wsl
  9. * @date 2024/2/20
  10. */
  11. @Data
  12. public class ChatResponseVO implements Serializable {
  13.     private static final long serialVersionUID = 1L;
  14.     @ApiModelProperty(value = "结果", notes = "结果")
  15.     private String result;
  16. }
复制代码
Controller

  1. package com.wsl.model.llm.api.controller;
  2. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  3. import com.wsl.model.llm.api.service.IAiAppService;
  4. import com.wsl.model.llm.api.vo.ChatResponseVO;
  5. import io.swagger.annotations.Api;
  6. import io.swagger.annotations.ApiOperation;
  7. import io.swagger.annotations.ApiParam;
  8. import lombok.extern.slf4j.Slf4j;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.web.bind.annotation.*;
  11. /**
  12. * AI应用Controller
  13. *
  14. * @author wsl
  15. * @date 2024/02/19
  16. */
  17. @Slf4j
  18. @RestController
  19. @Api(tags = "AI应用")
  20. @RequestMapping("/llm/middle")
  21. public class AiAppController {
  22.     @Autowired
  23.     private IAiAppService service;
  24.     @PostMapping("/chat-message")
  25.     @ApiOperation("向大模型发起对话请求")
  26.     public ChatResponseVO chatMessage(
  27.             @ApiParam(value = "模型类型(ErnieBot/SparkDesk/ChatGlm/QianWen)", required = true) @RequestParam String modelType,
  28.             @ApiParam(value = "消息参数", required = true) @RequestBody ChatRequestDTO dto) {
  29.         try {
  30.             return service.chatMessage(modelType, dto);
  31.         } catch (Exception e) {
  32.             throw new RuntimeException(e);
  33.         }
  34.     }
  35. }
复制代码
Service

  1. package com.wsl.model.llm.api.service;
  2. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  3. import com.wsl.model.llm.api.vo.ChatResponseVO;
  4. /**
  5. * AI应用Service
  6. *
  7. * @author wsl
  8. * @date 2024/02/19
  9. */
  10. public interface IAiAppService {
  11.     /**
  12.      * 向大模型发起对话请求-根据模型编码、用户ID
  13.      *
  14.      * @param modelType 模型类型
  15.      * @param dto       消息参数
  16.      * @return ChatResponseVO
  17.      * @throws Exception 异常
  18.      */
  19.     ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception;
  20. }
复制代码
Service实现类

  1. package com.wsl.model.llm.api.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.util.StrUtil;
  4. import cn.hutool.extra.spring.SpringUtil;
  5. import com.wsl.model.llm.api.constant.enums.ModelTypeEnum;
  6. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  7. import com.wsl.model.llm.api.dto.MessageDTO;
  8. import com.wsl.model.llm.api.service.IAiAppService;
  9. import com.wsl.model.llm.api.service.ModelService;
  10. import com.wsl.model.llm.api.vo.ChatResponseVO;
  11. import org.springframework.stereotype.Service;
  12. import java.util.List;
  13. /**
  14. * AI应用ServiceImpl
  15. *
  16. * @author wsl
  17. * @date 2024/02/19
  18. */
  19. @Service("aiAppService")
  20. public class AiAppServiceImpl implements IAiAppService {
  21.     @Override
  22.     public ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception {
  23.         this.checkMessages(dto.getMessages());
  24.         // 根据枚举类ModelTypeEnum中的枚举值判断模型类型,并调用对应的模型实现类的方法
  25.         ModelService modelService = getModelService(modelType);
  26.         return modelService.chatMessage(dto);
  27.     }
  28.     /**
  29.      * 检查消息参数是否符合规范
  30.      *
  31.      * @param messages 消息参数
  32.      */
  33.     private void checkMessages(List<MessageDTO> messages) {
  34.         if (CollUtil.isNotEmpty(messages)) {
  35.             // messages参数个数必须为奇数并且奇数个数的消息role必须为user,偶数个数的消息role必须为assistant
  36.             if (messages.size() % 2 == 0) {
  37.                 throw new RuntimeException("messages参数个数必须为奇数");
  38.             }
  39.             for (int i = 0; i < messages.size(); i++) {
  40.                 if (i % 2 == 0) {
  41.                     if (!"user".equals(messages.get(i).getRole())) {
  42.                         throw new RuntimeException("messages奇数参数的role必须为user");
  43.                     }
  44.                 } else {
  45.                     if (!"assistant".equals(messages.get(i).getRole())) {
  46.                         throw new RuntimeException("messages偶数参数的role必须为assistant");
  47.                     }
  48.                 }
  49.             }
  50.         }
  51.     }
  52.     /**
  53.      * 根据模型类型获取对应的模型服务
  54.      *
  55.      * @param modelType 模型类型
  56.      * @return 模型服务
  57.      */
  58.     private ModelService getModelService(String modelType) {
  59.         try {
  60.             // 将模型类型字符串转换为枚举值
  61.             ModelTypeEnum modelTypeEnum = ModelTypeEnum.valueOf(modelType);
  62.             // 根据枚举值获取对应的实现类Bean的名称
  63.             String beanName = modelTypeEnum.name();
  64.             beanName = StrUtil.toCamelCase(beanName) + "Service";
  65.             return SpringUtil.getBean(beanName);
  66.         } catch (IllegalArgumentException e) {
  67.             throw new RuntimeException("模型类型错误");
  68.         }
  69.     }
  70. }
复制代码
 模子Service

  1. package com.wsl.model.llm.api.service;
  2. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  3. import com.wsl.model.llm.api.vo.ChatResponseVO;
  4. /**
  5. * 模型服务
  6. *
  7. * @author wsl
  8. * @date 2024/2/19
  9. */
  10. public interface ModelService {
  11.     /**
  12.      * 发起请求
  13.      *
  14.      * @param dto 请求参数
  15.      * @return 返回值
  16.      * @throws Exception 异常
  17.      */
  18.     ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception;
  19. }
复制代码
 入参转换类

  1. package com.wsl.model.llm.api.convert;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.collection.CollUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.alibaba.fastjson.JSONObject;
  6. import com.wsl.model.llm.api.dto.*;
  7. import org.mapstruct.Mapper;
  8. import org.mapstruct.factory.Mappers;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. /**
  12. * 聊天请求转换器
  13. *
  14. * @author wsl
  15. * @date 2024/2/22
  16. */
  17. @Mapper
  18. public interface ChatRequestConvert {
  19.     ChatRequestConvert INSTANCE = Mappers.getMapper(ChatRequestConvert.class);
  20.     /**
  21.      * 通用请求转换为文心一言请求
  22.      *
  23.      * @param dto 通用请求
  24.      * @return 文心一言请求
  25.      */
  26.     default JSONObject convertErnieBot(ChatRequestDTO dto) {
  27.         ErnieBotDTO ernieBotDTO = new ErnieBotDTO();
  28.         ernieBotDTO.setMessages(dto.getMessages());
  29.         ernieBotDTO.setSystem(dto.getSystem());
  30.         JSONObject jsonObject = new JSONObject();
  31.         BeanUtil.copyProperties(ernieBotDTO, jsonObject);
  32.         BeanUtil.copyProperties(dto.getParams(), jsonObject);
  33.         return jsonObject;
  34.     }
  35.     /**
  36.      * 通用请求转换为通义千问请求
  37.      *
  38.      * @param dto 通用请求
  39.      * @return 通义千问请求
  40.      */
  41.     default QianWenDTO convertQianwen(ChatRequestDTO dto) {
  42.         QianWenDTO qianWenDTO = new QianWenDTO();
  43.         qianWenDTO.setModel("qwen-turbo");
  44.         QianWenInputDTO input = new QianWenInputDTO();
  45.         String system = dto.getSystem();
  46.         if (StrUtil.isNotBlank(system)) {
  47.             MessageDTO messageDTO = new MessageDTO("system", system);
  48.             dto.getMessages().add(0, messageDTO);
  49.         }
  50.         input.setMessages(dto.getMessages());
  51.         JSONObject parametersJsonObject = new JSONObject();
  52.         BeanUtil.copyProperties(dto.getParams(), parametersJsonObject);
  53.         qianWenDTO.setInput(input);
  54.         qianWenDTO.setParameters(parametersJsonObject);
  55.         return qianWenDTO;
  56.     }
  57.     /**
  58.      * 通用请求转换为智谱清言请求
  59.      *
  60.      * @param dto 通用请求
  61.      * @return 智谱清言请求
  62.      */
  63.     default JSONObject convertChatGlm(ChatRequestDTO dto) {
  64.         ChatGlmDTO chatGlmDTO = new ChatGlmDTO();
  65.         String system = dto.getSystem();
  66.         if (StrUtil.isNotBlank(system)) {
  67.             MessageDTO messageDTO = new MessageDTO("system", system);
  68.             dto.getMessages().add(0, messageDTO);
  69.         }
  70.         chatGlmDTO.setMessages(dto.getMessages());
  71.         chatGlmDTO.setModel("glm-4");
  72.         JSONObject jsonObject = new JSONObject();
  73.         BeanUtil.copyProperties(chatGlmDTO, jsonObject);
  74.         BeanUtil.copyProperties(dto.getParams(), jsonObject);
  75.         return jsonObject;
  76.     }
  77.     /**
  78.      * 通用请求转换为讯飞星火请求
  79.      *
  80.      * @param dto 通用请求
  81.      * @return 讯飞星火请求
  82.      */
  83.     default SparkDeskDTO convertSparkDesk(ChatRequestDTO dto) {
  84.         SparkDeskDTO sparkDeskDTO = new SparkDeskDTO();
  85.         SparkDeskPayloadDTO payload = new SparkDeskPayloadDTO();
  86.         SparkDeskPayloadMessageDTO payloadMessage = new SparkDeskPayloadMessageDTO();
  87.         String system = dto.getSystem();
  88.         if (StrUtil.isNotBlank(system)) {
  89.             MessageDTO messageDTO = new MessageDTO("system", system);
  90.             dto.getMessages().add(0, messageDTO);
  91.         }
  92.         payloadMessage.setText(dto.getMessages());
  93.         payload.setMessage(payloadMessage);
  94.         SparkDeskParameterChatDTO parameterChat = new SparkDeskParameterChatDTO();
  95.         parameterChat.setDomain("generalv3.5");
  96.         JSONObject parameterChatJsonObject = new JSONObject();
  97.         BeanUtil.copyProperties(parameterChat, parameterChatJsonObject);
  98.         BeanUtil.copyProperties(dto.getParams(), parameterChatJsonObject);
  99.         SparkDeskParameterDTO parameter = new SparkDeskParameterDTO();
  100.         parameter.setChat(parameterChatJsonObject);
  101.         sparkDeskDTO.setPayload(payload);
  102.         sparkDeskDTO.setParameter(parameter);
  103.         return sparkDeskDTO;
  104.     }
  105.     /**
  106.      * 通用请求转换为通义千问请求
  107.      *
  108.      * @param dto 通用请求
  109.      * @return 通义千问请求
  110.      */
  111.     default FaRuiDTO convertFaRui(ChatRequestDTO dto) {
  112.         FaRuiDTO faRuiDTO = new FaRuiDTO();
  113.         List<MessageDTO> messages = dto.getMessages();
  114.         String prompt = messages.get(messages.size() - 1).getContent();
  115.         FaRuiInputDTO input = new FaRuiInputDTO();
  116.         if (messages.size() == 1) {
  117.             messages = new ArrayList<>();
  118.         }
  119.         if (CollUtil.isNotEmpty(messages)) {
  120.             messages.remove(messages.size() - 1);
  121.             List<FaRuiHistoryDTO> history = convertFaRuiHistory(messages);
  122.             input.setHistory(history);
  123.         }
  124.         input.setPrompt(prompt);
  125.         faRuiDTO.setParameters(dto.getParams());
  126.         faRuiDTO.setInput(input);
  127.         return faRuiDTO;
  128.     }
  129.     /**
  130.      * 通用消息转换为通义法睿历史消息
  131.      *
  132.      * @param messages 通用消息
  133.      * @return 通义法睿历史消息
  134.      */
  135.     default List<FaRuiHistoryDTO> convertFaRuiHistory(List<MessageDTO> messages) {
  136.         List<FaRuiHistoryDTO> history = new ArrayList<>();
  137.         int size = messages.size();
  138.         for (int i = 0; i < size; i += 2) {
  139.             FaRuiHistoryDTO messagePair = new FaRuiHistoryDTO();
  140.             messagePair.setUser(messages.get(i).getContent());
  141.             if (i + 1 < size) {
  142.                 messagePair.setBot(messages.get(i + 1).getContent());
  143.             }
  144.             history.add(messagePair);
  145.         }
  146.         return history;
  147.     }
  148. }
复制代码
文心一言实现类

  1. package com.wsl.model.llm.api.service.impl;
  2. import cn.hutool.http.HttpRequest;
  3. import cn.hutool.http.HttpResponse;
  4. import cn.hutool.http.HttpUtil;
  5. import cn.hutool.json.JSONUtil;
  6. import com.alibaba.fastjson.JSON;
  7. import com.alibaba.fastjson.JSONObject;
  8. import com.wsl.model.llm.api.convert.ChatRequestConvert;
  9. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  10. import com.wsl.model.llm.api.service.ModelService;
  11. import com.wsl.model.llm.api.vo.ChatResponseVO;
  12. import lombok.extern.slf4j.Slf4j;
  13. import org.springframework.stereotype.Service;
  14. /**
  15. * 文心一言 大模型服务
  16. *
  17. * @author wsl
  18. * @link https://console.bce.baidu.com/tools/?_=1708497709522&u=qfdc#/api?product=AI&project=%E5%8D%83%E5%B8%86%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%B9%B3%E5%8F%B0&parent=ERNIE-Bot&api=rpc%2F2.0%2Fai_custom%2Fv1%2Fwenxinworkshop%2Fchat%2Fcompletions&method=post
  19. * @date 2024/2/19
  20. */
  21. @Service("ErnieBotService")
  22. @Slf4j
  23. public class ErnieBotServiceImpl implements ModelService {
  24.     private String appSecret = "?";
  25.     private String apiKey = "?";
  26.     private static final String TOKEN_URL_TEMPLATE = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s";
  27.     private static final String CHAT_URL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=%s";
  28.     @Override
  29.     public ChatResponseVO chatMessage(ChatRequestDTO dto) {
  30.         JSONObject ernieBot = ChatRequestConvert.INSTANCE.convertErnieBot(dto);
  31.         String requestBody = JSONUtil.toJsonStr(ernieBot);
  32.         log.info("文心一言请求参数 ernieBot request:{}", requestBody);
  33.         HttpResponse response = HttpUtil.createPost(String.format(CHAT_URL, getAccessToken(apiKey, appSecret)))
  34.                 .body(requestBody)
  35.                 .header("Content-Type", "application/json")
  36.                 .execute();
  37.         if (response == null) {
  38.             throw new RuntimeException("HTTP response is null");
  39.         }
  40.         log.info("文心一言返回结果 ernieBot response:{}", response.body());
  41.         if (response.body() == null || response.body().trim().isEmpty()) {
  42.             throw new RuntimeException("HTTP response body is null or empty");
  43.         }
  44.         JSONObject jsonObject = JSON.parseObject(response.body());
  45.         if (!jsonObject.containsKey("result")) {
  46.             throw new RuntimeException(JSONObject.toJSONString(jsonObject));
  47.         }
  48.         ChatResponseVO vo = new ChatResponseVO();
  49.         vo.setResult(jsonObject.getString("result"));
  50.         return vo;
  51.     }
  52.     /**
  53.      * 从用户的AK,SK生成鉴权签名(Access Token)
  54.      *
  55.      * @param appId     应用ID
  56.      * @param appSecret 应用密钥
  57.      * @return token
  58.      */
  59.     public String getAccessToken(String appId, String appSecret) {
  60.         String url = String.format(TOKEN_URL_TEMPLATE, apiKey, appSecret);
  61.         try (HttpResponse response = HttpRequest.post(url)
  62.                 .header("Content-Type", "application/json")
  63.                 .header("Accept", "application/json")
  64.                 .execute()) {
  65.             JSONObject jsonObject = JSON.parseObject(response.body());
  66.             String accessToken = jsonObject.getString("access_token");
  67.             return accessToken;
  68.         }
  69.     }
  70. }
复制代码
  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import javax.validation.constraints.NotNull;
  5. import java.io.Serializable;
  6. import java.util.List;
  7. /**
  8. * 文心一言 请求 DTO
  9. *
  10. * @author wsl
  11. * @date 2024/2/20
  12. */
  13. @Data
  14. public class ErnieBotDTO implements Serializable {
  15.     private static final long serialVersionUID = 1L;
  16.     @ApiModelProperty(value = "聊天上下文信息", notes = "说明:\n" +
  17.             "(1)messages成员不能为空,1个成员表示单轮对话,多个成员表示多轮对话;例如:\n" +
  18.             "· 1个成员示例,"messages": [ {"role": "user","content": "你好"}]\n" +
  19.             "· 3个成员示例,"messages": [ {"role": "user","content": "你好"},{"role":"assistant","content":"需要什么帮助"},{"role":"user","content":"自我介绍下"}]\n" +
  20.             "(2)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
  21.             "(3)成员数目必须为奇数,成员中message的role值说明如下:奇数位messsage的role值必须为user或function,偶数位message的role值为assistant,第一个message的role不能是function。例如:\n" +
  22.             "示例中message中的role值分别为user、assistant、user、assistant、user;奇数位(红框)message中的role值为user,即第1、3、5个message中的role值为user;偶数位(蓝框)值为assistant,即第2、4个message中的role值为assistant",
  23.             example = "[{"role":"system","content":"您好,我是智谱清言,我可以帮您查询天气,您可以输入:查询{{destination}}的天气,查询{{destination}}未来{{num_day}}天的天气"},{"role":"user","content":"查询三亚未来5天的天气"},{"role":"assistant","content":"正在查询三亚未来5天的天气"},{"role":"assistant","content":"三亚未来5天的天气是晴天"}]")
  24.     @NotNull(message = "聊天上下文信息不能为空")
  25.     private List<MessageDTO> messages;
  26.     @ApiModelProperty(value = "模型人设", notes = "主要用于人设设定,例如,你是xxx公司制作的AI助手,最大20000字符", example = "qwen-turbo")
  27.     private String system;
  28.     @ApiModelProperty(value = "温度", notes = "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。默认0.8,范围 [0, 1.0],不能为0", example = "0.8")
  29.     private Float temperature;
  30. }
复制代码
讯飞星火实现类

  1. package com.wsl.model.llm.api.service.impl;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONException;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.wsl.model.llm.api.convert.ChatRequestConvert;
  6. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  7. import com.wsl.model.llm.api.dto.SparkDeskDTO;
  8. import com.wsl.model.llm.api.dto.SparkDeskHeaderDTO;
  9. import com.wsl.model.llm.api.service.ModelService;
  10. import com.wsl.model.llm.api.vo.ChatResponseVO;
  11. import lombok.extern.slf4j.Slf4j;
  12. import okhttp3.*;
  13. import org.springframework.stereotype.Service;
  14. import javax.crypto.Mac;
  15. import javax.crypto.spec.SecretKeySpec;
  16. import java.net.URL;
  17. import java.nio.charset.StandardCharsets;
  18. import java.text.SimpleDateFormat;
  19. import java.util.*;
  20. import java.util.concurrent.CompletableFuture;
  21. import java.util.concurrent.TimeUnit;
  22. /**
  23. * 讯飞星火 大模型服务
  24. *
  25. * @author wsl
  26. * @link https://www.xfyun.cn/doc/spark/Web.html
  27. * @date 2024/2/19
  28. */
  29. @Service("SparkDeskService")
  30. @Slf4j
  31. public class SparkDeskServiceImpl implements ModelService {
  32.     private String appId = "?";
  33.     private String appSecret = "?";
  34.     private String appKey = "?";
  35.     public static final String HOST_URL = "https://spark-api.xf-yun.com/v3.5/chat";
  36.     @Override
  37.     public ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception {
  38.         ChatResponseVO vo = new ChatResponseVO();
  39.         SparkDeskDTO sparkDeskDTO = ChatRequestConvert.INSTANCE.convertSparkDesk(dto);
  40.         sparkDeskDTO.setHeader(new SparkDeskHeaderDTO(appId));
  41.         String authUrl = getAuthUrl(HOST_URL, appKey, appSecret).replace("http://", "ws://").replace("https://", "wss://");
  42.         Request request = new Request.Builder().url(authUrl).build();
  43.         OkHttpClient client = new OkHttpClient.Builder().build();
  44.         StringBuilder sb = new StringBuilder();
  45.         CompletableFuture<String> messageReceived = new CompletableFuture<>();
  46.         String body = JSON.toJSONString(sparkDeskDTO);
  47.         WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
  48.             @Override
  49.             public void onOpen(WebSocket webSocket, Response response) {
  50.                 //发送消息
  51.                 log.info("讯飞星火请求参数 sparkDesk request:{}", body);
  52.                 webSocket.send(body);
  53.             }
  54.             @Override
  55.             public void onMessage(WebSocket webSocket, String text) {
  56.                 try {
  57.                     JSONObject obj = JSON.parseObject(text);
  58.                     // 使用Optional来避免空指针异常,并在内容不存在时抛出异常
  59.                     Optional<String> contentOptional = Optional.ofNullable(obj)
  60.                             .map(jsonObject -> jsonObject.getJSONObject("payload"))
  61.                             .map(payload -> payload.getJSONObject("choices"))
  62.                             .map(choices -> choices.getJSONArray("text"))
  63.                             .map(jsonArray -> jsonArray.getJSONObject(0))
  64.                             .map(jsonObject -> jsonObject.getString("content"));
  65.                     String str = contentOptional.orElseThrow(() -> new RuntimeException(JSONObject.toJSONString(obj)));
  66.                     sb.append(str);
  67.                     // 检查header和status字段
  68.                     Optional<Long> statusOptional = Optional.ofNullable(obj)
  69.                             .map(jsonObject -> jsonObject.getJSONObject("header"))
  70.                             .map(header -> header.getLong("status"));
  71.                     // 如果status为2,则关闭WebSocket并完成CompletableFuture
  72.                     if (statusOptional.isPresent() && statusOptional.get() == 2) {
  73.                         webSocket.close(1000, "Closing WebSocket connection");
  74.                         messageReceived.complete(text);
  75.                     }
  76.                 } catch (JSONException e) {
  77.                     throw new RuntimeException(e);
  78.                 }
  79.             }
  80.         });
  81.         messageReceived.get(60, TimeUnit.SECONDS);
  82.         webSocket.close(1000, "Closing WebSocket connection");
  83.         log.info("讯飞星火返回结果 sparkDesk response:{}", sb);
  84.         vo.setResult(sb.toString());
  85.         return vo;
  86.     }
  87.     /**
  88.      * 鉴权方法
  89.      *
  90.      * @param hostUrl   服务地址
  91.      * @param apiKey    apiKey
  92.      * @param apiSecret apiSecret
  93.      * @return 返回鉴权url
  94.      * @throws Exception 异常
  95.      */
  96.     public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
  97.         URL url = new URL(hostUrl);
  98.         // 时间
  99.         SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
  100.         format.setTimeZone(TimeZone.getTimeZone("GMT"));
  101.         String date = format.format(new Date());
  102.         // 拼接
  103.         String preStr = "host: " + url.getHost() + "\n" +
  104.                 "date: " + date + "\n" +
  105.                 "GET " + url.getPath() + " HTTP/1.1";
  106.         // SHA256加密
  107.         Mac mac = Mac.getInstance("hmacsha256");
  108.         SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
  109.         mac.init(spec);
  110.         byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
  111.         // Base64加密
  112.         String sha = Base64.getEncoder().encodeToString(hexDigits);
  113.         String authorization = String.format("api_key="%s", algorithm="%s", headers="%s", signature="%s"", apiKey, "hmac-sha256", "host date request-line", sha);
  114.         // 拼接地址
  115.         HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
  116.                 addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).
  117.                 addQueryParameter("date", date).
  118.                 addQueryParameter("host", url.getHost()).
  119.                 build();
  120.         return httpUrl.toString();
  121.     }
  122. }
复制代码

  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import java.io.Serializable;
  5. /**
  6. * 讯飞星火 请求 DTO
  7. *
  8. * @author wsl
  9. * @date 2024/2/20
  10. */
  11. @Data
  12. public class SparkDeskDTO implements Serializable {
  13.     private static final long serialVersionUID = 1L;
  14.     @ApiModelProperty(value = "头部", notes = "头部")
  15.     private SparkDeskHeaderDTO header;
  16.     @ApiModelProperty(value = "参数", notes = "参数")
  17.     private SparkDeskParameterDTO parameter;
  18.     @ApiModelProperty(value = "有效载荷", notes = "有效载荷")
  19.     private SparkDeskPayloadDTO payload;
  20. }
复制代码
  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.AllArgsConstructor;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import java.io.Serializable;
  7. /**
  8. * 讯飞星火 Header DTO
  9. *
  10. * @author wsl
  11. * @date 2024/2/20
  12. */
  13. @Data
  14. @NoArgsConstructor
  15. @AllArgsConstructor
  16. public class SparkDeskHeaderDTO implements Serializable {
  17.     private static final long serialVersionUID = 1L;
  18.     @ApiModelProperty(value = "应用appid", notes = "从开放平台控制台创建的应用中获取")
  19.     private String app_id;
  20. }
复制代码

  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import java.io.Serializable;
  5. /**
  6. * 讯飞星火 聊天 参数 DTO
  7. *
  8. * @author wsl
  9. * @date 2024/2/20
  10. */
  11. @Data
  12. public class SparkDeskParameterChatDTO implements Serializable {
  13.     private static final long serialVersionUID = 1L;
  14.     @ApiModelProperty(value = "指定访问的领域", notes = "generalv3指向V3版本;generalv3.5指向V3.5版本")
  15.     private String domain;
  16.     @ApiModelProperty(value = "温度", notes = "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。默认0.8,范围 [0, 2.0],不能为0", example = "0.8")
  17.     private Float temperature;
  18.     @ApiModelProperty(value = "最大标记", notes = "模型回答的tokens的最大长度;取值范围[1,8192],默认为2048", example = "2048")
  19.     private Integer max_tokens;
  20. }
复制代码
  1. package com.wsl.model.llm.api.dto;
  2. import com.alibaba.fastjson.JSONObject;
  3. import io.swagger.annotations.ApiModelProperty;
  4. import lombok.Data;
  5. import java.io.Serializable;
  6. /**
  7. * 讯飞星火 参数 DTO
  8. *
  9. * @author wsl
  10. * @date 2024/2/20
  11. */
  12. @Data
  13. public class SparkDeskParameterDTO implements Serializable {
  14.     private static final long serialVersionUID = 1L;
  15.     @ApiModelProperty(value = "聊天参数", notes = "聊天参数")
  16.     private JSONObject chat;
  17. }
复制代码
  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import java.io.Serializable;
  5. /**
  6. * 讯飞星火 有效载荷 DTO
  7. *
  8. * @author wsl
  9. * @date 2024/2/20
  10. */
  11. @Data
  12. public class SparkDeskPayloadDTO implements Serializable {
  13.     private static final long serialVersionUID = 1L;
  14.     @ApiModelProperty(value = "消息", notes = "消息")
  15.     private SparkDeskPayloadMessageDTO message;
  16. }
复制代码

  1. package com.wsl.model.llm.api.dto;
  2. import lombok.Data;
  3. import javax.validation.constraints.NotNull;
  4. import java.io.Serializable;
  5. import java.util.List;
  6. /**
  7. * 讯飞星火 有效载荷 消息 DTO
  8. *
  9. * @author wsl
  10. * @date 2024/2/20
  11. */
  12. @Data
  13. public class SparkDeskPayloadMessageDTO implements Serializable {
  14.     private static final long serialVersionUID = 1L;
  15.     @NotNull(message = "聊天上下文信息不能为空")
  16.     private List<MessageDTO> text;
  17. }
复制代码
 通义千问实现类

  1. package com.wsl.model.llm.api.service.impl;
  2. import cn.hutool.http.HttpRequest;
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.wsl.model.llm.api.convert.ChatRequestConvert;
  6. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  7. import com.wsl.model.llm.api.dto.QianWenDTO;
  8. import com.wsl.model.llm.api.service.ModelService;
  9. import com.wsl.model.llm.api.vo.ChatResponseVO;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.stereotype.Service;
  12. import java.util.Optional;
  13. /**
  14. * 通义千问 大模型服务
  15. *
  16. * @author wsl
  17. * @link https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.6922140bTYj6qJ#602895ef3dtl1
  18. * @date 2024/2/19
  19. */
  20. @Slf4j
  21. @Service("QianWenService")
  22. public class QianWenServiceImpl implements ModelService {
  23.     private String apiKey = "?";
  24.     @Override
  25.     public ChatResponseVO chatMessage(ChatRequestDTO dto) {
  26.         QianWenDTO qianwen = ChatRequestConvert.INSTANCE.convertQianwen(dto);
  27.         String url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
  28.         String json = JSON.toJSONString(qianwen);
  29.         log.info("通义千问请求参数 qianwen request:{}", json);
  30.         String result = HttpRequest.post(url)
  31.                 .header("Authorization", "Bearer " + apiKey)
  32.                 .header("Content-Type", "application/json")
  33.                 .body(json)
  34.                 .execute().body();
  35.         log.info("通义千问返回结果 qianwen response:{}", result);
  36.         ChatResponseVO vo = new ChatResponseVO();
  37.         JSONObject jsonObject = JSON.parseObject(result);
  38.         Optional<String> textOptional = Optional.ofNullable(jsonObject.getJSONObject("output"))
  39.                 .map(output -> output.getString("text"));
  40.         if (!textOptional.isPresent()) {
  41.             throw new RuntimeException(JSONObject.toJSONString(jsonObject));
  42.         }
  43.         vo.setResult(textOptional.get());
  44.         return vo;
  45.     }
  46. }
复制代码
  1. package com.wsl.model.llm.api.dto;
  2. import io.swagger.annotations.ApiModelProperty;
  3. import lombok.Data;
  4. import javax.validation.constraints.NotNull;
  5. import java.io.Serializable;
  6. import java.util.List;
  7. /**
  8. * 通义千问 输入 DTO
  9. *
  10. * @author wsl
  11. * @date 2024/2/20
  12. */
  13. @Data
  14. public class QianWenInputDTO implements Serializable {
  15.     private static final long serialVersionUID = 1L;
  16.     @ApiModelProperty(value = "聊天上下文信息", notes = "说明:\n" +
  17.             "(1)messages成员不能为空,1个成员表示单轮对话,多个成员表示多轮对话;例如:\n" +
  18.             "· 1个成员示例,"messages": [ {"role": "user","content": "你好"}]\n" +
  19.             "· 3个成员示例,"messages": [ {"role": "user","content": "你好"},{"role":"assistant","content":"需要什么帮助"},{"role":"user","content":"自我介绍下"}]\n" +
  20.             "(2)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
  21.             "(3)成员数目必须为奇数,成员中message的role值说明如下:奇数位messsage的role值必须为user或function,偶数位message的role值为assistant,第一个message的role不能是function。例如:\n" +
  22.             "示例中message中的role值分别为user、assistant、user、assistant、user;奇数位(红框)message中的role值为user,即第1、3、5个message中的role值为user;偶数位(蓝框)值为assistant,即第2、4个message中的role值为assistant",
  23.             example = "[{"role":"system","content":"您好,我是智谱清言,我可以帮您查询天气,您可以输入:查询{{destination}}的天气,查询{{destination}}未来{{num_day}}天的天气"},{"role":"user","content":"查询三亚未来5天的天气"},{"role":"assistant","content":"正在查询三亚未来5天的天气"},{"role":"assistant","content":"三亚未来5天的天气是晴天"}]")
  24.     @NotNull(message = "聊天上下文信息不能为空")
  25.     private List<MessageDTO> messages;
  26. }
复制代码
智谱清言实现类

  1. package com.wsl.model.llm.api.service.impl;
  2. import cn.hutool.http.HttpResponse;
  3. import cn.hutool.http.HttpUtil;
  4. import cn.hutool.json.JSONUtil;
  5. import com.alibaba.fastjson.JSON;
  6. import com.alibaba.fastjson.JSONArray;
  7. import com.alibaba.fastjson.JSONObject;
  8. import com.auth0.jwt.JWT;
  9. import com.auth0.jwt.algorithms.Algorithm;
  10. import com.wsl.model.llm.api.convert.ChatRequestConvert;
  11. import com.wsl.model.llm.api.dto.ChatRequestDTO;
  12. import com.wsl.model.llm.api.service.ModelService;
  13. import com.wsl.model.llm.api.vo.ChatResponseVO;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.springframework.stereotype.Service;
  16. import java.util.Date;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. import java.util.Optional;
  20. /**
  21. * 智谱清言 大模型服务
  22. *
  23. * @author wsl
  24. * @link https://open.bigmodel.cn/dev/api#glm-4
  25. * @date 2024/2/19
  26. */
  27. @Slf4j
  28. @Service("ChatGlmService")
  29. public class ChatGlmServiceImpl implements ModelService {
  30.     private String apiKey = "?";
  31.     @Override
  32.     public ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception {
  33.         JSONObject chatGlm = ChatRequestConvert.INSTANCE.convertChatGlm(dto);
  34.         String url = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
  35.         String requestBody = JSONUtil.toJsonStr(chatGlm);
  36.         log.info("智谱清言请求参数 chatGlm request:{}", requestBody);
  37.         HttpResponse response = HttpUtil.createPost(url).body(requestBody).header("Content-Type", "application/json").header("Authorization", "Bearer " + generateToken(apiKey, 3600)).execute();
  38.         log.info("智谱清言返回结果 chatGlm response:{}", Optional.ofNullable(response).map(HttpResponse::body).orElse(""));
  39.         ChatResponseVO vo = new ChatResponseVO();
  40.         Optional<JSONObject> jsonObject = Optional.ofNullable(JSON.parseObject(response.body()));
  41.         jsonObject.ifPresent(json -> {
  42.             Optional<JSONArray> choices = Optional.ofNullable(json.getJSONArray("choices"));
  43.             choices.ifPresent(choiceArray -> {
  44.                 if (!choiceArray.isEmpty()) {
  45.                     Optional<JSONObject> firstChoiceMessage = Optional.ofNullable(choiceArray.getJSONObject(0).getJSONObject("message"));
  46.                     firstChoiceMessage.ifPresent(message -> {
  47.                         String content = message.getString("content");
  48.                         if (content != null) {
  49.                             vo.setResult(content);
  50.                         } else {
  51.                             throw new RuntimeException(response.body());
  52.                         }
  53.                     });
  54.                 }
  55.             });
  56.             throw new RuntimeException(response.body());
  57.         });
  58.         return vo;
  59.     }
  60.     /**
  61.      * 生成token
  62.      *
  63.      * @param apikey     apikey
  64.      * @param expSeconds 过期时间
  65.      * @return token
  66.      * @throws Exception 异常
  67.      */
  68.     public static String generateToken(String apikey, int expSeconds) throws Exception {
  69.         String[] parts = apikey.split("\\.");
  70.         if (parts.length != 2) {
  71.             throw new Exception("Invalid apikey");
  72.         }
  73.         String id = parts[0];
  74.         String secret = parts[1];
  75.         Map<String, Object> payload = new HashMap<>(16);
  76.         payload.put("api_key", id);
  77.         payload.put("exp", new Date(System.currentTimeMillis() + expSeconds * 1000));
  78.         payload.put("timestamp", new Date(System.currentTimeMillis()));
  79.         Algorithm algorithm = Algorithm.HMAC256(secret);
  80.         return JWT.create().withHeader(new HashMap<String, Object>(16) {{
  81.             put("alg", "HS256");
  82.             put("sign_type", "SIGN");
  83.         }}).withPayload(payload).sign(algorithm);
  84.     }
  85. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表