目录

引言

代码完整地址

入参

 出参

Controller

Service

Service实现类

 模型Service

 入参转换类

文心一言实现类

讯飞星火实现类

 通义千问实现类

智谱清言实现类


引言

本文将介绍如何使用Java语言,结合Spring Boot框架,集成国内热门大模型API,包括文心一言、讯飞星火、通义千问、智谱清言。

在开始前,请确保您已经按照各模型官网的指引,完成了相应的资源申请和配置。这些资源是调用大模型API的必要凭证,务必妥善保管。接下来,我们将通过具体的代码示例和步骤说明,引导您完成Spring Boot项目和大模型API集成。

代码完整地址

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

入参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;

@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;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.wsl.model.llm.api.dto;

import io.swagger.annotations.ApiModelProperty;

import lombok.AllArgsConstructor;

import lombok.Data;

import java.io.Serializable;

@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;

}

 出参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.wsl.model.llm.api.vo;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

@Data

public class ChatResponseVO implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "结果", notes = "结果")

private String result;

}

Controller

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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.*;

@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);

}

}

}

Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.wsl.model.llm.api.service;

import com.wsl.model.llm.api.dto.ChatRequestDTO;

import com.wsl.model.llm.api.vo.ChatResponseVO;

public interface IAiAppService {

ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception;

}

Service实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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;

@Service("aiAppService")

public class AiAppServiceImpl implements IAiAppService {

@Override

public ChatResponseVO chatMessage(String modelType, ChatRequestDTO dto) throws Exception {

this.checkMessages(dto.getMessages());

ModelService modelService = getModelService(modelType);

return modelService.chatMessage(dto);

}

private void checkMessages(List<MessageDTO> messages) {

if (CollUtil.isNotEmpty(messages)) {

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");

}

}

}

}

}

private ModelService getModelService(String modelType) {

try {

ModelTypeEnum modelTypeEnum = ModelTypeEnum.valueOf(modelType);

String beanName = modelTypeEnum.name();

beanName = StrUtil.toCamelCase(beanName) + "Service";

return SpringUtil.getBean(beanName);

} catch (IllegalArgumentException e) {

throw new RuntimeException("模型类型错误");

}

}

}

 模型Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.wsl.model.llm.api.service;

import com.wsl.model.llm.api.dto.ChatRequestDTO;

import com.wsl.model.llm.api.vo.ChatResponseVO;

public interface ModelService {

ChatResponseVO chatMessage(ChatRequestDTO dto) throws Exception;

}

 入参转换类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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;

@Mapper

public interface ChatRequestConvert {

ChatRequestConvert INSTANCE = Mappers.getMapper(ChatRequestConvert.class);

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;

}

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;

}

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;

}

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;

}

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;

}

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;

}

}

文心一言实现类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
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;

@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;

}

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;

}

}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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;

@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;

}

讯飞星火实现类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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;

@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<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);

Optional<Long> statusOptional = Optional.ofNullable(obj)

.map(jsonObject -> jsonObject.getJSONObject("header"))

.map(header -> header.getLong("status"));

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;

}

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";

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));

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();

}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.wsl.model.llm.api.dto;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

@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;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;

@Data

@NoArgsConstructor

@AllArgsConstructor

public class SparkDeskHeaderDTO implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "应用appid", notes = "从开放平台控制台创建的应用中获取")

private String app_id;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.wsl.model.llm.api.dto;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

@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;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.wsl.model.llm.api.dto;

import com.alibaba.fastjson.JSONObject;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

@Data

public class SparkDeskParameterDTO implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "聊天参数", notes = "聊天参数")

private JSONObject chat;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.wsl.model.llm.api.dto;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import java.io.Serializable;

@Data

public class SparkDeskPayloadDTO implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "消息", notes = "消息")

private SparkDeskPayloadMessageDTO message;

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.wsl.model.llm.api.dto;

import lombok.Data;

import javax.validation.constraints.NotNull;

import java.io.Serializable;

import java.util.List;

@Data

public class SparkDeskPayloadMessageDTO implements Serializable {

private static final long serialVersionUID = 1L;

@NotNull(message = "聊天上下文信息不能为空")

private List<MessageDTO> text;

}

 通义千问实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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;

@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;

}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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;

@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;

}

智谱清言实现类

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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;

@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;

}

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);

}

}