Commit 97ec2c2e3e8eafaf0b1ee4e7d6f280455b5c7066

Authored by qianbao
1 parent 25c2a140

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

src/main/java/com/xly/agent/AgentSystemPrompt.java
... ... @@ -2,21 +2,22 @@ package com.xly.agent;
2 2  
3 3 public class AgentSystemPrompt {
4 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   - public static final String sSystemPrompt = "你是一个专业的智能助手,请根据以下规则精准匹配并调用工具:\n" +
13   - "【核心决策逻辑】\n" +
14   - "1. 单一工具场景:当系统仅提供一个可用工具时,除非用户是在进行无意义的闲聊(如打招呼),否则必须无条件调用该工具,禁止向用户索要额外信息或进行解释;\n" +
15   - "2. 多工具场景:当系统提供多个工具时,必须先精准识别用户的核心业务意图,然后匹配唯一对应的工具进行调用;\n" +
16   - "\n" +
17   - "【参数提取与调用规范】\n" +
18   - "3. 触发条件:匹配工具时,仅关注工具描述中“当用户”和“时,必须调用本工具”之间的语义关联,无需过度纠结参数的详细描述;\n" +
19   - "4. 缺省处理:调用工具前,不需要询问用户提供缺失的参数。如果某个必填参数在用户输入中找不到,请直接省略该字段(不要填入\"\"、null或0);\n" +
20   - "5. 格式要求:提取参数时严格按标注类型赋值,数字不带引号,字符串需去除首尾空格;\n" +
21   - "6. 绝对禁止:在任何情况下,禁止向用户反问以确认参数或意图,你的输出只能是最终回复或合法的工具调用请求。";
22   -}
  5 +// public static final String sSystemPrompt = "你是一个专业的智能助手,请根据以下规则精准匹配并调用工具:\n" +
  6 +// "【核心决策逻辑】\n" +
  7 +// "1. 单一工具绝对调用:当系统仅提供一个可用工具时,必须无条件调用该工具。即使参数不全,也必须发起调用,严禁返回向用户索要信息或者不调用工具;\n" +
  8 +// "2. 多工具意图匹配:当系统提供多个工具时,必须先精准识别用户的核心业务意图,然后匹配唯一对应的工具进行调用;\n" +
  9 +// "\n" +
  10 +// "【参数提取与调用规范】\n" +
  11 +// "3. 触发条件:匹配工具时,仅关注工具描述中“当用户”和“时,必须调用本工具”之间的语义关联;\n" +
  12 +// "4. 参数补全与缺省处理:\n" +
  13 +// " - 如果当前是全新的独立请求,且必填参数缺失,请直接省略该字段发起调用(不要反问用户)。\n" +
  14 +// "5. 格式要求:提取参数时严格按标注类型赋值,数字不带引号,字符串需去除首尾空格;\n" +
  15 +// "6. 绝对禁止:在任何情况下,禁止向用户反问以确认参数或意图,你的输出只能是最终回复或合法的工具调用请求。";
  16 +
  17 + public static final String sSystemPrompt = "你是一个无情的参数提取器,不是聊天助手。\n" +
  18 + "【绝对指令】\n" +
  19 + "1. 你的唯一任务是提取用户输入中的参数,并立即调用工具。\n" +
  20 + "2. 严禁与用户进行任何对话!严禁输出“您好”、“请提供”、“需要补充”等任何纯文本回复!\n" +
  21 + "3. 无论用户输入多么简短(例如只输入了公司名),或者参数缺失多么严重,你都必须直接调用工具,将缺失的字段留空或填0。\n" +
  22 + "4. 不要试图去引导用户,引导用户是你的后端程序要做的事,你只需要负责把提取到的参数传出去。";
  23 +}
23 24 \ No newline at end of file
... ...
src/main/java/com/xly/service/XlyErpService.java
... ... @@ -30,6 +30,7 @@ import dev.langchain4j.agent.tool.ToolSpecification;
30 30 import dev.langchain4j.data.message.AiMessage;
31 31 import dev.langchain4j.data.message.ChatMessage;
32 32  
  33 +import dev.langchain4j.memory.ChatMemory;
33 34 import dev.langchain4j.model.ollama.OllamaChatModel;
34 35 import dev.langchain4j.service.AiServices;
35 36 import dev.langchain4j.service.Result;
... ... @@ -864,26 +865,6 @@ public class XlyErpService {
864 865 }
865 866  
866 867  
867   -
868   - /***
869   - * @Author 钱豹
870   - * @Date 11:22 2026/1/31
871   - * @Param
872   - * @return
873   - * @Description 动态参数补齐处理
874   - **/
875   - private String dotoolExecutionRequests(AiMessage aiMessage){
876   - String textTs = aiMessage.text();
877   - if(aiMessage.hasToolExecutionRequests()){
878   - List<ToolExecutionRequest> toolExecutionRequests = aiMessage.toolExecutionRequests();
879   - toolExecutionRequests.forEach(toolRequests->{
880   - String arguments = toolRequests.arguments();
881   - log.info(arguments);
882   - });
883   - }
884   - return textTs;
885   - }
886   -
887 868 /***
888 869 * 存入全部场景
889 870 * @Author 钱豹
... ... @@ -974,9 +955,6 @@ public class XlyErpService {
974 955 .chatModel(chatModel)
975 956 .chatMemoryProvider(operableChatMemoryProvider)
976 957 .tools(executors,immediateReturnToolNames)
977   -// .toolProvider(dynamicToolProvider)
978   -// .returnBehavior(ReturnBehavior.IMMEDIATE)
979   -// .toolChoice(ChatCompletionToolChoice.ofRequired())
980 958 .build();
981 959 UserSceneSessionService.ERP_AGENT_CACHE.put(userId, aiAgent);
982 960 // log.info("用户{}Agent构建完成,已选场景:{},场景ID{}", userId, session.isSceneSelected() ? session.getCurrentScene().getSSceneName() : "未选(全场景匹配)", dynamicToolProvider.sSceneIdMap.get(userId));
... ... @@ -995,8 +973,10 @@ public class XlyErpService {
995 973 executors.put(one.getToolSpecification(),one.getToolExecutor());
996 974 });
997 975 }
  976 + ChatMemory chatMemory = operableChatMemoryProvider.get(session.getUserId());
998 977 ErpAiAgent aiAgent = AiServices.builder(ErpAiAgent.class)
999 978 .chatModel(chatModel)
  979 + .chatMemory(chatMemory)
1000 980 .chatMemoryProvider(operableChatMemoryProvider)
1001 981 .tools(executors,immediateReturnToolNames)
1002 982 .build();
... ...
src/main/java/com/xly/tool/DynamicToolProvider.java
1 1 package com.xly.tool;
2 2  
3 3  
  4 +import cn.hutool.core.date.DateUtil;
4 5 import cn.hutool.core.util.BooleanUtil;
5 6 import cn.hutool.core.util.ObjectUtil;
6 7 import cn.hutool.core.util.StrUtil;
... ... @@ -23,11 +24,8 @@ import com.xly.service.UserSceneSessionService;
23 24 import com.xly.util.*;
24 25 import dev.langchain4j.agent.tool.*;
25 26  
26   -import dev.langchain4j.data.message.ChatMessage;
27   -import dev.langchain4j.data.message.ChatMessageType;
28   -import dev.langchain4j.data.message.ToolExecutionResultMessage;
  27 +import dev.langchain4j.data.message.*;
29 28  
30   -import dev.langchain4j.data.message.UserMessage;
31 29 import dev.langchain4j.memory.ChatMemory;
32 30  
33 31 import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
... ... @@ -208,6 +206,7 @@ public class DynamicToolProvider implements ToolProvider {
208 206 log.error("返回信息",e);
209 207 String askMsg = e.getMessage();
210 208 session.setSFunPrompts(askMsg);
  209 + addSystemMessage(session, askMsg);
211 210 // 需要提问用户 → 抛异常停止循环
212 211 throw new RuntimeException(askMsg);
213 212 }
... ... @@ -221,6 +220,7 @@ public class DynamicToolProvider implements ToolProvider {
221 220 // 4.1 参数缺失,生成“提问”消息,直接返给客户
222 221 String askMsg = buildAskUserMessage(meta, missing,args);
223 222 session.setSFunPrompts(askMsg);
  223 + addSystemMessage(session, askMsg);
224 224 return String.valueOf(successResult(toolExecutionRequest, askMsg));
225 225 }
226 226 // ====================== 返回时带终止指令 ======================
... ... @@ -311,10 +311,16 @@ public class DynamicToolProvider implements ToolProvider {
311 311 【工具调用规则】
312 312 1. 每个自定义方法最多只能调用 1 次
313 313 2. 只要方法返回结果,必须停止调用
314   - 3. 禁止重复调用同一个方法
315   - 4. 禁止无意义循环调用
316   - 5. 当用户输入包含【数据确认】、确认数据、确认、第*条数据确认时,必须调用本工具
  314 + 3. 当用户输入包含【数据确认】、确认数据、确认、第*条数据确认时,必须调用本工具
317 315 """;
  316 +// String forceToolPrompt = """
  317 +// 【工具调用规则】
  318 +// 1. 每个自定义方法最多只能调用 1 次
  319 +// 2. 只要方法返回结果,必须停止调用
  320 +// 3. 禁止重复调用同一个方法
  321 +// 4. 禁止无意义循环调用
  322 +// 5. 当用户输入包含【数据确认】、确认数据、确认、第*条数据确认时,必须调用本工具
  323 +// """;
318 324 stoolDesc.append(forceToolPrompt);
319 325 if (ObjectUtil.isNotEmpty(meta.getStoolDesc())) {
320 326 stoolDesc.append("MethodNo:").append(meta.getSMethodNo())
... ... @@ -370,6 +376,15 @@ public class DynamicToolProvider implements ToolProvider {
370 376 schemaBuilder.addEnumProperty(paramDesc, constList, paramDesc);
371 377 }
372 378 break;
  379 + case "date":
  380 + case "datetime":
  381 + // 核心技巧:在描述中强制要求 AI 进行格式转换
  382 + String dateDesc = paramDesc + "。【重要】请将用户输入的任意日期(如'明天'、'2026/5/1'、'下周三')自动转换为 'yyyy-MM-dd' 格式的字符串,当前日期:"+ DateUtil.now() +"。";
  383 + schemaBuilder.addStringProperty(paramDesc, dateDesc);
  384 + if (bEmpty) {
  385 + requiredParams.add(paramDesc);
  386 + }
  387 + break;
373 388 default:
374 389 schemaBuilder.addStringProperty(paramDesc, paramDesc);
375 390 break;
... ... @@ -381,13 +396,17 @@ public class DynamicToolProvider implements ToolProvider {
381 396 } catch (Exception e) {
382 397 e.printStackTrace();
383 398 }
384   -
  399 + List<String> requiredParamsNew = new ArrayList<>();
  400 + if(requiredParams!=null && requiredParams.size()>0){
  401 + requiredParamsNew.add(requiredParams.get(0));
  402 + }
385 403 if (meta.getIBizType() == 4 || meta.getIBizType() == 5) {
386 404 schemaBuilder.addStringProperty("operateType", "操作类型:全部确认/合并确认/单行确认");
387 405 requiredParams.add("operateType");
  406 + requiredParamsNew.add("operateType");
388 407 }
389 408 if (!requiredParams.isEmpty()) {
390   - schemaBuilder.required(requiredParams);
  409 + schemaBuilder.required(requiredParamsNew);
391 410 }
392 411 JsonObjectSchema parameters = schemaBuilder.build();
393 412 return builder
... ... @@ -473,6 +492,7 @@ public class DynamicToolProvider implements ToolProvider {
473 492 List<String> missing = checkRequiredParams(args, paramRuleDataCheck);
474 493 if (!missing.isEmpty()) {
475 494 String askMsg = buildAskUserMessage(meta, missing,args);
  495 + addSystemMessage(session, askMsg);
476 496 return askMsg;
477 497 }
478 498  
... ... @@ -503,6 +523,7 @@ public class DynamicToolProvider implements ToolProvider {
503 523 List<ParamRule> paramRuleDataMiss = getMissParamRuleAfter(args, paramRuleData);
504 524 String sSystemPrompt = buildMissParamPrompt(session,paramRuleDataMiss);
505 525 session.setSSystemPrompt(sSystemPrompt);
  526 + addSystemMessage(session, askMsg);
506 527 return askMsg;
507 528 }
508 529 return executeTool(meta, args, paramRuleData, session.getUserId(), session);
... ... @@ -801,12 +822,18 @@ public class DynamicToolProvider implements ToolProvider {
801 822 if(ObjectUtil.isEmpty(sBizContent) && iBizType == 4){
802 823 sBizContent ="Sp_Ai_AddCommonAfterNew";
803 824 if(ObjectUtil.isEmpty(args.get("sSlaveId"))){
804   - throw new BusinessException(-1,"请选择操作数据");
  825 + //插入AI记忆
  826 + String sMsg = "请选择操作数据";
  827 + addSystemMessage(session, sMsg);
  828 + throw new BusinessException(-1,sMsg);
805 829 }
806 830 List<Map<String,Object>> sRowData = doGetFromDataWq( meta, args, session);
807 831 if(ObjectUtil.isEmpty(sRowData)){
808   - session.setSFunPrompts("选择的数据ID:"+args.get("sSlaveId")+"不存在,请重新选择。");
809   - throw new BusinessException(-1,"选择的数据ID:"+args.get("sSlaveId")+"不存在,请重新选择。");
  832 + String sMsg = "选择的数据ID:"+args.get("sSlaveId")+"不存在,请重新选择。";
  833 + session.setSFunPrompts(sMsg);
  834 + //插入AI记忆
  835 + addSystemMessage(session, sMsg);
  836 + throw new BusinessException(-1,sMsg);
810 837 }
811 838 Map<String,Object> dataOne = DeepCopyUtils.deepCopy(args);
812 839 dataOne.remove("sSlaveId");
... ... @@ -859,6 +886,7 @@ public class DynamicToolProvider implements ToolProvider {
859 886 } else {
860 887 String sMsgText = "未找到对应的数据";
861 888 session.setSFunPrompts(sMsgText);
  889 + addSystemMessage(session, sMsgText);
862 890 throw new BusinessException(-1,sMsgText);
863 891 }
864 892 }
... ... @@ -869,6 +897,12 @@ public class DynamicToolProvider implements ToolProvider {
869 897 return "操作成功";
870 898 }
871 899  
  900 + private void addSystemMessage(UserSceneSession session,String sMsg){
  901 +// if(ObjectUtil.isNotEmpty(operableChatMemoryProvider) && ObjectUtil.isNotEmpty(operableChatMemoryProvider.get(session.getUserId()))){
  902 +// operableChatMemoryProvider.get(session.getUserId()).add(SystemMessage.from("上一次工具调用失败。原因:" + sMsg + "。请根据用户意图修正参数,并再次调用工具。"));
  903 +// }
  904 + }
  905 +
872 906 private List<Map<String,Object>> doGetFromDataWq(ToolMeta meta, Map<String, Object> args,UserSceneSession session){
873 907 String sUrl = meta.getSendUrl();
874 908 Map<String,Object> sBody = new HashMap<>();
... ...