当前位置:   article > 正文

LLM(大语言模型)——Springboot集成文心一言、讯飞星火、通义千问、智谱清言_讯飞llm 开发 java

讯飞llm 开发 java

目录

引言

代码完整地址

入参

 出参

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. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/494964
推荐阅读
相关标签
  

闽ICP备14008679号