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)