Commit 148bdd8c0a43e97412156fb5d65e55320c74cfd3

Authored by qianbao
1 parent 0c696213

添加未清选择 改成动态引导语

src/main/java/com/xly/agent/ErpAiAgent.java
1 1 package com.xly.agent;
2 2  
3 3  
4   -import dev.langchain4j.service.MemoryId;
5   -import dev.langchain4j.service.SystemMessage;
6   -import dev.langchain4j.service.UserMessage;
7   -import dev.langchain4j.service.V;
  4 +import dev.langchain4j.service.*;
8 5  
9 6 /**
10 7 * 优化后:新增场景专属交互规则,大模型仅处理当前场景业务指令
... ... @@ -13,7 +10,7 @@ public interface ErpAiAgent {
13 10  
14 11 @SystemMessage("{{sSystemPrompt}}")
15 12 @UserMessage("用户输入:{{userInput}}")
16   - String chat(
  13 + Result<String> chat(
17 14 @MemoryId String userId,
18 15 @V("userInput") String userInput,
19 16 @V("sSystemPrompt") String sSystemPrompt
... ...
src/main/java/com/xly/service/XlyErpService.java
... ... @@ -22,13 +22,18 @@ import com.xly.thread.AiSqlErrorHistoryThread;
22 22 import com.xly.thread.AiUserAgentQuestionThread;
23 23 import com.xly.thread.MultiThreadPoolServer;
24 24 import com.xly.tool.DynamicToolProvider;
  25 +import com.xly.tool.ToolSpecificationHolder;
25 26 import com.xly.util.*;
  27 +import dev.langchain4j.agent.tool.ReturnBehavior;
26 28 import dev.langchain4j.agent.tool.ToolExecutionRequest;
  29 +import dev.langchain4j.agent.tool.ToolSpecification;
27 30 import dev.langchain4j.data.message.AiMessage;
28 31 import dev.langchain4j.data.message.ChatMessage;
29 32  
30 33 import dev.langchain4j.model.ollama.OllamaChatModel;
31 34 import dev.langchain4j.service.AiServices;
  35 +import dev.langchain4j.service.Result;
  36 +import dev.langchain4j.service.tool.ToolExecutor;
32 37 import lombok.RequiredArgsConstructor;
33 38 import lombok.extern.slf4j.Slf4j;
34 39 import org.apache.commons.lang3.time.DateFormatUtils;
... ... @@ -131,7 +136,8 @@ public class XlyErpService {
131 136 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName())
132 137 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo()))
133 138 ){
134   - sResponMessage = aiAgent.chat(userId, input , AgentSystemPrompt.sSystemPrompt);
  139 + Result<String> rs = aiAgent.chat(userId, input , AgentSystemPrompt.sSystemPrompt);
  140 + sResponMessage = rs.content();
135 141 }
136 142  
137 143 if(ObjectUtil.isNotEmpty(session.getCurrentTool())
... ... @@ -350,7 +356,8 @@ public class XlyErpService {
350 356 if(ObjectUtil.isNotEmpty(session.getSSystemPrompt()) && isConfirmed){
351 357 sSystemPrompt = session.getSSystemPrompt();
352 358 }
353   - sResponMessage = aiAgent.chat(userId, input,sSystemPrompt);
  359 + Result<String> rs = aiAgent.chat(userId, input,sSystemPrompt);
  360 + sResponMessage = rs.content();
354 361 }
355 362 methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSMethodName():StrUtil.EMPTY;
356 363 if(ObjectUtil.isNotEmpty(session.getCurrentTool())
... ... @@ -945,19 +952,24 @@ public class XlyErpService {
945 952 // 4. 获取/创建用Agent
946 953 ErpAiAgent aiAgent = UserSceneSessionService.ERP_AGENT_CACHE.get(userId);
947 954 if(ObjectUtil.isEmpty(aiAgent)){
  955 + List<ToolSpecificationHolder> dataList = dynamicToolProvider.sceneToolCacheMap.get(session.getCurrentScene().getSId());
  956 + Set<String> immediateReturnToolNames = new HashSet<>();
  957 + Map<ToolSpecification, ToolExecutor> executors = new HashMap<>();
  958 + if(ObjectUtil.isNotEmpty(dataList)){
  959 + dataList.forEach(one->{
  960 + immediateReturnToolNames.add(one.getsName());
  961 + executors.put(one.getToolSpecification(),one.getToolExecutor());
  962 + });
  963 + }
948 964 aiAgent = AiServices.builder(ErpAiAgent.class)
949 965 .chatModel(chatModel)
950 966 .chatMemoryProvider(operableChatMemoryProvider)
951   - .toolProvider(dynamicToolProvider)
952   -// .toolChoice(ChatCompletionToolChoice.ofRequired()) // 👈 必须调用一个工具
  967 + .tools(executors,immediateReturnToolNames)
  968 +// .toolProvider(dynamicToolProvider)
  969 +// .returnBehavior(ReturnBehavior.IMMEDIATE)
  970 +// .toolChoice(ChatCompletionToolChoice.ofRequired())
953 971 .build();
954 972 UserSceneSessionService.ERP_AGENT_CACHE.put(userId, aiAgent);
955   - // 初始化AiService 以防止热加载太慢 找不到相应的方法
956   -// try{
957   -// aiAgent.chat(userId, "initAiService",AgentSystemPrompt.sSystemPrompt);
958   -// }catch (Exception e){
959   -// e.printStackTrace();
960   -// }
961 973 log.info("用户{}Agent构建完成,已选场景:{},场景ID{}", userId, session.isSceneSelected() ? session.getCurrentScene().getSSceneName() : "未选(全场景匹配)", dynamicToolProvider.sSceneIdMap.get(userId));
962 974 }
963 975 return aiAgent;
... ...
src/main/java/com/xly/tool/DynamicToolProvider.java
... ... @@ -14,6 +14,7 @@ import com.xly.config.OperableChatMemoryProvider;
14 14 import com.xly.constant.*;
15 15 import com.xly.entity.*;
16 16 import com.xly.exception.dto.BusinessException;
  17 +import com.xly.exception.dto.DataException;
17 18 import com.xly.mapper.ParamRuleMapper;
18 19 import com.xly.mapper.ToolMetaMapper;
19 20 import com.xly.service.DynamicExeDbService;
... ... @@ -30,12 +31,7 @@ import dev.langchain4j.memory.ChatMemory;
30 31  
31 32 import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
32 33 import dev.langchain4j.model.chat.request.json.JsonSchema;
33   -import dev.langchain4j.service.tool.ToolExecutor;
34   -import dev.langchain4j.service.tool.ToolProvider;
35   -
36   -import dev.langchain4j.service.tool.ToolProviderRequest;
37   -
38   -import dev.langchain4j.service.tool.ToolProviderResult;
  34 +import dev.langchain4j.service.tool.*;
39 35  
40 36  
41 37 import lombok.Getter;
... ... @@ -59,58 +55,42 @@ import java.util.stream.IntStream;
59 55 @RequiredArgsConstructor
60 56 public class DynamicToolProvider implements ToolProvider {
61 57  
62   - // private final ToolMetaMapper toolMetaMapper;
63 58 private final ObjectMapper objectMapper;
64 59 private final ToolMetaMapper toolMetaMapper;
65 60 private final ParamRuleMapper paramRuleMapper;
66 61 private final DynamicExeDbService dynamicExeDbService;
67 62 private final OperableChatMemoryProvider operableChatMemoryProvider;
68 63  
69   -
70   - // 内存缓存:toolName -> ToolSpecificationHolder
71 64 private final Map<String, ToolSpecificationHolder> toolCache = new ConcurrentHashMap<>();
72 65 public final Map<String, String> sSceneIdMap = new ConcurrentHashMap<>();
73   -
74   - private final Map<String, List<ToolSpecificationHolder>> sceneToolCacheMap = new ConcurrentHashMap<>();
  66 + public final Map<String, List<ToolSpecificationHolder>> sceneToolCacheMap = new ConcurrentHashMap<>();
75 67 private final List<ParamRule> paramRuleDataAll = new ArrayList<>();
76 68  
77 69 @Value("${erp.baseurl}")
78 70 private String baseUrl;
79 71  
80   - /***
81   - * @Author 钱豹
82   - * @Date 14:05 2026/2/10
83   - * @Param []
84   - * @return void
85   - * @Description 移除所有动态方法缓存
86   - **/
87 72 public void cleanAllToolProvider() {
88 73 toolCache.clear();
89 74 sceneToolCacheMap.clear();
90 75 paramRuleDataAll.clear();
91 76 }
92 77  
93   - /**
94   - * 初始化时加载所有启用的工具到缓存
95   - */
96 78 @jakarta.annotation.PostConstruct
97 79 public void init() {
98   - //
99 80 List<ToolMeta> metas = toolMetaMapper.findAll();
100 81 for (ToolMeta meta : metas) {
101 82 try {
102   - //补全业务类型查询类显示的字段
103 83 doSetToolAIshowfieldShow(meta);
104 84 ToolSpecification spec = buildToolSpecification(meta);
105 85 ToolExecutor executor = createToolExecutor(meta);
106   - toolCache.put(meta.getSMethodNo(), new ToolSpecificationHolder(spec, executor));
  86 + toolCache.put(meta.getSMethodNo(), new ToolSpecificationHolder(spec, executor,meta.getSMethodNo()));
107 87 log.info("已加载动态工具:{}", meta.getSMethodNo());
108 88 String sceneId = meta.getSSceneId();
109 89 List<ToolSpecificationHolder> dataList = new ArrayList<>();
110 90 if(ObjectUtil.isNotEmpty(sceneToolCacheMap.get(sceneId))) {
111 91 dataList = sceneToolCacheMap.get(sceneId);
112 92 }
113   - dataList.add(new ToolSpecificationHolder(spec, executor));
  93 + dataList.add(new ToolSpecificationHolder(spec, executor,meta.getSMethodNo()));
114 94 sceneToolCacheMap.put(sceneId, dataList);
115 95 } catch (Exception e) {
116 96 e.printStackTrace();
... ... @@ -119,8 +99,136 @@ public class DynamicToolProvider implements ToolProvider {
119 99 }
120 100 }
121 101  
122   - //补全业务类型查询类显示的字段
123   - //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"窗体查询","5":"按钮执行","6":"其它"}
  102 + @Override
  103 + public ToolProviderResult provideTools(ToolProviderRequest request) {
  104 + String sUserId = request.chatMemoryId().toString();
  105 + Map<ToolSpecification, ToolExecutor> executors = new HashMap<>();
  106 + UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(sUserId);
  107 +
  108 + // ====================== 防重复调用核心拦截 ======================
  109 + if (session != null && (session.getToolExecuted() || StrUtil.isNotBlank(session.getSFunPrompts()))) {
  110 + log.info("工具已执行/等待用户回复,禁止提供工具");
  111 + return ToolProviderResult.builder().build();
  112 + }
  113 +
  114 + List<ToolSpecificationHolder> datalist = new ArrayList<>();
  115 + List<ToolMeta> toolMetaAll = new ArrayList<>();
  116 +
  117 + if(session.getCurrentTool() != null){
  118 + toolMetaAll.add(session.getCurrentTool());
  119 + log.info("使用 currentTool: {}", session.getCurrentTool().getSMethodNo());
  120 + } else {
  121 + toolMetaAll = session.getAuthTool();
  122 + }
  123 +
  124 + if(ObjectUtil.isNotEmpty(toolMetaAll)){
  125 + toolMetaAll = toolMetaAll.stream()
  126 + .filter(to -> to.getSSceneId().equals(sSceneIdMap.get(sUserId)))
  127 + .collect(Collectors.toUnmodifiableList());
  128 +
  129 + if(ObjectUtil.isNotEmpty(toolMetaAll)){
  130 + toolMetaAll.forEach(to -> {
  131 + ToolSpecificationHolder holder = toolCache.get(to.getSMethodNo());
  132 + if (holder != null) {
  133 + datalist.add(holder);
  134 + log.debug("添加工具到提供器: {}", to.getSMethodNo());
  135 + } else {
  136 + log.warn("工具缓存缺失: {}", to.getSMethodNo());
  137 + }
  138 + });
  139 + }
  140 + }
  141 +
  142 + datalist.forEach(holder -> {
  143 + executors.put(holder.getToolSpecification(), holder.getToolExecutor());
  144 + });
  145 + log.info("provideTools 返回工具数量: {}", executors.size());
  146 + return ToolProviderResult.builder().addAll(executors).build();
  147 + }
  148 +
  149 +
  150 + /***
  151 + * @Author 钱豹
  152 + * @Date 23:59 2026/5/17
  153 + * @Param [meta]
  154 + * @return dev.langchain4j.service.tool.ToolExecutor
  155 + * @Description AI 自动调用工具执行方法
  156 + **/
  157 + private ToolExecutor createToolExecutor(ToolMeta meta) {
  158 + log.info("创建工具执行器: {}", meta.getSMethodNo());
  159 +
  160 + return (toolExecutionRequest, memoryId) -> {
  161 + log.info("===== 工具执行器开始执行 =====");
  162 + log.info("工具编号: {}", meta.getSMethodNo());
  163 + log.info("工具名称: {}", meta.getSMethodName());
  164 + log.info("memoryId: {}", memoryId);
  165 + log.info("请求参数: {}", toolExecutionRequest.arguments());
  166 + UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(memoryId.toString());
  167 + session.setCurrentTool(meta);
  168 +
  169 + // ====================== 防重复调用:立即上锁 ======================
  170 + session.setToolExecuted(true);
  171 + if (StrUtil.isNotBlank(session.getSFunPrompts())) {
  172 + // 关键:返回 工具执行失败 = 框架强制停止循环
  173 + throw new IllegalStateException("STOP_INVOCATION: 任务已完成,停止调用");
  174 +// return "【任务已完成】请勿重复调用工具,请直接总结结果回复用户:"+session.getSFunPrompts();
  175 + }
  176 + //解析参数失败
  177 + Map<String, Object> argsNew;
  178 + try {
  179 + argsNew = objectMapper.readValue(toolExecutionRequest.arguments(), new TypeReference<>() {});
  180 + log.info("解析后的参数: {}", argsNew);
  181 + } catch (Exception e) {
  182 + log.error("参数解析失败", e);
  183 + // 抛异常
  184 + throw new RuntimeException("参数解析失败,请重新输入");
  185 + }
  186 +
  187 + //获取之前获取的参数
  188 + Map<String, Object> args = session.getArgs();
  189 + if(ObjectUtil.isEmpty(args)) args = new HashMap<>();
  190 + Map<String, Object> finalArgs = args;
  191 + argsNew.forEach((k, v)->{
  192 + //获取对应的英文参数
  193 + List<ParamRule> data = meta.getParamRuleList().stream().filter(one->one.getSParam().equals(k)).collect(Collectors.toUnmodifiableList());
  194 + if(ObjectUtil.isNotEmpty(data) && data.size()>0){
  195 + finalArgs.remove(data.get(0).getSParamValue());
  196 + finalArgs.remove(data.get(0).getSParam());
  197 + }
  198 + if(ObjectUtil.isNotEmpty(v)){
  199 + finalArgs.put(k,v);
  200 + }
  201 + });
  202 +
  203 + // 2 【补全动态参数】动态参数补全
  204 + try{
  205 + args = applyValues(args, meta.getParamRuleListCheck());
  206 + }catch (Exception e){
  207 + log.error("返回信息",e);
  208 + String askMsg = e.getMessage();
  209 + session.setSFunPrompts(askMsg);
  210 + // 需要提问用户 → 抛异常停止循环
  211 + throw new RuntimeException(askMsg);
  212 + }
  213 + // 2.1 【自动补全】应用参数的默认值
  214 + List<ParamRule> paramRuleData = meta.getParamRuleListAll();
  215 + args = applyDefaultValues(args, paramRuleData);
  216 + session.setArgs(args);
  217 + // 3. 【自动校验】检查必填项
  218 + List<String> missing = checkRequiredParams(args, paramRuleData);
  219 + if (!missing.isEmpty()) {
  220 + // 4.1 参数缺失,生成“提问”消息,直接返给客户
  221 + String askMsg = buildAskUserMessage(meta, missing,args);
  222 + session.setSFunPrompts(askMsg);
  223 + return String.valueOf(successResult(toolExecutionRequest, askMsg));
  224 + }
  225 + // ====================== 返回时带终止指令 ======================
  226 +// String resp = JSONUtil.toJsonStr(finalArgs) ;
  227 + String resp = doDynamicTool( meta, session);
  228 + return String.valueOf(successResult(toolExecutionRequest, resp));
  229 + };
  230 + }
  231 +
124 232 private void doSetToolAIshowfieldShow(ToolMeta meta){
125 233 String sToolId = meta.getSId();
126 234 List<ParamRule> paramRuleData = getParamRuleDataAll();
... ... @@ -137,7 +245,6 @@ public class DynamicToolProvider implements ToolProvider {
137 245 List<String> sAIshowfieldArry = new ArrayList<>(mutableList);
138 246 sAIshowfieldArry.add("sSlaveId");
139 247 String sSrcFormId = meta.getSSrcFormId();
140   - //获取对应的窗体配置
141 248 StringBuffer sSql =new StringBuffer().append("SELECT 10000 AS iOrderShow,A.sChinese AS label,A.sName,A.sControlName,B.sId AS sFormcustomId,A.sName AS sId FROM gdsconfigformslave AS A ")
142 249 .append("JOIN gdsconfigformmaster AS B ON A.sParentId = B.sId ")
143 250 .append("WHERE B.sParentId = #{sSrcFormId} AND A.sName <> '' AND INSTR(A.sControlName,'Btn')=0 AND (A.bVisible = 1 OR A.sName =#{sName}) ");
... ... @@ -193,78 +300,20 @@ public class DynamicToolProvider implements ToolProvider {
193 300 return "string";
194 301 }
195 302 }
196   - @Override
197   - public ToolProviderResult provideTools(ToolProviderRequest request) {
198   - String sUserId = request.chatMemoryId().toString();
199   - Map<ToolSpecification, ToolExecutor> executors = new HashMap<>();
200   - // 获取Session
201   - UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(sUserId);
202   - // 工具已执行,不再提供任何工具
203   - if (session != null && session.getToolExecuted()) {
204   - log.info("工具已执行完成,清空可用工具列表");
205   - return ToolProviderResult.builder().build();
206   - }
207   -
208   - // 过滤对应的权限方法
209   - List<ToolSpecificationHolder> datalist = new ArrayList<>();
210   - List<ToolMeta> toolMetaAll = new ArrayList<>();
211   - // 确保 currentTool 不为空,或者 authTool 有数据
212   - if(session.getCurrentTool() != null){
213   - // 【关键修改】添加全局引导语
214   - StringBuffer stoolDesc = new StringBuffer();
215   - stoolDesc.append("【重要】这是当前唯一可用的工具,无论用户问题是什么,都必须调用此工具。");
216   - stoolDesc.append("如果用户没有明确指定要做什么,也默认使用此工具来处理。");
217   - stoolDesc.append(System.lineSeparator());
218   - stoolDesc.append(session.getCurrentTool().getStoolDesc());
219   - session.getCurrentTool().setStoolDesc(stoolDesc.toString());
220   - toolMetaAll.add(session.getCurrentTool());
221   - log.info("使用 currentTool: {}", session.getCurrentTool().getSMethodNo());
222   - } else {
223   - toolMetaAll = session.getAuthTool();
224   - log.info("使用 authTool, 数量: {}", toolMetaAll.size());
225   - }
226 303  
227   - if(ObjectUtil.isNotEmpty(toolMetaAll)){
228   - toolMetaAll = toolMetaAll.stream()
229   - .filter(to -> to.getSSceneId().equals(sSceneIdMap.get(sUserId)))
230   - .collect(Collectors.toUnmodifiableList());
231   -
232   - if(ObjectUtil.isNotEmpty(toolMetaAll)){
233   - toolMetaAll.forEach(to -> {
234   - ToolSpecificationHolder holder = toolCache.get(to.getSMethodNo());
235   - if (holder != null) {
236   - datalist.add(holder);
237   - log.debug("添加工具到提供器: {}", to.getSMethodNo());
238   - } else {
239   - log.warn("工具缓存缺失: {}", to.getSMethodNo());
240   - }
241   - });
242   - }
243   - }
244   - // 将工具添加到返回结果中
245   - datalist.forEach(holder -> {
246   - executors.put(holder.getToolSpecification(), holder.getToolExecutor());
247   - });
248   - log.info("provideTools 返回工具数量: {}", executors.size());
249   - return ToolProviderResult.builder().addAll(executors).build();
250   - }
251 304  
252 305 private ToolSpecification buildToolSpecification(ToolMeta meta) {
253 306 ToolSpecification.Builder builder = ToolSpecification.builder()
254 307 .name(meta.getSMethodNo());
255 308  
256 309 StringBuffer stoolDesc = new StringBuffer();
257   - StringBuffer sbt = new StringBuffer();
258   - StringBuffer xt = new StringBuffer();
259   -
260   - // 强制指令【完全保留】
261   - String forceToolPrompt = StrUtil.EMPTY;
262   -// """
263   -// 【重要·强制指令】
264   -// 1. 这是当前唯一可用工具,必须调用,禁止直接回答
265   -// 2. 用户输入包含:确认、全部确认、合并确认、行号(第1行/第一行等)
266   -// 3. 必须调用本工具,只调用一次!
267   -// """;
  310 + String forceToolPrompt = """
  311 + 【工具调用规则】
  312 + 1. 每个自定义方法最多只能调用 1 次
  313 + 2. 只要方法返回结果,必须停止调用
  314 + 3. 禁止重复调用同一个方法
  315 + 4. 禁止无意义循环调用
  316 + """;
268 317 stoolDesc.append(forceToolPrompt);
269 318 if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) {
270 319 stoolDesc.append("MethodNo:").append(meta.getSMethodNo())
... ... @@ -287,97 +336,51 @@ public class DynamicToolProvider implements ToolProvider {
287 336 String sRuleCost = getConstMeg(paramRule);
288 337 switch (paramType.toLowerCase()) {
289 338 case "string":
290   - if (bEmpty) sbt.append(paramDesc).append("(字符串)、");
291   - else xt.append(paramDesc).append("(字符串)、");
292 339 schemaBuilder.addStringProperty(paramDesc, paramDesc);
293 340 break;
294   -
295 341 case "integer":
296 342 case "int":
297   - if (bEmpty) sbt.append(paramDesc).append("(数字)、");
298   - else xt.append(paramDesc).append("(数字)、");
299 343 schemaBuilder.addIntegerProperty(paramDesc, paramDesc);
300 344 break;
301   -
302 345 case "number":
303 346 case "double":
304 347 case "float":
305   - if (bEmpty) sbt.append(paramDesc).append("(浮点)、");
306   - else xt.append(paramDesc).append("(浮点)、");
307 348 schemaBuilder.addNumberProperty(paramDesc, paramDesc);
308 349 break;
309   -
310 350 case "boolean":
311 351 case "bool":
312   - if (bEmpty) sbt.append(paramDesc).append("(布尔)、");
313   - else xt.append(paramDesc).append("(布尔)、");
314 352 schemaBuilder.addBooleanProperty(paramDesc, paramDesc);
315 353 break;
316   -
317 354 case "array":
318   - // 印后工艺:从SQL获取可选工艺列表
319   - String postProcessArray = getArrrayBySql(paramRule);
320   - List<String> postProcessEnums = new ArrayList<>();
321   - if (StrUtil.isNotBlank(postProcessArray)) {
322   - postProcessEnums = Arrays.asList(postProcessArray.split("/"));
323   - }
324   - StringBuffer paramDescTs = new StringBuffer();
325   - paramDescTs.append(paramDesc).append("(数组类型,可多选印后工艺 [").append(postProcessArray).append("]");
326   - // 处理默认值
327   - if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) ||
328   - (ObjectUtil.isNotEmpty(postProcessArray) && postProcessArray.split("/").length == 1)) {
329   - String defaultVal = (ObjectUtil.isNotEmpty(postProcessArray) && postProcessArray.split("/").length == 1)
330   - ? postProcessArray
331   - : paramRule.getSDefaultValue();
332   - paramDescTs.append(",默认值[").append(defaultVal).append("]");
333   - } else {
334   - paramDescTs.append(",无默认值");
335   - }
336   - paramDescTs.append(")、");
337   - // 有枚举值走枚举,没有走普通字符串
338   - if (postProcessEnums.isEmpty()) {
339   - sbt.append(paramDescTs);
340   - schemaBuilder.addStringProperty(paramDesc, paramDescTs.toString());
  355 + String enumStr = getArrrayBySql(paramRule);
  356 + List<String> enums = StrUtil.isNotBlank(enumStr) ? Arrays.asList(enumStr.split("/")) : new ArrayList<>();
  357 + if (enums.isEmpty()) {
  358 + schemaBuilder.addStringProperty(paramDesc, paramDesc + "(数组)");
341 359 } else {
342   - xt.append(paramDescTs);
343   - schemaBuilder.addEnumProperty(paramDesc, postProcessEnums, paramDescTs.toString());
  360 + schemaBuilder.addEnumProperty(paramDesc, enums, paramDesc + "(数组)");
344 361 }
345 362 break;
346   -
347 363 case "enum":
348   - // 印后工艺单选枚举
349   - List<String> postProcessList = new ArrayList<>();
350   - if (StrUtil.isNotBlank(sRuleCost)) {
351   - postProcessList = Arrays.asList(sRuleCost.split("/"));
352   - }
353   - if (postProcessList.isEmpty()) {
354   - schemaBuilder.addStringProperty(paramDesc, sRuleCost);
  364 + String constStr = getConstMeg(paramRule);
  365 + List<String> constList = StrUtil.isNotBlank(constStr) ? Arrays.asList(constStr.split("/")) : new ArrayList<>();
  366 + if (constList.isEmpty()) {
  367 + schemaBuilder.addStringProperty(paramDesc, paramDesc);
355 368 } else {
356   - schemaBuilder.addEnumProperty(paramDesc, postProcessList, sRuleCost);
  369 + schemaBuilder.addEnumProperty(paramDesc, constList, paramDesc);
357 370 }
358 371 break;
359 372 default:
360 373 schemaBuilder.addStringProperty(paramDesc, paramDesc);
361 374 break;
362 375 }
363   -
364 376 if (bEmpty) {
365 377 requiredParams.add(paramDesc);
366 378 }
367 379 }
368   -
369   -// if (ObjectUtil.isNotEmpty(sbt)) {
370   -// stoolDesc.append(System.lineSeparator()).append("1.必填参数:").append(sbt);
371   -// }
372   -// if (ObjectUtil.isNotEmpty(xt)) {
373   -// stoolDesc.append(System.lineSeparator()).append("2.选填参数:").append(xt);
374   -// }
375   -
376 380 } catch (Exception e) {
377 381 e.printStackTrace();
378 382 }
379 383  
380   - // 固定添加 operateType
381 384 if (meta.getIBizType() == 4 || meta.getIBizType() == 5) {
382 385 schemaBuilder.addStringProperty("operateType", "操作类型:全部确认/合并确认/单行确认");
383 386 requiredParams.add("operateType");
... ... @@ -392,13 +395,6 @@ public class DynamicToolProvider implements ToolProvider {
392 395 .build();
393 396 }
394 397  
395   - /***
396   - * @Author 钱豹
397   - * @Date 23:42 2026/2/3
398   - * @Param [sConstConfig, sRule]
399   - * @return java.lang.String
400   - * @Description 常量类型枚举
401   - **/
402 398 private String getConstMeg(ParamRule paramRule){
403 399 if(!RuleCode.CONST.getCode().equals(paramRule.getSRule())){
404 400 return StrUtil.EMPTY;
... ... @@ -414,13 +410,11 @@ public class DynamicToolProvider implements ToolProvider {
414 410 paramRule.setSRuleTs(sb.toString());
415 411 return sb.toString();
416 412 }
417   -
418 413 return StrUtil.EMPTY;
419 414 }
420   - //数组类型枚举
  415 +
421 416 private String getArrrayBySql(ParamRule paramRule){
422 417 Boolean bCheckArray = !(RuleCode.SQL.getCode().equals(paramRule.getSRule()));
423   -// {"string":"字符","integer":"数字","double":"浮点","boolean":"布尔型","array":"数组","enum":"枚举"}
424 418 Boolean bCheckArray2= !("array".equals(paramRule.getSType()) || "enum".equals(paramRule.getSType()));
425 419 Boolean bCheckArray3= ObjectUtil.isEmpty(paramRule.getSParamConfig());
426 420 if(bCheckArray || bCheckArray2 || bCheckArray3){
... ... @@ -434,10 +428,8 @@ public class DynamicToolProvider implements ToolProvider {
434 428 if(data.get(0).containsKey("sGroupName")){
435 429 Map<Object, List<Map<String, Object>>> groupData = data.stream()
436 430 .collect(Collectors.groupingBy(map -> map.get("sGroupName")));
437   - // 打印结果
438 431 Integer sUpKeySize =groupData.size();
439 432 groupData.forEach((key, value) -> {
440   -// System.out.println("key: " + key);
441 433 value.forEach(one->{
442 434 sb.append(one.get(paramRule.getSParamValue())).append("/");
443 435 });
... ... @@ -446,8 +438,6 @@ public class DynamicToolProvider implements ToolProvider {
446 438 sb.append(",");
447 439 }
448 440 });
449   -
450   -
451 441 }else{
452 442 data.forEach(one->{
453 443 if(ObjectUtil.isNotEmpty(one.get(paramRule.getSParamValue()))){
... ... @@ -462,135 +452,34 @@ public class DynamicToolProvider implements ToolProvider {
462 452 return sb.toString();
463 453 }
464 454  
465   -
466   - /***
467   - * @Author 钱豹
468   - * @Date 15:08 2026/1/30
469   - * @Param []
470   - * @return java.util.List<com.xly.entity.ParamRule>
471   - * @Description 获取所有参数
472   - **/
473 455 private List<ParamRule> getParamRuleDataAll(){
474 456 if(paramRuleDataAll==null || paramRuleDataAll.size()==0){
475 457 paramRuleDataAll.addAll(paramRuleMapper.findAll());
476 458 }
477 459 return paramRuleDataAll;
478 460 }
479   - /***
480   - * @Author 钱豹
481   - * @Date 12:37 2026/3/16
482   - * @Param [meta]
483   - * @return dev.langchain4j.service.tool.ToolExecutor
484   - * @Description 参数采集执行器
485   - **/
486   - private ToolExecutor createToolExecutor(ToolMeta meta) {
487   - log.info("创建工具执行器: {}", meta.getSMethodNo());
488   -
489   - return (toolExecutionRequest, memoryId) -> {
490   - log.info("===== 工具执行器开始执行 =====");
491   - log.info("工具编号: {}", meta.getSMethodNo());
492   - log.info("工具名称: {}", meta.getSMethodName());
493   - log.info("memoryId: {}", memoryId);
494   - log.info("请求参数: {}", toolExecutionRequest.arguments());
495 461  
496   - UserSceneSession session = UserSceneSessionService.USER_SCENE_SESSION_CACHE.get(memoryId.toString());
497   - session.setCurrentTool(meta); // 标记当前工具
498   - // 防止重复执行
499   - if (session.getToolExecuted()) {
500   - log.warn("工具已执行过,拒绝重复调用: {}", meta.getSMethodNo());
501   - return String.valueOf(successResult(toolExecutionRequest, "{\"status\":\"already_executed\"}"));
502   - }
503   - //标记执行了(所有工具方法只执行一次)
504   - session.setToolExecuted(true);
505   - // 1. 解析参数
506   - Map<String, Object> argsNew;
507   - try {
508   - argsNew = objectMapper.readValue(toolExecutionRequest.arguments(), new TypeReference<>() {});
509   - log.info("解析后的参数: {}", argsNew);
510   - } catch (Exception e) {
511   - log.error("参数解析失败", e);
512   - String errorMsg = "参数解析失败,请重新输入";
513   - return String.valueOf(errorResult(toolExecutionRequest, errorMsg));
514   - }
515   - Map<String, Object> args = session.getArgs();
516   - if(ObjectUtil.isEmpty(args)){
517   - args = new HashMap<>();
518   - }
519   - Map<String, Object> finalArgs = args;
520   - argsNew.forEach((k, v)->{
521   - //获取对应的英文参数
522   - List<ParamRule> data = meta.getParamRuleList().stream().filter(one->one.getSParam().equals(k)).collect(Collectors.toUnmodifiableList());
523   - if(ObjectUtil.isNotEmpty(data) && data.size()>0){
524   - finalArgs.remove(data.get(0).getSParamValue());
525   - finalArgs.remove(data.get(0).getSParam());
526   - }
527   - if(ObjectUtil.isNotEmpty(v)){
528   - finalArgs.put(k,v);
529   - }
530   - });
531   - // 2 【补全动态参数】动态参数补全
532   - try{
533   - args = applyValues(args, meta.getParamRuleListCheck());
534   - }catch (Exception e){
535   - log.error("返回信息",e);
536   - String askMsg = e.getMessage();
537   - session.setSFunPrompts(askMsg);
538   - return String.valueOf(successResult(toolExecutionRequest, askMsg));
539   - }
540   - List<ParamRule> paramRuleData = meta.getParamRuleListAll();
541   - // 2.1 【自动补全】应用参数的默认值
542   - args = applyDefaultValues(args, paramRuleData);
543   - session.setArgs(args);
544   - // 3. 【自动校验】检查必填项
545   - List<String> missing = checkRequiredParams(args, paramRuleData);
546   - if (!missing.isEmpty()) {
547   - // 4.1 参数缺失,生成“提问”消息,直接返给客户
548   - String askMsg = buildAskUserMessage(meta, missing,args);
549   - session.setSFunPrompts(askMsg);
550   - return String.valueOf(successResult(toolExecutionRequest, askMsg));
551   - }
552   - // 2. 获取必填参数规则
553   -// List<ParamRule> sParamRules = meta.getParamRuleListCheck();
554   -// List<String> missingParams = getRequiredParams(sParamRules);
555   -// String collectPrompt = buildCollectParamsPrompt(meta, missingParams, args);
556   -// log.info("参数缺失,返回收集提示: {}", collectPrompt);
557   - return String.valueOf(successResult(toolExecutionRequest, JSONUtil.toJsonStr(finalArgs)));
558   - };
559   - }
560 462  
561 463 public String doDynamicTool(ToolMeta meta,UserSceneSession session) {
562 464 List<ParamRule> paramRuleData = meta.getParamRuleListAll();
563 465 List<ParamRule> paramRuleDataCheck = meta.getParamRuleListCheck();
564 466 Map<String, Object> args = session.getArgs();
565 467  
566   - // 3. 【自动校验】检查必填项
567 468 List<String> missing = checkRequiredParams(args, paramRuleDataCheck);
568 469 if (!missing.isEmpty()) {
569   - // 4.1 参数缺失,生成“提问”消息,直接返给客户
570 470 String askMsg = buildAskUserMessage(meta, missing,args);
571   - //告知AI 缺失参数
572   -// operableChatMemoryProvider.get(session.getUserId()).add(UserMessage.from(askMsg));
573 471 return askMsg;
574 472 }
575 473  
576   - // 6. 【最终确认信息】所有检测通过后,需要和客户确认交互
577 474 List<ChatMessage> chatMessage = operableChatMemoryProvider.getCurrentChatMessages(session.getUserId());
578 475 ChatMessage userMessage = getLastUserMessage(chatMessage);
579 476 String input = "";
580 477 if (userMessage != null) {
581 478 input = StrUtil.replace(getChatMessageContent(userMessage), "用户输入:", StrUtil.EMPTY);
582 479 }
583   -// {"0":"查询","1":"执行"} 查询不需要确认
  480 +
584 481 Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认");
585   -// //判断是否生成数据
586   -// List<Map<String,Object>> sRowData = new ArrayList<>();
587   -// String sHandleType = "merge";
588   -// if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){
589   -// Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData());
590   -// sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData");
591   -// sHandleType = sRowDataMap.get("sHandleType").toString();
592   -// }
593   - //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"}
  482 +
594 483 if((isConfirmed && (4== meta.getIBizType() ||1== meta.getIBizType()))
595 484 || 2== meta.getIBizType()
596 485 || 3== meta.getIBizType()
... ... @@ -599,40 +488,29 @@ public class DynamicToolProvider implements ToolProvider {
599 488 || 8== meta.getIBizType()
600 489 )
601 490 {
602   - // 确认后必填项校验
603 491 List<String> missingAfter = checkConfirmAfterParam(args, paramRuleData);
604 492 if (!missingAfter.isEmpty()) {
605   - // 4.1 参数缺失,生成“提问”消息,直接返给客户
606 493 String askMsg = buildAskUserMessage(meta, missingAfter,args);
607   - return askMsg;
  494 + throw new DataException(askMsg);
608 495 }
609   - // 7. 【业务校验】执行业务层面的逻辑校验 + 所有校验通过,执行核心业务逻辑
610 496 return executeTool(meta, args, paramRuleData, session.getUserId(), session);
611 497 }
  498 +
612 499 String askconfirmMsg =StrUtil.EMPTY;
613 500 if(4== meta.getIBizType() || meta.getIBizType()==5){
614 501 askconfirmMsg = doGetFromData( meta,args,session);
615   - // ===================== 加入这一行 =====================
616 502 String sSystemPrompt = buildDynamicSystemPrompt(session);
617 503 session.setSSystemPrompt(sSystemPrompt);
618   - //===================== AI 返回需要确认 =====================
619 504 return executeWithConfirmation(askconfirmMsg, session, meta);
620 505 }else{
621 506 askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta);
622 507 }
623   - // 返回需要确认的结果
624 508 return executeWithConfirmation(askconfirmMsg, session, meta);
625 509 }
626 510  
627   - /**
628   - * 安全获取 ChatMessage 内容,适配你当前所有版本
629   - * 不依赖 UserMessage / .text()
630   - */
631 511 private String getChatMessageContent(ChatMessage message) {
632 512 if (message == null) return "";
633   -
634 513 try {
635   - // 通用反射获取内容(兼容所有版本)
636 514 return message.toString()
637 515 .replace("ChatMessage{", "")
638 516 .replace("}", "")
... ... @@ -642,9 +520,9 @@ public class DynamicToolProvider implements ToolProvider {
642 520 return "";
643 521 }
644 522 }
  523 +
645 524 private ChatMessage getLastUserMessage(List<ChatMessage> messages) {
646 525 if (messages == null || messages.isEmpty()) return null;
647   -
648 526 for (int i = messages.size() - 1; i >= 0; i--) {
649 527 ChatMessage msg = messages.get(i);
650 528 if (msg.type() == ChatMessageType.USER) {
... ... @@ -654,14 +532,6 @@ public class DynamicToolProvider implements ToolProvider {
654 532 return null;
655 533 }
656 534  
657   -
658   - /***
659   - * @Author 钱豹
660   - * @Date 15:16 2026/2/9
661   - * @Param [argMap]
662   - * @return java.lang.String
663   - * @Description MAP转提示
664   - **/
665 535 private String getDefMessage(Map<String,Object> argMap,String sName,ToolMeta meta){
666 536 List<ParamRule> showList = meta.getParamRuleListAll();
667 537 List<ParamRule> showListData = new ArrayList<>();
... ... @@ -684,67 +554,31 @@ public class DynamicToolProvider implements ToolProvider {
684 554 appendConfirm(markdown,sName);
685 555 return markdown.toString();
686 556 }
687   - /***
688   - * @Author 钱豹
689   - * @Date 14:56 2026/2/9
690   - * @Param [markdown]
691   - * @return void
692   - * @Description 全部确认
693   - **/
  557 +
694 558 private void appendConfirmAll(StringBuilder markdown,String sName){
695 559 sName = ObjectUtil.isEmpty(sName)?StrUtil.EMPTY:"["+sName+"]";
696 560 markdown.append("请确认是否执行").append(sName).append("操作?1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"第1行确认"\n");
697   - //全部确认 ,部分确认,取消
698 561 markdown.append("回复:&emsp;&emsp;").append("**<a href=\"#\" data-action=\"reset\" data-text=\"全部确认\" >全部确认</a>**").append("&emsp;")
699 562 .append("**<a href=\"#\" data-action=\"reset\" data-text=\"合并确认\" >合并确认</a>**").append("&emsp;")
700 563 .append("**<a href=\"#\" data-action=\"reset\" data-text=\"取消\" >取消</a>**");
701 564 }
702   - /***
703   - * @Author 钱豹
704   - * @Date 14:56 2026/2/9
705   - * @Param [markdown]
706   - * @return void
707   - * @Description 单条确认
708   - **/
  565 +
709 566 private void appendConfirm(StringBuilder markdown,String sName){
710 567 sName = ObjectUtil.isEmpty(sName)?StrUtil.EMPTY:"["+sName+"]";
711 568 markdown.append("请确认是否执行").append(sName).append("操作?请回复:&emsp;&emsp;").append("**<a href=\"#\" data-action=\"reset\" data-text=\"确认\">确认</a>**").append("&emsp;")
712 569 .append("**<a href=\"#\" data-action=\"reset\" data-text=\"取消\">取消</a>**");
713 570 }
714 571  
715   - private ChatMessage getLasterUserMssage(List<ChatMessage> chatMessage){
716   - if(chatMessage!=null){
717   - for(int i=chatMessage.size()-1;i>0;i--){
718   - ChatMessage data = chatMessage.get(i);
719   - ChatMessageType sType = data.type();
720   - if(ChatMessageType.USER.equals(sType)){
721   - return chatMessage.get(i);
722   - }
723   - }
724   - }
725   - return null;
726   - }
727   -
728   - /***
729   - * @Author 钱豹
730   - * @Date 13:35 2026/1/31
731   - * @Param [args, paramDefs]
732   - * @return java.util.Map<java.lang.String,java.lang.Object>
733   - * @Description Map 值转换
734   - **/
735 572 private Map<String, Object> transformationArgs(Map<String, Object> args, List<ParamRule> paramDefs) {
736 573 Map<String, Object> result = new HashMap<>(args);
737 574 paramDefs.forEach(pd->{
738 575 String name = pd.getSParam();
739 576 String sValue = pd.getSParamValue();
740   - //中文
741 577 Boolean bCheck = result.containsKey(name) && ObjectUtil.isNotEmpty(result.get(name));
742   - //英文字段
743 578 Boolean bCheck2 = result.containsKey(sValue) && ObjectUtil.isNotEmpty(result.get(sValue));
744 579 if (!bCheck2 && bCheck ) {
745 580 result.put(sValue,args.get(name));
746 581 }
747   - //常量value -> key 转换 用于后面入库
748 582 if(RuleCode.CONST.getCode().equals(pd.getSRule()) && ObjectUtil.isNotEmpty(result.get(sValue))){
749 583 String sData = result.get(sValue).toString();
750 584 Map<String,Object> configData = JSONUtil.parseObj(pd.getSParamConfig());
... ... @@ -758,13 +592,9 @@ public class DynamicToolProvider implements ToolProvider {
758 592 return result;
759 593 }
760 594  
761   - /**
762   - * 动态参数补全SQL
763   - */
764 595 private Map<String, Object> applyValues(Map<String, Object> args, List<ParamRule> paramDefsCheck) {
765 596 Map<String, Object> result = new HashMap<>(args);
766 597 result = transformationArgs( result, paramDefsCheck);
767   - //根据iOrder 排序
768 598 List<ParamRule> paramDefs = new ArrayList<>(paramDefsCheck);
769 599 paramDefs.sort(Comparator.comparing(ParamRule::getIOrder));
770 600 for (ParamRule pd : paramDefs) {
... ... @@ -777,38 +607,30 @@ public class DynamicToolProvider implements ToolProvider {
777 607 bCheck = bCheck && ObjectUtil.isNotEmpty(result.get(name));
778 608 bCheck = bCheck && RuleCode.SQL.getCode().equals(sRule);
779 609 bCheck = bCheck && ObjectUtil.isNotEmpty(pd.getSParamConfig());
780   -// bCheck = bCheck && !"array".equals(sType);
781 610  
782 611 Boolean bCheck2 = result.containsKey(sValue);
783 612 bCheck2 = bCheck2 && ObjectUtil.isNotEmpty(result.get(sValue));
784 613 bCheck2 = bCheck2 && RuleCode.SQL.getCode().equals(sRule);
785 614 bCheck2 = bCheck2 && ObjectUtil.isNotEmpty(pd.getSParamConfig());
786   -// bCheck2 = bCheck2 && !"array".equals(sType);
787   - //存在动态SQL 并且是枚举的需要
  615 +
788 616 if ((bCheck || bCheck2) &&("enum".equals(sType) || "string".equals(sType))){
789 617 String sSql = pd.getSParamConfig();
790 618 String sKey = bCheck?name:sValue;
791 619 String nameValue = result.get(sKey).toString();
792 620 List<Map<String,Object>> dataList = dynamicExeDbService.findSql(result,sSql);
793   - //传入的参数无效返回继续盘问消息
794 621 if(ObjectUtil.isEmpty(dataList)){
795 622 throw new BusinessException(ErrorCode.PARAM_REQUIRED,String.format("%s 您描述的%s 不存在,请重新告诉我",name,nameValue));
796 623 }
797   - //如果SQL没有条件 多个数据集中进行匹配 如果只匹配一个也算成功
798 624 if(ObjectUtil.isNotEmpty(args.get(name)) || ObjectUtil.isNotEmpty(args.get(sValue))){
799 625 if(("enum".equals(sType) ||"string".equals(sType)) && (args.get(name) instanceof List || args.get(sValue) instanceof List)){
800   - //枚举返回了数组 纠正成字符串
801 626 if(args.get(name) instanceof List){
802 627 args.put(name,((List<?>) args.get(name)).get(0));
803   -// args.put(sValue,((List<?>) args.get(name)).get(0));
804 628 }
805 629 if(args.get(sValue) instanceof List){
806 630 args.put(sValue,((List<?>) args.get(sValue)).get(0));
807   -// args.put(name,((List<?>) args.get(sValue)).get(0));
808 631 }
809 632 }
810 633 List<Map<String,Object>> dataListNew = dataList.stream().filter(one-> one.get(sValue).equals(args.get(name)) || one.get(sValue).equals(args.get(sValue))).collect(Collectors.toUnmodifiableList());
811   - //如果枚举类型枚举值中又不存在AI 返回的数据
812 634 if("enum".equals(sType) && ObjectUtil.isEmpty(dataListNew)){
813 635 args.remove(name);
814 636 args.remove(sValue);
... ... @@ -827,7 +649,6 @@ public class DynamicToolProvider implements ToolProvider {
827 649 result.put(sValue, dataList.get(0).get(sValue));
828 650 result.put(name, dataList.get(0).get(sValue));
829 651 }else{
830   - //赋值到
831 652 String[] sCopyToA = sCopyTo.split(",");
832 653 for(String sCopyToOne:sCopyToA){
833 654 String[] sCopyToOneA = sCopyToOne.split(":");
... ... @@ -836,7 +657,6 @@ public class DynamicToolProvider implements ToolProvider {
836 657 }
837 658 }
838 659 StringBuffer sData=new StringBuffer();
839   - //存在多个形成提示语
840 660 if(dataList.size()>1){
841 661 List<Map<String, Object>> finalDataList = dataList;
842 662 IntStream.range(0, dataList.size())
... ... @@ -858,17 +678,12 @@ public class DynamicToolProvider implements ToolProvider {
858 678 return result;
859 679 }
860 680  
861   - /**
862   - * 应用参数的默认值
863   - */
864 681 private Map<String, Object> applyDefaultValues(Map<String, Object> args, List<ParamRule> paramDefs) {
865 682 Map<String, Object> result = new HashMap<>(args);
866 683 for (ParamRule pd : paramDefs) {
867 684 String name = pd.getSParam();
868 685 if ((!result.containsKey(name)|| ObjectUtil.isEmpty(result.get(name)))
869 686 && ObjectUtil.isNotEmpty(pd.getSDefaultValue())
870   -// && !"enum".equals(pd.getSType())
871   -// && !"array".equals(pd.getSType())
872 687 ) {
873 688 Object defaultValue = pd.getSDefaultValue();
874 689 result.put(name, defaultValue);
... ... @@ -876,18 +691,7 @@ public class DynamicToolProvider implements ToolProvider {
876 691 }
877 692 return result;
878 693 }
879   - /**
880   - * 检查必填参数
881   - */
882   - private List<String> getRequiredParams(List<ParamRule> paramDefs) {
883   - return paramDefs.stream()
884   - .map(ParamRule::getSParam)
885   - .toList();
886   - }
887 694  
888   - /**
889   - * 检查必填参数
890   - */
891 695 private List<String> checkRequiredParams(Map<String, Object> args, List<ParamRule> paramDefs) {
892 696 Map<String,Object> returnMap = transformationArgs( args, paramDefs);
893 697 return paramDefs.stream()
... ... @@ -915,10 +719,6 @@ public class DynamicToolProvider implements ToolProvider {
915 719 return bDbZero || bBhcs || (!returnMap.containsKey(pd.getSParamValue()) || (ObjectUtil.isEmpty(returnMap.get(pd.getSParamValue()))));
916 720 }
917 721  
918   -
919   - /**
920   - * 确认后必填参数
921   - */
922 722 private List<String> checkConfirmAfterParam(Map<String, Object> args, List<ParamRule> paramDefs) {
923 723 Map<String,Object> returnMap = transformationArgs( args, paramDefs);
924 724 return paramDefs.stream()
... ... @@ -931,13 +731,8 @@ public class DynamicToolProvider implements ToolProvider {
931 731 .toList();
932 732 }
933 733  
934   -
935   - /**
936   - * 模拟执行工具
937   - */
938 734 public String executeTool(ToolMeta meta, Map<String, Object> args, List<ParamRule> paramRuleData,String userId,UserSceneSession session ) {
939 735 log.info("执行工具:{},参数:{}", meta.getSMethodNo(), args);
940   - // 2.2 将中文key转换成英文key
941 736 args = transformationArgs( args, paramRuleData);
942 737 String sReturn ="执行成功";
943 738 try{
... ... @@ -951,100 +746,86 @@ public class DynamicToolProvider implements ToolProvider {
951 746 session.setBCleanMemory(true);
952 747 }
953 748 return sReturn;
954   -
955 749 }
956   - /****
957   - * @Author 钱豹
958   - * @Date 10:26 2026/2/1
959   - * @Param
960   - * @return
961   - * @Description 返回结果后 执行业务类
962   - **/
  750 +
963 751 private String executeToolAfter(ToolMeta meta, Map<String, Object> args,List<ParamRule> paramDefs,UserSceneSession session) {
964   -// {"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"}
965   - String sBizContent = meta.getSBizContent();
966   - Integer iBizType = meta.getIBizType();
967   - args.put("sUserId", session.getUserId());
968   - args.put("sLoginId", session.getUserName());
969   - args.put("sMakePerson", session.getUserName());
970   - args.put("sBrId", session.getSBrandsId());
971   - args.put("sBrandsId", session.getSBrandsId());
972   - args.put("sSuId", session.getSSubsidiaryId());
973   - args.put("sSrcFormId", meta.getSSrcFormId());
974   - args.put("sControlName", meta.getSControlName());
975   - args.put("iBizType", iBizType);
976   - args.put("sSubsidiaryId", session.getSSubsidiaryId());
977   - args.put("sToolId", meta.getSId());
978   - if (iBizType == 1 || iBizType == 4) {
979   - Map<String, Object> data = new HashMap<>(args);
980   - data.put("sData", JSONObject.toJSONString((data)));
981   - if(ObjectUtil.isEmpty(sBizContent) && iBizType == 4){
982   - sBizContent ="Sp_Ai_AddCommonAfterNew";
983   - //获取未清数据
984   - if(ObjectUtil.isEmpty(args.get("sSlaveId"))){
985   - throw new BusinessException(-1,"请选择操作数据");
986   - }
987   - List<Map<String,Object>> sRowData = doGetFromDataWq( meta, args, session);
988   - data.put("sRowData", JSONObject.toJSONString(sRowData));
989   - }
990   - Map<String, Object> searMap = this.dynamicExeDbService.getDoProMap(sBizContent, data);
991   - Map<String, Object> sReturn = this.dynamicExeDbService.getCallPro(searMap, sBizContent);
992   - Integer sCode = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SCODE)) ? Integer.valueOf(sReturn.get(ProcedureConstant.SCODE).toString()) : 0;
993   - String sMsgText = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SRETURN)) ? sReturn.get(ProcedureConstant.SRETURN).toString() : "操作成功";
994   - if (sCode < 0) {
995   - sMsgText = ObjectUtil.isEmpty(sMsgText) ? "调用过程sCode:" + Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()) : sMsgText;
996   - session.setSFunPrompts(sMsgText);
997   - throw new BusinessException(sCode,sMsgText);
998   - }
999   - Map<String,Object> outMap = (Map<String, Object>) sReturn.get("outMap");
1000   - String sId = ObjectUtil.isNotEmpty(outMap.get("sBillId")) ? outMap.get("sBillId").toString() : "";
1001   - session.setSCopyTo(meta.getSControlName());
1002   - session.setSCopyToSrcId(sId);
1003   - session.setSFunPrompts(sMsgText);
1004   - return sMsgText;
1005   - } else if (iBizType == 2 && ObjectUtil.isNotEmpty(sBizContent)) {
1006   - //SQL查询
1007   - if (sBizContent.toLowerCase().startsWith("update")) {
1008   - this.dynamicExeDbService.updateSql(args, sBizContent);
1009   - } else if (sBizContent.toLowerCase().startsWith("delete")) {
1010   - this.dynamicExeDbService.delSql(args, sBizContent);
1011   - } else if (sBizContent.toLowerCase().startsWith("insert")) {
1012   - this.dynamicExeDbService.addSql(args, sBizContent);
1013   - } else {
1014   - List<Map<String, Object>> retData = this.dynamicExeDbService.findSql(args, sBizContent);
1015   - if (ObjectUtil.isNotEmpty(retData)) {
1016   - StringBuffer sb = new StringBuffer();
1017   - retData.forEach(one -> {
1018   - one.forEach((k, v) -> {
1019   - sb.append(v).append(" ");
1020   - });
1021   - sb.append("<br/>");
1022   - });
1023   - if (ObjectUtil.isNotEmpty(retData)) {
1024   - sb.append("请根据这些信息安排今天的工作吧!如果有具体任务需要进一步处理,请告诉我");
1025   - }
1026   - session.setSFunPrompts(sb.toString());
1027   - if ("queryTodayTask".equals(meta.getSMethodNo())) {
1028   - session.setBCleanMemory(true);
1029   - }
1030   - return sb.toString();
1031   - } else {
1032   - String sMsgText = "未找到对应的数据";
1033   - session.setSFunPrompts(sMsgText);
1034   - throw new BusinessException(-1,sMsgText);
1035   - }
1036   - }
1037   - } else if (iBizType == 3) {
1038   - return HttpsRequestUtil.me().doRequestHttp(sBizContent, JSONUtil.toJsonStr(args),
1039   - new HashMap<>(), "POST", "JSON");
1040   - }
1041   - return "操作成功";
  752 + String sBizContent = meta.getSBizContent();
  753 + Integer iBizType = meta.getIBizType();
  754 + args.put("sUserId", session.getUserId());
  755 + args.put("sLoginId", session.getUserName());
  756 + args.put("sMakePerson", session.getUserName());
  757 + args.put("sBrId", session.getSBrandsId());
  758 + args.put("sBrandsId", session.getSBrandsId());
  759 + args.put("sSuId", session.getSSubsidiaryId());
  760 + args.put("sSrcFormId", meta.getSSrcFormId());
  761 + args.put("sControlName", meta.getSControlName());
  762 + args.put("iBizType", iBizType);
  763 + args.put("sSubsidiaryId", session.getSSubsidiaryId());
  764 + args.put("sToolId", meta.getSId());
  765 + if (iBizType == 1 || iBizType == 4) {
  766 + Map<String, Object> data = new HashMap<>(args);
  767 + data.put("sData", JSONObject.toJSONString((data)));
  768 + if(ObjectUtil.isEmpty(sBizContent) && iBizType == 4){
  769 + sBizContent ="Sp_Ai_AddCommonAfterNew";
  770 + if(ObjectUtil.isEmpty(args.get("sSlaveId"))){
  771 + throw new BusinessException(-1,"请选择操作数据");
  772 + }
  773 + List<Map<String,Object>> sRowData = doGetFromDataWq( meta, args, session);
  774 + data.put("sRowData", JSONObject.toJSONString(sRowData));
  775 + }
  776 + Map<String, Object> searMap = this.dynamicExeDbService.getDoProMap(sBizContent, data);
  777 + Map<String, Object> sReturn = this.dynamicExeDbService.getCallPro(searMap, sBizContent);
  778 + Integer sCode = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SCODE)) ? Integer.valueOf(sReturn.get(ProcedureConstant.SCODE).toString()) : 0;
  779 + String sMsgText = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SRETURN)) ? sReturn.get(ProcedureConstant.SRETURN).toString() : "操作成功";
  780 + if (sCode < 0) {
  781 + sMsgText = ObjectUtil.isEmpty(sMsgText) ? "调用过程sCode:" + Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()) : sMsgText;
  782 + session.setSFunPrompts(sMsgText);
  783 + throw new BusinessException(sCode,sMsgText);
  784 + }
  785 + Map<String,Object> outMap = (Map<String, Object>) sReturn.get("outMap");
  786 + String sId = ObjectUtil.isNotEmpty(outMap.get("sBillId")) ? outMap.get("sBillId").toString() : "";
  787 + session.setSCopyTo(meta.getSControlName());
  788 + session.setSCopyToSrcId(sId);
  789 + session.setSFunPrompts(sMsgText);
  790 + return sMsgText;
  791 + } else if (iBizType == 2 && ObjectUtil.isNotEmpty(sBizContent)) {
  792 + if (sBizContent.toLowerCase().startsWith("update")) {
  793 + this.dynamicExeDbService.updateSql(args, sBizContent);
  794 + } else if (sBizContent.toLowerCase().startsWith("delete")) {
  795 + this.dynamicExeDbService.delSql(args, sBizContent);
  796 + } else if (sBizContent.toLowerCase().startsWith("insert")) {
  797 + this.dynamicExeDbService.addSql(args, sBizContent);
  798 + } else {
  799 + List<Map<String, Object>> retData = this.dynamicExeDbService.findSql(args, sBizContent);
  800 + if (ObjectUtil.isNotEmpty(retData)) {
  801 + StringBuffer sb = new StringBuffer();
  802 + retData.forEach(one -> {
  803 + one.forEach((k, v) -> {
  804 + sb.append(v).append(" ");
  805 + });
  806 + sb.append("<br/>");
  807 + });
  808 + if (ObjectUtil.isNotEmpty(retData)) {
  809 + sb.append("请根据这些信息安排今天的工作吧!如果有具体任务需要进一步处理,请告诉我");
  810 + }
  811 + session.setSFunPrompts(sb.toString());
  812 + if ("queryTodayTask".equals(meta.getSMethodNo())) {
  813 + session.setBCleanMemory(true);
  814 + }
  815 + return sb.toString();
  816 + } else {
  817 + String sMsgText = "未找到对应的数据";
  818 + session.setSFunPrompts(sMsgText);
  819 + throw new BusinessException(-1,sMsgText);
  820 + }
  821 + }
  822 + } else if (iBizType == 3) {
  823 + return HttpsRequestUtil.me().doRequestHttp(sBizContent, JSONUtil.toJsonStr(args),
  824 + new HashMap<>(), "POST", "JSON");
  825 + }
  826 + return "操作成功";
1042 827 }
1043 828  
1044   - /***
1045   - * @Author
1046   - * 确认获取未清数据
1047   - **/
1048 829 private List<Map<String,Object>> doGetFromDataWq(ToolMeta meta, Map<String, Object> args,UserSceneSession session){
1049 830 String sUrl = meta.getSendUrl();
1050 831 Map<String,Object> sBody = new HashMap<>();
... ... @@ -1064,13 +845,9 @@ public class DynamicToolProvider implements ToolProvider {
1064 845 headers.put("Authorization",session.getAuthorization());
1065 846 String result;
1066 847 try{
1067   - // 1. 获取实例
1068 848 result = HttpsRequestUtil.me().doRequestHttp(sUrl,JSONObject.toJSONString(sBody),headers,"POST","JSON");
1069 849 log.info("请求URL========================{}", sUrl);
1070 850 log.info("请求URLresult========================{}", result);
1071   - log.info("JSON==========================={}", JSONObject.toJSONString(sBody));
1072   - log.info("headers=============================={}", JSONObject.toJSONString(headers));
1073   - log.info("请求URL,JSON,headers=={},{},{}",sUrl,JSONObject.toJSONString(sBody),JSONObject.toJSONString(headers));
1074 851 ErpResult erpResult = JsonUtils.toObject(result,ErpResult.class);
1075 852 if(ObjectUtil.isNotEmpty(erpResult)
1076 853 && ObjectUtil.isNotEmpty(erpResult.getDataset())
... ... @@ -1081,19 +858,10 @@ public class DynamicToolProvider implements ToolProvider {
1081 858 return erpResult.getDataset().getRows().get(0).getDataSet();
1082 859 }
1083 860 }catch (Exception e){
1084   -// result ="执行异常:"+e.getMessage();
1085 861 }
1086 862 return new ArrayList<>();
1087 863 }
1088 864  
1089   -
1090   - /***
1091   - * @Author 钱豹
1092   - * @Date 23:38 2026/2/5
1093   - * @Param []
1094   - * @return void
1095   - * @Description 窗体获取数据方法 未清或者明细
1096   - **/
1097 865 private String doGetFromData(ToolMeta meta, Map<String, Object> args,UserSceneSession session){
1098 866 String sUrl = meta.getSendUrl();
1099 867 Map<String,Object> sBody = new HashMap<>();
... ... @@ -1101,7 +869,6 @@ public class DynamicToolProvider implements ToolProvider {
1101 869 sBody.put("pageSize",10000);
1102 870 log.info("doGetFromData========================");
1103 871 List<Map<String,Object>> list = new ArrayList<>();
1104   - //移除确认标识
1105 872 args.remove("operateType");
1106 873 if(ObjectUtil.isNotEmpty(args)){
1107 874 List<ParamRule> paramDefs = meta.getParamRuleList();
... ... @@ -1140,13 +907,9 @@ public class DynamicToolProvider implements ToolProvider {
1140 907 headers.put("Authorization",session.getAuthorization());
1141 908 String result;
1142 909 try{
1143   - // 1. 获取实例
1144 910 result = HttpsRequestUtil.me().doRequestHttp(sUrl,JSONObject.toJSONString(sBody),headers,"POST","JSON");
1145 911 log.info("请求URL========================{}", sUrl);
1146 912 log.info("请求URLresult========================{}", result);
1147   - log.info("JSON==========================={}", JSONObject.toJSONString(sBody));
1148   - log.info("headers=============================={}", JSONObject.toJSONString(headers));
1149   - log.info("请求URL,JSON,headers=={},{},{}",sUrl,JSONObject.toJSONString(sBody),JSONObject.toJSONString(headers));
1150 913 ErpResult erpResult = JsonUtils.toObject(result,ErpResult.class);
1151 914 result = buildResultMessageWithTable( meta, erpResult, session);
1152 915 }catch (Exception e){
... ... @@ -1155,13 +918,9 @@ public class DynamicToolProvider implements ToolProvider {
1155 918 return result;
1156 919 }
1157 920  
1158   - /**
1159   - * 构建 窗体获取数据方法 未清或者明细
1160   - */
1161 921 public String buildResultMessageWithTable(ToolMeta meta,ErpResult erpResult,UserSceneSession session){
1162 922 Map<Integer,Map<String,Object>> currentRowData = new HashMap<>();
1163 923 ErpDataset dataset = erpResult.getDataset();
1164   - //返回错误信息
1165 924 if(erpResult.getCode()<0 && ObjectUtil.isNotEmpty(erpResult.getMsg())){
1166 925 return erpResult.getMsg();
1167 926 }
... ... @@ -1175,7 +934,6 @@ public class DynamicToolProvider implements ToolProvider {
1175 934 }
1176 935 List<Map<String, Object>> recordData = findFieldNameByChinese(sAIshowfieldShow, rows);
1177 936 int recordCount = dataset != null ? dataset.getTotalCount() : 0;
1178   - // 动态生成表头
1179 937 Set<String> headers = new LinkedHashSet<>();
1180 938 for (Map<String, Object> record : sAIshowfieldShow) {
1181 939 String chineseName = (String) record.get("label");
... ... @@ -1185,7 +943,6 @@ public class DynamicToolProvider implements ToolProvider {
1185 943 }
1186 944  
1187 945 StringBuilder markdown = new StringBuilder();
1188   - //状态
1189 946 if(ObjectUtil.isNotEmpty(session.getArgs())){
1190 947 markdown.append("**查询条件**:");
1191 948 List<ParamRule> pr = session.getCurrentTool().getParamRuleListCheck();
... ... @@ -1205,7 +962,6 @@ public class DynamicToolProvider implements ToolProvider {
1205 962 .append(one.getSParam()).append(":").append(argsOld.get(one.getSParam()).toString()).append(" ")
1206 963 .append(" <span style=\"font-weight: bold;font-size: 22px;position: absolute;right: -5px;top: -12px;color: #ff4d4f;display: inline-block;\" data-action=\"clearSql\" data-text=\""+one.getSParam()+","+one.getSParamValue()+"\" onclick=\"clearSql('"+one.getSParam()+","+one.getSParamValue()+"')\"> × </span>")
1207 964 .append("</span>");
1208   -// markdown.append(one.getSParam()).append(":").append(argsOld.get(one.getSParam()).toString()).append(" ");
1209 965 }
1210 966 }
1211 967 }
... ... @@ -1225,39 +981,29 @@ public class DynamicToolProvider implements ToolProvider {
1225 981 markdown.append(",显示前").append(rows.size()).append("条。如需查看全部,请指定筛选条件。");
1226 982 }
1227 983 }
1228   -// markdown.append("\n---\n");
1229 984 markdown.append("\n\n").append("| 序号 | ");
1230 985 headers.forEach(header -> markdown.append(header).append(" | "));
1231 986 markdown.append("\n|").append("---|".repeat(headers.size() + 1)).append("\n");
1232   - // 填充表格数据
1233 987 List<Map<String,Object>> machineData = new LinkedList<>();
1234 988 for (int i = 0; i < recordData.size(); i++) {
1235   - // 保存隐藏列的值(如"唯一"字段)
1236 989 String uniqueValue = recordData.get(i).get("sSlaveId") != null ? recordData.get(i).get("sSlaveId").toString() : "";
1237 990 markdown.append("| ").append(i + 1).append(" | ");
1238 991 Map<String,Object> rMap = new HashMap<>();
1239 992 for (String header : headers) {
1240   - // 这里需要根据你的数据结构来获取对应的值
1241 993 Object value = recordData.get(i)!= null ? recordData.get(i).get(header) : null;
1242 994 markdown.append(value != null ? value : "—").append(" | ");
1243 995 rMap.put(header,value);
1244 996 }
1245 997 rMap.put("sSlaveId",uniqueValue);
1246 998 rMap.put("唯一",uniqueValue);
1247   - // 在行末添加隐藏数据的特殊标记(AI可以解析)
1248 999 markdown.append(" <span style=\"display:none;\">HIDDEN_DATA:")
1249 1000 .append(JSONUtil.toJsonStr(rMap))
1250 1001 .append("</span>");
1251   -// markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->");
1252 1002 markdown.append("\n");
1253 1003 machineData.add(rMap);
1254 1004 currentRowData.put(i + 1,recordData.get(i));
1255 1005 }
1256 1006 markdown.append(">");
1257   -// // 4. 机器可读的结构化数据(只出现一次!)
1258   -// markdown.append("<!-- MACHINE_DATA_START -->\n");
1259   -// markdown.append(JSONUtil.toJsonStr(machineData));
1260   -// markdown.append("\n<!-- MACHINE_DATA_END -->\n\n");
1261 1007 if(meta.getIBizType()==4){
1262 1008 markdown.append("\n---\n");
1263 1009 appendConfirmAll(markdown,meta.getSControlName());
... ... @@ -1295,40 +1041,8 @@ public class DynamicToolProvider implements ToolProvider {
1295 1041 }
1296 1042 """.formatted(rowJson, methodNo);
1297 1043 }
1298   -// public String buildDynamicSystemPrompt(UserSceneSession session) {
1299   -// // 动态获取当前工具方法号
1300   -// String methodNo = session.getCurrentTool().getSMethodNo();
1301   -// String promptHead = """
1302   -// 【极强约束·必须执行】
1303   -// 1. 禁止说话!禁止解释!
1304   -// 2. 必须调用工具,固定方法编号:MethodNo = %s
1305   -// 3. 只输出JSON,无任何其他内容
1306   -// 以下是【行号 → sSlaveId】对应数据:
1307   -// """.formatted(methodNo);
1308   -//
1309   -// Map<Integer, Map<String, Object>> rowDataMap = session.getCurrentRowData();
1310   -// StringBuilder rowDataSb = new StringBuilder();
1311   -// if (ObjectUtil.isNotEmpty(rowDataMap)) {
1312   -// for (Map.Entry<Integer, Map<String, Object>> entry : rowDataMap.entrySet()) {
1313   -// int rowNum = entry.getKey();
1314   -// String sSlaveId = StrUtil.toString(entry.getValue().get("sSlaveId"));
1315   -// rowDataSb.append("第").append(rowNum).append("行 → ").append(sSlaveId).append("\n");
1316   -// }
1317   -// }
1318   -// String promptFoot = """
1319   -// 【输出JSON格式·严格遵守】
1320   -// 根据用户选择的行,自动填写 sSlaveId,多行用英文逗号拼接
1321   -// {
1322   -// "operateType": "全部确认/合并确认/单行确认",
1323   -// "sSlaveId": "填写对应的ID,多个用逗号分隔"
1324   -// }
1325   -// """;
1326   -// return promptHead + rowDataSb + promptFoot;
1327   -// }
1328   -
1329   - // 辅助方法:根据中文名查找字段名(通过映射关系转换)
  1044 +
1330 1045 private List<Map<String, Object>> findFieldNameByChinese(List<Map<String, Object>> sAIshowfieldShow,List<Map<String, Object>> rows){
1331   - //获取映射关系
1332 1046 Map<String,String> keyMappings = new HashMap<>();
1333 1047 List<String> selectedKeys = new ArrayList<>();
1334 1048 sAIshowfieldShow.forEach(one->{
... ... @@ -1342,13 +1056,6 @@ public class DynamicToolProvider implements ToolProvider {
1342 1056 return sRowData;
1343 1057 }
1344 1058  
1345   - /***
1346   - * @Author 钱豹
1347   - * @Date 2:04 2026/2/6
1348   - * @Param [rows, limit, selectedKeys]
1349   - * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
1350   - * @Description 返回指定数量并筛选指定key
1351   - **/
1352 1059 public List<Map<String, Object>> getFilteredDataStream(List<Map<String, Object>> rows,
1353 1060 int limit,
1354 1061 List<String> selectedKeys,
... ... @@ -1357,13 +1064,11 @@ public class DynamicToolProvider implements ToolProvider {
1357 1064 return Collections.emptyList();
1358 1065 }
1359 1066 return rows.stream()
1360   - .limit(limit) // 限制数量
  1067 + .limit(limit)
1361 1068 .map(original -> {
1362   - // 创建新的Map,只包含指定key
1363 1069 Map<String, Object> filtered = new HashMap<>();
1364 1070 selectedKeys.forEach(key -> {
1365 1071 if (original.containsKey(key)) {
1366   - //指定映射的key 放中文
1367 1072 filtered.put(keyMappings.get(key), original.get(key));
1368 1073 }
1369 1074 });
... ... @@ -1373,9 +1078,6 @@ public class DynamicToolProvider implements ToolProvider {
1373 1078 .collect(Collectors.toList());
1374 1079 }
1375 1080  
1376   - /**
1377   - * 构建确认操作消息
1378   - */
1379 1081 private String buildConfirmUserMessage(ToolMeta meta, Map<String, Object> args) {
1380 1082 StringBuilder markdown = new StringBuilder();
1381 1083 markdown.append("参数提取如下:\n\n");
... ... @@ -1390,11 +1092,6 @@ public class DynamicToolProvider implements ToolProvider {
1390 1092 return markdown.toString();
1391 1093 }
1392 1094  
1393   -
1394   -
1395   - /**
1396   - * 构建提问消息
1397   - */
1398 1095 private String buildAskUserMessage(ToolMeta meta, List<String> missing,Map<String,Object> arg) {
1399 1096 StringBuilder sb = new StringBuilder();
1400 1097 sb.append("缺少参数请补全:\n");
... ... @@ -1429,18 +1126,15 @@ public class DynamicToolProvider implements ToolProvider {
1429 1126 return sb.toString();
1430 1127 }
1431 1128  
1432   - // 创建提前成功的结果
1433 1129 private String createEarlySuccessResult(ToolExecutionRequest request, String message) {
1434   - // 设置一个标志,告诉执行器不要继续执行
1435 1130 return JSONUtil.toJsonStr(Map.of(
1436 1131 "status", "success",
1437 1132 "message", message,
1438   - // 关键标志
1439 1133 "executionCompleted", true,
1440 1134 "data", successResult(request, message)
1441 1135 ));
1442 1136 }
1443   - // 创建终止执行的结果
  1137 +
1444 1138 private String createTerminationResult(String message) {
1445 1139 return JSONUtil.toJsonStr(Map.of(
1446 1140 "status", "terminated",
... ... @@ -1449,70 +1143,30 @@ public class DynamicToolProvider implements ToolProvider {
1449 1143 ));
1450 1144 }
1451 1145  
1452   - /***
1453   - * @Author 钱豹
1454   - * @Date 10:15 2026/1/31
1455   - * @Param [request, errorMsg]
1456   - * @return dev.langchain4j.data.message.ToolExecutionResultMessage
1457   - * @Description 错误返回
1458   - **/
1459 1146 private ToolExecutionResultMessage errorResult(ToolExecutionRequest request, String errorMsg) {
1460 1147 return ToolExecutionResultMessage.from(request, errorMsg);
1461 1148 }
1462   - /***
1463   - * @Author 钱豹
1464   - * @Date 10:15 2026/1/31
1465   - * @Param [request, text]
1466   - * @return dev.langchain4j.data.message.ToolExecutionResultMessage
1467   - * @Description 构建正确返回
1468   - **/
  1149 +
1469 1150 private ToolExecutionResultMessage successResult(ToolExecutionRequest request, String text) {
1470 1151 return ToolExecutionResultMessage.from(request, text);
1471 1152 }
1472 1153  
1473   - /**
1474   - * 执行方法后需要用户确认的扩展版本
1475   - */
1476 1154 private String executeWithConfirmation(String initialResult, UserSceneSession session,ToolMeta meta) {
1477   -
1478   - // 第一步:执行原始操作,返回初步结果
1479 1155 Map<String, Object> step1Result = new HashMap<>();
1480 1156 step1Result.put("initialResult", initialResult);
1481 1157 step1Result.put("status", "PENDING_CONFIRMATION");
1482 1158 step1Result.put("confirmationRequired", true);
1483 1159 step1Result.put("confirmationMessage", initialResult);
1484   -// // 将确认状态保存到对话记忆
1485   -// chatMemory.add(UserMessage.from("SYSTEM: 等待用户确认操作"));
1486 1160 String userMessage = formatConfirmationResult(step1Result);
1487 1161 session.setCurrentTool(meta);
1488   -// session.setSFunPrompts(userMessage);
1489   - // 6. 返回确认请求
1490   -// return ToolExecutionResultMessage.from(request,userMessage);
1491   -// ToolExecutionResultMessage.from(request, text);
1492 1162 return userMessage;
1493 1163 }
1494 1164  
1495 1165 private String formatConfirmationResult(Map<String, Object> result) {
1496   - return String.format(
1497   - """
1498   - %s
1499   - """,
1500   - result.get("initialResult"),
1501   - result.get("confirmationMessage")
1502   - );
  1166 + return String.format("%s", result.get("initialResult"));
1503 1167 }
1504 1168  
1505   - /***
1506   - * @Author 钱豹
1507   - * @Date 0:54 2026/2/4
1508   - * @Param [userResponse]
1509   - * @return boolean
1510   - * @Description 检查是确认
1511   - **/
1512 1169 public boolean isConfirmed(String userResponse) {
1513 1170 return userResponse.matches("(?i)(确认|全部确认|部分确认|是|yes|confirm|true|是的|可以|没问题|确定|好的|生成|)");
1514 1171 }
1515   -
1516   -
1517   -
1518   -}
  1172 +}
1519 1173 \ No newline at end of file
... ...
src/main/java/com/xly/tool/ToolSpecificationHolder.java
... ... @@ -7,9 +7,11 @@ import dev.langchain4j.agent.tool.ToolSpecification;
7 7 public class ToolSpecificationHolder {
8 8 private final ToolSpecification toolSpecification;
9 9 private final ToolExecutor toolExecutor;
10   - public ToolSpecificationHolder(ToolSpecification toolSpecification, ToolExecutor toolExecutor) {
  10 + private final String sName;
  11 + public ToolSpecificationHolder(ToolSpecification toolSpecification, ToolExecutor toolExecutor,String sName) {
11 12 this.toolSpecification = toolSpecification;
12 13 this.toolExecutor = toolExecutor;
  14 + this.sName = sName;
13 15 }
14 16  
15 17 public ToolSpecification getToolSpecification() {
... ... @@ -18,4 +20,11 @@ public class ToolSpecificationHolder {
18 20  
19 21 public ToolExecutor getToolExecutor() {
20 22 return toolExecutor;
21   - }}
  23 + }
  24 +
  25 + public String getsName() {
  26 + return sName;
  27 + }
  28 +
  29 +}
  30 +
... ...
src/main/resources/application.yml
... ... @@ -135,8 +135,8 @@ langchain4j:
135 135 ollama:
136 136 # 聊天模型配置(用于一般对话)
137 137 base-url: http://112.82.245.194:11434
138   -# chat-model-name: qwen2.5:7b-instruct
139   - chat-model-name: qwen3:14b
  138 + chat-model-name: qwen2.5:7b-instruct
  139 +# chat-model-name: qwen3:14b
140 140 # chat-model-name: qwen3.5:9b
141 141 # SQL/代码模型配置(专门用于代码和SQL生成)
142 142 sql-model-name: qwen2.5-coder:7b
... ... @@ -171,4 +171,4 @@ tts:
171 171 timeout: 30000
172 172 max-connections: 10
173 173 erp:
174   - baseurl: http://8.130.144.93:8080/xlyEntry_saas
175 174 \ No newline at end of file
  175 + baseurl: http://118.178.19.35:8080/xlyEntry_saas
176 176 \ No newline at end of file
... ...