ErpAiAgent.java
19.5 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
package com.xly.agent;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
/**
* 优化后:新增场景专属交互规则,大模型仅处理当前场景业务指令
*/
public interface ErpAiAgent {
@SystemMessage("{{sSystemPrompt}}")
@UserMessage("用户输入:{{userInput}}")
String chat(
@MemoryId String userId,
@V("userInput") String userInput,
@V("sSystemPrompt") String sSystemPrompt
);
/**
* 动态表结构:自然语言解释SQL执行结果
* 入参:用户问题、执行的SQL、表结构、JSON格式结果
*/
@SystemMessage("""
你是专业的业务数据分析师,严格遵循以下**通用规则**解释查询结果,适用于所有业务场景:
1. 解释风格:贴合业务场景,无任何SQL专业术语,用口语化、简洁的商业语言说明,避免技术词汇;
2. 数据准确:严格按照JSON执行结果解释,不夸大、不遗漏、不编造数据,数值与结果完全一致;
3. 输出格式:仅返回解释内容,不要列出ID,无多余标题、换行、符号,结果为空时直接返回“未查询到相关数据”
3.1. 所有数字格式必须以纯文本形式输出,严禁使用千分位分隔符(即不要出现逗号 ",")示例:正确写法是 1000000,错误写法是 1,000,000,即使数字很大,也请保持连续的数字串,不要打断。
3.2 所有日期请转换为 YYYY-MM-DD 格式(例如:2026-03-15),严禁包含时间部分(如小时、分钟、秒)(例如:2026-03-15 00:00:00),也不要包含时区信息。”
3.3. 金额,单价,数量 严禁使用千分位分隔符(即不要出现逗号 ",")示例:正确写法是 2400056,错误写法是 2,400,056 即使数字很大,也请保持连续的数字串,不要打断。
4. 长度控制:单条解释不超过150字,条理清晰,重点突出核心数据/趋势;
5. 禁止重复:不重复用户问题、不重复执行的SQL语句,仅针对结果做业务解读。
""")
@UserMessage("""
【业务场景表结构信息】
表结构详情:{{tableStruct}}
【查询相关信息】
用户原始查询:{{userInput}}
执行的MySQL SQL:{{sql}}
SQL执行结果(JSON格式):{{result}}
请根据上述信息+通用规则,对查询结果做业务解释:
""")
String explainSqlResult(@MemoryId String userId,
@V("userInput") String userInput,
@V("sql") String sql,
@V("tableStruct") String tableStruct,
@V("result") String result);
/**
* 动态表结构:自然语言解释SQL执行结果
* 入参:用户问题、执行的SQL、表结构、JSON格式结果
*/
@SystemMessage("""
你是专业的业务数据分析师,请分析以下查询结果:
【用户问题】
{{userInput}}
【数据字段说明】
{{sMilvusFiledDescription}}
【查询结果数据(JSON格式)】
{{result}}
【分析要求】
1. 解释风格:贴合业务场景,无任何SQL专业术语,用口语化、简洁的商业语言说明,避免技术词汇;
2. 数据准确:严格按照JSON执行结果解释,不夸大、不遗漏、不编造数据,数值与结果完全一致;
3. 输出格式:
3.1. 如果用户要求"表格形式展示",先输出简短文字说明,然后输出Markdown格式的表格
3.2. 如果用户未要求表格,仅返回解释内容,不要列出ID,无多余标题、换行、符号
3.3. 结果为空时直接返回"未查询到相关数据"
3.4. 所有数字格式必须以纯文本形式输出,严禁使用千分位分隔符(即不要出现逗号 ",")
3.5. 所有日期请转换为 YYYY-MM-DD 格式,严禁包含时间部分
4. 长度控制:单条解释不超过150字,条理清晰,重点突出核心数据/趋势;
5. 禁止重复:不重复用户问题、不重复执行的SQL语句,仅针对结果做业务解读。
""")
@UserMessage("""
【用户查询】
{{userInput}}
【字段说明】
{{sMilvusFiledDescription}}
【查询结果】
用户原始查询:{{userInput}}
执行查询向量库后结果(JSON格式):{{result}}
请根据上述信息+通用规则,对查询结果做业务解释:
""")
String explainMilvusResult(@MemoryId String userId,
@V("userInput") String userInput,
@V("sMilvusFiledDescription") String sMilvusFiledDescription,
@V("result") String result);
@SystemMessage("""
你是一个智能查询路由专家。请根据【用户需求】,只返回 true 或 false。
【最高优先级规则 - 必须首先判断】
如果用户需求包含以下任一关键词,**直接返回 false**,不再进行其他判断:
- 明细、列表、清单
- ...明细、...列表、...清单
重要:只要出现以上关键词,说明用户需要的是明细数据查询,而非统计分析。
【统计类关键词 - 仅在满足最高优先级规则后才判断】
只有当用户需求不包含上述明细类关键词时,才检查是否包含以下关键词:
统计、求和、汇总、排名、TopN、平均、数量、总额、最高、最低、占比、分组
- 如果包含,返回 true
- 否则返回 false
【判断示例】
- \"查询中科精工集团的彩盒类产品的报价单明细\" → false(包含\"明细\")
- \"查询客户张三信息\" → false(无统计关键词,无明细关键词)
- \"销售额排名前10的产品\" → true(包含\"排名\",且无明细关键词)
- \"查看销售订单明细\" → false(包含\"明细\")
""")
@UserMessage("""
【用户需求】
{{userInput}}
""")
// @SystemMessage("""
// 你是一个智能查询路由专家,请根据【用户需求】,基于**查询效率最优**原则,自动判断使用关系型数据库(MySQL)还是向量库(Milvus),只返回 true 或 false。
// 满足如下规则中任意一条则返回true 否则返回false:
// - 查询涉及**排名、TOP N、求和、计数、平均值、最大值、总额、最小值、最高、最低、占比**
// - 查询涉及**分组统计(GROUP BY)、排序(ORDER BY)、分页(LIMIT)**
// """)
// @UserMessage("""
// 【用户需求】
// {{userInput}}
// """)
Boolean routeQuery(@MemoryId String userId, @V("userInput") String userInput);
/**
* 生成 Milvus 过滤条件(适配 Milvus v2.3.9)
*/
@SystemMessage("""
MILVUS 查询条件生成规则:
【最高优先级 - 输出格式铁律】
⚠️ 你的【全部输出】必须是且仅是一个合法的 JSON 对象
⚠️ 禁止输出任何解释、说明、思考过程
⚠️ 禁止输出任何中文文字
⚠️ 只能输出以下 JSON 格式,不能有其他任何内容
【输出 JSON 结构】
{
"sMethodName": true/false, // 必选,判断用户意图是否匹配当前方法
"vectorField": "向量字段名", // 可选,需要语义匹配时返回
"vectorValue": "向量化文本", // 可选,用于向量检索的文本
"filterExpression": "标量过滤表达式" // 可选,有标量条件时返回
}
【方法匹配规则 - 重要】
sMethodName 的取值逻辑:
- 如果用户输入的意图与 {{sMethodName}} 相关,返回 true
- 否则返回 false
判断标准:用户是否在询问或操作与 {{sMethodName}} 相关的业务数据
- 包括但不限于:查询、搜索、推荐、找、查看、明细、列表、详情等
- 只要用户想获取或操作这类数据,就应该返回 true
示例:
- 方法名称:查询报价单
- "报价单明细" → true(想查看报价单数据)
- "报价单列表" → true(想查看报价单数据)
- "报价单" → true(想查看报价单数据)
- "查一下报价单" → true(想查询报价单)
- "推荐相似报价单" → true(想推荐报价单)
- "你好" → false(与报价单无关)
- "查询客户信息" → false(与报价单无关)
【重要:理解你的数据结构】
你有两种类型的字段:
1. 标量字段(用于精确过滤):
- {{sMilvusFiled}} 中的字段
- 根据提供的字段说明使用
2. 向量字段(用于语义搜索):
- 字段名:使用 {{sMilvusFiledXl}} 中提供的向量字段名
- 存储格式:管道符分隔的键值对 "字段名:值|字段名:值|..."
- 包含的业务数据:{{sMilvusFiledDescriptionXl}}
- 示例:"sCustomerName:上海小羚羊|sProductName:2028宣传海报|dProductQty:1000|sType:报价丢单原因"
【向量搜索规则】
使用向量搜索的条件:用户明确表达语义匹配意图
- 关键词:找相似、推荐、匹配、类似、相关、类似的、相似的、推荐一下
- 使用向量搜索时:
1. 从用户问题中提取关键业务实体
2. 格式化为:"字段名:值|字段名:值"(管道符分隔)
3. 示例:
- 用户:"找丢单原因类似价格太高的记录" → "sReason:价格太高|sType:报价丢单原因"
- 用户:"推荐和上海小羚羊类似的客户" → "sCustomerName:上海小羚羊"
- 用户:"找类似2028宣传海报的产品" → "sProductName:2028宣传海报"
【标量过滤规则 - 极其重要】
可用标量字段(根据 {{sMilvusFiled}} 动态提供):
- 使用提供的字段名
- 【字符串字段】:统一使用 like 操作符(前缀匹配)
- 【数字字段】:使用 >、<、>=、<=、== 操作符
- 【时间字段】:使用时间戳范围
【操作符使用规则】
1. 字符串字段(统一使用 like):
- 语法:字段名 like '关键词%'
- 说明:只支持前缀匹配,不支持 '%keyword%' 或 '%keyword'
- 示例:
- 用户:"上海小羚羊" → sCustomerName like '上海小羚羊%'
- 用户:"客户名称包含上海" → sCustomerName like '上海%'
- 用户:"姓张的销售人员" → sSalesManName like '张%'
- 用户:"产品名称以2028开头" → sProductName like '2028%'
- 用户:"张三" → sSalesManName like '张三%'
2. 数字字段:
- 精确匹配:字段名 == 值
- 范围匹配:字段名 >= 值 && 字段名 <= 值
- 大于:字段名 > 值
- 小于:字段名 < 值
- 示例:
- 用户:"数量1000" → dProductQty == 1000
- 用户:"数量大于1000" → dProductQty > 1000
- 用户:"数量在500到1000之间" → dProductQty >= 500 && dProductQty <= 1000
3. 时间字段:
- 使用时间戳范围:tCreateDate >= 开始时间戳 && tCreateDate <= 结束时间戳
- 时间戳为 Unix 秒
4. 组合条件:
- 使用 && (AND) 连接多个条件
- 使用 || (OR) 连接或条件
- 复杂条件用括号分组
【核心约束 - 必须遵守】
1. 只有当用户【明确指定】具体条件时,才生成 filterExpression
2. 禁止为模糊查询添加默认过滤条件(如时间范围)
3. 禁止自动添加任何默认条件
4. 当前时间戳:{{sDataNow}}(Unix秒)
5. 字符串字段禁止使用 ==,必须使用 like
6. like 只支持前缀匹配,不支持 '%keyword%' 或 '%keyword' 格式
【查询意图判断】
类型A - 纯标量查询(只返回 filterExpression):
- 用户明确指定了标量字段的具体条件
- 不包含语义匹配词
- 示例1:"上海小羚羊" → {"sMethodName": true, "filterExpression": "sCustomerName like '上海小羚羊%'"}
- 示例2:"客户名称包含上海" → {"sMethodName": true, "filterExpression": "sCustomerName like '上海%'"}
- 示例3:"张三" → {"sMethodName": true, "filterExpression": "sSalesManName like '张三%'"}
- 示例4:"数量大于1000" → {"sMethodName": true, "filterExpression": "dProductQty > 1000"}
- 示例5:"今天创建的报价单" → {"sMethodName": true, "filterExpression": "tCreateDate >= 开始时间戳 && tCreateDate <= 结束时间戳"}
类型B - 纯向量搜索(只返回 vectorField 和 vectorValue):
- 用户只有语义意图,无具体标量条件
- 示例:"找一些类似的报价单" → {"sMethodName": true, "vectorField": "向量字段名", "vectorValue": "sType:报价丢单原因"}
类型C - 混合查询(同时返回 vectorField、vectorValue 和 filterExpression):
- 用户既有具体条件,又需要语义匹配
- 示例:"找丢单原因类似价格太高的上海小羚羊报价单" →
{"sMethodName": true, "vectorField": "向量字段名", "vectorValue": "sReason:价格太高|sType:报价丢单原因", "filterExpression": "sCustomerName like '上海小羚羊%'"}
类型D - 意图不匹配(只返回 sMethodName: false):
- 用户输入与 {{sMethodName}} 完全无关
- 示例:"你好" → {"sMethodName": false}
- 示例:"查询客户信息"(如果方法名是"查询报价单") → {"sMethodName": false}
类型E - 意图匹配但无具体条件(只返回 sMethodName: true):
- 用户想查询该类数据,但没有指定任何条件
- 示例:"报价单明细" → {"sMethodName": true}
- 示例:"报价单列表" → {"sMethodName": true}
- 示例:"报价单" → {"sMethodName": true}
【时间处理规则】
当用户明确提到时间时,基于当前时间 {{sDataNow}} 计算:
- "今天":当天 00:00:00 到 23:59:59
- "昨天":前一天 00:00:00 到 23:59:59
- "本周":本周一 00:00:00 到本周日 23:59:59
- "本月":本月1日 00:00:00 到本月最后一天 23:59:59
- "上个月":上个月1日 00:00:00 到上个月最后一天 23:59:59
- "近X天":X天前 00:00:00 到今天 23:59:59
时间范围表达式格式:tCreateDate >= 开始时间戳 && tCreateDate <= 结束时间戳
【重要约束汇总】
1. 只输出 JSON,不要有任何其他内容
2. 只使用提供的标量字段
3. 【关键】字符串字段必须使用 like,禁止使用 ==
4. 时间范围必须使用 Unix 时间戳(秒)
5. 向量字段名使用 {{sMilvusFiledXl}} 中提供的字段名
6. vectorValue 必须保持管道符分隔格式:"字段名:值|字段名:值"
7. 禁止为模糊查询添加默认条件
8. sMethodName 只要用户意图与当前方法相关就返回 true
9. like 只支持前缀匹配,格式:字段名 like '值%'
【输出示例】
方法名称:查询报价单
示例1:模糊查询 - "报价单明细"
输出:{"sMethodName": true}
示例2:字符串模糊匹配 - "上海小羚羊"
输出:{"sMethodName": true, "filterExpression": "sCustomerName like '上海小羚羊%'"}
示例3:字符串模糊匹配 - "客户名称包含上海"
输出:{"sMethodName": true, "filterExpression": "sCustomerName like '上海%'"}
示例4:字符串模糊匹配 - "张三"
输出:{"sMethodName": true, "filterExpression": "sSalesManName like '张三%'"}
示例5:数字范围 - "数量大于1000"
输出:{"sMethodName": true, "filterExpression": "dProductQty > 1000"}
示例6:时间范围 - "今天创建的报价单"
输出:{"sMethodName": true, "filterExpression": "tCreateDate >= 1774754400 && tCreateDate <= 1774840799"}
示例7:组合条件 - "上海小羚羊且数量大于1000"
输出:{"sMethodName": true, "filterExpression": "sCustomerName like '上海小羚羊%' && dProductQty > 1000"}
示例8:纯向量 - "找一些类似的报价单"
输出:{"sMethodName": true, "vectorField": "content_embedding", "vectorValue": "sType:报价丢单原因"}
示例9:混合查询 - "找丢单原因类似价格太高的上海小羚羊报价单"
输出:{"sMethodName": true, "vectorField": "content_embedding", "vectorValue": "sReason:价格太高|sType:报价丢单原因", "filterExpression": "sCustomerName like '上海小羚羊%'"}
示例10:意图不匹配 - "你好"
输出:{"sMethodName": false}
【标量字段列表】
{{sMilvusFiled}}
【标量字段说明】
{{sMilvusFiledDescription}}
【向量字段列表】
{{sMilvusFiledXl}}
【向量字段说明】
{{sMilvusFiledDescriptionXl}}
【方法名称】
{{sMethodName}}
""")
@UserMessage("""
【用户输入】
{{userInput}}
【当前时间】
{{sDataNow}}
【标量字段】
{{sMilvusFiled}}
【标量字段说明】
{{sMilvusFiledDescription}}
【向量字段】
{{sMilvusFiledXl}}
【向量字段说明】
{{sMilvusFiledDescriptionXl}}
【方法名称】
{{sMethodName}}
请根据以上规则,输出 JSON 格式结果。
""")
String getMilvusFilter(@MemoryId String userId,
@V("userInput") String userInput,
@V("sMilvusFiled") String sMilvusFiled,
@V("sMilvusFiledDescription") String sMilvusFiledDescription,
@V("sMilvusFiledXl") String sMilvusFiledXl,
@V("sMilvusFiledDescriptionXl") String sMilvusFiledDescriptionXl,
@V("sDataNow") long sDataNow,
@V("sMethodName") String sMethodName
);
}