Commit 3ba4dd16713378014482af5311db50e2f21bbb8e
1 parent
9cc28448
添加向量库
Showing
3 changed files
with
136 additions
and
41 deletions
src/main/java/com/xly/service/XlyErpService.java
| @@ -37,6 +37,7 @@ import dev.langchain4j.service.MemoryId; | @@ -37,6 +37,7 @@ import dev.langchain4j.service.MemoryId; | ||
| 37 | import dev.langchain4j.service.V; | 37 | import dev.langchain4j.service.V; |
| 38 | import io.milvus.v2.common.DataType; | 38 | import io.milvus.v2.common.DataType; |
| 39 | import io.milvus.v2.service.collection.request.CreateCollectionReq; | 39 | import io.milvus.v2.service.collection.request.CreateCollectionReq; |
| 40 | +import jnr.ffi.annotations.In; | ||
| 40 | import lombok.RequiredArgsConstructor; | 41 | import lombok.RequiredArgsConstructor; |
| 41 | import lombok.extern.slf4j.Slf4j; | 42 | import lombok.extern.slf4j.Slf4j; |
| 42 | import org.apache.commons.lang3.time.DateFormatUtils; | 43 | import org.apache.commons.lang3.time.DateFormatUtils; |
| @@ -227,6 +228,25 @@ public class XlyErpService { | @@ -227,6 +228,25 @@ public class XlyErpService { | ||
| 227 | } | 228 | } |
| 228 | } | 229 | } |
| 229 | 230 | ||
| 231 | + /**** | ||
| 232 | + * @Author 钱豹 | ||
| 233 | + * @Date 8:54 2026/4/7 | ||
| 234 | + * @Param [userInput, userId, sUserName, sBrandsId, sSubsidiaryId, sUserType, authorization] | ||
| 235 | + * @return com.xly.entity.AiResponseDTO | ||
| 236 | + * @Description 换一换 | ||
| 237 | + **/ | ||
| 238 | + public AiResponseDTO change(String userId , | ||
| 239 | + String sUserName , | ||
| 240 | + String sBrandsId , | ||
| 241 | + String sSubsidiaryId, | ||
| 242 | + String sUserType, | ||
| 243 | + String authorization, | ||
| 244 | + Integer iPage) { | ||
| 245 | + // 1. 初始化用户场景会话(权限内场景) | ||
| 246 | + UserSceneSession session = userSceneSessionService.getUserSceneSession(userId,sUserName,sBrandsId,sSubsidiaryId,sUserType,authorization); | ||
| 247 | + return getChange(session, iPage); | ||
| 248 | + } | ||
| 249 | + | ||
| 230 | /*** | 250 | /*** |
| 231 | * @Author 钱豹 | 251 | * @Author 钱豹 |
| 232 | * @Date 19:18 2026/1/27 | 252 | * @Date 19:18 2026/1/27 |
| @@ -270,7 +290,7 @@ public class XlyErpService { | @@ -270,7 +290,7 @@ public class XlyErpService { | ||
| 270 | // 3. 未选场景:先展示场景选择界面,处理用户序号选择 | 290 | // 3. 未选场景:先展示场景选择界面,处理用户序号选择 |
| 271 | if (!session.isSceneSelected() && ValiDataUtil.me().isPureNumber(input)){ | 291 | if (!session.isSceneSelected() && ValiDataUtil.me().isPureNumber(input)){ |
| 272 | // 3.1 尝试处理场景选择(输入序号则匹配,否则展示选择提示) | 292 | // 3.1 尝试处理场景选择(输入序号则匹配,否则展示选择提示) |
| 273 | - return handleSceneSelect(userId, input, session); | 293 | + return handleSceneSelect(userId, input, session,1); |
| 274 | } | 294 | } |
| 275 | // 4. 构建Agent,执行业务交互,如果返回为null,说明大模型没有判段出场景,必判断出后才能继续 | 295 | // 4. 构建Agent,执行业务交互,如果返回为null,说明大模型没有判段出场景,必判断出后才能继续 |
| 276 | ErpAiAgent aiAgent = createErpAiAgent(userId, input, session); | 296 | ErpAiAgent aiAgent = createErpAiAgent(userId, input, session); |
| @@ -942,7 +962,7 @@ public class XlyErpService { | @@ -942,7 +962,7 @@ public class XlyErpService { | ||
| 942 | /** | 962 | /** |
| 943 | * 处理用户场景选择:输入序号→匹配场景→更新会话状态 | 963 | * 处理用户场景选择:输入序号→匹配场景→更新会话状态 |
| 944 | */ | 964 | */ |
| 945 | - private AiResponseDTO handleSceneSelect(String userId, String userInput, UserSceneSession session) { | 965 | + private AiResponseDTO handleSceneSelect(String userId, String userInput, UserSceneSession session,Integer page) { |
| 946 | // 1. 尝试根据序号匹配场景 | 966 | // 1. 尝试根据序号匹配场景 |
| 947 | boolean selectSuccess = session.selectSceneByInput(userInput); | 967 | boolean selectSuccess = session.selectSceneByInput(userInput); |
| 948 | String sceneName = StrUtil.EMPTY; | 968 | String sceneName = StrUtil.EMPTY; |
| @@ -954,20 +974,34 @@ public class XlyErpService { | @@ -954,20 +974,34 @@ public class XlyErpService { | ||
| 954 | UserSceneSessionService.ERP_AGENT_CACHE.remove(userId); | 974 | UserSceneSessionService.ERP_AGENT_CACHE.remove(userId); |
| 955 | //清除记忆缓存 | 975 | //清除记忆缓存 |
| 956 | operableChatMemoryProvider.clearSpecifiedMemory(userId); | 976 | operableChatMemoryProvider.clearSpecifiedMemory(userId); |
| 957 | - StringBuffer aiText = new StringBuffer().append(" <div style='line-height:1.8;width:100%;'>") | ||
| 958 | - .append("<div style=\"color: #333;\">") | ||
| 959 | - .append("智能体选择成功! 现在可以问她相关问题(如" + String.join("、", session.getCurrentScene().getSSceneContext()) + ")") | ||
| 960 | - .append("</div>"); | ||
| 961 | - //插入用户常用问题 | ||
| 962 | - aiText.append(getSelectAgent(session,1)); | ||
| 963 | - sceneName = ObjectUtil.isNotEmpty(session.getCurrentScene())?session.getCurrentScene().getSSceneName():StrUtil.EMPTY; | ||
| 964 | - methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSControlName():StrUtil.EMPTY; | ||
| 965 | - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(aiText.toString()).sSceneName(session.getCurrentScene().getSSceneName()).build(); | 977 | + return getChange( session, page); |
| 966 | } else { | 978 | } else { |
| 967 | // 3. 选择失败:重新展示场景选择提示 | 979 | // 3. 选择失败:重新展示场景选择提示 |
| 968 | return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(session.buildSceneSelectHint()).build(); | 980 | return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(session.buildSceneSelectHint()).build(); |
| 969 | } | 981 | } |
| 970 | } | 982 | } |
| 983 | + | ||
| 984 | + /*** | ||
| 985 | + * @Author 钱豹 | ||
| 986 | + * @Date 9:00 2026/4/7 | ||
| 987 | + * @Param [session, page] | ||
| 988 | + * @return com.xly.entity.AiResponseDTO | ||
| 989 | + * @Description 用户输入数字换一换 | ||
| 990 | + **/ | ||
| 991 | + private AiResponseDTO getChange(UserSceneSession session,Integer page){ | ||
| 992 | + StringBuffer aiText = new StringBuffer().append(" <div style='line-height:1.8;width:100%;'>") | ||
| 993 | + .append("<div style=\"color: #333;\">") | ||
| 994 | + .append("智能体选择成功! 现在可以问她相关问题(如" + String.join("、", session.getCurrentScene().getSSceneContext()) + ")") | ||
| 995 | + .append("</div>"); | ||
| 996 | + //插入用户常用问题 | ||
| 997 | + aiText.append(getSelectAgent(session,page)); | ||
| 998 | + String sceneName = ObjectUtil.isNotEmpty(session.getCurrentScene())?session.getCurrentScene().getSSceneName():StrUtil.EMPTY; | ||
| 999 | + String methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSControlName():StrUtil.EMPTY; | ||
| 1000 | + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(aiText.toString()).sSceneName(session.getCurrentScene().getSSceneName()).build(); | ||
| 1001 | + } | ||
| 1002 | + | ||
| 1003 | + | ||
| 1004 | + | ||
| 971 | /*** | 1005 | /*** |
| 972 | * @Author 钱豹 | 1006 | * @Author 钱豹 |
| 973 | * @Date 11:45 2026/3/13 | 1007 | * @Date 11:45 2026/3/13 |
| @@ -978,18 +1012,24 @@ public class XlyErpService { | @@ -978,18 +1012,24 @@ public class XlyErpService { | ||
| 978 | private String getSelectAgent(UserSceneSession session,Integer page){ | 1012 | private String getSelectAgent(UserSceneSession session,Integer page){ |
| 979 | List<ToolMeta> toolMetaAll = session.getAuthTool(); | 1013 | List<ToolMeta> toolMetaAll = session.getAuthTool(); |
| 980 | String sSceneId = session.getCurrentScene().getSId(); | 1014 | String sSceneId = session.getCurrentScene().getSId(); |
| 1015 | + String sToolId = session.getCurrentTool().getSId(); | ||
| 981 | toolMetaAll = toolMetaAll.stream().filter(to-> to.getSSceneId().equals(session.getCurrentScene().getSId())).collect(Collectors.toUnmodifiableList()); | 1016 | toolMetaAll = toolMetaAll.stream().filter(to-> to.getSSceneId().equals(session.getCurrentScene().getSId())).collect(Collectors.toUnmodifiableList()); |
| 982 | StringBuffer sb = new StringBuffer(); | 1017 | StringBuffer sb = new StringBuffer(); |
| 983 | - List<List<ToolMeta>> firstFive = ListUtil.split(toolMetaAll,5); | ||
| 984 | - List<ToolMeta> showList; | ||
| 985 | - if(ObjectUtil.isNotEmpty(firstFive.get(page-1))){ | ||
| 986 | - showList = firstFive.get(page-1); | ||
| 987 | - page = page + 1; | ||
| 988 | - }else{ | ||
| 989 | - showList = firstFive.get(0); | ||
| 990 | - page = 1; | 1018 | + //获取用户最近五次问题 |
| 1019 | + List<Map<String,Object>> data = getAiUserAgentQuestion(session.getUserId(),sSceneId, sToolId, page,3); | ||
| 1020 | + Integer iPageCount = 0; | ||
| 1021 | + if(page==1){ | ||
| 1022 | + iPageCount =getAiUserAgentQuestionCount(session.getUserId(), sSceneId, sToolId); | ||
| 1023 | + } | ||
| 1024 | + List<ToolMeta> showListAll = new ArrayList<>(); | ||
| 1025 | + if(ObjectUtil.isNotEmpty(data)){ | ||
| 1026 | + data.forEach(one->{ | ||
| 1027 | + ToolMeta tm = new ToolMeta(); | ||
| 1028 | + tm.setSMethodName(one.get("sUserInput").toString()); | ||
| 1029 | + }); | ||
| 991 | } | 1030 | } |
| 992 | - showList.forEach(one->{ | 1031 | + showListAll.addAll(toolMetaAll); |
| 1032 | + showListAll.forEach(one->{ | ||
| 993 | sb.append("<div style=\"color: #4096ff; margin-top: 5px;display:flex;align-items:center;font-size:12px;\" data-action=\"reset\" data-text=\"") | 1033 | sb.append("<div style=\"color: #4096ff; margin-top: 5px;display:flex;align-items:center;font-size:12px;\" data-action=\"reset\" data-text=\"") |
| 994 | .append(one.getSMethodName()).append("\" onclick=\"reset(").append(one.getSMethodName()).append("\">") | 1034 | .append(one.getSMethodName()).append("\" onclick=\"reset(").append(one.getSMethodName()).append("\">") |
| 995 | .append("<span style=\"display:inline-block; width:5px; height:5px; background-color:#ccc; border-radius:50%; margin-right:5px;\"></span>") | 1035 | .append("<span style=\"display:inline-block; width:5px; height:5px; background-color:#ccc; border-radius:50%; margin-right:5px;\"></span>") |
| @@ -997,11 +1037,53 @@ public class XlyErpService { | @@ -997,11 +1037,53 @@ public class XlyErpService { | ||
| 997 | .append(" </div>"); | 1037 | .append(" </div>"); |
| 998 | }); | 1038 | }); |
| 999 | sb.append("</div>"); | 1039 | sb.append("</div>"); |
| 1000 | - sb.append(" <div style=\"color: #4096ff; margin-top: 5px;width:100%;text-align:right;margin-right:10px;font-size:12px;\" data-action=\"resetTag\" data-text=\"").append(sSceneId).append(",").append(page).append("\" onclick=\"reset(换一换)\">"); | 1040 | + sb.append(" <div style=\"color: #4096ff; margin-top: 5px;width:100%;text-align:right;margin-right:10px;font-size:12px;\" data-action=\"resetTag\" data-text=\"").append(sSceneId).append(",").append(sToolId).append(",").append(page+1).append(",").append(iPageCount).append("\" onclick=\"reset(换一换)\">"); |
| 1001 | sb.append(" 换一换").append("</div>"); | 1041 | sb.append(" 换一换").append("</div>"); |
| 1002 | return sb.toString(); | 1042 | return sb.toString(); |
| 1003 | } | 1043 | } |
| 1004 | 1044 | ||
| 1045 | + /*** | ||
| 1046 | + * @Author 钱豹 | ||
| 1047 | + * @Date 10:02 2026/4/7 | ||
| 1048 | + * @Param [sLoginId, sSceneId, sMethodId, iPageNum, iPageSize] | ||
| 1049 | + * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>> | ||
| 1050 | + * @Description 换一换获取最近数据 | ||
| 1051 | + **/ | ||
| 1052 | + private List<Map<String,Object>> getAiUserAgentQuestion(String sLoginId, String sSceneId, String sMethodId, Integer iPageNum,Integer iPageSize){ | ||
| 1053 | + StringBuffer sb = new StringBuffer().append("SELECT sId,iUpdate,sUserInput FROM ai_user_agent_question ") | ||
| 1054 | + .append("WHERE sLoginId = #{sLoginId} ") | ||
| 1055 | + .append("AND sSceneId = #{sSceneId} ") | ||
| 1056 | + .append("AND sMethodId = #{sMethodId} ") | ||
| 1057 | + .append("AND IFNULL(sUserInput,'') <> '' ") | ||
| 1058 | + .append("ORDER BY iUpdate DESC,tUpdateDate DESC ") | ||
| 1059 | + .append("LIMIT #{iPageNum},#{iPageSize} "); | ||
| 1060 | + Map<String,Object> serMap = new HashMap<>(); | ||
| 1061 | + serMap.put("sLoginId",sLoginId); | ||
| 1062 | + serMap.put("sSceneId",sSceneId); | ||
| 1063 | + serMap.put("sMethodId",sMethodId); | ||
| 1064 | + serMap.put("iPageNum",iPageNum); | ||
| 1065 | + serMap.put("iPageSize",iPageSize); | ||
| 1066 | + return dynamicExeDbService.findSql(serMap,sb.toString()); | ||
| 1067 | + } | ||
| 1068 | + | ||
| 1069 | + private Integer getAiUserAgentQuestionCount(String sLoginId, String sSceneId, String sMethodId){ | ||
| 1070 | + StringBuffer sb = new StringBuffer().append("SELECT COUNT(1) AS iCount FROM ai_user_agent_question ") | ||
| 1071 | + .append("WHERE sLoginId = #{sLoginId} ") | ||
| 1072 | + .append("AND sSceneId = #{sSceneId} ") | ||
| 1073 | + .append("AND sMethodId = #{sMethodId} ") | ||
| 1074 | + .append("AND IFNULL(sUserInput,'') <> '' "); | ||
| 1075 | + Map<String,Object> serMap = new HashMap<>(); | ||
| 1076 | + serMap.put("sLoginId",sLoginId); | ||
| 1077 | + serMap.put("sSceneId",sSceneId); | ||
| 1078 | + serMap.put("sMethodId",sMethodId); | ||
| 1079 | + List<Map<String,Object>> data = dynamicExeDbService.findSql(serMap,sb.toString()); | ||
| 1080 | + if(ObjectUtil.isNotEmpty(data)){ | ||
| 1081 | + return (Integer) data.get(0).get("iCount"); | ||
| 1082 | + } | ||
| 1083 | + return 0; | ||
| 1084 | + } | ||
| 1085 | + | ||
| 1086 | + | ||
| 1005 | /** | 1087 | /** |
| 1006 | * @Author 钱豹 | 1088 | * @Author 钱豹 |
| 1007 | * @Date 13:32 2026/2/6 | 1089 | * @Date 13:32 2026/2/6 |
| @@ -1056,25 +1138,5 @@ public class XlyErpService { | @@ -1056,25 +1138,5 @@ public class XlyErpService { | ||
| 1056 | String sChatMessage = chatiAgent.chat(session.getUserId(), input); | 1138 | String sChatMessage = chatiAgent.chat(session.getUserId(), input); |
| 1057 | return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(sChatMessage).systemText(StrUtil.EMPTY).sReturnType(ReturnTypeCode.HTML.getCode()).build(); | 1139 | return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(sChatMessage).systemText(StrUtil.EMPTY).sReturnType(ReturnTypeCode.HTML.getCode()).build(); |
| 1058 | } | 1140 | } |
| 1059 | - /*** | ||
| 1060 | - * @Author 钱豹 | ||
| 1061 | - * @Date 12:14 2026/3/8 | ||
| 1062 | - * @Param [chatMessage, memoryId] | ||
| 1063 | - * @return void | ||
| 1064 | - * @Description 随便聊聊移除最后一个AI 返回信息 跟系统询问的随便聊聊 | ||
| 1065 | - **/ | ||
| 1066 | - private void removeMssageSbll(List<ChatMessage> chatMessage,String memoryId){ | ||
| 1067 | - if(chatMessage!=null){ | ||
| 1068 | -// operableChatMemoryProvider.deleteSingleMessage(memoryId,chatMessage.get(chatMessage.size()-1)); | ||
| 1069 | - for(int i=chatMessage.size()-1;i>0;i--){ | ||
| 1070 | - ChatMessage data = chatMessage.get(i); | ||
| 1071 | - ChatMessageType sType = data.type(); | ||
| 1072 | - if(ChatMessageType.SYSTEM.equals(sType)){ | ||
| 1073 | - operableChatMemoryProvider.deleteSingleMessage(memoryId,data); | ||
| 1074 | - return; | ||
| 1075 | - } | ||
| 1076 | - } | ||
| 1077 | - } | ||
| 1078 | - } | ||
| 1079 | 1141 | ||
| 1080 | } | 1142 | } |
| 1081 | \ No newline at end of file | 1143 | \ No newline at end of file |
src/main/java/com/xly/tts/service/PythonTtsProxyService.java
| @@ -68,6 +68,26 @@ public class PythonTtsProxyService { | @@ -68,6 +68,26 @@ public class PythonTtsProxyService { | ||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | /** | 70 | /** |
| 71 | + * @Author 钱豹 | ||
| 72 | + * @Date 8:54 2026/4/7 | ||
| 73 | + * @Param [request] | ||
| 74 | + * @return org.springframework.http.ResponseEntity<com.xly.tts.bean.TTSResponseDTO> | ||
| 75 | + * @Description 换一换 | ||
| 76 | + **/ | ||
| 77 | + public ResponseEntity<TTSResponseDTO> change(TTSRequestDTO request) { | ||
| 78 | + String userInput = request.getText(); | ||
| 79 | + String sUserId = request.getUserid(); | ||
| 80 | + String sUserName = request.getUsername(); | ||
| 81 | + String sBrandsId = request.getBrandsid(); | ||
| 82 | + String sSubsidiaryId = request.getSubsidiaryid(); | ||
| 83 | + String sUserType = request.getUsertype(); | ||
| 84 | + String authorization = request.getAuthorization(); | ||
| 85 | + Integer iPage = Integer.valueOf(userInput); | ||
| 86 | + AiResponseDTO voiceText = xlyErpService.change(sUserId, sUserName , sBrandsId , sSubsidiaryId, sUserType, authorization,iPage); | ||
| 87 | + return synthesizeStreamAi(request, voiceText); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + /** | ||
| 71 | * 【保持原有返回类型】AI对话 + 流式TTS | 91 | * 【保持原有返回类型】AI对话 + 流式TTS |
| 72 | */ | 92 | */ |
| 73 | public ResponseEntity<TTSResponseDTO> synthesizeStreamAi(TTSRequestDTO request) { | 93 | public ResponseEntity<TTSResponseDTO> synthesizeStreamAi(TTSRequestDTO request) { |
src/main/java/com/xly/web/TTSStreamController.java
| @@ -77,6 +77,19 @@ public class TTSStreamController { | @@ -77,6 +77,19 @@ public class TTSStreamController { | ||
| 77 | pythonTtsProxyService.shutdown(); | 77 | pythonTtsProxyService.shutdown(); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | + /*** | ||
| 81 | + * @Author 钱豹 | ||
| 82 | + * @Date 8:53 2026/4/7 | ||
| 83 | + * @Param [request] | ||
| 84 | + * @return org.springframework.http.ResponseEntity<com.xly.tts.bean.TTSResponseDTO> | ||
| 85 | + * @Description 换一换 | ||
| 86 | + **/ | ||
| 87 | + @PostMapping(value = "/stream/change", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE}) | ||
| 88 | + public ResponseEntity<TTSResponseDTO> change(@RequestBody TTSRequestDTO request) { | ||
| 89 | + return pythonTtsProxyService.change(request); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + | ||
| 80 | /** | 93 | /** |
| 81 | * 流式合成语音(代理到Python服务) | 94 | * 流式合成语音(代理到Python服务) |
| 82 | */ | 95 | */ |