赞
踩
目录
本文将介绍如何使用Java语言,结合Spring Boot框架,集成国内热门大模型API,包括文心一言、讯飞星火、通义千问、智谱清言。
在开始前,请确保您已经按照各模型官网的指引,完成了相应的资源申请和配置。这些资源是调用大模型API的必要凭证,务必妥善保管。接下来,我们将通过具体的代码示例和步骤说明,引导您完成Spring Boot项目和大模型API集成。
https://github.com/wangsilingwsl/model-integrate.git
- package com.wsl.model.llm.api.dto;
-
- import com.alibaba.fastjson.JSONObject;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import javax.validation.constraints.NotNull;
- import java.io.Serializable;
- import java.util.List;
-
- /**
- * 聊天请求 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class ChatRequestDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "聊天上下文信息", notes = "(1)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
- "(2)成员数目必须为奇数\n" +
- "(3)示例中message中的role值分别为user、assistant;奇数位message中的role值为user;偶数位值为assistant",
- example = "[{\"role\":\"user\",\"content\":\"你好\"},{\"role\":\"assistant\",\"content\":\"需要什么帮助\"},{\"role\":\"user\",\"content\":\"自我介绍下\"}]")
- @NotNull(message = "聊天上下文信息不能为空")
- private List<MessageDTO> messages;
-
- @ApiModelProperty(value = "模型人设", notes = "主要用于人设设定,例如,你是xxx公司制作的AI助手,最大20000字符", example = "你是一名天气助手,需要提供天气查询服务")
- private String system;
-
- @ApiModelProperty(value = "请求参数", notes = "请求参数", example = "{\"key\":\"value\"}")
- private JSONObject params;
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.AllArgsConstructor;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 消息 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- @AllArgsConstructor
- public class MessageDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "角色", notes = "说明: user-用户, assistant-助手", example = "user")
- private String role;
-
- @ApiModelProperty(value = "消息内容", notes = "说明: 消息内容", example = "你好")
- private String content;
-
- }
- package com.wsl.model.llm.api.vo;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 聊天响应 VO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class ChatResponseVO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "结果", notes = "结果")
- private String result;
-
- }
- package com.wsl.model.llm.api.controller;
-
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.service.IAiAppService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiParam;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
-
- /**
- * AI应用Controller
- *
- * @author wsl
- * @date 2024/02/19
- */
- @Slf4j
- @RestController
- @Api(tags = "AI应用")
- @RequestMapping("/llm/middle")
- public class AiAppController {
-
- @Autowired
- private IAiAppService service;
-
- @PostMapping("/chat-message")
- @ApiOperation("向大模型发起对话请求")
- public ChatResponseVO chatMessage(
- @ApiParam(value = "模型类型(ErnieBot/SparkDesk/ChatGlm/QianWen)", required = true) @RequestParam String modelType,
- @ApiParam(value = "消息参数", required = true) @RequestBody ChatRequestDTO dto) {
- try {
- return service.chatMessage(modelType, dto);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- }
- package com.wsl.model.llm.api.service;
-
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
-
- /**
- * AI应用Service
- *
- * @author wsl
- * @date 2024/02/19
- */
- public interface IAiAppService {
-
- /**
- * 向大模型发起对话请求-根据模型编码、用户ID
- *
- * @param modelType 模型类型
- * @param dto 消息参数
- * @return ChatResponseVO
- * @throws Exception 异常
- */
- ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception;
-
- }
- package com.wsl.model.llm.api.service.impl;
-
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.extra.spring.SpringUtil;
- import com.wsl.model.llm.api.constant.enums.ModelTypeEnum;
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.dto.MessageDTO;
- import com.wsl.model.llm.api.service.IAiAppService;
- import com.wsl.model.llm.api.service.ModelService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import org.springframework.stereotype.Service;
-
- import java.util.List;
-
- /**
- * AI应用ServiceImpl
- *
- * @author wsl
- * @date 2024/02/19
- */
- @Service("aiAppService")
- public class AiAppServiceImpl implements IAiAppService {
-
- @Override
- public ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception {
- this.checkMessages(dto.getMessages());
- // 根据枚举类ModelTypeEnum中的枚举值判断模型类型,并调用对应的模型实现类的方法
- ModelService modelService = getModelService(modelType);
- return modelService.chatMessage(dto);
- }
-
- /**
- * 检查消息参数是否符合规范
- *
- * @param messages 消息参数
- */
- private void checkMessages(List<MessageDTO> messages) {
- if (CollUtil.isNotEmpty(messages)) {
- // messages参数个数必须为奇数并且奇数个数的消息role必须为user,偶数个数的消息role必须为assistant
- if (messages.size() % 2 == 0) {
- throw new RuntimeException("messages参数个数必须为奇数");
- }
- for (int i = 0; i < messages.size(); i++) {
- if (i % 2 == 0) {
- if (!"user".equals(messages.get(i).getRole())) {
- throw new RuntimeException("messages奇数参数的role必须为user");
- }
- } else {
- if (!"assistant".equals(messages.get(i).getRole())) {
- throw new RuntimeException("messages偶数参数的role必须为assistant");
- }
- }
- }
- }
- }
-
-
- /**
- * 根据模型类型获取对应的模型服务
- *
- * @param modelType 模型类型
- * @return 模型服务
- */
- private ModelService getModelService(String modelType) {
- try {
- // 将模型类型字符串转换为枚举值
- ModelTypeEnum modelTypeEnum = ModelTypeEnum.valueOf(modelType);
- // 根据枚举值获取对应的实现类Bean的名称
- String beanName = modelTypeEnum.name();
- beanName = StrUtil.toCamelCase(beanName) + "Service";
- return SpringUtil.getBean(beanName);
- } catch (IllegalArgumentException e) {
- throw new RuntimeException("模型类型错误");
- }
- }
-
- }
- package com.wsl.model.llm.api.service;
-
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
-
- /**
- * 模型服务
- *
- * @author wsl
- * @date 2024/2/19
- */
- public interface ModelService {
-
- /**
- * 发起请求
- *
- * @param dto 请求参数
- * @return 返回值
- * @throws Exception 异常
- */
- ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception;
-
- }
- package com.wsl.model.llm.api.convert;
-
- import cn.hutool.core.bean.BeanUtil;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.util.StrUtil;
- import com.alibaba.fastjson.JSONObject;
- import com.wsl.model.llm.api.dto.*;
- import org.mapstruct.Mapper;
- import org.mapstruct.factory.Mappers;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * 聊天请求转换器
- *
- * @author wsl
- * @date 2024/2/22
- */
- @Mapper
- public interface ChatRequestConvert {
-
- ChatRequestConvert INSTANCE = Mappers.getMapper(ChatRequestConvert.class);
-
- /**
- * 通用请求转换为文心一言请求
- *
- * @param dto 通用请求
- * @return 文心一言请求
- */
- default JSONObject convertErnieBot(ChatRequestDTO dto) {
- ErnieBotDTO ernieBotDTO = new ErnieBotDTO();
- ernieBotDTO.setMessages(dto.getMessages());
- ernieBotDTO.setSystem(dto.getSystem());
-
- JSONObject jsonObject = new JSONObject();
- BeanUtil.copyProperties(ernieBotDTO, jsonObject);
- BeanUtil.copyProperties(dto.getParams(), jsonObject);
- return jsonObject;
- }
-
- /**
- * 通用请求转换为通义千问请求
- *
- * @param dto 通用请求
- * @return 通义千问请求
- */
- default QianWenDTO convertQianwen(ChatRequestDTO dto) {
- QianWenDTO qianWenDTO = new QianWenDTO();
- qianWenDTO.setModel("qwen-turbo");
- QianWenInputDTO input = new QianWenInputDTO();
- String system = dto.getSystem();
- if (StrUtil.isNotBlank(system)) {
- MessageDTO messageDTO = new MessageDTO("system", system);
- dto.getMessages().add(0, messageDTO);
- }
- input.setMessages(dto.getMessages());
-
- JSONObject parametersJsonObject = new JSONObject();
- BeanUtil.copyProperties(dto.getParams(), parametersJsonObject);
-
- qianWenDTO.setInput(input);
- qianWenDTO.setParameters(parametersJsonObject);
-
- return qianWenDTO;
- }
-
- /**
- * 通用请求转换为智谱清言请求
- *
- * @param dto 通用请求
- * @return 智谱清言请求
- */
- default JSONObject convertChatGlm(ChatRequestDTO dto) {
- ChatGlmDTO chatGlmDTO = new ChatGlmDTO();
-
- String system = dto.getSystem();
- if (StrUtil.isNotBlank(system)) {
- MessageDTO messageDTO = new MessageDTO("system", system);
- dto.getMessages().add(0, messageDTO);
- }
-
- chatGlmDTO.setMessages(dto.getMessages());
- chatGlmDTO.setModel("glm-4");
- JSONObject jsonObject = new JSONObject();
- BeanUtil.copyProperties(chatGlmDTO, jsonObject);
- BeanUtil.copyProperties(dto.getParams(), jsonObject);
- return jsonObject;
- }
-
- /**
- * 通用请求转换为讯飞星火请求
- *
- * @param dto 通用请求
- * @return 讯飞星火请求
- */
- default SparkDeskDTO convertSparkDesk(ChatRequestDTO dto) {
- SparkDeskDTO sparkDeskDTO = new SparkDeskDTO();
- SparkDeskPayloadDTO payload = new SparkDeskPayloadDTO();
- SparkDeskPayloadMessageDTO payloadMessage = new SparkDeskPayloadMessageDTO();
-
- String system = dto.getSystem();
- if (StrUtil.isNotBlank(system)) {
- MessageDTO messageDTO = new MessageDTO("system", system);
- dto.getMessages().add(0, messageDTO);
- }
-
- payloadMessage.setText(dto.getMessages());
- payload.setMessage(payloadMessage);
-
- SparkDeskParameterChatDTO parameterChat = new SparkDeskParameterChatDTO();
- parameterChat.setDomain("generalv3.5");
- JSONObject parameterChatJsonObject = new JSONObject();
-
- BeanUtil.copyProperties(parameterChat, parameterChatJsonObject);
- BeanUtil.copyProperties(dto.getParams(), parameterChatJsonObject);
-
- SparkDeskParameterDTO parameter = new SparkDeskParameterDTO();
- parameter.setChat(parameterChatJsonObject);
-
- sparkDeskDTO.setPayload(payload);
- sparkDeskDTO.setParameter(parameter);
-
- return sparkDeskDTO;
- }
-
- /**
- * 通用请求转换为通义千问请求
- *
- * @param dto 通用请求
- * @return 通义千问请求
- */
- default FaRuiDTO convertFaRui(ChatRequestDTO dto) {
- FaRuiDTO faRuiDTO = new FaRuiDTO();
-
- List<MessageDTO> messages = dto.getMessages();
- String prompt = messages.get(messages.size() - 1).getContent();
-
- FaRuiInputDTO input = new FaRuiInputDTO();
- if (messages.size() == 1) {
- messages = new ArrayList<>();
- }
- if (CollUtil.isNotEmpty(messages)) {
- messages.remove(messages.size() - 1);
- List<FaRuiHistoryDTO> history = convertFaRuiHistory(messages);
- input.setHistory(history);
- }
- input.setPrompt(prompt);
- faRuiDTO.setParameters(dto.getParams());
- faRuiDTO.setInput(input);
-
- return faRuiDTO;
- }
-
- /**
- * 通用消息转换为通义法睿历史消息
- *
- * @param messages 通用消息
- * @return 通义法睿历史消息
- */
- default List<FaRuiHistoryDTO> convertFaRuiHistory(List<MessageDTO> messages) {
- List<FaRuiHistoryDTO> history = new ArrayList<>();
- int size = messages.size();
- for (int i = 0; i < size; i += 2) {
- FaRuiHistoryDTO messagePair = new FaRuiHistoryDTO();
- messagePair.setUser(messages.get(i).getContent());
- if (i + 1 < size) {
- messagePair.setBot(messages.get(i + 1).getContent());
- }
- history.add(messagePair);
- }
- return history;
- }
-
- }
- package com.wsl.model.llm.api.service.impl;
-
- import cn.hutool.http.HttpRequest;
- import cn.hutool.http.HttpResponse;
- import cn.hutool.http.HttpUtil;
- import cn.hutool.json.JSONUtil;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- import com.wsl.model.llm.api.convert.ChatRequestConvert;
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.service.ModelService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
-
- /**
- * 文心一言 大模型服务
- *
- * @author wsl
- * @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
- * @date 2024/2/19
- */
- @Service("ErnieBotService")
- @Slf4j
- public class ErnieBotServiceImpl implements ModelService {
-
- private String appSecret = "?";
-
- private String apiKey = "?";
-
- 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";
-
- private static final String CHAT_URL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=%s";
-
-
- @Override
- public ChatResponseVO chatMessage(ChatRequestDTO dto) {
- JSONObject ernieBot = ChatRequestConvert.INSTANCE.convertErnieBot(dto);
- String requestBody = JSONUtil.toJsonStr(ernieBot);
- log.info("文心一言请求参数 ernieBot request:{}", requestBody);
-
- HttpResponse response = HttpUtil.createPost(String.format(CHAT_URL, getAccessToken(apiKey, appSecret)))
- .body(requestBody)
- .header("Content-Type", "application/json")
- .execute();
-
- if (response == null) {
- throw new RuntimeException("HTTP response is null");
- }
-
- log.info("文心一言返回结果 ernieBot response:{}", response.body());
- if (response.body() == null || response.body().trim().isEmpty()) {
- throw new RuntimeException("HTTP response body is null or empty");
- }
- JSONObject jsonObject = JSON.parseObject(response.body());
- if (!jsonObject.containsKey("result")) {
- throw new RuntimeException(JSONObject.toJSONString(jsonObject));
- }
- ChatResponseVO vo = new ChatResponseVO();
- vo.setResult(jsonObject.getString("result"));
- return vo;
- }
-
- /**
- * 从用户的AK,SK生成鉴权签名(Access Token)
- *
- * @param appId 应用ID
- * @param appSecret 应用密钥
- * @return token
- */
- public String getAccessToken(String appId, String appSecret) {
- String url = String.format(TOKEN_URL_TEMPLATE, apiKey, appSecret);
-
- try (HttpResponse response = HttpRequest.post(url)
- .header("Content-Type", "application/json")
- .header("Accept", "application/json")
- .execute()) {
- JSONObject jsonObject = JSON.parseObject(response.body());
- String accessToken = jsonObject.getString("access_token");
- return accessToken;
- }
- }
-
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import javax.validation.constraints.NotNull;
- import java.io.Serializable;
- import java.util.List;
-
- /**
- * 文心一言 请求 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class ErnieBotDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "聊天上下文信息", notes = "说明:\n" +
- "(1)messages成员不能为空,1个成员表示单轮对话,多个成员表示多轮对话;例如:\n" +
- "· 1个成员示例,\"messages\": [ {\"role\": \"user\",\"content\": \"你好\"}]\n" +
- "· 3个成员示例,\"messages\": [ {\"role\": \"user\",\"content\": \"你好\"},{\"role\":\"assistant\",\"content\":\"需要什么帮助\"},{\"role\":\"user\",\"content\":\"自我介绍下\"}]\n" +
- "(2)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
- "(3)成员数目必须为奇数,成员中message的role值说明如下:奇数位messsage的role值必须为user或function,偶数位message的role值为assistant,第一个message的role不能是function。例如:\n" +
- "示例中message中的role值分别为user、assistant、user、assistant、user;奇数位(红框)message中的role值为user,即第1、3、5个message中的role值为user;偶数位(蓝框)值为assistant,即第2、4个message中的role值为assistant",
- example = "[{\"role\":\"system\",\"content\":\"您好,我是智谱清言,我可以帮您查询天气,您可以输入:查询{{destination}}的天气,查询{{destination}}未来{{num_day}}天的天气\"},{\"role\":\"user\",\"content\":\"查询三亚未来5天的天气\"},{\"role\":\"assistant\",\"content\":\"正在查询三亚未来5天的天气\"},{\"role\":\"assistant\",\"content\":\"三亚未来5天的天气是晴天\"}]")
- @NotNull(message = "聊天上下文信息不能为空")
- private List<MessageDTO> messages;
-
- @ApiModelProperty(value = "模型人设", notes = "主要用于人设设定,例如,你是xxx公司制作的AI助手,最大20000字符", example = "qwen-turbo")
- private String system;
-
- @ApiModelProperty(value = "温度", notes = "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。默认0.8,范围 [0, 1.0],不能为0", example = "0.8")
- private Float temperature;
-
- }
- package com.wsl.model.llm.api.service.impl;
-
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONException;
- import com.alibaba.fastjson.JSONObject;
- import com.wsl.model.llm.api.convert.ChatRequestConvert;
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.dto.SparkDeskDTO;
- import com.wsl.model.llm.api.dto.SparkDeskHeaderDTO;
- import com.wsl.model.llm.api.service.ModelService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import lombok.extern.slf4j.Slf4j;
- import okhttp3.*;
- import org.springframework.stereotype.Service;
-
- import javax.crypto.Mac;
- import javax.crypto.spec.SecretKeySpec;
- import java.net.URL;
- import java.nio.charset.StandardCharsets;
- import java.text.SimpleDateFormat;
- import java.util.*;
- import java.util.concurrent.CompletableFuture;
- import java.util.concurrent.TimeUnit;
-
- /**
- * 讯飞星火 大模型服务
- *
- * @author wsl
- * @link https://www.xfyun.cn/doc/spark/Web.html
- * @date 2024/2/19
- */
- @Service("SparkDeskService")
- @Slf4j
- public class SparkDeskServiceImpl implements ModelService {
-
- private String appId = "?";
-
- private String appSecret = "?";
-
- private String appKey = "?";
-
- public static final String HOST_URL = "https://spark-api.xf-yun.com/v3.5/chat";
-
- @Override
- public ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception {
- ChatResponseVO vo = new ChatResponseVO();
- SparkDeskDTO sparkDeskDTO = ChatRequestConvert.INSTANCE.convertSparkDesk(dto);
- sparkDeskDTO.setHeader(new SparkDeskHeaderDTO(appId));
-
- String authUrl = getAuthUrl(HOST_URL, appKey, appSecret).replace("http://", "ws://").replace("https://", "wss://");
- Request request = new Request.Builder().url(authUrl).build();
- OkHttpClient client = new OkHttpClient.Builder().build();
- StringBuilder sb = new StringBuilder();
- CompletableFuture<String> messageReceived = new CompletableFuture<>();
- String body = JSON.toJSONString(sparkDeskDTO);
- WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
- @Override
- public void onOpen(WebSocket webSocket, Response response) {
- //发送消息
- log.info("讯飞星火请求参数 sparkDesk request:{}", body);
- webSocket.send(body);
- }
-
- @Override
- public void onMessage(WebSocket webSocket, String text) {
- try {
- JSONObject obj = JSON.parseObject(text);
-
- // 使用Optional来避免空指针异常,并在内容不存在时抛出异常
- Optional<String> contentOptional = Optional.ofNullable(obj)
- .map(jsonObject -> jsonObject.getJSONObject("payload"))
- .map(payload -> payload.getJSONObject("choices"))
- .map(choices -> choices.getJSONArray("text"))
- .map(jsonArray -> jsonArray.getJSONObject(0))
- .map(jsonObject -> jsonObject.getString("content"));
- String str = contentOptional.orElseThrow(() -> new RuntimeException(JSONObject.toJSONString(obj)));
-
- sb.append(str);
-
- // 检查header和status字段
- Optional<Long> statusOptional = Optional.ofNullable(obj)
- .map(jsonObject -> jsonObject.getJSONObject("header"))
- .map(header -> header.getLong("status"));
-
- // 如果status为2,则关闭WebSocket并完成CompletableFuture
- if (statusOptional.isPresent() && statusOptional.get() == 2) {
- webSocket.close(1000, "Closing WebSocket connection");
- messageReceived.complete(text);
- }
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- }
- });
- messageReceived.get(60, TimeUnit.SECONDS);
- webSocket.close(1000, "Closing WebSocket connection");
- log.info("讯飞星火返回结果 sparkDesk response:{}", sb);
- vo.setResult(sb.toString());
- return vo;
- }
-
- /**
- * 鉴权方法
- *
- * @param hostUrl 服务地址
- * @param apiKey apiKey
- * @param apiSecret apiSecret
- * @return 返回鉴权url
- * @throws Exception 异常
- */
-
- public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
- URL url = new URL(hostUrl);
- // 时间
- SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
- format.setTimeZone(TimeZone.getTimeZone("GMT"));
- String date = format.format(new Date());
- // 拼接
- String preStr = "host: " + url.getHost() + "\n" +
- "date: " + date + "\n" +
- "GET " + url.getPath() + " HTTP/1.1";
- // SHA256加密
- Mac mac = Mac.getInstance("hmacsha256");
- SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
- mac.init(spec);
-
- byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
- // Base64加密
- String sha = Base64.getEncoder().encodeToString(hexDigits);
- String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
- // 拼接地址
- HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
- addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).
- addQueryParameter("date", date).
- addQueryParameter("host", url.getHost()).
- build();
- return httpUrl.toString();
- }
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 讯飞星火 请求 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class SparkDeskDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "头部", notes = "头部")
- private SparkDeskHeaderDTO header;
-
- @ApiModelProperty(value = "参数", notes = "参数")
- private SparkDeskParameterDTO parameter;
-
- @ApiModelProperty(value = "有效载荷", notes = "有效载荷")
- private SparkDeskPayloadDTO payload;
-
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- import java.io.Serializable;
-
- /**
- * 讯飞星火 Header DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class SparkDeskHeaderDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "应用appid", notes = "从开放平台控制台创建的应用中获取")
- private String app_id;
-
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 讯飞星火 聊天 参数 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class SparkDeskParameterChatDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "指定访问的领域", notes = "generalv3指向V3版本;generalv3.5指向V3.5版本")
- private String domain;
-
- @ApiModelProperty(value = "温度", notes = "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。默认0.8,范围 [0, 2.0],不能为0", example = "0.8")
- private Float temperature;
-
- @ApiModelProperty(value = "最大标记", notes = "模型回答的tokens的最大长度;取值范围[1,8192],默认为2048", example = "2048")
- private Integer max_tokens;
-
- }
- package com.wsl.model.llm.api.dto;
-
- import com.alibaba.fastjson.JSONObject;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 讯飞星火 参数 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class SparkDeskParameterDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "聊天参数", notes = "聊天参数")
- private JSONObject chat;
-
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import java.io.Serializable;
-
- /**
- * 讯飞星火 有效载荷 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class SparkDeskPayloadDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "消息", notes = "消息")
- private SparkDeskPayloadMessageDTO message;
-
- }
- package com.wsl.model.llm.api.dto;
-
- import lombok.Data;
-
- import javax.validation.constraints.NotNull;
- import java.io.Serializable;
- import java.util.List;
-
- /**
- * 讯飞星火 有效载荷 消息 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class SparkDeskPayloadMessageDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @NotNull(message = "聊天上下文信息不能为空")
- private List<MessageDTO> text;
-
- }
- package com.wsl.model.llm.api.service.impl;
-
- import cn.hutool.http.HttpRequest;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONObject;
- import com.wsl.model.llm.api.convert.ChatRequestConvert;
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.dto.QianWenDTO;
- import com.wsl.model.llm.api.service.ModelService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
-
- import java.util.Optional;
-
- /**
- * 通义千问 大模型服务
- *
- * @author wsl
- * @link https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.6922140bTYj6qJ#602895ef3dtl1
- * @date 2024/2/19
- */
- @Slf4j
- @Service("QianWenService")
- public class QianWenServiceImpl implements ModelService {
-
- private String apiKey = "?";
-
- @Override
- public ChatResponseVO chatMessage(ChatRequestDTO dto) {
- QianWenDTO qianwen = ChatRequestConvert.INSTANCE.convertQianwen(dto);
- String url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
- String json = JSON.toJSONString(qianwen);
- log.info("通义千问请求参数 qianwen request:{}", json);
-
- String result = HttpRequest.post(url)
- .header("Authorization", "Bearer " + apiKey)
- .header("Content-Type", "application/json")
- .body(json)
- .execute().body();
-
- log.info("通义千问返回结果 qianwen response:{}", result);
- ChatResponseVO vo = new ChatResponseVO();
- JSONObject jsonObject = JSON.parseObject(result);
- Optional<String> textOptional = Optional.ofNullable(jsonObject.getJSONObject("output"))
- .map(output -> output.getString("text"));
-
- if (!textOptional.isPresent()) {
- throw new RuntimeException(JSONObject.toJSONString(jsonObject));
- }
-
- vo.setResult(textOptional.get());
- return vo;
-
- }
-
- }
- package com.wsl.model.llm.api.dto;
-
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
-
- import javax.validation.constraints.NotNull;
- import java.io.Serializable;
- import java.util.List;
-
- /**
- * 通义千问 输入 DTO
- *
- * @author wsl
- * @date 2024/2/20
- */
- @Data
- public class QianWenInputDTO implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- @ApiModelProperty(value = "聊天上下文信息", notes = "说明:\n" +
- "(1)messages成员不能为空,1个成员表示单轮对话,多个成员表示多轮对话;例如:\n" +
- "· 1个成员示例,\"messages\": [ {\"role\": \"user\",\"content\": \"你好\"}]\n" +
- "· 3个成员示例,\"messages\": [ {\"role\": \"user\",\"content\": \"你好\"},{\"role\":\"assistant\",\"content\":\"需要什么帮助\"},{\"role\":\"user\",\"content\":\"自我介绍下\"}]\n" +
- "(2)最后一个message为当前请求的信息,前面的message为历史对话信息\n" +
- "(3)成员数目必须为奇数,成员中message的role值说明如下:奇数位messsage的role值必须为user或function,偶数位message的role值为assistant,第一个message的role不能是function。例如:\n" +
- "示例中message中的role值分别为user、assistant、user、assistant、user;奇数位(红框)message中的role值为user,即第1、3、5个message中的role值为user;偶数位(蓝框)值为assistant,即第2、4个message中的role值为assistant",
- example = "[{\"role\":\"system\",\"content\":\"您好,我是智谱清言,我可以帮您查询天气,您可以输入:查询{{destination}}的天气,查询{{destination}}未来{{num_day}}天的天气\"},{\"role\":\"user\",\"content\":\"查询三亚未来5天的天气\"},{\"role\":\"assistant\",\"content\":\"正在查询三亚未来5天的天气\"},{\"role\":\"assistant\",\"content\":\"三亚未来5天的天气是晴天\"}]")
- @NotNull(message = "聊天上下文信息不能为空")
- private List<MessageDTO> messages;
-
- }
- package com.wsl.model.llm.api.service.impl;
-
- import cn.hutool.http.HttpResponse;
- import cn.hutool.http.HttpUtil;
- import cn.hutool.json.JSONUtil;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.JSONArray;
- import com.alibaba.fastjson.JSONObject;
- import com.auth0.jwt.JWT;
- import com.auth0.jwt.algorithms.Algorithm;
- import com.wsl.model.llm.api.convert.ChatRequestConvert;
- import com.wsl.model.llm.api.dto.ChatRequestDTO;
- import com.wsl.model.llm.api.service.ModelService;
- import com.wsl.model.llm.api.vo.ChatResponseVO;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
-
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Optional;
-
- /**
- * 智谱清言 大模型服务
- *
- * @author wsl
- * @link https://open.bigmodel.cn/dev/api#glm-4
- * @date 2024/2/19
- */
- @Slf4j
- @Service("ChatGlmService")
- public class ChatGlmServiceImpl implements ModelService {
-
- private String apiKey = "?";
-
- @Override
- public ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception {
- JSONObject chatGlm = ChatRequestConvert.INSTANCE.convertChatGlm(dto);
- String url = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
- String requestBody = JSONUtil.toJsonStr(chatGlm);
- log.info("智谱清言请求参数 chatGlm request:{}", requestBody);
-
- HttpResponse response = HttpUtil.createPost(url).body(requestBody).header("Content-Type", "application/json").header("Authorization", "Bearer " + generateToken(apiKey, 3600)).execute();
-
- log.info("智谱清言返回结果 chatGlm response:{}", Optional.ofNullable(response).map(HttpResponse::body).orElse(""));
-
- ChatResponseVO vo = new ChatResponseVO();
- Optional<JSONObject> jsonObject = Optional.ofNullable(JSON.parseObject(response.body()));
- jsonObject.ifPresent(json -> {
- Optional<JSONArray> choices = Optional.ofNullable(json.getJSONArray("choices"));
- choices.ifPresent(choiceArray -> {
- if (!choiceArray.isEmpty()) {
- Optional<JSONObject> firstChoiceMessage = Optional.ofNullable(choiceArray.getJSONObject(0).getJSONObject("message"));
- firstChoiceMessage.ifPresent(message -> {
- String content = message.getString("content");
- if (content != null) {
- vo.setResult(content);
- } else {
- throw new RuntimeException(response.body());
- }
- });
- }
- });
- throw new RuntimeException(response.body());
- });
- return vo;
- }
-
- /**
- * 生成token
- *
- * @param apikey apikey
- * @param expSeconds 过期时间
- * @return token
- * @throws Exception 异常
- */
- public static String generateToken(String apikey, int expSeconds) throws Exception {
- String[] parts = apikey.split("\\.");
- if (parts.length != 2) {
- throw new Exception("Invalid apikey");
- }
-
- String id = parts[0];
- String secret = parts[1];
-
- Map<String, Object> payload = new HashMap<>(16);
- payload.put("api_key", id);
- payload.put("exp", new Date(System.currentTimeMillis() + expSeconds * 1000));
- payload.put("timestamp", new Date(System.currentTimeMillis()));
-
- Algorithm algorithm = Algorithm.HMAC256(secret);
- return JWT.create().withHeader(new HashMap<String, Object>(16) {{
- put("alg", "HS256");
- put("sign_type", "SIGN");
- }}).withPayload(payload).sign(algorithm);
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。