package com.xly.agent; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; 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.ArrayList; 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(); } /*** * @Author 钱豹 * @Date 21:59 2026/7/30 * @Param [partList] * @return java.lang.String * @Description 动态部件获取提示【印刷行业|增强校验版】 **/ public static String generateChoicePartPrompt(List partList) { StringBuilder sb = new StringBuilder(); sb.append("你是印刷行业部件提取工具。你的职责是理解印刷工艺需求文本,筛选部件名称,内文就是内页。\n\n"); sb.append("【执行顺序&强制规则(严格按顺序执行)】\n"); sb.append("第一步:逐条遍历【部件列表】内所有名称,逐一核对用户文本,收集所有匹配部件,不得跳过任何列表项。\n"); sb.append("第二步:处理名称转换:识别到“内文”统一替换为“内页”,最终结果禁止出现\"内文\"\n"); sb.append("第三步:扩展提取:扫描全文,如果存在印刷书刊部件名词,附带材质、克重、P数、工艺等描述,但该名词不在【部件列表】中,追加该部件名称;无配套描述则不追加。\n\n"); sb.append("【任务】\n"); sb.append("阅读用户印刷需求输入,提取所有符合规则的部件名称,全部输出,一个都不能少。\n"); sb.append("【输出要求】\n"); sb.append("- 只输出一个 JSON 数组,如:[\"封面\",\"内页\",\"拉页\"]\n"); sb.append("- 如果用户输入中明确提到了某个部件,即使措辞略有不同,也要输出匹配的列表项\n"); sb.append("- 如果没有任何部件匹配,输出 \"none\"\n"); sb.append("- 不要输出解释、思考过程或 JSON 以外的任何文字。\n\n"); sb.append("【部件列表】\n"); sb.append(JSONUtil.toJsonStr(partList)); sb.append("\n\n【印刷业务示例】\n"); sb.append("- 用户输入:封面工艺:烫金,内文用纸80g双胶 → [\"封面\",\"内页\"]\n"); sb.append("- 用户输入:内文250g铜版纸,插页8P、157g哑粉,封面157g哑粉烫金 → [\"内页\",\"封面\",\"插页\"]\n"); sb.append("- 用户输入:护封材质特种纸,夹页4色印刷 → [\"护封\",\"夹页\"]\n"); sb.append("- 用户输入\"你好\" → \"none\"\n"); sb.append("\n**重要**:务必逐条核对部件列表,禁止遗漏列表内匹配项;不要受示例限制。\n"); return sb.toString(); } }