Commit 97ec2c2e3e8eafaf0b1ee4e7d6f280455b5c7066
1 parent
25c2a140
添加未清选择 改成动态引导语
Showing
3 changed files
with
68 additions
and
53 deletions
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<>(); | ... | ... |