diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..c32584c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/GTPApplication.java b/holiday03-GTP/src/main/java/com/sikadi/user/GTPApplication.java index 9a98217..8d188b6 100644 --- a/holiday03-GTP/src/main/java/com/sikadi/user/GTPApplication.java +++ b/holiday03-GTP/src/main/java/com/sikadi/user/GTPApplication.java @@ -1,29 +1,13 @@ package com.sikadi.user; -import com.bwie.common.domain.XfXhConfig; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import top.hualuo.XhStreamClient; @SpringBootApplication public class GTPApplication { - @Autowired - private XfXhConfig xfXhConfig; public static void main(String[] args) { SpringApplication.run(GTPApplication.class, args); } - @Bean - public XhStreamClient xhStreamClient (){ - return XhStreamClient.builder() - .apiHost(xfXhConfig.getApiHost()) - .apiPath(xfXhConfig.getApiPath()) - .appId(xfXhConfig.getAppId()) - .apiKey(xfXhConfig.getApiKey()) - .apiSecret(xfXhConfig.getApiSecret()) - .build(); - } } diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/controller/GTPController.java b/holiday03-GTP/src/main/java/com/sikadi/user/controller/GTPController.java index 1347de8..2bc0e57 100644 --- a/holiday03-GTP/src/main/java/com/sikadi/user/controller/GTPController.java +++ b/holiday03-GTP/src/main/java/com/sikadi/user/controller/GTPController.java @@ -1,12 +1,15 @@ package com.sikadi.user.controller; -import com.bwie.common.domain.StringAppGTP; +import com.sikadi.user.listener.XfXhStreamClient; +import com.sikadi.user.mapper.XfXhConfig; import com.sikadi.user.service.GTPService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import top.hualuo.XhStreamClient; + +import javax.annotation.Resource; @RestController public class GTPController { @@ -16,10 +19,19 @@ public class GTPController { - @PostMapping("/chat") - public String chat(@RequestBody StringAppGTP stringAppGTP){ - return gtpService.chat(stringAppGTP); - } + /** + * 发送问题 + * + * @param question 问题 + * @return 星火大模型的回答 + */ + @GetMapping("/sendQuestion") + public String sendQuestion(@RequestParam("question") String question) { + return gtpService.sendQuestion(question); + } + + + } diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/dto/MsgDTO.java b/holiday03-GTP/src/main/java/com/sikadi/user/dto/MsgDTO.java new file mode 100644 index 0000000..3a0e6cb --- /dev/null +++ b/holiday03-GTP/src/main/java/com/sikadi/user/dto/MsgDTO.java @@ -0,0 +1,50 @@ +package com.sikadi.user.dto; + + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 消息对象 + * + * @author 狐狸半面添 + * @create 2023-09-15 0:42 + */ +/** + * 消息对象 + * + * @author 狐狸半面添 + * @create 2023-09-15 0:42 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MsgDTO { + /** + * 角色 + */ + private String role; + /** + * 消息内容 + */ + private String content; + /** + * 响应结果字段:结果序号,取值为[0,10]; 当前为保留字段,开发者可忽略 + */ + private Integer index; + + public static final String ROLE_USER = "user"; + public static final String ROLE_ASSISTANT = "assistant"; + + public static MsgDTO createUserMsg(String content) { + return new MsgDTO(ROLE_USER, content, null); + } + + public static MsgDTO createAssistantMsg(String content) { + return new MsgDTO(ROLE_ASSISTANT, content, null); + } + +} diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/dto/request/RequestDTO.java b/holiday03-GTP/src/main/java/com/sikadi/user/dto/request/RequestDTO.java new file mode 100644 index 0000000..7bad235 --- /dev/null +++ b/holiday03-GTP/src/main/java/com/sikadi/user/dto/request/RequestDTO.java @@ -0,0 +1,97 @@ +package com.sikadi.user.dto.request; + + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.sikadi.user.dto.MsgDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @description: + * @author: 不愧是我 + * @date: 2023/9/28 10:12 + * @param: + * @return: + **/ +/** + * 请求参数 + * 对应生成的 JSON 结构参考 resources/demo-json/request.json + * + * @author 狐狸半面添 + * @create 2023-09-15 0:42 + */ +@NoArgsConstructor +@Data +public class RequestDTO { + + @JsonProperty("header") + private HeaderDTO header; + @JsonProperty("parameter") + private ParameterDTO parameter; + @JsonProperty("payload") + private PayloadDTO payload; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class HeaderDTO { + /** + * 应用appid,从开放平台控制台创建的应用中获取 + */ + @JSONField(name = "app_id") + private String appId; + /** + * 每个用户的id,用于区分不同用户,最大长度32 + */ + @JSONField(name = "uid") + private String uid; + } + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class ParameterDTO { + private ChatDTO chat; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class ChatDTO { + /** + * 指定访问的领域,general指向V1.5版本 generalv2指向V2版本。注意:不同的取值对应的url也不一样! + */ + @JsonProperty("domain") + private String domain; + /** + * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高 + */ + @JsonProperty("temperature") + private Float temperature; + /** + * 模型回答的tokens的最大长度 + */ + @JSONField(name = "max_tokens") + private Integer maxTokens; + } + } + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class PayloadDTO { + @JsonProperty("message") + private MessageDTO message; + + @NoArgsConstructor + @Data + @AllArgsConstructor + public static class MessageDTO { + @JsonProperty("text") + private List text; + } + } +} diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/mapper/XfXhConfig.java b/holiday03-GTP/src/main/java/com/sikadi/user/mapper/XfXhConfig.java new file mode 100644 index 0000000..eac5376 --- /dev/null +++ b/holiday03-GTP/src/main/java/com/sikadi/user/mapper/XfXhConfig.java @@ -0,0 +1,48 @@ +package com.sikadi.user.mapper; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author 狐狸半面添 + * @create 2023-09-15 0:46 + */ +@Configuration +@ConfigurationProperties(prefix = "xfxh") +@Data +public class XfXhConfig { + /** + * 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat + */ + private String hostUrl; + /** + * 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2 + */ + private String domain; + /** + * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1] + */ + private Float temperature; + /** + * 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。 + */ + private Integer maxTokens; + /** + * 大模型回复问题的最大响应时长,单位 s + */ + private Integer maxResponseTime; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String appId; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String apiKey; + /** + * 用于权限验证,从服务接口认证信息中获取 + */ + private String apiSecret; + +} diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/service/GTPService.java b/holiday03-GTP/src/main/java/com/sikadi/user/service/GTPService.java index bfb4829..6ce9f56 100644 --- a/holiday03-GTP/src/main/java/com/sikadi/user/service/GTPService.java +++ b/holiday03-GTP/src/main/java/com/sikadi/user/service/GTPService.java @@ -1,7 +1,6 @@ package com.sikadi.user.service; -import com.bwie.common.domain.StringAppGTP; - public interface GTPService { - String chat(StringAppGTP stringAppGTP); + + String sendQuestion(String question); } diff --git a/holiday03-GTP/src/main/java/com/sikadi/user/service/impl/GTPServiceImpl.java b/holiday03-GTP/src/main/java/com/sikadi/user/service/impl/GTPServiceImpl.java index 20555e4..fec9a96 100644 --- a/holiday03-GTP/src/main/java/com/sikadi/user/service/impl/GTPServiceImpl.java +++ b/holiday03-GTP/src/main/java/com/sikadi/user/service/impl/GTPServiceImpl.java @@ -1,27 +1,74 @@ package com.sikadi.user.service.impl; -import com.bwie.common.domain.StringAppGTP; -import com.bwie.common.utils.XfXhListener; +import cn.hutool.core.util.StrUtil; +import com.sikadi.user.dto.MsgDTO; +import com.sikadi.user.listener.XfXhStreamClient; +import com.sikadi.user.listener.XfXhWebSocketListener; +import com.sikadi.user.mapper.XfXhConfig; import com.sikadi.user.service.GTPService; +import lombok.extern.log4j.Log4j2; +import okhttp3.WebSocket; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import top.hualuo.XhStreamClient; -import top.hualuo.dto.MsgDTO; -import java.util.Arrays; +import javax.annotation.Resource; +import java.util.Collections; +import java.util.UUID; + + @Service +@Log4j2 public class GTPServiceImpl implements GTPService { - @Autowired - private XhStreamClient xhStreamClient; + @Resource + private XfXhStreamClient xfXhStreamClient; + + @Resource + private XfXhConfig xfXhConfig; @Override - public String chat(StringAppGTP stringAppGTP) { + public String sendQuestion(String question) { + // 如果是无效字符串,则不对大模型进行请求 + if (StrUtil.isBlank(question)) { + return "无效问题,请重新输入"; + } - MsgDTO dto = MsgDTO.builder().role(MsgDTO.Role.USER.getName()).content(stringAppGTP.getProblem()).build(); - - - return xhStreamClient.sendMsg("123", Arrays.asList(dto),new XfXhListener());; + // 创建消息对象 + MsgDTO msgDTO = MsgDTO.createUserMsg(question); + // 创建监听器 + XfXhWebSocketListener listener = new XfXhWebSocketListener(); + // 发送问题给大模型,生成 websocket 连接 + WebSocket webSocket = xfXhStreamClient.sendMsg(UUID.randomUUID().toString().substring(0, 10), Collections.singletonList(msgDTO), listener); + if (webSocket == null) { + // 归还令牌 + xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS); + return "系统内部错误,请联系管理员"; + } + try { + int count = 0; + // 为了避免死循环,设置循环次数来定义超时时长 + int maxCount = xfXhConfig.getMaxResponseTime() * 5; + while (count <= maxCount) { + Thread.sleep(200); + if (listener.isWsCloseFlag()) { + break; + } + count++; + } + if (count > maxCount) { + return "大模型响应超时,请联系管理员"; + } + // 响应大模型的答案 + return listener.getAnswer().toString(); + } catch (InterruptedException e) { + log.error("错误:" + e.getMessage()); + return "系统内部错误,请联系管理员"; + } finally { + // 关闭 websocket 连接 + webSocket.close(1000, ""); + // 归还令牌 + xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS); + } } } diff --git a/holiday03-GTP/src/main/resources/bootstrap.yml b/holiday03-GTP/src/main/resources/bootstrap.yml index ecd2ddc..d1cf37d 100644 --- a/holiday03-GTP/src/main/resources/bootstrap.yml +++ b/holiday03-GTP/src/main/resources/bootstrap.yml @@ -29,8 +29,21 @@ spring: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} namespace: Sikadi xfxh: - apiHost: spark-api.xf-yun.com - apiPath: /v1.1/chat + # 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat + hostUrl: https://spark-api.xf-yun.com/v2.1/chat + # 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2 + domain: generalv2 + # 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1] + temperature: 0.5 + # 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。 + maxTokens: 2048 + # 大模型回复问题的最大响应时长,单位 s + maxResponseTime: 30 + # 允许同时连接大模型的 websocket 数,如果是普通(免费)用户为 2,超过这个数连接响应会报错,具体参考官网。 + QPS: 2 + # 用于权限验证,从服务接口认证信息中获取 appId: 9f22c545 - apiKey: N2NjMDU3ZTQwYjBkODI5ZGM3ZTVjYWY3 - apiSecret: a8e17cdcbc9addd7f21e81dff71bc177 + # 用于权限验证,从服务接口认证信息中获取 + apiKey: a8e17cdcbc9addd7f21e81dff71bc177 + # 用于权限验证,从服务接口认证信息中获取 + apiSecret: N2NjMDU3ZTQwYjBkODI5ZGM3ZTVjYWY3 diff --git a/holiday03-common/pom.xml b/holiday03-common/pom.xml index 02e0aec..272e4ac 100644 --- a/holiday03-common/pom.xml +++ b/holiday03-common/pom.xml @@ -125,9 +125,37 @@ - io.github.a812086325 - xfxh-java - 1.0 + org.springframework.boot + spring-boot-configuration-processor + true + + + cn.hutool + hutool-all + 5.8.18 + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba + fastjson + 1.2.67 + + + org.java-websocket + Java-WebSocket + 1.3.8 + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + org.projectlombok + lombok diff --git a/holiday03-common/src/main/java/com/bwie/common/domain/XfXhConfig.java b/holiday03-common/src/main/java/com/bwie/common/domain/XfXhConfig.java deleted file mode 100644 index b0e5917..0000000 --- a/holiday03-common/src/main/java/com/bwie/common/domain/XfXhConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.bwie.common.domain; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConfigurationProperties(prefix = "xfxh") -@Data -public class XfXhConfig { - - private StringAppGTP apiHost; - - private StringAppGTP apiPath; - - private StringAppGTP appId; - - private StringAppGTP apiKey; - - private StringAppGTP apiSecret; - -} - - diff --git a/holiday03-common/src/main/java/com/bwie/common/utils/XfXhListener.java b/holiday03-common/src/main/java/com/bwie/common/utils/XfXhListener.java deleted file mode 100644 index 11b7eb9..0000000 --- a/holiday03-common/src/main/java/com/bwie/common/utils/XfXhListener.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.bwie.common.utils; - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.nacos.shaded.org.checkerframework.checker.nullness.qual.Nullable; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import top.hualuo.dto.MsgDTO; -import top.hualuo.dto.ResponseDTO; - -import javax.validation.constraints.NotNull; -import java.util.List; - -public class XfXhListener extends WebSocketListener { - @Override - public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { - super.onOpen(webSocket, response); - - } - - @Override - public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { - super.onMessage(webSocket, text); - System.out.println("text:\n" + text); - ResponseDTO responseData = JSONObject.parseObject(text, ResponseDTO.class); - if(0 == responseData.getHeader().getCode()){ - ResponseDTO.PayloadDTO pl = responseData.getPayload(); - List tests = pl.getChoices().getText(); - MsgDTO textDTO = tests.stream().findFirst().orElse(new MsgDTO()); - - System.out.println(textDTO.toString()); - - if(2 == responseData.getHeader().getStatus()){ - ResponseDTO.PayloadDTO.UsageDTO.TextDTO testDto = pl.getUsage().getText(); - Integer totalTokens = testDto.getTotalTokens(); - System.out.println("本次花费:"+totalTokens + " tokens"); - - - webSocket.close(3,"客户端主动断开链接"); - } - - - }else{ - System.out.println("返回结果错误:\n" + responseData.getHeader().getCode()+ responseData.getHeader().getMessage() ); - } - } - - @Override - public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { - super.onFailure(webSocket, t, response); - } - - @Override - public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { - super.onClosed(webSocket, code, reason); - } -}