本文基于上一篇文章GPT+向量数据库+Function calling=垂直领域小助手进行了改进,对其中的循环请求GPT、FuctionCalling循环请求、MSGList缩容等进行了修改和优化,使的相关请求更加抽象和方便后续做延伸。文章中涉及业务属性的内容已经进行了过滤,请忽略。代码中部分类工具类可能没有展示,但是注释层做了解释,请使用其他方案进行替换。
请求参数
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;/*** @author * @date 2024/3/14**/
@Getter
@Setter
@Accessors(chain = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MsgDto {/*** databaseName 数据库名称*/private String databaseName = "";/*** collectionName 集合名称*/private String collectionName = "";/*** appCode*/private String appCode = "";/*** 子模型*/private String aiModel = "gpt-3.5-turbo";/*** 会话内容-把会话内容信息存到一起,可为空* 如果想持续获取内容,建议在初次生成后获取并存入*/private String sessionId;/*** 消息体内容*/private String prompt;/*** token数,可为空,默认300*/private Integer maxTokens;/*** 调节信息 ,可为空,默认0.0*/private Double temperature;/*** 输出Json ,默认True*/private Boolean outputJsonFlag = Boolean.TRUE;/*** 排名分数阈值(建议0.75)*/private String scoreFlag = "0.85";/*** 获取功能跳转链接和方式-排名分数阈值*/private String appPermissionsFunctionScoreFlag = "0.85";/*** 根据保险产品分类获取保险产品列表-排名分数阈值*/private String insuranceProductInfoScoreFlag = "0.8";/*** 是否返回所有linkList数据*/private Boolean linkListAllSwitch = Boolean.FALSE;
}
Apollo配置内容
可使用其他配置方式替代
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;/*** @author * @date 2024/5/19**/
@Getter
@Setter
@Accessors(chain = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MsgConfigDto {/*** databaseName 数据库名称*/private String databaseName = "";/*** collectionName 集合名称*/private String collectionName = "";/*** appCode*/private String appCode = "";/*** 子模型* gpt-4o|gpt-3.5-turbo*/private String aiModel = "gpt-3.5-turbo";/*** token数,可为空,默认300*/private Integer maxTokens = 500;/*** 排名分数阈值(建议0.75)*/private String scoreFlag = "0.85";/*** 获取功能跳转链接和方式-排名分数阈值*/private String appPermissionsFunctionScoreFlag = "0.85";/*** 根据保险产品分类获取保险产品列表-排名分数阈值*/private String insuranceProductInfoScoreFlag = "0.8";/*** 文字转音频控制开关*/private Boolean textToSpeechFlag = Boolean.TRUE;/*** 文字转音频字数限制,当字数超过这个限制之后则不转换为音频*/private Integer textToSpeechLengthLimit = 200;/*** msgLinkedList超过该数量后开始做截断处理*/private Integer msgLinkedListLimit = 30;/*** 文字转语音对应的模型*/private String audioSpeechModel = "tts-1-hd";/*** 查询APP金刚位配置的collection名称*/private String appPermissionsFunctionConfigCollectionName = "";/*** 查询保险产品配置的collection名称*/private String insuranceProductInfoCollectionName = "";
}
请求体主要内容
/*** 存放用户个人信息的地方 key为用户姓名*/private static Map<String, MsgUserInfoDto> USER_NAME_MAP = new HashMap<>(10);@Overridepublic Response<MsgResponseDto> innerChatCompletionsV2(MsgDto msgDto) {//参数判断-工具类不再添加,请自行做替换处理!!!CommonAssert.notNull(msgDto, ErrorCode.PARAMS_IS_NULL);CommonAssert.notBlank(msgDto.getDatabaseName(),ErrorCode.PARAMS_IS_NULL);CommonAssert.notBlank(msgDto.getCollectionName(),ErrorCode.PARAMS_IS_NULL);CommonAssert.notBlank(msgDto.getAppCode(),ErrorCode.PARAMS_IS_NULL);CommonAssert.notBlank(msgDto.getAiModel(),ErrorCode.PARAMS_IS_NULL);CommonAssert.notBlank(msgDto.getPrompt(),ErrorCode.PARAMS_IS_NULL);String sessionId = msgDto.getSessionId();if(StringUtils.isBlank(sessionId)){//唯一ID生成器,请自行替换处理!!!sessionId = uniqueCodeComponent.getUniqueCode();msgDto.setSessionId(sessionId);}//这里做了一个模拟的异步MQ通知,接收方接收到信息后会自动调用多智能体进行处理,该部分忽略!!try {Message<Long> message = Message.of(BrokerUserMQConstant.TOPIC_SDB_BROKER_USER, BrokerUserMQConstant.TAG_GPT_USER_MSG_INFO_TEST,sessionId, msgDto);MessageResult send = producer.send(message);log.info("innerChatCompletionsV2 send = {}", JsonUtil.getJsonString(send));} catch (Exception e) {log.error("innerChatCompletionsV2 has exception , msgDto = {}", JsonUtil.getJsonString(msgDto), e);}//覆盖请求中的参数体,这里是因为原请求方是原生APP,因此后续为了不用APP做升级改成了动态配置进行覆盖MsgConfigDto gptMsgConfig = apolloConfigHolder.getGptMsgConfig();MsgConfigDto msgConfigDto = CopyUtil.copyBean(gptMsgConfig, MsgConfigDto.class);this.buildMsgDto(msgDto,msgConfigDto);Database db = CLIENT.database(msgDto.getDatabaseName());Collection collection = db.collection(msgDto.getCollectionName());String prompt = msgDto.getPrompt();SearchByEmbeddingItemsParam searchByEmbeddingItemsParam = SearchByEmbeddingItemsParam.newBuilder().withEmbeddingItems(Collections.singletonList(prompt))// 若使用 HNSW 索引,则需要指定参数 ef,ef 越大,召回率越高,但也会影响检索速度.withParams(new HNSWSearchParams(200))// 设置标量字段的 Filter 表达式,过滤所需查询的文档.withRetrieveVector(false)// 指定 Top K 的 K 值.withLimit(3)// 使用 filter 过滤数据
// .withFilter(new Filter(Filter.in("bookName", Arrays.asList("三国演义","西游记"))))// 指定返回的 fields
// .withOutputFields(Arrays.asList("chapterName", "lawText"))
//这里可以忽略哈,替换成自己的向量数据库参数后可使用自己的字段.withOutputFields(Arrays.asList("insuranceDesc", "insuranceProductName","insuranceProductSellingPoints","insuranceConsultantEnterpriseWeChatAccount","insuranceProductInfo")).build();SearchRes searchRes = collection.searchByEmbeddingItems(searchByEmbeddingItemsParam);AtomicReference searchFlag = new AtomicReference(Boolean.FALSE);if(Objects.equals(searchRes.getCode(),0) && CollectionUtils.isNotEmpty(searchRes.getDocuments())){log.info("VectorTestController innerChatCompletionsV2 searchByEmbeddingItems -----------成功");StringBuilder promptBuilder = new StringBuilder();promptBuilder.append("提供的资料以\"...\"开头和结尾,请以提供的资料为参考进行问题的解答,如果提供的资料无法回答再根据用户实际问题进行谨慎回答。");promptBuilder.append("...开始\n");List<List<Document>> documentArray = searchRes.getDocuments();documentArray.forEach(documents -> {documents.forEach(document -> {if(document.getScore() < Double.parseDouble(msgDto.getScoreFlag())){return;}document.getDocFields().forEach(docField -> {promptBuilder.append(docField.getValue());promptBuilder.append("\n");searchFlag.set(Boolean.TRUE);});});});promptBuilder.append("...结束\n");promptBuilder.append("用户问题\n:");promptBuilder.append(prompt);promptBuilder.append("\n");if((Boolean)searchFlag.get()){prompt = promptBuilder.toString();}else {prompt = "在XX行业中," + prompt;}}else {prompt = "在XX行业中," + prompt;log.info("VectorTestController innerChatCompletionsV2 searchByEmbeddingItems -----------失败,searchRes = {}, searchByEmbeddingItemsParam = {}", JsonUtil.getJsonString(searchRes),JsonUtil.getJsonString(searchByEmbeddingItemsParam));}log.info("innerChatCompletionsV2 sessionId = {}, msgDto = {}",sessionId,JsonUtil.getJsonString(msgDto));LinkedList<MessageV2> msgLinkedList = SESSION_MSG_MAP.get(sessionId);if(CollectionUtils.isEmpty(msgLinkedList)){msgLinkedList = new LinkedList<>();MessageV2 systemMessage = new MessageV2();systemMessage.setRole("system");String newSysMsg = sysMsgStr + "";systemMessage.setContent(newSysMsg);msgLinkedList.add(systemMessage);MessageV2 userMessage = new MessageV2();userMessage.setRole("user");userMessage.setContent(prompt);msgLinkedList.add(userMessage);SESSION_MSG_MAP.put(sessionId,msgLinkedList);}else{MessageV2 userMessage = new MessageV2();userMessage.setRole("user");userMessage.setContent(prompt);msgLinkedList.add(userMessage);}//构建GPT同样请求体ChatCompletionBodyV2 body = this.buildGptBody(msgDto,msgConfigDto,msgLinkedList);ChatCompletionResponseV2 gptResponse = this.gptRequestInvoke(msgDto, msgConfigDto, msgLinkedList, body);MsgResponseDto msgResponse = new MsgResponseDto();msgResponse.setRequestMsg(msgDto);msgResponse.setSessionId(sessionId);msgResponse.setResponseMsg(gptResponse);msgLinkedList = msgLinkedList.stream().peek(msgLinked -> {String content = msgLinked.getContent();String role = msgLinked.getRole();//app方法配置中返回内容做特殊处理if (Objects.equals(role,"assistant") && StringUtils.isNotBlank(content) && content.contains("appFunctionConfig")) {content.replaceAll("\\n", "");content.replaceAll("\\\"", "");}msgLinked.setContent(content);}).collect(Collectors.toCollection(LinkedList::new));MessageV2 lastMsg = msgLinkedList.getLast();//如果最后一个消息是GPT返回的且内容不为空,且内容长度符合要求,且文字转音频开关是打开的则转换音频数据if(Objects.nonNull(lastMsg)){String speechUrl = this.textToSpeech(lastMsg,msgConfigDto);if(StringUtils.isNotBlank(speechUrl)){msgResponse.setSpeechUrl(speechUrl);}}LinkedList<MessageV2> responseMsgLinkedList = new LinkedList<>();responseMsgLinkedList.add(lastMsg);msgResponse.setMsgLinkedList(responseMsgLinkedList);if(msgDto.getLinkListAllSwitch()){msgResponse.setMsgLinkedListAll(msgLinkedList);}log.info("innerChatCompletionsV2 SESSION_MSG_MAP = {}", JsonUtil.getJsonString(SESSION_MSG_MAP));return ResponseUtil.makeSuccess(msgResponse);}
构建GPT请求体
/*** 构建GPT同样请求体* @param msgDto* @param msgConfigDto* @param msgLinkedList* @return*/private ChatCompletionBodyV2 buildGptBody(MsgDto msgDto,MsgConfigDto msgConfigDto,LinkedList<MessageV2> msgLinkedList){log.info("VectorTestController buildGptBody param, msgDto = {},msgConfigDto = {},msgLinkedList = {}",JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(msgLinkedList));ChatCompletionBodyV2 body = new ChatCompletionBodyV2();body.setLogit_bias(new HashMap<>());if(msgLinkedList.size() > msgConfigDto.getMsgLinkedListLimit()){//这里的处理方案可以再优化为过滤msg类型,只保留role类型为 user|system|assistant ,把tolls的请求和返回内容进行过滤掉可以大大减少上线文内容。同时只保留指定数量的消息体内容List<MessageV2> msgLinkedListRequest = new ArrayList<>(6);msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(0),MessageV2.class));msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(msgLinkedList.size() - 5),MessageV2.class));msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(msgLinkedList.size() - 4),MessageV2.class));msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(msgLinkedList.size() - 3),MessageV2.class));msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(msgLinkedList.size() - 2),MessageV2.class));msgLinkedListRequest.add(CopyUtil.copyBean(msgLinkedList.get(msgLinkedList.size() - 1),MessageV2.class));body.setMessages(msgLinkedListRequest);}else {body.setMessages(msgLinkedList);}body.setModel(msgDto.getAiModel());body.setN(1);if(Objects.nonNull(msgDto.getTemperature())){body.setTemperature(msgDto.getTemperature());}else {body.setTemperature(0.0);}if(Objects.nonNull(msgDto.getMaxTokens())){body.setMax_tokens(msgDto.getMaxTokens());}else {body.setMax_tokens(300);}body.setStream(Boolean.FALSE);if (msgDto.getOutputJsonFlag()) {ResponseFormat responseFormat = new ResponseFormat();responseFormat.setType("json_object");body.setResponse_format(responseFormat);}body.setTool_choice("auto");List<Tool> toolList = new ArrayList<>(3);body.setTools(toolList);
// toolList.add(this.buildBuyInsuranceProductUrl());
// toolList.add(this.buildAppFunctionTools());toolList.add(this.buildSaveUserInfoTool());toolList.add(this.buildGetUserInfoTool());
// toolList.add(this.buildSaveUserInsuranceCompanyPreferenceTool());
// toolList.add(this.buildGetUserInsuranceCompanyPreferenceTool());
// toolList.add(this.buildSaveUserInsurancePreferenceTool());
// toolList.add(this.buildGetUserInsurancePreferenceTool());log.info("VectorTestController buildGptBody result,body = {} msgDto = {},msgConfigDto = {},msgLinkedList = {}",JsonUtil.getJsonString(body),JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(msgLinkedList));return body;}
构建GPT请求方法
/*** 执行GPT请求* @param msgDto 拥挤请求消息体* @param msgConfigDto 请求配置信息* @param msgLinkedList 消息体内容* @param body GPT请求体* @return*/private ChatCompletionResponseV2 gptRequestInvoke(MsgDto msgDto,MsgConfigDto msgConfigDto,LinkedList<MessageV2> msgLinkedList,ChatCompletionBodyV2 body){log.info("gptRequestInvoke request param, msgDto = {}, msgConfigDto = {},msgLinkedList = {}, body = {}",JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(msgLinkedList),JsonUtil.getJsonString(body));String sessionId = msgDto.getSessionId();Boolean gptRunCheck = Boolean.TRUE;ChatCompletionResponseV2 gptResponse = null;//循环调用GPT,知道GPT没有进行toolCall回调为止;while (gptRunCheck){//构建GPT请求gptResponse = this.completionBuild(sessionId, msgDto.getAppCode(), body);if(Objects.nonNull(gptResponse)){List<ChoiceV2> choices = gptResponse.getChoices();for (ChoiceV2 choice : choices){ChoiceV2.ResMessage message = choice.getMessage();MessageV2 assistantMessage = new MessageV2();assistantMessage.setRole(message.getRole());if(StringUtils.isNotBlank(message.getContent())){assistantMessage.setContent(message.getContent());}msgLinkedList.add(assistantMessage);if(CollectionUtils.isEmpty(message.getTool_calls())){gptRunCheck = Boolean.FALSE;break;}List<ChoiceV2.ResToolCall> resToolCalls = message.getTool_calls();List<MessageV2.ToolCall> toolCalls = new ArrayList<>(resToolCalls.size());resToolCalls.forEach(callFunction -> {MessageV2.Function function = callFunction.getFunction();
// assistantMessage.setName(function.getName());
// assistantMessage.setTool_call_id(callFunction.getId());MessageV2.ToolCall toolCall = new MessageV2.ToolCall();toolCall.setId(callFunction.getId());toolCall.setType(callFunction.getType());toolCall.setFunction(function);toolCalls.add(toolCall);});assistantMessage.setTool_calls(toolCalls);for(ChoiceV2.ResToolCall toolCall : resToolCalls){MessageV2.Function function = toolCall.getFunction();if(Objects.isNull(function)|| StringUtils.isBlank(function.getName())|| StringUtils.isBlank(function.getArguments())){//没有GPT信息也进行中断循环返回log.error("gptRequestInvoke gptResponse function value is null sessionId = {}, msgDto = {} , gptResponse = {} , function = {}",sessionId,JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(gptResponse),JsonUtil.getJsonString(function));MessageV2 errorAssistantMessage = new MessageV2();errorAssistantMessage.setRole("assistant");errorAssistantMessage.setContent("信息好像丢失了,请更详细的描述您的要求并重试!");msgLinkedList.add(errorAssistantMessage);gptRunCheck = Boolean.FALSE;break;}String functionName = function.getName();if(functionName.contains("saveUserInfo")){String functionArguments = function.getArguments();//arguments value :{"searchKey":"蓝医保·长期医疗险(家庭版)"}JSONObject param = JSONObject.parseObject(functionArguments);String insuranceProductInfo = this.saveUserInfo(param.getString("username"),StringUtils.isNotBlank(param.getString("age")) ? Integer.parseInt(param.getString("age")) : null,param.getString("province"),param.getString("city"),param.getString("address"),param.getString("gender"),param.getString("socialSecurity"),param.getString("educationLevel"),param.getString("pastMedicalHistory"));MessageV2 functionReqMessage = new MessageV2();functionReqMessage.setRole("tool");functionReqMessage.setContent(insuranceProductInfo);functionReqMessage.setTool_call_id(toolCall.getId());functionReqMessage.setName(functionName);msgLinkedList.add(functionReqMessage);}else if(functionName.contains("getUserInfo")){String functionArguments = function.getArguments();//arguments value :{"searchKey":"蓝医保·长期医疗险(家庭版)"}JSONObject param = JSONObject.parseObject(functionArguments);String userInfo = this.getUserInfo(param.getString("username"));MessageV2 functionReqMessage = new MessageV2();functionReqMessage.setRole("tool");functionReqMessage.setContent(userInfo);functionReqMessage.setTool_call_id(toolCall.getId());functionReqMessage.setName(functionName);msgLinkedList.add(functionReqMessage);}}body.setMessages(new ArrayList<>(msgLinkedList));}log.info("gptRequestInvoke ResponseBody sessionId = {}, msgDto = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(gptResponse));} else{//没有GPT信息也进行中断循环返回log.error("gptRequestInvoke gptResponse value is null sessionId = {}, msgDto = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(gptResponse));MessageV2 errorAssistantMessage = new MessageV2();errorAssistantMessage.setRole("assistant");errorAssistantMessage.setContent("信息好像丢失了,请更详细的描述您的要求并重试!");msgLinkedList.add(errorAssistantMessage);gptResponse = new ChatCompletionResponseV2();gptResponse.setId(uniqueCodeComponent.getUniqueCode());List<ChoiceV2> choices = new ArrayList<>();gptResponse.setChoices(choices);ChoiceV2 choiceV2 = new ChoiceV2();choices.add(choiceV2);ChoiceV2.ResMessage message = new ChoiceV2.ResMessage();choiceV2.setMessage(message);message.setContent(errorAssistantMessage.getContent());message.setRole(errorAssistantMessage.getRole());gptRunCheck = Boolean.FALSE;break;}}//保证顺序为 user|assistant 交替进行MessageV2 last = msgLinkedList.getLast();MessageV2 messageV2 = msgLinkedList.get(msgLinkedList.size() - 2);if(Objects.equals(last.getRole(),"user") && Objects.equals(messageV2.getRole(),"assistant")){msgLinkedList.removeLast();}else if(Objects.equals(last.getRole(),"user") && Objects.equals(messageV2.getRole(),"system")){msgLinkedList.removeLast();}else if(Objects.equals(last.getRole(),"user") && Objects.equals(messageV2.getRole(),"function")){msgLinkedList.removeLast();}log.info("gptRequestInvoke request response, msgDto = {}, msgConfigDto = {},msgLinkedList = {}, body = {}",JsonUtil.getJsonString(msgDto),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(msgLinkedList),JsonUtil.getJsonString(body));return gptResponse;}/*** 构建GPT请求* @param sessionId 会话内容* @param appCode 租户编号* @param body 消息提提* @return*/private ChatCompletionResponseV2 completionBuild(String sessionId,String appCode, ChatCompletionBodyV2 body){SdbCommonAssert.notBlank(sessionId,ErrorCode.PARAMS_IS_NULL,"sessionId value not null");SdbCommonAssert.notBlank(appCode,ErrorCode.PARAMS_IS_NULL,"appCode value not null");SdbCommonAssert.notNull(body,ErrorCode.PARAMS_IS_NULL,"body value not null");ChatCompletionRequestV2 chatRequest = new ChatCompletionRequestV2();chatRequest.setAppCode(appCode);chatRequest.setChatCompletionBody(body);log.info("innerChatCompletionsV2 chatRequest sessionId = {}, appCode = {}, chatRequest = {}",sessionId,JsonUtil.getJsonString(appCode),JsonUtil.getJsonString(chatRequest));com.shuidihuzhu.common.web.model.Response<ChatCompletionResponseV2> gptResponse = null;try {#这里替换为GPT官方文流式|非流式请求接口gptResponse = client.innerChatCompletionsV2(chatRequest);}catch (Exception e){log.error("innerChatCompletionsV2 gptResponse value is null, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse),e);return null;}log.info("innerChatCompletionsV2 chatRequest result sessionId = {}, appCode = {}, chatRequest = {},gptResponse = {}",sessionId,JsonUtil.getJsonString(appCode),JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));if(!Objects.equals(gptResponse.getCode(),0)|| Objects.isNull(gptResponse.getData())){log.error("innerChatCompletionsV2 gptResponse value is null, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));return null;}List<ChoiceV2> choices = gptResponse.getData().getChoices();if(CollectionUtils.isEmpty(choices)){log.error("innerChatCompletionsV2 choices value is empty, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));return null;}//检查内容 默认 TRUE 正确Boolean checkChoice = Boolean.TRUE;for (ChoiceV2 choice : choices){if(Objects.isNull(choice)|| Objects.isNull(choice.getMessage())){log.error("innerChatCompletionsV2 choice value is null, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));checkChoice = Boolean.FALSE;continue;}ChoiceV2.ResMessage message = choice.getMessage();if(StringUtils.isBlank(message.getContent())&& CollectionUtils.isEmpty(message.getTool_calls())){log.error("innerChatCompletionsV2 choice value is null, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));checkChoice = Boolean.FALSE;}}if(!checkChoice){log.error("innerChatCompletionsV2 choice value is null, sessionId = {}, chatRequest = {} , gptResponse = {}",sessionId,JsonUtil.getJsonString(chatRequest),JsonUtil.getJsonString(gptResponse));return null;}return gptResponse.getData();}
其他小方法
buildMsgDto
private void buildMsgDto (MsgDto msgDto,MsgConfigDto msgConfigDto){if(StringUtils.isNotBlank(msgConfigDto.getDatabaseName())){msgDto.setDatabaseName(msgConfigDto.getDatabaseName());}if(StringUtils.isNotBlank(msgConfigDto.getCollectionName())){msgDto.setCollectionName(msgConfigDto.getCollectionName());}if(StringUtils.isNotBlank(msgConfigDto.getAppCode())){msgDto.setAppCode(msgConfigDto.getAppCode());}if(StringUtils.isNotBlank(msgConfigDto.getAiModel())){msgDto.setAiModel(msgConfigDto.getAiModel());}if(Objects.nonNull(msgConfigDto.getMaxTokens())){msgDto.setMaxTokens(msgConfigDto.getMaxTokens());}if(StringUtils.isNotBlank(msgConfigDto.getScoreFlag())){msgDto.setScoreFlag(msgConfigDto.getScoreFlag());}if(StringUtils.isNotBlank(msgConfigDto.getAppPermissionsFunctionScoreFlag())){msgDto.setAppPermissionsFunctionScoreFlag(msgConfigDto.getAppPermissionsFunctionScoreFlag());}if(StringUtils.isNotBlank(msgConfigDto.getInsuranceProductInfoScoreFlag())){msgDto.setInsuranceProductInfoScoreFlag(msgConfigDto.getInsuranceProductInfoScoreFlag());}}
类初始化Client
/*** 链接信息*/private static VectorDBClient CLIENT = null;/*** 获取腾讯云client信息* @return client*/@PostConstructprivate void initVectorDBClient(){if(CLIENT == null){log.info("VectorTestController buildVectorDBClient -----------开始");// 创建VectorDB ClientConnectParam connectParam = ConnectParam.newBuilder().withUrl(apolloConfigHolder.getDashVector2040312EndPoint()).withUsername(apolloConfigHolder.getDashVectorUserName2040312()).withKey(apolloConfigHolder.getDashVectorApiKey2040312()).withTimeout(30).build();log.info("VectorTestController buildVectorDBClient -----------参数,connectParam = {}", JsonUtil.getJsonString(connectParam));CLIENT = new VectorDBClient(connectParam, ReadConsistencyEnum.EVENTUAL_CONSISTENCY);log.info("VectorTestController buildVectorDBClient -----------成功");}}
buildSaveUserInfoTool
private Tool buildSaveUserInfoTool(){Tool saveUserInfoTool = new Tool();saveUserInfoTool.setType("function");Tool.ToolFunction saveUserInfoFunction = new Tool.ToolFunction();saveUserInfoFunction.setName("saveUserInfo");saveUserInfoFunction.setDescription("引导用户向该接口传入用户的基本信息,基本信息内容包括工号、姓名、年龄、省份、城市、地区、性别、是否有社保、学历等信息");JSONObject saveUserInfoJsonObject = new JSONObject();saveUserInfoFunction.setParameters(saveUserInfoJsonObject);saveUserInfoJsonObject.put("type","object");JSONObject saveUserInfoPropertiesObject = new JSONObject();saveUserInfoJsonObject.put("properties",saveUserInfoPropertiesObject);saveUserInfoJsonObject.put("required", new JSONArray(List.of("username")));JSONObject userNameParam = new JSONObject();userNameParam.put("type","string");userNameParam.put("description","姓名|用户信息|用户身份证姓名");saveUserInfoPropertiesObject.put("username",userNameParam);JSONObject ageParam = new JSONObject();ageParam.put("type","integer");ageParam.put("description","数字类型的年龄");saveUserInfoPropertiesObject.put("age",ageParam);JSONObject provinceParam = new JSONObject();provinceParam.put("type","string");provinceParam.put("description","用户所在的省份|指定用户所在的省份");saveUserInfoPropertiesObject.put("province",provinceParam);JSONObject cityParam = new JSONObject();cityParam.put("type","string");cityParam.put("description","用户所在的城市|指定用户所在的城市和位置");saveUserInfoPropertiesObject.put("city",cityParam);JSONObject addressParam = new JSONObject();addressParam.put("type","string");addressParam.put("description","用户所在的详细地址|指定用户的详细地址和详细家庭地址");saveUserInfoPropertiesObject.put("address",addressParam);JSONObject genderParam = new JSONObject();genderParam.put("type","string");genderParam.put("description","用户的性别,默认未知");genderParam.put("enum",List.of("男","女","未知"));saveUserInfoPropertiesObject.put("gender",genderParam);JSONObject socialSecurityParam = new JSONObject();socialSecurityParam.put("type","string");socialSecurityParam.put("description","用户是否缴纳了社保,默认为有社保");socialSecurityParam.put("enum",List.of("有社保","无社保","未知"));saveUserInfoPropertiesObject.put("socialSecurity",socialSecurityParam);JSONObject educationLevelParam = new JSONObject();educationLevelParam.put("type","string");educationLevelParam.put("description","用户的学习以及用户的受教育程度");educationLevelParam.put("enum",List.of("初中及以下学历","初中及同等学历","高中","大专","本科","硕士","博士及以上","中专","其他"));saveUserInfoPropertiesObject.put("educationLevel",educationLevelParam);saveUserInfoTool.setFunction(saveUserInfoFunction);return saveUserInfoTool;}
buildGetUserInfoTool
private Tool buildGetUserInfoTool(){Tool getUserInfoTool = new Tool();getUserInfoTool.setType("function");Tool.ToolFunction getUserInfoFunction = new Tool.ToolFunction();getUserInfoFunction.setName("getUserInfo");getUserInfoFunction.setDescription("当需要获取用户信息时,引导用户向该接口传入用户的姓名,可以获取到已经保存的用户信息,该信息内容包括年龄、省份、城市、地区、性别、学历等信息.虽然可以获取到这些字段,但是对应的字段值可能为空,如果为空可以重复引导用户调用\"saveUserInfo\"接口进行用户信息保存");JSONObject getUserInfoJsonObject = new JSONObject();getUserInfoFunction.setParameters(getUserInfoJsonObject);getUserInfoJsonObject.put("type","object");JSONObject buyInsuranceProductPropertiesObject = new JSONObject();getUserInfoJsonObject.put("properties",buyInsuranceProductPropertiesObject);getUserInfoJsonObject.put("required", new JSONArray(List.of("username")));JSONObject usernameParam = new JSONObject();buyInsuranceProductPropertiesObject.put("username",usernameParam);usernameParam.put("type","string");usernameParam.put("description","用户姓名和购买人姓名或者经纪人姓名");getUserInfoTool.setFunction(getUserInfoFunction);return getUserInfoTool;}
MsgUserInfoDto
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;/*** @author * @date 2024/5/10**/
@Getter
@Setter
@Accessors(chain = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MsgUserInfoDto {/*** 工号*/private String businessCode;/*** 姓名*/private String username;/*** 年龄*/private Integer age;/*** 当前所处省份*/private String locationProvince;/*** 当前所处城市*/private String locationCity;/*** 当前所处详细地址*/private String locationAddress;/*** 性别 1.男【MALE】 2.女【FEMALE】 3.未知【UNKNOWN】*/private String gender;/*** 是否有社保 1.有社保【hasSocialSecurity】 2.无社保【notHadSocialSecurity】*/private String socialSecurity;/*** 学历* 1.初中及以下学历【JUNIOR01】* 2.初中及同等学历【JUNIOR】* 3.高中【HIGH】* 4.大专【COLLEGE】* 5.本科【BACHELOR】* 6.硕士【MASTER】* 7.博士及以上【DOCTOR】* 8.中专【SECONDARY】* 10.其他【OTHER】*/private String educationLevel;
}
saveUserInfo
/*** 保存用户信息 主键为用户姓名* @param username 用户姓名,不可为空* @param age 年龄* @param province 省份* @param city 城市* @param address 地址* @param gender 性别* @param socialSecurity 是否有社保* @param educationLevel 学历* @param pastMedicalHistory 既往病史:* @return*/private String saveUserInfo(String username,Integer age,String province,String city,String address,String gender,String socialSecurity,String educationLevel){if(StringUtils.isBlank(username)){return "用户没有输入姓名等关键字,请重新引导用户输入姓名,尽量用更加婉转的方式";}MsgUserInfoDto userInfoDto = USER_NAME_MAP.get(username);if(Objects.isNull(userInfoDto)){userInfoDto = new MsgUserInfoDto();userInfoDto.setUsername(username);}if(Objects.nonNull(age)){userInfoDto.setAge(age);}if(StringUtils.isNotBlank(province)){userInfoDto.setLocationProvince(province);}if(StringUtils.isNotBlank(city)){userInfoDto.setLocationCity(city);}if(StringUtils.isNotBlank(address)){userInfoDto.setLocationAddress(address);}if(StringUtils.isNotBlank(gender)){userInfoDto.setGender(gender);}if(StringUtils.isNotBlank(socialSecurity)){userInfoDto.setSocialSecurity(socialSecurity);}if(StringUtils.isNotBlank(educationLevel)){userInfoDto.setEducationLevel(educationLevel);}USER_NAME_MAP.put(username,userInfoDto);return JsonUtil.getJsonString(userInfoDto);}
getUserInfo
/*** 获取用户信息 主键为用户姓名* @param username 用户姓名,不可为空* @return*/private String getUserInfo(String username){if(StringUtils.isBlank(username)){return "用户没有输入姓名等关键字,请重新引导用户输入姓名,尽量用更加婉转的方式";}MsgUserInfoDto userInfoDto = USER_NAME_MAP.get(username);if(Objects.isNull(userInfoDto)){return "该用户还没有保存个人信息,请引导用户向该接口传入用户的基本信息,基本信息内容包括姓名、年龄、省份、城市、地区、性别、是否有社保、学历等,可以通过多轮访问多轮问询的方式,滚动存储";}return JsonUtil.getJsonString(userInfoDto);}
MsgResponseDto
import com.shuidihuzhu.ai.alps.client.model.completions.v2.ChatCompletionResponseV2;
import com.shuidihuzhu.ai.alps.client.model.completions.v2.MessageV2;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;import java.util.LinkedList;/*** @author * @date 2024/3/14**/
@Getter
@Setter
@Accessors(chain = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class MsgResponseDto {/*** 传入的消息体*/private MsgDto requestMsg;/*** 返回的内容*/private ChatCompletionResponseV2 responseMsg;/*** 生成或者传入的消息体ID*/private String sessionId;/*** 目前该SessionId下存储的消息内容*/private LinkedList<MessageV2> msgLinkedList;/*** 目前该SessionId下存储的消息内容*/private LinkedList<MessageV2> msgLinkedListAll;/*** 音频地址*/private String speechUrl;
}
文字转语音
/*** 返回音频地址* @param lastMsg 最后一条消息内容* @return*/private String textToSpeech(MessageV2 lastMsg, MsgConfigDto msgConfigDto){String speechUrl = null;if(Objects.equals(lastMsg.getRole(),"assistant")&& StringUtils.isNotBlank(lastMsg.getContent())&& lastMsg.getContent().length() <= msgConfigDto.getTextToSpeechLengthLimit()&& msgConfigDto.getTextToSpeechFlag()){AudioSpeechRequest audioSpeechRequest = new AudioSpeechRequest();audioSpeechRequest.setModel(msgConfigDto.getAudioSpeechModel());audioSpeechRequest.setInput(lastMsg.getContent());audioSpeechRequest.setVoice("onyx");com.shuidihuzhu.common.web.model.Response<String> speechResponse = null;try {#这里替换为GPT官方文档的文字转语音接口speechResponse = client.innerAudioSpeech(msgConfigDto.getAppCode(), audioSpeechRequest);}catch (Exception e){log.error("innerChatCompletionsV2 textToSpeech innerAudioSpeech has exception, lastMsg = {}, msgConfigDto = {} , audioSpeechRequest = {} , speechResponse = {}",JsonUtil.getJsonString(lastMsg),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(audioSpeechRequest),JsonUtil.getJsonString(speechResponse));return null;}if(!Objects.equals(speechResponse.getCode(),0)|| StringUtils.isBlank(speechResponse.getData())){log.error("innerChatCompletionsV2 textToSpeech speechResponse value is null, lastMsg = {}, msgConfigDto = {} , audioSpeechRequest = {} , speechResponse = {}",JsonUtil.getJsonString(lastMsg),JsonUtil.getJsonString(msgConfigDto),JsonUtil.getJsonString(audioSpeechRequest),JsonUtil.getJsonString(speechResponse));return null;}speechUrl = speechResponse.getData();}return speechUrl;}