LLM(大语言模型)——Springboot集成文心一言、讯飞星火、通义千问、智谱清言
目录
引言
代码完整地址
入参
出参
Controller
Service
Service实现类
模型Service
入参转换类
文心一言实现类
讯飞星火实现类
通义千问实现类
智谱清言实现类
引言
本文将介绍如何使用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;}
Controller
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 {@Autowiredprivate 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);}}}
Service
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;}
Service实现类
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 {@Overridepublic 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必须为assistantif (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("模型类型错误");}}}
模型Service
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";@Overridepublic 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";@Overridepublic 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() {@Overridepublic void onOpen(WebSocket webSocket, Response response) {//发送消息log.info("讯飞星火请求参数 sparkDesk request:{}", body);webSocket.send(body);}@Overridepublic 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并完成CompletableFutureif (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 = "?";@Overridepublic 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 = "?";@Overridepublic 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);}}
相关文章:

LLM(大语言模型)——Springboot集成文心一言、讯飞星火、通义千问、智谱清言
目录 引言 代码完整地址 入参 出参 Controller Service Service实现类 模型Service 入参转换类 文心一言实现类 讯飞星火实现类 通义千问实现类 智谱清言实现类 引言 本文将介绍如何使用Java语言,结合Spring Boot框架,集成国内热门大模型API&am…...

什么是堆?什么是栈?
在计算机科学中,"堆(heap)"和"栈(stack)"是两种用于存储数据的数据结构,它们在内存管理中扮演着不同的角色。 堆(Heap): 动态分配内存:…...

【镜像转存】利用交互式学习平台killercoda转存K8S镜像至Docker私人仓库
文章目录 1. 镜像转存需求2. 注册并登陆 killercoda URL3. 打开playground4. 在线拉取K8S镜像并打上标签5. 推送K8S镜像到Docker私有仓库6. 登陆Docker私有仓库查看 1. 镜像转存需求 因K8S镜像在不开代理的情况下,拉取超时、下载缓慢,导致镜像拉取不下来…...

ov多域名SSL数字证书1200元一年送一月
随着互联网的发展,不论是个人用户还是企事业单位都不止有一个网站,为了保护网站安全,就需要为网站安装SSL证书,而SSL证书中的通配符SSL证书和多域名SSL证书都可以同时保护多个域名站点。其中,多域名SSL证书可以同时保护…...

MySQL 系统变量查看与设置(System Variables Configuration)
MySQL中有大量的系统变量控制服务器的行为,大部分的系统变量是不需要我们调整的,保持默认即可。但为了获得更高的性能和稳定性,有时需要适当对部分变量进行调整,本文总结了MySQL中系统变量的查看与设置方法。 目录 一、变量的类型…...

【Docker】apache 容器化部署
Apache环境标准软件基于Bitnami apache 构建。当前版本为2.4.58 你可以通过轻云UC部署工具直接安装部署,也可以手动按如下文档操作,该项目已经全面开源,可以从如下环境获取 配置文件地址: https://gitee.com/qingplus/qingcloud-platform Q…...

基于element-plus +腾讯云COS实现图片上传
① 首先基于element-plus里面的:http-request"upload"自定义封装事件写一个点击事件之后基于腾讯云的 登录 对象存储控制台 ,创建存储桶。获取存储桶名称和 地域名称登录 访问管理控制台 ,获取您的项目 SecretId 和 SecretKey。在项目中安装 …...

Kafka模拟器产生数据仿真-集成StructuredStreaming做到”毫秒“级实时响应StreamData落地到mysql
这是仿真过程某图: 仿真实战kafka kafka消费sink端和StructuredStreaming集成通信成功 , 数据接收全部接收 数据落地情况: 全部接收到并all存入mysql 下面就简单分享一下StructuredStreaming代码吧 import org.apache.spark.sql.function…...

IDEA如何删除git最新一次远程提交
IDEA如何删除git最新一次远程提交 选择应用 -> Git -> Show History 选择最新提交上一次提交 -> Reset Current Branch to Here… Reset 提示框选择 Hard push到远程分支 -> 选择Force Push 结果验证 (最新分支已被删除)...

什么是单向数据流
单向数据流是一种数据流动的模式,通常用于前端框架(如 React、Vue 等)中。在单向数据流中,数据只能从一个方向流向另一个方向,不会出现数据的双向流动。这种模式有助于简化数据的管理和状态的维护,提高代码…...

Qt 线程池 QThreadPool
一.Qt 线程池 QThreadPool介绍 Qt线程池是一种管理多个线程的并发编程模型,通过使用线程池可以提高性能、控制并发度、提供任务队列和简化线程管理。 在Qt中,线程池的使用主要涉及以下几个步骤: 创建任务类:需要定义一个任务类&am…...

【兔子机器人】实现从初始状态到站立
一、遥想星空up主的方法 由于我有卡位结构,无法做到劈腿,而且底盘也不一样,无法使用此方法 但是其代码思想是可以借鉴的。 参考视频: 【【开源啦!】无刷轮腿平衡机器人】 【精准空降到 01:16】 https://www.bilibili…...

ImportError: cannot import name ‘open_filename‘ from ‘pdfminer.utils‘已搞定
报错内容 ImportError: cannot import name ‘open_filename’ from ‘pdfminer.utils’ 第一步:pip uninstall pdfminer 解决办法 pip3 install pdfminer.six注意不要 pip install pdfminer.six是安装不了的...

一文解决Word中公式插入问题(全免费/latex公式输入/texsWord)
分文不花,搞定你的word公式输入/texsWord完全使用指南 背景 碎碎念:折折腾腾至少装了几个小时,遇到了若干大坑。遇到的问题网上都搜索不到答案!!!就让我来当指路的小火柴吧。 本篇适用于在word中输入la…...

C语言实战——扫雷游戏
目录 1. 扫雷游戏分析和设计2.扫雷游戏的代码实现 1. 扫雷游戏分析和设计 1.1扫雷游戏的功能说明 使用控制台实现经典的扫雷游戏游戏可以通过菜单实现继续玩或者退出游戏扫雷的棋盘是9*9的格子默认随机布置10个雷可以排查雷 如果位置不是雷,就显示周围有几个雷 如果…...

.Net使用ElasticSearch
文章目录 前言主体内容一.Kibana中ElasticSearch的基础操作1.GET(查询)1.POST(新增)1.PUT(修改)1.DELET(删除) 二.在.Net中,对ElasticSearch进行基础操作1.DotNet连接Ela…...

HTML5、CSS3面试题(二)
上一章:HTML5、CSS3面试题(一) 哪些是块级元素那些是行内元素,各有什么特点 ?(必会) 行内元素: a、span、b、img、strong、input、select、lable、em、button、textarea 、selecting 块级元素࿱…...

sqllab第十一关通关笔记
知识点: 发现登录框就可以尝试注入登录框一般都是字符型注入通过注入可以获取其他表的信息绕过手段 单引号闭合联合注入也可以进行错误注入 首先看界面是一个登录框;通过admin admin登录进去,发现页面会把用户名和密码的登录信息打印出来&am…...

机械女生,双非本985硕,目前学了C 基础知识,转嵌入式还是java更好?
作为单片机项目开发的卖课佬,个人建议,先转嵌入式单片机开发方向,哈哈。 java我也学过,还学过oracle、mysql数据库,只是当时没做笔记,找不好充分的装逼证据了。 从实习通过业余时间,学到快正式毕…...

Python之字符串操作大全(29种方法)
本章详细介绍了常用的29种字符串操作方法及代码示例。 1. 重复输出字符串 print(x * 20) 输出:xxxxxxxxxxxxxxxxxxxx 2. 通过索引获取字符串 print(hello world[2:5]) 输出:llo 3. in 判断字符是否在字符串内 print(e in hello world) 输出&…...

ArcGIS学习(十五)用地适宜性评价
ArcGIS学习(十五)用地适宜性评价 本任务给大家带来的内容是用地适宜性评价。 用地适宜性评价是大家在平时工作中最常接触到的分析场景之一。尤其是在国土空间规划的大背景下,用地适宜性评价变得越来越重要。 此外,我们之前的任务主要是使用矢量数据进行分析。本案例是主讲…...

【matlab】如何将.mat文件与.nii文件互转
【matlab】如何将.mat文件与.nii文件互转 .mat转为.nii文件 有时候代码需要读取的是.nii文件,但是如何现有的数据是.mat格式,需要将.mata转化为.nii文件 1、先加载.mat文件 % 加载.mat文件 load(your_mat_file.mat); % 请将your_mat_file.mat替换为实…...

Uni-app开发Canvas当子组件示例,点点绘制图形
前言 使用Uni-app 实现封装一个Canvas渲染的子组件,实现通过传入两点绘制一条完整的路程 具体逻辑看我发的后端和数据库设计 C# 根据两点名称,寻找两短路程的最优解,【有数据库设计,完整代码】 即使不了解具体逻辑,该…...

从金蝶云星空到钉钉通过接口配置打通数据
从金蝶云星空到钉钉通过接口配置打通数据 对接系统金蝶云星空 金蝶K/3Cloud(金蝶云星空)是移动互联网时代的新型ERP,是基于WEB2.0与云技术的新时代企业管理服务平台。金蝶K/3Cloud围绕着“生态、人人、体验”,旨在帮助企业打造面…...

Unreal发布Android在刘海屏手机上不能全屏显示问题
Unreal 4.27发布Android在刘海屏手机上不能全屏显示问题 Android设置全屏刘海屏全屏设置4.27设置刘海屏在部分手机不能显示问题 Android设置全屏 AndroidManifest.xml文件配置 ...<activity android:name"com.epicgames.ue4.GameActivity" android:label"st…...

hive库表占用空间大小的命令
1、查每个hive表占用的空间大小 hdfs dfs -du -h /user/hive/warehouse 2、按占用空间大小降序排列 hdfs dfs -du /user/hive/warehouse/ipms.db | sort -nr 3、查某一个分区占用空间大小(单位G) hadoop fs -ls /user/hive/warehouse/ipms.db/dw_ft_se_nt_u_gen…...

关于go中的select
笔记仓库:gitee.com/xiaoyinhui 代码中的解释纯个人理解,有不对的望指出 package testsimport ("fmt""testing" )var uCnt int 0func TestSelece(t *testing.T) {// 对于 select 语句,在进入该语句时,会按源…...

【Node.js从基础到高级运用】十一、构建RESTful API
在本篇博客中,我们将综合之前讨论的内容,深入探索如何使用Node.js构建一个RESTful API。我们将重点讨论设计合理的API端点,展示如何通过代码实现这些端点,并指导如何使用Postman测试我们的API,确保其按预期工作。 前提…...

Python和MATLAB数字信号波形和模型模拟
要点 Python和MATLAB实现以下波形和模型模拟 以给定采样率模拟正弦信号,生成给定参数的方波信号,生成给定参数隔离矩形脉冲,生成并绘制线性调频信号。快速傅里叶变换结果释义:复数离散傅里叶变换、频率仓和快速傅里叶变换移位&am…...
华为OD技术C卷“测试用例执行计划”Java解答
描述 示例 算法思路1 整体思路是,先读取特性的优先级和测试用例覆盖的特性列表,然后计算每个测试用例的优先级,并将其与测试用例的索引存储到二维数组中。最后按照优先级和索引排序,输出测试用例的索引,即为执行顺序。…...