SystemPromptGenerator.java
8.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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<ParamRule> 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<ParamRule> 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<ToolMeta> 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();
}
}