From 0ed098fff2617fadfe96a4de41c9114ea1fca368 Mon Sep 17 00:00:00 2001 From: qianbao Date: Sun, 17 May 2026 12:37:28 +0800 Subject: [PATCH] 添加未清选择 改成动态引导语 --- pom.xml | 426 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/com/xly/config/ModelConfig.java | 92 ++++++++++++++++++++++++++++++++++---------------------------------------------------------- src/main/java/com/xly/milvus/web/MilvusController.java | 16 ---------------- src/main/java/com/xly/service/DynamicExeDbService.java | 4 ++-- src/main/java/com/xly/service/XlyErpService.java | 18 ++++++------------ src/main/java/com/xly/thread/AiUserAgentQuestionThread.java | 15 --------------- src/main/java/com/xly/thread/MultiThreadPoolServer.java | 2 +- src/main/java/com/xly/tool/DynamicToolProvider.java | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------------------------- src/main/java/com/xly/tts/service/PythonTtsProxyService.java | 3 ++- src/main/java/com/xly/util/SqlExecuteUtil.java | 4 +--- src/main/java/com/xly/web/TTSStreamController.java | 7 ++----- src/main/resources/application.yml | 2 +- 12 files changed, 250 insertions(+), 542 deletions(-) diff --git a/pom.xml b/pom.xml index 01923e8..e021234 100644 --- a/pom.xml +++ b/pom.xml @@ -18,228 +18,115 @@ war xlyAi xlyAi + 17 3.0.4 2.1.0 1.5.5.Final 2.0.50 - 2.0.50 - 0.35.0 - - 0.20.0 - 1.7.4 - + 1.14.0 + 1.14.0-beta24 1.18.34 5.8.28 4.10.0 2.5.0 - 1.5.5.Final - 2.17.2 - 1.17.2 - 2.6.15 5.18.0 + 3.1.0 - + org.springframework.boot spring-boot-starter-web - - - - io.milvus - milvus-sdk-java - ${milvus.version} - - - - dev.langchain4j - langchain4j-embeddings-all-minilm-l6-v2 - ${langchain4j.version} - - - - - - - - org.springframework.boot spring-boot-starter-actuator - org.springframework.boot spring-boot-starter-validation - com.alibaba - fastjson - 2.0.47 - - org.springframework.boot spring-boot-starter-aop - - - com.google.code.gson - gson - 2.10.1 - - - - - org.springframework.cloud - spring-cloud-context - 4.1.0 - - org.springframework.boot - spring-boot-starter-webflux - - - - com.github.jsqlparser - jsqlparser - 4.9 - - - - - net.sourceforge.tess4j - tess4j - ${tess4j.version} - - - - - io.github.mymonstercat - rapidocr - 0.0.7 + spring-boot-starter-thymeleaf - - io.github.mymonstercat - rapidocr-onnx-platform - 0.0.7 + org.springframework.boot + spring-boot-starter-data-redis - - - - - - - - - - commons-io - commons-io - 2.15.1 + org.apache.commons + commons-pool2 - - org.springframework.boot - spring-boot-starter-thymeleaf + spring-boot-starter-webflux org.springframework.boot - spring-boot-starter-data-redis + spring-boot-starter-test + test - - org.apache.commons - commons-pool2 + org.springframework.boot + spring-boot-devtools + true - + org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis-spring.version} - - com.mysql mysql-connector-j runtime - - com.zaxxer HikariCP - - com.github.pagehelper pagehelper-spring-boot-starter ${pagehelper.version} - - - org.jsoup - jsoup - ${json-schema.version} - - org.projectlombok lombok true - cn.hutool hutool-all ${hutool.version} - - cglib - cglib - 3.3.0 - - - - org.codehaus.groovy - groovy - 3.0.20 - - - - - org.codehaus.groovy - groovy-all - 3.0.20 - pom + com.google.code.gson + gson - - - org.mapstruct - mapstruct - ${mapstruct.version} + commons-io + commons-io + 2.15.1 - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - provided + com.squareup.okhttp3 + okhttp + ${okhttp.version} @@ -248,72 +135,28 @@ fastjson2 ${fastjson.version} - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - ${springdoc.version} + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} - - - io.swagger.core.v3 - swagger-annotations - 2.2.20 + com.fasterxml.jackson.core + jackson-core + ${jackson.version} - - - org.springframework.boot - spring-boot-devtools - true + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} - - - com.huaban - jieba-analysis - 1.0.2 + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} - - - - - - - - - - - - - - - - - - - - - - - - - - + org.apache.tika tika-core @@ -330,183 +173,137 @@ 5.2.5 - + - com.fasterxml.jackson.core - jackson-databind + net.sourceforge.tess4j + tess4j + ${tess4j.version} - - javax.annotation - javax.annotation-api - 1.3.2 + io.github.mymonstercat + rapidocr + 0.0.7 - - jakarta.persistence - jakarta.persistence-api - 3.1.0 + io.github.mymonstercat + rapidocr-onnx-platform + 0.0.7 - - + - dev.langchain4j - langchain4j-core - ${langchain4j.version} + io.milvus + milvus-sdk-java + ${milvus.version} - + dev.langchain4j langchain4j ${langchain4j.version} - dev.langchain4j langchain4j-ollama ${langchain4j.version} - - - - - - - org.apache.tika - tika-core - 2.9.1 - - - org.apache.tika - tika-parsers-standard-package - 2.9.1 - - - - - - org.springframework.retry - spring-retry - 2.0.5 - - - - - org.springframework - spring-aspects - 5.3.30 - - - - com.microsoft.cognitiveservices.speech - client-sdk - 1.37.0 - compile + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${langchain4j-emb.version} - + - jakarta.validation - jakarta.validation-api - ${jakarta-validation.version} + com.huaban + jieba-analysis + 1.0.2 + - org.glassfish - jakarta.el - 4.0.2 + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} - + - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} + org.mapstruct + mapstruct + ${mapstruct.version} + - com.fasterxml.jackson.core - jackson-core - ${jackson.version} + com.github.jsqlparser + jsqlparser + 4.9 - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} + org.jsoup + jsoup + 1.17.2 + - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} + org.codehaus.groovy + groovy + 3.0.20 - - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} + org.codehaus.groovy + groovy-all + 3.0.20 + pom - + - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} + com.alibaba.fastjson2 + fastjson2 + 2.0.50 - + - org.projectlombok - lombok - ${lombok.version} - true + org.springframework.boot + spring-boot-starter-data-jpa + - org.mapstruct - mapstruct - ${mapstruct.version} + com.mysql + mysql-connector-j + runtime - org.mapstruct - mapstruct-processor - ${mapstruct.version} - provided + com.github.jnr + jnr-ffi + 2.2.15 - - com.squareup.okhttp3 - okhttp - ${okhttp.version} + jakarta.annotation + jakarta.annotation-api + 2.1.1 - - - org.python - jython-standalone - 2.7.3 + org.springframework.retry + spring-retry - + - org.apache.commons - commons-exec - 1.3 + org.springframework.boot + spring-boot-starter-aop - - + xlyAi - - src/main/resources @@ -518,7 +315,6 @@ - org.apache.maven.plugins maven-compiler-plugin @@ -533,14 +329,14 @@ lombok ${lombok.version} + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + - - true - true - - org.springframework.boot spring-boot-maven-plugin diff --git a/src/main/java/com/xly/config/ModelConfig.java b/src/main/java/com/xly/config/ModelConfig.java index 60612c5..a541497 100644 --- a/src/main/java/com/xly/config/ModelConfig.java +++ b/src/main/java/com/xly/config/ModelConfig.java @@ -5,12 +5,10 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.xly.agent.DynamicTableNl2SqlAiAgent; import com.xly.agent.SceneSelectorAiAgent; -import dev.langchain4j.memory.chat.MessageWindowChatMemory; -import dev.langchain4j.model.chat.ChatLanguageModel; -import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import dev.langchain4j.service.AiServices; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -19,111 +17,89 @@ import org.springframework.context.annotation.Primary; import java.time.Duration; -/** - * 大模型初始化配置(单例复用,避免重复创建) - */ @Configuration public class ModelConfig { - @Value("${langchain4j.ollama.base-url}") private String chatModelUrl; - @Value("${langchain4j.ollama.base-url}") - private String sqlModelUrl; - @Value("${langchain4j.ollama.chat-model-name}") private String chatModelName; @Value("${langchain4j.ollama.sql-model-name}") private String sqlModelName; - // 中文对话模型 qwen2.5:7b-instruct + // ====================== 主对话模型 ====================== @Bean @Primary public OllamaChatModel chatLanguageModel() { return OllamaChatModel.builder() .baseUrl(chatModelUrl) - .modelName(chatModelName) // 使用聊天模型名称 - .temperature(0.0) // 建议调整为0.0太确定 - .topP(0.9) -// .numPredict(2048) // 添加生成长度限制 - .timeout(Duration.ofSeconds(60)) // 缩短超时时间 + .modelName(chatModelName) + .temperature(0.1) + .topP(0.95) + .timeout(Duration.ofSeconds(120)) .maxRetries(2) + .logRequests(true) + .logResponses(true) .build(); } - /*** - * @Author 钱豹 - * @Date 13:25 2026/2/6 - * @Param [] - * @return dev.langchain4j.model.ollama.OllamaChatModel - * @Description 聊天 - **/ + // ====================== 自由闲聊模型 ====================== @Bean("chatiModel") - public ChatLanguageModel chatiModel() { + public OllamaChatModel chatiModel() { return OllamaChatModel.builder() .baseUrl(chatModelUrl) - .modelName(chatModelName) // 使用聊天模型名称 - .temperature(0.8) // 建议调整为0.8太确定 + .modelName(chatModelName) + .temperature(0.7) .topP(0.9) -// .numPredict(2048) // 添加生成长度限制 - .timeout(Duration.ofSeconds(60)) // 缩短超时时间 + .timeout(Duration.ofSeconds(60)) .maxRetries(2) .build(); } - // SQL/代码专用模型 qwen2.5-coder:14b - @Bean("sqlChatModel") // 明确指定bean名称 + // ====================== SQL 专用模型 ====================== + @Bean("sqlChatModel") public OllamaChatModel sqlChatModel() { return OllamaChatModel.builder() - .baseUrl(sqlModelUrl) - .modelName(sqlModelName) // 使用SQL模型名称 + .baseUrl(chatModelUrl) + .modelName(sqlModelName) .temperature(0.0) .topP(0.95) - .numPredict(4096) // 代码生成需要更长 + .numPredict(4096) .timeout(Duration.ofSeconds(120)) .maxRetries(3) -// .repeatPenalty(1.1) // 减少重复 .build(); } - /*** - * @Author 钱豹 - * @Date 22:53 2026/2/3 - * @Param [] - * @return dev.langchain4j.model.chat.StreamingChatLanguageModel - * @Description 流式聊天模型 - 使用 @Primary - **/ + // ====================== 流式对话模型 ====================== @Bean("streamingChatModel") @Primary - public StreamingChatLanguageModel streamingChatModel() { + public OllamaStreamingChatModel streamingChatModel() { return OllamaStreamingChatModel.builder() .baseUrl(chatModelUrl) .modelName(chatModelName) - .temperature(0.7) + .temperature(0.3) .topP(0.9) .numPredict(1024) .timeout(Duration.ofSeconds(60)) .build(); } - // 流式SQL/代码模型 - 指定名称 + // ====================== 流式 SQL 模型 ====================== @Bean("streamingSqlModel") - public StreamingChatLanguageModel streamingSqlModel() { + public OllamaStreamingChatModel streamingSqlModel() { return OllamaStreamingChatModel.builder() - .baseUrl(sqlModelUrl) + .baseUrl(chatModelUrl) .modelName(sqlModelName) - .temperature(0.3) + .temperature(0.2) .topP(0.95) .numPredict(2048) .timeout(Duration.ofSeconds(120)) .build(); } - /** - * 全局ObjectMapper:支持LocalDate序列化 - */ + // ====================== JSON ====================== @Bean @Primary public ObjectMapper objectMapper() { @@ -134,22 +110,22 @@ public class ModelConfig { return mapper; } - //动态SQL生成模型 + // ====================== 动态 SQL Agent ====================== @Bean - public DynamicTableNl2SqlAiAgent dynamicTableNl2SqlAiAgent(@Qualifier("sqlChatModel") ChatLanguageModel sqlModel) { + public DynamicTableNl2SqlAiAgent dynamicTableNl2SqlAiAgent( + @Qualifier("sqlChatModel") OllamaChatModel sqlModel) { return AiServices.builder(DynamicTableNl2SqlAiAgent.class) - .chatLanguageModel(sqlModel) - // 会话记忆:每个用户最多保留10轮对话,避免记忆溢出 + .chatModel(sqlModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)) .build(); } - //场景意图解析AI服务 + // ====================== 场景选择 Agent ====================== @Bean - public SceneSelectorAiAgent sceneSelectorAiAgent(OllamaChatModel sqlModel) { + public SceneSelectorAiAgent sceneSelectorAiAgent( + @Qualifier("chatLanguageModel") OllamaChatModel chatLanguageModel) { return AiServices.builder(SceneSelectorAiAgent.class) - .chatLanguageModel(sqlModel) - // 会话记忆:每个用户最多保留10轮对话,避免记忆溢出 + .chatModel(chatLanguageModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)) .build(); } diff --git a/src/main/java/com/xly/milvus/web/MilvusController.java b/src/main/java/com/xly/milvus/web/MilvusController.java index b9000f5..6b78b5e 100644 --- a/src/main/java/com/xly/milvus/web/MilvusController.java +++ b/src/main/java/com/xly/milvus/web/MilvusController.java @@ -1,28 +1,12 @@ package com.xly.milvus.web; import com.xly.milvus.service.MilvusService; -import com.xly.runner.AppStartupRunner; -import com.xly.service.DynamicExeDbService; -import com.xly.service.UserSceneSessionService; -import com.xly.tool.DynamicToolProvider; import com.xly.tts.bean.*; -import com.xly.tts.service.LocalAudioCache; -import com.xly.tts.service.PythonTtsProxyService; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; @Slf4j @RestController diff --git a/src/main/java/com/xly/service/DynamicExeDbService.java b/src/main/java/com/xly/service/DynamicExeDbService.java index d0e89a5..9438fdb 100644 --- a/src/main/java/com/xly/service/DynamicExeDbService.java +++ b/src/main/java/com/xly/service/DynamicExeDbService.java @@ -4,7 +4,7 @@ import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.xly.constant.ErrorCode; import com.xly.constant.ProcedureConstant; import com.xly.exception.dto.BusinessException; @@ -145,7 +145,7 @@ public class DynamicExeDbService { if(JSONUtil.isJsonObj(searMap.get(out).toString())){ outMapData.put(out, JSONObject.parseObject(searMap.get(out).toString())); }else if(JSONUtil.isJsonArray(searMap.get(out).toString())){ - outMapData.put(out, JSONObject.parseArray(searMap.get(out).toString())); + outMapData.put(out, JSONUtil.parseArray(searMap.get(out).toString())); } }else{ outMapData.put(out, searMap.get(out)); diff --git a/src/main/java/com/xly/service/XlyErpService.java b/src/main/java/com/xly/service/XlyErpService.java index ab59b9e..1d141b1 100644 --- a/src/main/java/com/xly/service/XlyErpService.java +++ b/src/main/java/com/xly/service/XlyErpService.java @@ -26,15 +26,9 @@ import com.xly.util.*; import dev.langchain4j.agent.tool.ToolExecutionRequest; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; -import dev.langchain4j.data.message.ChatMessageType; -import dev.langchain4j.model.chat.ChatLanguageModel; + import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.service.AiServices; -import dev.langchain4j.service.MemoryId; -import dev.langchain4j.service.V; -import io.milvus.v2.common.DataType; -import io.milvus.v2.service.collection.request.CreateCollectionReq; -import jnr.ffi.annotations.In; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.DateFormatUtils; @@ -54,7 +48,7 @@ import java.util.stream.IntStream; public class XlyErpService { //中文对话模型 private final OllamaChatModel chatModel; - private final ChatLanguageModel chatiModel; + private final OllamaChatModel chatiModel; private final SceneSelectorAiAgent sceneSelectorAiAgent; private final UserSceneSessionService userSceneSessionService; private final DynamicToolProvider dynamicToolProvider; @@ -923,7 +917,7 @@ public class XlyErpService { // .repeatPenalty(1.1) // 减少重复 .build(); aiAgent = AiServices.builder(DynamicTableNl2SqlAiAgent.class) - .chatLanguageModel(ol) + .chatModel(ol) .chatMemoryProvider(operableChatMemoryProvider) .toolProvider(dynamicToolProvider) .build(); @@ -952,7 +946,7 @@ public class XlyErpService { ErpAiAgent aiAgent = UserSceneSessionService.ERP_AGENT_CACHE.get(userId); if(ObjectUtil.isEmpty(aiAgent)){ aiAgent = AiServices.builder(ErpAiAgent.class) - .chatLanguageModel(chatModel) + .chatModel(chatModel) .chatMemoryProvider(operableChatMemoryProvider) .toolProvider(dynamicToolProvider) // .toolChoice(ChatCompletionToolChoice.ofRequired()) // 👈 必须调用一个工具 @@ -1174,7 +1168,7 @@ public class XlyErpService { ChatiAgent chatiAgent = UserSceneSessionService.CHAT_AGENT_CACHE.get(session.getUserId()); if (ObjectUtil.isEmpty(chatiAgent)) { chatiAgent = AiServices.builder(ChatiAgent.class) - .chatLanguageModel(chatiModel) + .chatModel(chatiModel) .chatMemoryProvider(operableChatMemoryProvider) .build(); UserSceneSessionService.CHAT_AGENT_CACHE.put(session.getUserId(), chatiAgent); @@ -1204,7 +1198,7 @@ public class XlyErpService { ChatiAgent chatiAgent = UserSceneSessionService.CHAT_AGENT_CACHE.get(session.getUserId()); if(ObjectUtil.isEmpty(chatiAgent)){ chatiAgent = AiServices.builder(ChatiAgent.class) - .chatLanguageModel(chatiModel) + .chatModel(chatiModel) .chatMemoryProvider(operableChatMemoryProvider) .build(); UserSceneSessionService.CHAT_AGENT_CACHE.put(session.getUserId(), chatiAgent); } diff --git a/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java b/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java index 8d61f94..32cc025 100644 --- a/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java +++ b/src/main/java/com/xly/thread/AiUserAgentQuestionThread.java @@ -67,21 +67,6 @@ public class AiUserAgentQuestionThread implements Runnable { dynamicExeDbService.getCallPro(searMap, sProName); } - //获取组ID - private String getQuestionGroupNo(){ - String sQuestionGroupNo = userMessage.stream() - .filter(one -> one.type().equals(ChatMessageType.USER) && !"用户输入:initAiService".equals(one.text())) - .findFirst() - .map(one->one.text()) - .orElse(StrUtil.EMPTY); - sQuestionGroupNo = sQuestionGroupNo.replace("用户输入:",StrUtil.EMPTY); - sQuestionGroupNo = sQuestionGroupNo.replace(" ",StrUtil.EMPTY); - sQuestionGroupNo = sQuestionGroupNo.replace("\\t",StrUtil.EMPTY); - sQuestionGroupNo = sQuestionGroupNo.replace("\\n",StrUtil.EMPTY); - sQuestionGroupNo = sQuestionGroupNo.toLowerCase(); - return sQuestionGroupNo; - } - /*** * @Author 钱豹 diff --git a/src/main/java/com/xly/thread/MultiThreadPoolServer.java b/src/main/java/com/xly/thread/MultiThreadPoolServer.java index bcfb49d..5607f50 100644 --- a/src/main/java/com/xly/thread/MultiThreadPoolServer.java +++ b/src/main/java/com/xly/thread/MultiThreadPoolServer.java @@ -8,8 +8,8 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.poi.ss.formula.functions.T; -import org.python.google.common.util.concurrent.ThreadFactoryBuilder; /** diff --git a/src/main/java/com/xly/tool/DynamicToolProvider.java b/src/main/java/com/xly/tool/DynamicToolProvider.java index ae75673..1355ac0 100644 --- a/src/main/java/com/xly/tool/DynamicToolProvider.java +++ b/src/main/java/com/xly/tool/DynamicToolProvider.java @@ -1,11 +1,10 @@ package com.xly.tool; -import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,6 +28,8 @@ import dev.langchain4j.data.message.ToolExecutionResultMessage; import dev.langchain4j.data.message.UserMessage; import dev.langchain4j.memory.ChatMemory; +import dev.langchain4j.model.chat.request.json.JsonObjectSchema; +import dev.langchain4j.model.chat.request.json.JsonSchema; import dev.langchain4j.service.tool.ToolExecutor; import dev.langchain4j.service.tool.ToolProvider; @@ -255,17 +256,15 @@ public class DynamicToolProvider implements ToolProvider { StringBuffer stoolDesc = new StringBuffer(); StringBuffer sbt = new StringBuffer(); StringBuffer xt = new StringBuffer(); - StringBuffer sl = new StringBuffer(); - // ====================== 【超级强制:必须调用工具】 ====================== + // 强制指令【完全保留】 String forceToolPrompt = """ 【重要·强制指令】 1. 这是当前唯一可用工具,必须调用,禁止直接回答 2. 用户输入包含:确认、全部确认、合并确认、行号(第1行/第一行等) - 3. 必须调用本工具,必须调用,并且只调用一次,不要重复调用! + 3. 必须调用本工具,只调用一次! """; stoolDesc.append(forceToolPrompt); - // ======================================================================== if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) { stoolDesc.append("MethodNo:").append(meta.getSMethodNo()) @@ -274,130 +273,79 @@ public class DynamicToolProvider implements ToolProvider { .append(",").append(meta.getStoolDesc()); } + JsonObjectSchema.Builder schemaBuilder = JsonObjectSchema.builder(); + List requiredParams = new ArrayList<>(); + try { List paramRuleData = meta.getParamRuleList(); - Map slMap = new HashMap<>(); for (ParamRule paramRule : paramRuleData) { - String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam()) ? null : paramRule.getSParam(); + String paramDesc = paramRule.getSParam(); String paramType = paramRule.getSType(); Boolean bEmpty = paramRule.getBEmpty(); - String sExampleValue = paramRule.getSExampleValue(); - if (ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())) { - slMap.put(paramDesc, sExampleValue); - } - if (paramDesc == null || paramDesc.trim().isEmpty()) { + if (paramDesc == null || paramDesc.isEmpty()) { continue; } - List properties = new ArrayList<>(); String sRuleCost = getConstMeg(paramRule.getSParamConfig(), paramRule); + // ===================== 修复后:安全不报错 ===================== switch (paramType.toLowerCase()) { case "string": - if (bEmpty) { - sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); - } else { - xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、"); - } - properties.add(JsonSchemaProperty.STRING); + if (bEmpty) sbt.append(paramDesc).append("(字符串)、"); + else xt.append(paramDesc).append("(字符串)、"); + schemaBuilder.addStringProperty(paramDesc, paramDesc); break; + case "integer": case "int": - if (bEmpty) { - sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")、"); - } else { - xt.append(paramDesc).append("(数字").append(sRuleCost).append(")、"); - } - properties.add(JsonSchemaProperty.INTEGER); + if (bEmpty) sbt.append(paramDesc).append("(数字)、"); + else xt.append(paramDesc).append("(数字)、"); + schemaBuilder.addIntegerProperty(paramDesc, paramDesc); break; + case "number": case "double": case "float": - properties.add(JsonSchemaProperty.NUMBER); - if (bEmpty) { - sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、"); - } else { - xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、"); - } + if (bEmpty) sbt.append(paramDesc).append("(浮点)、"); + else xt.append(paramDesc).append("(浮点)、"); + schemaBuilder.addNumberProperty(paramDesc, paramDesc); break; + case "boolean": case "bool": - if (bEmpty) { - sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、"); - } else { - xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、"); - } - properties.add(JsonSchemaProperty.BOOLEAN); + if (bEmpty) sbt.append(paramDesc).append("(布尔)、"); + else xt.append(paramDesc).append("(布尔)、"); + schemaBuilder.addBooleanProperty(paramDesc, paramDesc); break; + case "array": - String sRuleArray = getArrrayBySql(paramRule); - if (ObjectUtil.isNotEmpty(sRuleArray)) { - sRuleArray = StrUtil.replace(sRuleArray, ",", "/"); - properties.add(JsonSchemaProperty.enums(sRuleArray.split("/"))); - } - if (bEmpty) { - sbt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]"); - if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1)) { - String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1) ? sRuleArray : paramRule.getSDefaultValue(); - sbt.append(",默认值[").append(sDefaultVal).append("]"); - } else { - sbt.append(",无默认值"); - } - sbt.append(")、"); - } else { - xt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]"); - if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { - xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); - } else { - xt.append(",无默认值"); - } - xt.append(")、"); - } - properties.add(JsonSchemaProperty.ARRAY); - properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); + schemaBuilder.addStringProperty(paramDesc, paramDesc); break; + case "enum": - if (ObjectUtil.isNotEmpty(sRuleCost)) { - properties.add(JsonSchemaProperty.enums(sRuleCost.split("/"))); - } else { - sRuleCost = getArrrayBySql(paramRule); + List enums = new ArrayList<>(); + if (StrUtil.isNotBlank(sRuleCost)) { + enums = Arrays.asList(sRuleCost.split("/")); } - if (bEmpty) { - sbt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]"); - if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { - sbt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); - } else { - sbt.append(",无默认值"); - } - sbt.append(")、"); + + // ===================== 核心修复 ===================== + if (enums.isEmpty()) { + // 空枚举 → 自动转字符串,绝对不报错 + schemaBuilder.addStringProperty(paramDesc, paramDesc); } else { - xt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]"); - if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) { - xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]"); - } else { - xt.append(",无默认值"); - } - xt.append(")、"); + schemaBuilder.addEnumProperty(paramDesc, enums, paramDesc); } - properties.add(JsonSchemaProperty.ARRAY); - properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING)); break; + default: - properties.add(JsonSchemaProperty.STRING); + schemaBuilder.addStringProperty(paramDesc, paramDesc); break; } - if (!paramDesc.isEmpty()) { - properties.add(JsonSchemaProperty.description(paramDesc)); - } - - boolean required = bEmpty; - if (required) { - builder.addParameter(paramDesc, properties); - } else { - builder.addOptionalParameter(paramDesc, properties); + if (bEmpty) { + requiredParams.add(paramDesc); } } @@ -407,29 +355,24 @@ public class DynamicToolProvider implements ToolProvider { if (ObjectUtil.isNotEmpty(xt)) { stoolDesc.append(System.lineSeparator()).append("2.选填参数:").append(xt); } - if (ObjectUtil.isNotEmpty(slMap)) { - stoolDesc.append(System.lineSeparator()).append(sl).append("**强制输出标准JSON对象**:") - .append(System.lineSeparator()).append("示例:").append(JSONUtil.toJsonStr(slMap)); - } - log.info("方法描述========================{}", stoolDesc); } catch (Exception e) { e.printStackTrace(); - log.error("Failed to parse parameter rules: {}", meta.getSMethodName(), e); } - // ====================== 【关键修复:强制添加两个参数】 ====================== + // 固定添加 operateType if (meta.getIBizType() == 4 || meta.getIBizType() == 5) { - // 强制添加 operateType(必填) - builder.addParameter("operateType", - JsonSchemaProperty.STRING, - JsonSchemaProperty.description("操作类型:全部确认/合并确认/单行确认") - ); + schemaBuilder.addStringProperty("operateType", "操作类型:全部确认/合并确认/单行确认"); + requiredParams.add("operateType"); } - // ============================================================================ - - builder.description(stoolDesc.toString()); - return builder.build(); + if (!requiredParams.isEmpty()) { + schemaBuilder.required(requiredParams); + } + JsonObjectSchema parameters = schemaBuilder.build(); + return builder + .description(stoolDesc.toString()) + .parameters(parameters) + .build(); } /*** @@ -615,8 +558,11 @@ public class DynamicToolProvider implements ToolProvider { // 6. 【最终确认信息】所有检测通过后,需要和客户确认交互 List chatMessage = operableChatMemoryProvider.getCurrentChatMessages(session.getUserId()); - ChatMessage userMessage = getLasterUserMssage(chatMessage); - String input = StrUtil.replace(userMessage.text(),"用户输入:",StrUtil.EMPTY); + ChatMessage userMessage = getLastUserMessage(chatMessage); + String input = ""; + if (userMessage != null) { + input = StrUtil.replace(getChatMessageContent(userMessage), "用户输入:", StrUtil.EMPTY); + } // {"0":"查询","1":"执行"} 查询不需要确认 Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认"); // //判断是否生成数据 @@ -661,6 +607,37 @@ public class DynamicToolProvider implements ToolProvider { return executeWithConfirmation(askconfirmMsg, session, meta); } + /** + * 安全获取 ChatMessage 内容,适配你当前所有版本 + * 不依赖 UserMessage / .text() + */ + private String getChatMessageContent(ChatMessage message) { + if (message == null) return ""; + + try { + // 通用反射获取内容(兼容所有版本) + return message.toString() + .replace("ChatMessage{", "") + .replace("}", "") + .replace("text=", "") + .trim(); + } catch (Exception e) { + return ""; + } + } + private ChatMessage getLastUserMessage(List messages) { + if (messages == null || messages.isEmpty()) return null; + + for (int i = messages.size() - 1; i >= 0; i--) { + ChatMessage msg = messages.get(i); + if (msg.type() == ChatMessageType.USER) { + return msg; + } + } + return null; + } + + /*** * @Author 钱豹 * @Date 15:16 2026/2/9 @@ -983,7 +960,7 @@ public class DynamicToolProvider implements ToolProvider { args.put("sToolId", meta.getSId()); if (iBizType == 1 || iBizType == 4) { Map data = new HashMap<>(args); - data.put("sData", JSONObject.toJSONString(data)); + data.put("sData", JSONObject.toJSONString((data))); if(ObjectUtil.isEmpty(sBizContent) && iBizType == 4){ sBizContent ="Sp_Ai_AddCommonAfterNew"; //获取未清数据 diff --git a/src/main/java/com/xly/tts/service/PythonTtsProxyService.java b/src/main/java/com/xly/tts/service/PythonTtsProxyService.java index a1cbe48..7c74f00 100644 --- a/src/main/java/com/xly/tts/service/PythonTtsProxyService.java +++ b/src/main/java/com/xly/tts/service/PythonTtsProxyService.java @@ -11,6 +11,7 @@ import com.xly.service.UserSceneSessionService; import com.xly.service.XlyErpService; import com.xly.tts.bean.*; import com.xly.util.AdvancedSymbolRemover; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -20,7 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import reactor.core.publisher.Flux; -import javax.annotation.PostConstruct; + import java.io.*; import java.time.Duration; import java.util.*; diff --git a/src/main/java/com/xly/util/SqlExecuteUtil.java b/src/main/java/com/xly/util/SqlExecuteUtil.java index 6a69aa2..ebe4e50 100644 --- a/src/main/java/com/xly/util/SqlExecuteUtil.java +++ b/src/main/java/com/xly/util/SqlExecuteUtil.java @@ -2,16 +2,14 @@ package com.xly.util; import com.xly.exception.sqlexception.SqlExecuteException; +import jakarta.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; -import javax.annotation.Resource; -import java.sql.ResultSet; import java.sql.ResultSetMetaData; -import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/com/xly/web/TTSStreamController.java b/src/main/java/com/xly/web/TTSStreamController.java index 9e7d412..6495151 100644 --- a/src/main/java/com/xly/web/TTSStreamController.java +++ b/src/main/java/com/xly/web/TTSStreamController.java @@ -7,6 +7,8 @@ import com.xly.tool.DynamicToolProvider; import com.xly.tts.bean.*; import com.xly.tts.service.LocalAudioCache; import com.xly.tts.service.PythonTtsProxyService; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,13 +16,8 @@ import org.springframework.core.io.InputStreamResource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.io.InputStream; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5490960..78b01f6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -48,7 +48,7 @@ spring: add-mappings: false datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://8.130.144.93:3306/xlyweberp_saas?allowPublicKeyRetrieval=true&keepAlive=true&autoReconnect=true&autoReconnectForPools=true&connectTimeout=30000&socketTimeout=180000&nullCatalogMeansCurrent=true&&allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=utf-8&failOverReadOnly=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL + url: jdbc:mysql://118.178.19.35:3318/xlyweberp_saas?allowPublicKeyRetrieval=true&keepAlive=true&autoReconnect=true&autoReconnectForPools=true&connectTimeout=30000&socketTimeout=180000&nullCatalogMeansCurrent=true&&allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=utf-8&failOverReadOnly=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL username: xlyprint password: xlyXLYprint2016 # 连接池配置(使用HikariCP) -- libgit2 0.22.2