Commit e5fb3dd8f70f5116579991389476bd776b3799c1

Authored by qianbao
1 parent 781d3448

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

src/main/java/com/xly/agent/AgentSystemPrompt.java 0 → 100644
  1 +package com.xly.agent;
  2 +
  3 +public class AgentSystemPrompt {
  4 +
  5 + public static final String sSystemPrompt= "1. 方法匹配:先精准拆解用户查询的核心业务意图,再自动匹配唯一符合用户问题的工具方法(MethodNo),禁止自创,规则如下;\n" +
  6 + " 1.1 匹配方法时,无需考虑工具描述(@TOOL)中 1.必填参数,2.选填参数,示例,parameters内容 四个部分的内容;\n" +
  7 + " 1.2 匹配方法时,只关注工具描述(@TOOL)中 “当用户” 和 “时,必须调用本工具”两个短语之间的内容;\n" +
  8 + " 1.3 调用工具前,不需要询问用户提供缺失的参数\n" +
  9 + " 2. 参数提取:提取该工具的全部参数,与描述完全一致,严格按标注类型赋值,规则如下:\n" +
  10 + " 2.1 数字无引号,为空时禁止赋值0;\n" +
  11 + " 2.2 如果有空格需要去掉空格后再提取。";
  12 +}
... ...
src/main/java/com/xly/agent/ErpAiAgent.java
... ... @@ -10,18 +10,14 @@ import dev.langchain4j.service.V;
10 10 * 优化后:新增场景专属交互规则,大模型仅处理当前场景业务指令
11 11 */
12 12 public interface ErpAiAgent {
13   - @SystemMessage("""
14   - 1. 方法匹配:先精准拆解用户查询的核心业务意图,再自动匹配唯一符合用户问题的工具方法(MethodNo),禁止自创,规则如下;
15   - 1.1 匹配方法时,无需考虑工具描述(@TOOL)中 1.必填参数,2.选填参数,示例,parameters内容 四个部分的内容;
16   - 1.2 匹配方法时,只关注工具描述(@TOOL)中 “当用户” 和 “时,必须调用本工具”两个短语之间的内容;
17   - 1.3 调用工具前,不需要询问用户提供缺失的参数
18   - 2. 参数提取:提取该工具的全部参数,与描述完全一致,严格按标注类型赋值,规则如下:
19   - 2.1 数字无引号,为空时禁止赋值0;
20   - 2.2 如果有空格需要去掉空格后再提取。
21   - """)
22   - @UserMessage("用户输入:{{userInput}}")
23   - String chat(@MemoryId String userId, @V("userInput") String userInput);
24 13  
  14 + @SystemMessage("{{sSystemPrompt}}")
  15 + @UserMessage("用户输入:{{userInput}}")
  16 + String chat(
  17 + @MemoryId String userId,
  18 + @V("userInput") String userInput,
  19 + @V("sSystemPrompt") String sSystemPrompt
  20 + );
25 21 /**
26 22 * 动态表结构:自然语言解释SQL执行结果
27 23 * 入参:用户问题、执行的SQL、表结构、JSON格式结果
... ...
src/main/java/com/xly/entity/UserSceneSession.java
... ... @@ -75,6 +75,14 @@ public class UserSceneSession {
75 75 * 数据库类型 H: 缓存 D: 动态
76 76 */
77 77 private String dbCach;
  78 + /**
  79 + * @Author 钱豹
  80 + * @Date 22:55 2026/4/13
  81 + * @Param
  82 + * @return
  83 + * @Description AI 模板
  84 + **/
  85 + private String sSystemPrompt;
78 86  
79 87 /**
80 88 * 构建场景选择提示语:展示权限内场景,引导用户选择
... ...
src/main/java/com/xly/service/XlyErpService.java
... ... @@ -8,10 +8,7 @@ import cn.hutool.core.util.StrUtil;
8 8 import cn.hutool.json.JSONUtil;
9 9 import com.alibaba.fastjson2.JSON;
10 10 import com.alibaba.fastjson2.JSONObject;
11   -import com.xly.agent.ChatiAgent;
12   -import com.xly.agent.DynamicTableNl2SqlAiAgent;
13   -import com.xly.agent.ErpAiAgent;
14   -import com.xly.agent.SceneSelectorAiAgent;
  11 +import com.xly.agent.*;
15 12 import com.xly.config.OperableChatMemoryProvider;
16 13 import com.xly.constant.CommonConstant;
17 14 import com.xly.constant.ReturnTypeCode;
... ... @@ -140,7 +137,7 @@ public class XlyErpService {
140 137 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName())
141 138 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo()))
142 139 ){
143   - sResponMessage = aiAgent.chat(userId, input);
  140 + sResponMessage = aiAgent.chat(userId, input , AgentSystemPrompt.sSystemPrompt);
144 141 }
145 142  
146 143 if(ObjectUtil.isNotEmpty(session.getCurrentTool())
... ... @@ -351,7 +348,11 @@ public class XlyErpService {
351 348 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSInputTabelName())
352 349 && ObjectUtil.isNotEmpty(session.getCurrentTool().getSStructureMemo()))
353 350 ){
354   - sResponMessage = aiAgent.chat(userId, input);
  351 + String sSystemPrompt = AgentSystemPrompt.sSystemPrompt;
  352 + if(ObjectUtil.isNotEmpty(session.getSSystemPrompt())){
  353 + sSystemPrompt = session.getSSystemPrompt();
  354 + }
  355 + sResponMessage = aiAgent.chat(userId, input,sSystemPrompt);
355 356 }
356 357 methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSMethodName():StrUtil.EMPTY;
357 358 if(ObjectUtil.isNotEmpty(session.getCurrentTool())
... ... @@ -947,7 +948,7 @@ public class XlyErpService {
947 948 UserSceneSessionService.ERP_AGENT_CACHE.put(userId, aiAgent);
948 949 // 初始化AiService 以防止热加载太慢 找不到相应的方法
949 950 try{
950   - aiAgent.chat(userId, "initAiService");
  951 + aiAgent.chat(userId, "initAiService",AgentSystemPrompt.sSystemPrompt);
951 952 }catch (Exception e){
952 953 e.printStackTrace();
953 954 }
... ...
src/main/java/com/xly/tool/DynamicToolProvider.java
... ... @@ -10,11 +10,9 @@ import com.fasterxml.jackson.core.type.TypeReference;
10 10  
11 11 import com.fasterxml.jackson.databind.ObjectMapper;
12 12  
  13 +import com.xly.agent.AgentSystemPrompt;
13 14 import com.xly.config.OperableChatMemoryProvider;
14   -import com.xly.constant.ErrorCode;
15   -import com.xly.constant.ProcedureConstant;
16   -import com.xly.constant.RuleCode;
17   -import com.xly.constant.UrlErpConstant;
  15 +import com.xly.constant.*;
18 16 import com.xly.entity.*;
19 17 import com.xly.exception.dto.BusinessException;
20 18 import com.xly.mapper.ParamRuleMapper;
... ... @@ -245,80 +243,67 @@ public class DynamicToolProvider implements ToolProvider {
245 243 return ToolProviderResult.builder().addAll(executors).build();
246 244 }
247 245  
248   - /***
249   - * @Author 钱豹
250   - * @Date 15:07 2026/1/30
251   - * @Param [meta]
252   - * @return dev.langchain4j.agent.tool.ToolSpecification
253   - * @Description 参数注入 方法引导语注入(初始化调用)
254   - **/
255 246 private ToolSpecification buildToolSpecification(ToolMeta meta) {
256 247 ToolSpecification.Builder builder = ToolSpecification.builder()
257 248 .name(meta.getSMethodNo());
258   -// .description(meta.getStoolDesc());
  249 +
259 250 StringBuffer stoolDesc = new StringBuffer();
260 251 StringBuffer sbt = new StringBuffer();
261 252 StringBuffer xt = new StringBuffer();
262 253 StringBuffer sl = new StringBuffer();
263 254  
264   - if(ObjectUtil.isNotEmpty(meta.getStoolDesc())){
265   - stoolDesc.append("MethodNo:").append(meta.getSMethodNo()).append(",当用户").append(meta.getSMethodName());
266   -// if (meta.getIBizType()==4){
267   -// stoolDesc.append(",").append("并选择数据后执行["+meta.getSControlName()+"]操作");
268   -// }
269   - stoolDesc.append("时,必须调用本工具").append(meta.getSMethodNo()).append(",").append(meta.getStoolDesc());
270   -// if (meta.getIBizType()==4){
271   -// stoolDesc.append(",").append("并选择数据后执行 "+meta.getSControlName()+" 操作");
272   -//// .append("1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"1,3行确认"");
273   -// }
  255 + // ====================== 【超级强制:必须调用工具】 ======================
  256 + String forceToolPrompt = """
  257 + 【重要·强制指令】
  258 + 1. 这是当前唯一可用工具,必须调用,禁止直接回答
  259 + 2. 用户输入包含:确认、全部确认、合并确认、行号(第1行/第一行等)
  260 + 3. 必须调用本工具,必须调用,必须调用!
  261 + """;
  262 + stoolDesc.append(forceToolPrompt);
  263 + // ========================================================================
  264 +
  265 + if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) {
  266 + stoolDesc.append("MethodNo:").append(meta.getSMethodNo())
  267 + .append(",当用户").append(meta.getSMethodName())
  268 + .append("时,必须调用本工具").append(meta.getSMethodNo())
  269 + .append(",").append(meta.getStoolDesc());
274 270 }
275 271  
276 272 try {
277 273 List<ParamRule> paramRuleData = meta.getParamRuleList();
278   -// 1.必填参数:客户名称(字符串),产品名称(字符串),数量(数字);
279   -// 2.选填参数:产品描述(字符串),生产要求(字符串);
280   -// **强制输出标准JSON对象**:
281   -// 示例:{\"客户名称\":\"小羚羊软件开发有限公司\",\"产品名称\":\"企业宣传册\",\"数量\":1000,\"产品描述\":\"黑色注意色差\",\"生产要求\":\"上光,覆膜\"}
282   - Map<String,Object> slMap = new HashMap<>();
  274 + Map<String, Object> slMap = new HashMap<>();
  275 +
283 276 for (ParamRule paramRule : paramRuleData) {
284   -// String paramName = ObjectUtil.isEmpty(paramRule.getSParamValue())?null:paramRule.getSParamValue();
285   - String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam())?null:paramRule.getSParam();
  277 + String paramDesc = ObjectUtil.isEmpty(paramRule.getSParam()) ? null : paramRule.getSParam();
286 278 String paramType = paramRule.getSType();
287 279 Boolean bEmpty = paramRule.getBEmpty();
288 280 String sExampleValue = paramRule.getSExampleValue();
289   - //示例值,只有枚举放
290   - if(ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())){
291   - //英文
292   -// slMap.put(paramName,sExampleValue);
293   - //中文
294   - slMap.put(paramDesc,sExampleValue);
  281 +
  282 + if (ObjectUtil.isNotEmpty(sExampleValue) && "enum".equals(paramType.toLowerCase())) {
  283 + slMap.put(paramDesc, sExampleValue);
295 284 }
296 285 if (paramDesc == null || paramDesc.trim().isEmpty()) {
297 286 continue;
298 287 }
299   - // 构建参数属性
300   - //{"string":"字符","integer":"数字","double":"浮点","boolean":"布尔型","array":"数组","enum":"枚举"}
  288 +
301 289 List<JsonSchemaProperty> properties = new ArrayList<>();
302   - // 添加类型属性
303   -// String ,仅允许【{}】多选一,严格匹配)
304   - String sRuleCost = getConstMeg(paramRule.getSParamConfig(),paramRule);
305   -// 2. 付款方式:字符串类型,互斥枚举值[90天、60天、现结],默认值[现结]
306   -// 5. 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值
  290 + String sRuleCost = getConstMeg(paramRule.getSParamConfig(), paramRule);
  291 +
307 292 switch (paramType.toLowerCase()) {
308 293 case "string":
309   - if(bEmpty){
310   - sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、");
311   - }else{
312   - xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")").append("、");
  294 + if (bEmpty) {
  295 + sbt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、");
  296 + } else {
  297 + xt.append(paramDesc).append("(字符串").append(sRuleCost).append(")、");
313 298 }
314 299 properties.add(JsonSchemaProperty.STRING);
315 300 break;
316 301 case "integer":
317 302 case "int":
318   - if(bEmpty){
319   - sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、");
320   - }else{
321   - xt.append(paramDesc).append("(数字").append(sRuleCost).append(")").append("、");
  303 + if (bEmpty) {
  304 + sbt.append(paramDesc).append("(数字").append(sRuleCost).append(")、");
  305 + } else {
  306 + xt.append(paramDesc).append("(数字").append(sRuleCost).append(")、");
322 307 }
323 308 properties.add(JsonSchemaProperty.INTEGER);
324 309 break;
... ... @@ -326,91 +311,84 @@ public class DynamicToolProvider implements ToolProvider {
326 311 case "double":
327 312 case "float":
328 313 properties.add(JsonSchemaProperty.NUMBER);
329   - if(bEmpty){
330   - sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、");
331   - }else{
332   - xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")").append("、");
  314 + if (bEmpty) {
  315 + sbt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、");
  316 + } else {
  317 + xt.append(paramDesc).append("(浮点").append(sRuleCost).append(")、");
333 318 }
334 319 break;
335 320 case "boolean":
336 321 case "bool":
337   - if(bEmpty){
338   - sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、");
339   - }else{
340   - xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")").append("、");
  322 + if (bEmpty) {
  323 + sbt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、");
  324 + } else {
  325 + xt.append(paramDesc).append("(布尔型").append(sRuleCost).append(")、");
341 326 }
342 327 properties.add(JsonSchemaProperty.BOOLEAN);
343 328 break;
344 329 case "array":
345 330 String sRuleArray = getArrrayBySql(paramRule);
346   - // 生产要求:数组类型,可多选枚举值[上光、复膜、烫金],无默认值
347 331 if (ObjectUtil.isNotEmpty(sRuleArray)) {
348   - sRuleArray = StrUtil.replace(sRuleArray,",","/");
  332 + sRuleArray = StrUtil.replace(sRuleArray, ",", "/");
349 333 properties.add(JsonSchemaProperty.enums(sRuleArray.split("/")));
350 334 }
351   - if(bEmpty){
352   - //动态SQL 或者写死默认值的 动态SQL只存在一条数据 直接给默认值
353   - sbt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]");
354   - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)){
355   - String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length==1)?sRuleArray:paramRule.getSDefaultValue();
  335 + if (bEmpty) {
  336 + sbt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]");
  337 + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue()) || (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1)) {
  338 + String sDefaultVal = (ObjectUtil.isNotEmpty(sRuleArray) && sRuleArray.split("/").length == 1) ? sRuleArray : paramRule.getSDefaultValue();
356 339 sbt.append(",默认值[").append(sDefaultVal).append("]");
357   - }else{
  340 + } else {
358 341 sbt.append(",无默认值");
359 342 }
360 343 sbt.append(")、");
361   - }else{
362   - xt.append(paramDesc).append("(").append("数组类型") .append(",可多选枚举值 [").append(sRuleArray).append("]");
363   - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){
  344 + } else {
  345 + xt.append(paramDesc).append("(数组类型,可多选枚举值 [").append(sRuleArray).append("]");
  346 + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) {
364 347 xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]");
365   - }else{
  348 + } else {
366 349 xt.append(",无默认值");
367 350 }
368 351 xt.append(")、");
369 352 }
370 353 properties.add(JsonSchemaProperty.ARRAY);
371   - // 默认字符串数组
372 354 properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING));
373 355 break;
374 356 case "enum":
375   - // 处理枚举值
376 357 if (ObjectUtil.isNotEmpty(sRuleCost)) {
377 358 properties.add(JsonSchemaProperty.enums(sRuleCost.split("/")));
378   - }else{
  359 + } else {
379 360 sRuleCost = getArrrayBySql(paramRule);
380 361 }
381   - // eg: 付款方式(字符串,互斥枚举值[90天、60天、现结],默认值[现结])
382   - if(bEmpty){
383   - sbt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]");
384   - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){
  362 + if (bEmpty) {
  363 + sbt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]");
  364 + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) {
385 365 sbt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]");
386   - }else{
  366 + } else {
387 367 sbt.append(",无默认值");
388 368 }
389 369 sbt.append(")、");
390   - }else{
391   - xt.append(paramDesc).append("(").append("字符串") .append(",互斥枚举值 [").append(sRuleCost).append("]");
392   - if(ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())){
  370 + } else {
  371 + xt.append(paramDesc).append("(字符串,互斥枚举值 [").append(sRuleCost).append("]");
  372 + if (ObjectUtil.isNotEmpty(paramRule.getSDefaultValue())) {
393 373 xt.append(",默认值[").append(paramRule.getSDefaultValue()).append("]");
394   - }else{
  374 + } else {
395 375 xt.append(",无默认值");
396 376 }
397 377 xt.append(")、");
398 378 }
399 379 properties.add(JsonSchemaProperty.ARRAY);
400   - // 默认字符串数组
401 380 properties.add(JsonSchemaProperty.items(JsonSchemaProperty.STRING));
402 381 break;
403 382 default:
404 383 properties.add(JsonSchemaProperty.STRING);
405 384 break;
406 385 }
407   - // 添加描述
  386 +
408 387 if (!paramDesc.isEmpty()) {
409 388 properties.add(JsonSchemaProperty.description(paramDesc));
410 389 }
411   - // 检查是否必填
  390 +
412 391 boolean required = bEmpty;
413   - // 添加参数
414 392 if (required) {
415 393 builder.addParameter(paramDesc, properties);
416 394 } else {
... ... @@ -418,31 +396,33 @@ public class DynamicToolProvider implements ToolProvider {
418 396 }
419 397 }
420 398  
421   - if(ObjectUtil.isNotEmpty(sbt)){
422   - stoolDesc
423   - .append(System.lineSeparator())
424   - .append("1.必填参数:")
425   - .append(sbt);
  399 + if (ObjectUtil.isNotEmpty(sbt)) {
  400 + stoolDesc.append(System.lineSeparator()).append("1.必填参数:").append(sbt);
426 401 }
427   - if(ObjectUtil.isNotEmpty(xt)){
428   - stoolDesc
429   - .append(System.lineSeparator())
430   - .append("2.选填参数:")
431   - .append(xt);
  402 + if (ObjectUtil.isNotEmpty(xt)) {
  403 + stoolDesc.append(System.lineSeparator()).append("2.选填参数:").append(xt);
432 404 }
433   - if(ObjectUtil.isNotEmpty(slMap)){
434   - stoolDesc
435   - .append(System.lineSeparator())
436   - .append(sl)
437   - .append("**强制输出标准JSON对象**:").append(System.lineSeparator())
438   - .append("示例:").append(JSONUtil.toJsonStr(slMap));
  405 + if (ObjectUtil.isNotEmpty(slMap)) {
  406 + stoolDesc.append(System.lineSeparator()).append(sl).append("**强制输出标准JSON对象**:")
  407 + .append(System.lineSeparator()).append("示例:").append(JSONUtil.toJsonStr(slMap));
439 408 }
440   - log.info("方法描述========================{}",stoolDesc);
  409 +
  410 + log.info("方法描述========================{}", stoolDesc);
441 411 } catch (Exception e) {
442 412 e.printStackTrace();
443 413 log.error("Failed to parse parameter rules: {}", meta.getSMethodName(), e);
444   - // 参数解析失败时,创建无参数的工具规格
445 414 }
  415 +
  416 + // ====================== 【关键修复:强制添加两个参数】 ======================
  417 + if (meta.getIBizType() == 4 || meta.getIBizType() == 5) {
  418 + // 强制添加 operateType(必填)
  419 + builder.addParameter("operateType",
  420 + JsonSchemaProperty.STRING,
  421 + JsonSchemaProperty.description("操作类型:全部确认/合并确认/单行确认")
  422 + );
  423 + }
  424 + // ============================================================================
  425 +
446 426 builder.description(stoolDesc.toString());
447 427 return builder.build();
448 428 }
... ... @@ -628,14 +608,14 @@ public class DynamicToolProvider implements ToolProvider {
628 608 String input = StrUtil.replace(userMessage.text(),"用户输入:",StrUtil.EMPTY);
629 609 // {"0":"查询","1":"执行"} 查询不需要确认
630 610 Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认");
631   - //判断是否生成数据
632   - List<Map<String,Object>> sRowData = new ArrayList<>();
633   - String sHandleType = "merge";
634   - if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){
635   - Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData());
636   - sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData");
637   - sHandleType = sRowDataMap.get("sHandleType").toString();
638   - }
  611 +// //判断是否生成数据
  612 +// List<Map<String,Object>> sRowData = new ArrayList<>();
  613 +// String sHandleType = "merge";
  614 +// if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){
  615 +// Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData());
  616 +// sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData");
  617 +// sHandleType = sRowDataMap.get("sHandleType").toString();
  618 +// }
639 619 //{"1":"存储过程","2":"SQL查询","3":"第三方API","4":"ERP未清","5":"ERP列表(报表)","6":"ERP单据","7":"其它","8":"自然语言TEXT2SQL"}
640 620 if((isConfirmed && (4== meta.getIBizType() ||1== meta.getIBizType()))
641 621 || 2== meta.getIBizType()
... ... @@ -658,12 +638,12 @@ public class DynamicToolProvider implements ToolProvider {
658 638 String askconfirmMsg =StrUtil.EMPTY;
659 639 if(4== meta.getIBizType() || meta.getIBizType()==5){
660 640 askconfirmMsg = doGetFromData( meta,args,session);
661   - return executeWithConfirmation(askconfirmMsg,operableChatMemoryProvider.get(session.getUserId()), session, meta);
  641 + return executeWithConfirmation(askconfirmMsg, session, meta);
662 642 }else{
663 643 askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta);
664 644 }
665 645 // 返回需要确认的结果
666   - return executeWithConfirmation(askconfirmMsg,operableChatMemoryProvider.get(session.getUserId()), session, meta);
  646 + return executeWithConfirmation(askconfirmMsg, session, meta);
667 647 }
668 648  
669 649 /***
... ... @@ -955,6 +935,8 @@ public class DynamicToolProvider implements ToolProvider {
955 935 sReturn = executeToolAfter(meta, args,paramRuleData,session);
956 936 }catch (BusinessException e) {
957 937 return e.getMessage();
  938 + }finally {
  939 + session.setSSystemPrompt(StrUtil.EMPTY);
958 940 }
959 941 if(meta.getIActionType()==1){
960 942 session.setBCleanMemory(true);
... ... @@ -1197,7 +1179,10 @@ public class DynamicToolProvider implements ToolProvider {
1197 1179 rMap.put("sSlaveId",uniqueValue);
1198 1180 rMap.put("唯一",uniqueValue);
1199 1181 // 在行末添加隐藏数据的特殊标记(AI可以解析)
1200   - markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->");
  1182 + markdown.append(" <span style=\"display:none;\">HIDDEN_DATA:")
  1183 + .append(JSONUtil.toJsonStr(rMap))
  1184 + .append("</span>");
  1185 +// markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->");
1201 1186 markdown.append("\n");
1202 1187 machineData.add(rMap);
1203 1188 currentRowData.put(i + 1,recordData.get(i));
... ... @@ -1212,9 +1197,50 @@ public class DynamicToolProvider implements ToolProvider {
1212 1197 appendConfirmAll(markdown,meta.getSControlName());
1213 1198 }
1214 1199 session.setCurrentRowData(currentRowData);
  1200 + // ===================== 加入这一行 =====================
  1201 + String sSystemPrompt = buildDynamicSystemPrompt(session);
  1202 + session.setSSystemPrompt(sSystemPrompt);
  1203 + // ======================================================
1215 1204 return markdown.toString();
1216 1205 }
1217 1206  
  1207 + public String buildDynamicSystemPrompt(UserSceneSession session) {
  1208 +
  1209 + // 1. 强制指令 + 固定方法编号
  1210 + String promptHead = """
  1211 + 【极强约束·必须执行】
  1212 + 1. 禁止说话!禁止解释!
  1213 + 2. 必须调用工具,固定方法编号:MethodNo = queryUnprocessQuote
  1214 + 3. 只输出JSON,无任何其他内容
  1215 +
  1216 + 以下是【行号 → sSlaveId】对应数据:
  1217 + """;
  1218 +
  1219 + // 2. 动态拼接行号 + 动态 sSlaveId
  1220 + Map<Integer, Map<String, Object>> rowDataMap = session.getCurrentRowData();
  1221 + StringBuilder rowDataSb = new StringBuilder();
  1222 + if (ObjectUtil.isNotEmpty(rowDataMap)) {
  1223 + for (Map.Entry<Integer, Map<String, Object>> entry : rowDataMap.entrySet()) {
  1224 + int rowNum = entry.getKey();
  1225 + String sSlaveId = StrUtil.toString(entry.getValue().get("sSlaveId"));
  1226 + rowDataSb.append("第").append(rowNum).append("行 → ").append(sSlaveId).append("\n");
  1227 + }
  1228 + }
  1229 +
  1230 + // 3. 输出格式约束
  1231 + String promptFoot = """
  1232 + 【输出JSON格式·严格遵守】
  1233 + 根据用户选择的行,自动填写 sSlaveId,多行用英文逗号拼接
  1234 + {
  1235 + "operateType": "全部确认/合并确认/单行确认",
  1236 + "sSlaveId": "填写对应的ID,多个用逗号分隔"
  1237 + }
  1238 + """;
  1239 +
  1240 + // 拼接最终返回
  1241 + return promptHead + rowDataSb + promptFoot;
  1242 + }
  1243 +
1218 1244 // 辅助方法:根据中文名查找字段名(通过映射关系转换)
1219 1245 private List<Map<String, Object>> findFieldNameByChinese(List<Map<String, Object>> sAIshowfieldShow,List<Map<String, Object>> rows){
1220 1246 //获取映射关系
... ... @@ -1362,7 +1388,7 @@ public class DynamicToolProvider implements ToolProvider {
1362 1388 /**
1363 1389 * 执行方法后需要用户确认的扩展版本
1364 1390 */
1365   - private String executeWithConfirmation(String initialResult,ChatMemory chatMemory, UserSceneSession session,ToolMeta meta) {
  1391 + private String executeWithConfirmation(String initialResult, UserSceneSession session,ToolMeta meta) {
1366 1392  
1367 1393 // 第一步:执行原始操作,返回初步结果
1368 1394 Map<String, Object> step1Result = new HashMap<>();
... ... @@ -1377,6 +1403,7 @@ public class DynamicToolProvider implements ToolProvider {
1377 1403 // session.setSFunPrompts(userMessage);
1378 1404 // 6. 返回确认请求
1379 1405 // return ToolExecutionResultMessage.from(request,userMessage);
  1406 +// ToolExecutionResultMessage.from(request, text);
1380 1407 return userMessage;
1381 1408 }
1382 1409  
... ...
src/main/resources/templates/chat.html
... ... @@ -462,11 +462,11 @@
462 462 <script>
463 463 let sessionId ="";
464 464 let userid= "17522967560005776104370282597000";
465   - let username= "钱豹";
  465 + let username= "qianb";
466 466 let brandsid= "1111111111";
467 467 let subsidiaryid= "1111111111";
468 468 let usertype= "sysadmin";
469   - let authorization="CE444885A9BCFDDE1FD793F8A0931301E9D7DE6CEDD9DE4B83ECE2219C7829A8F3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1D4CAE6F9AC893752209A98011A981375391D4466816B7D3D1AF306E28B989121C538155B7ADAEE71E899235DC1122F426";
  469 + let authorization="CE444885A9BCFDDE1FD793F8A0931301E9D7DE6CEDD9DE4B83ECE2219C7829A8F3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1DDD971C9BA1323777865C7C7C720A0F7D9C9A98822AD3F0E3100F8DBBB5963377538155B7ADAEE71E899235DC1122F426";
470 470 let hrefLock = window.location.origin+"/xlyAi";
471 471  
472 472 const CONFIG = {
... ...