package com.xly.agent; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONWriter; import com.xly.entity.ParamRule; import com.xly.entity.ToolMeta; import com.xly.entity.UserSceneSession; import java.util.List; import java.util.stream.Collectors; public class SystemPromptGenerator { public static String generate(String toolName, UserSceneSession session,String sKnowledgeBase) { StringBuilder sb = new StringBuilder(); sb.append("你是一个无情的参数提取器,不是聊天助手。\n\n"); sb.append("【核心任务】\n"); sb.append("从用户输入中提取参数,并立即调用工具 ").append(toolName).append("。\n\n"); sb.append("【绝对指令】\n"); sb.append("1. 严禁与用户对话,严禁输出任何纯文本回复。\n"); sb.append("2. 无论参数缺失多么严重,都必须直接调用工具,缺失字段填空字符串或0。\n"); if(ObjectUtil.isNotEmpty(sKnowledgeBase)){ sb.append("3. 印刷行业知识库:提取参数前,请先理解行业知识库,再进行方法匹配和参数提取,知识库内容如下: \n"); sb.append(" - "+sKnowledgeBase); } sb.append("【参数提取规则】\n"); sb.append("用户输入格式为 \"参数名: 值\" 或 \"参数名 值\",你必须识别参数名并提取对应的值。\n\n"); sb.append("【可提取的参数列表】\n"); List dynamicParamRuleAll = session.getCurrentTool().getParamRuleListAll(); // 从参数定义中提取 for (ParamRule paramRule : dynamicParamRuleAll) { sb.append("- ").append(paramRule.getSParam()).append("\n"); } sb.append("\n【提取示例】\n"); // 动态生成示例(取前3个参数作为示例) int count = 0; for (ParamRule paramRule : dynamicParamRuleAll) { if (count++ >= 3) break; sb.append("- 用户输入 \"").append(paramRule.getSParam()).append(": {值}\" → 提取 ").append(paramRule.getSParam()).append("\n"); } if(ObjectUtil.isNotEmpty(session.getArgs())){ sb.append("【已提取参数】\n"); sb.append(JSON.toJSONString(session.getArgs(), JSONWriter.Feature.PrettyFormat)); } sb.append("\n【工具调用规则】\n"); sb.append("1. 每次只调用一次工具\n"); sb.append("2. 调用后立即停止,不要重复调用\n"); sb.append("3. 不要总结,不要回复用户,只调用工具"); return sb.toString(); } /** * 缺失参数统一提示模板:强制AI调用自定义方法,一次性回填所有参数 */ public static String buildMissParamPrompt(UserSceneSession session, List paramRuleDataMiss) { String methodNo = session.getCurrentTool().getSMethodNo(); // 1. 拼接缺失参数的描述(增加“参数名”和“英文名”的对应,方便模型映射) String paramDesc = paramRuleDataMiss.stream() .map(p -> String.format("- 参数名:%s (对应字段: %s),示例值:%s", p.getSParam(), p.getSParamValue(), p.getSExampleValue())) .collect(Collectors.joining("\n")); // 但根据你的需求,其实 prompt 里并不需要强行插入一个空的 JSON 片段, // 直接告诉模型去“合并”即可。 return String.format(""" 请你根据用户最新的输入,补充缺失的参数,并带着【所有完整参数】立即发起工具调用。 【核心执行规则】 1. **参数合并(至关重要)**:你必须保留上一轮对话中已经获取的参数(如 sSlaveId, operateType 等),并将用户本次提供的新参数与旧参数合并。 2. **必须调用工具**:必须调用工具 `%s`。绝对禁止直接输出 JSON 文本,也绝对禁止向用户反问或索要信息。 3. **完整调用**:请直接生成包含所有参数的 tool_calls 请求。 【已获取数据】 %s 【当前缺失的参数说明】 %s """, methodNo,JSONObject.toJSONString(session.getArgs()), paramDesc); } public static String buildDynamicSystemPrompt(UserSceneSession session) { // 获取当前工具编号(用于 JSON 的 name 字段) String methodNo = session.getCurrentTool().getSMethodNo(); return """ 【极强约束·必须执行】 1. 禁止说话!禁止解释! 2. 必须调用工具! 3. 只输出标准工具调用JSON! ### 任务背景 ### 用户会输入自然语言指令(如“第1行确认”、“全部合并”等)。 你的任务是:解析出【操作类型】和【目标行号】。 ### 解析规则(必须严格遵守) ### 1. **操作类型 (operateType)**: - 如果用户说“全部确认”、“生成多个单据”,识别为:"全部确认" - 如果用户说“合并确认”、“生成一个单据”,识别为:"合并确认" - 其他情况,默认为:"单行确认" 2. **行号提取 (rowNumbers)**: - 单选:如“第5行”,提取为 [5] - 多选:如“第1、3、5行”,提取为 [1, 3, 5] - 范围:如“第2到4行”,提取为 [2, 3, 4] - 全选:如“全部”、“所有”,提取为 ["ALL"] ### 输出格式规范 ### 请直接输出以下 JSON(不要任何 Markdown 格式,不要 ```json): {"name": "%s", "parameters": {"operateType": "解析出的类型", "rowNumbers": [行号数组或"ALL"]}} """.formatted(methodNo); } /** * 方法选择阶段:从可选方法树中,让大模型选出唯一最匹配的一个 sMethodNo。 * 与 generate()(参数提取)配套,是它的上游一步。 * * @param toolList 当前场景可选的方法列表(方法树扁平化后的叶子集合) * @param sKnowledgeBase 行业知识库,可为空 * @return 供 choiceMethod 使用的 sSystemPrompt */ public static String generateChoiceMethodPrompt(List toolList, String sKnowledgeBase) { StringBuilder sb = new StringBuilder(); sb.append("你是一个方法路由分类器。你的职责是理解用户意图,从候选方法中选出唯一最匹配的一项。\n\n"); sb.append("【任务】\n"); sb.append("阅读用户输入,与【可选方法列表】逐项比对语义,选出意图最贴合的一个方法,输出它的 sMethodNo。\n"); sb.append("判断时以用户的核心操作意图为准,只依据用户明确表达的内容,不推测未提及的需求。\n\n"); sb.append("【输出要求】\n"); sb.append("- 只输出一个 JSON 对象,形如:{\"sMethodNo\": \"方法编号\", \"confidence\": 0.0}\n"); sb.append("- sMethodNo 取自【可选方法列表】中的编号;均不匹配时取 \"none\"。\n"); sb.append("- confidence 为 0~1 的匹配置信度:意图明确取高值,表述模糊取低值。\n"); sb.append("- 不要输出解释、思考过程或 JSON 以外的任何文字。\n\n"); if (ObjectUtil.isNotEmpty(sKnowledgeBase)) { sb.append("【行业背景知识】\n"); sb.append("以下知识用于帮助你理解用户输入中的行业术语,请结合它判断意图:\n"); sb.append(sKnowledgeBase).append("\n\n"); } sb.append("【可选方法列表】\n"); for (ToolMeta tool : toolList) { sb.append("- sMethodNo: ").append(tool.getSMethodNo()) .append(" 名称: ").append(tool.getSMethodName()); // 描述为空时降级用场景名,保证每个方法都有判别依据 String desc = ObjectUtil.isNotEmpty(tool.getStoolDesc()) ? tool.getStoolDesc() : tool.getSceneName(); if (ObjectUtil.isNotEmpty(desc)) { sb.append(" 适用场景: ").append(desc); } sb.append("\n"); } sb.append("\n【示例】\n"); int count = 0; for (ToolMeta tool : toolList) { if (count++ >= 3) break; sb.append("- 用户输入涉及\"").append(tool.getSMethodName()) .append("\" → {\"sMethodNo\": \"").append(tool.getSMethodNo()) .append("\", \"confidence\": 0.9}\n"); } sb.append("- 用户输入\"你好\" → {\"sMethodNo\": \"none\", \"confidence\": 0}\n"); return sb.toString(); } }