diff --git a/pom.xml b/pom.xml
index e693acd..01923e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,6 +105,12 @@
org.springframework.boot
spring-boot-starter-webflux
+
+
+ com.github.jsqlparser
+ jsqlparser
+ 4.9
+
diff --git a/src/main/java/com/xly/agent/ErpAiAgent.java b/src/main/java/com/xly/agent/ErpAiAgent.java
index ba4f7d8..811d675 100644
--- a/src/main/java/com/xly/agent/ErpAiAgent.java
+++ b/src/main/java/com/xly/agent/ErpAiAgent.java
@@ -93,24 +93,18 @@ public interface ErpAiAgent {
@SystemMessage("""
你是一个智能查询路由专家。请根据【用户需求】,只返回 true 或 false。
-
【最高优先级规则 - 必须首先判断】
如果用户需求包含以下任一关键词,**直接返回 false**,不再进行其他判断:
- - 明细、详情、详细信息、详细内容、具体内容
- - 查询...明细、...详情、...记录、...列表、...清单
-
+ - 明细、列表、清单
+ - ...明细、...列表、...清单
重要:只要出现以上关键词,说明用户需要的是明细数据查询,而非统计分析。
-
【统计类关键词 - 仅在满足最高优先级规则后才判断】
只有当用户需求不包含上述明细类关键词时,才检查是否包含以下关键词:
统计、求和、汇总、排名、TopN、平均、数量、总额、最高、最低、占比、分组
-
- 如果包含,返回 true
- 否则返回 false
-
【判断示例】
- \"查询中科精工集团的彩盒类产品的报价单明细\" → false(包含\"明细\")
- - \"统计各产品销售额\" → true(包含\"统计\",且无明细关键词)
- \"查询客户张三信息\" → false(无统计关键词,无明细关键词)
- \"销售额排名前10的产品\" → true(包含\"排名\",且无明细关键词)
- \"查看销售订单明细\" → false(包含\"明细\")
@@ -119,90 +113,264 @@ public interface ErpAiAgent {
【用户需求】
{{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 标量过滤条件生成规则(严格遵守 - 当前版本 v2.3.9):
-
- 【重要输出约束】
- - 必须返回有效的 Milvus 过滤条件表达式
- - 禁止返回 true 或 false
- - 禁止返回空字符串以外的任何非表达式内容
- - 无条件时只返回空字符串 ""
-
- 1. 语法规范:
- - 允许的操作符:==, !=, like
- - 逻辑组合:&& (AND), || (OR)
- - 所有字段都是字符串类型,值必须使用单引号包裹
- - 字符串中的单引号需要转义:'O''Reilly'
-
- 2. 【重要】Milvus v2.3.9 like 操作符限制:
- - ✅ 支持:like '关键字%'(前缀匹配,以关键字开头)
- - ❌ 不支持:like '%关键字%'(包含匹配)
- - ❌ 不支持:like '%关键字'(后缀匹配)
-
- 3. 可用字段(只能使用这些字段):
- - {{sMilvusFiled}}
- 字段说明:
- - {{sMilvusFiledDescription}}
-
- 4. 提取规则:
- - 只使用上述可用字段,不要创建新字段
- - 如果用户提到了文档类型(如"报价单"、"订单"等),但可用字段中没有类型字段,则忽略该条件
-
- 【精确匹配规则】:
- - 当用户提供明确值时:字段 == '值'
- * 例如:"客户名称中科精工" → sCustomerName == '中科精工'
- * 例如:"单据号 INV001" → sBillNo == 'INV001'
-
- 5. 时间处理规则:
- - 当前系统时间:{{sDataNow}}(格式:yyyy-MM-dd)
- - 相对时间转换规则:
- * "今天/今日" → 当天 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月1日 00:00:00 到本年12月31日 23:59:59
- * "近X天" → 从 X 天前 00:00:00 到今天 23:59:59
- - 日期转时间戳:所有日期转换为 Unix 时间戳(秒)
- - 时间范围格式:字段 >= 起始时间戳 && 字段 <= 结束时间戳
- - 如果没有明确的时间需求,不要添加任何时间过滤条件
-
- 6. 示例:
- ✅ 正确输出:
- - "客户名称中科精工" → sCustomerName == '中科精工'
- - "中科精工的报价单明细" → sCustomerName == '中科精工'
- - "产品以彩盒开头" → sProductStyle like '彩盒%'
- - "无条件" → ""
-
- ❌ 错误输出(禁止):
- - "中科精工的报价单明细" → true
- - "中科精工的报价单明细" → false
- - "中科精工的报价单明细" → 1
-
- 7. 输出格式:
- - 仅返回纯过滤条件,无任何解释、换行、备注
- - 单条件:sCustomerName == '中科精工'
- - 多条件:(sCustomerName == '中科精工' && sProductStyle like '彩盒%')
- - 无条件:直接返回空字符串 ""
+ 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}}
+ @UserMessage("""
+ 【用户输入】
+ {{userInput}}
+
【当前时间】
- - {{sDataNow}}
- 【可用字段】
- - {{sMilvusFiled}}
- 【字段说明】
- - {{sMilvusFiledDescription}}
- """)
+ {{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("sDataNow") String sDataNow);
-
+ @V("sMilvusFiledXl") String sMilvusFiledXl,
+ @V("sMilvusFiledDescriptionXl") String sMilvusFiledDescriptionXl,
+ @V("sDataNow") long sDataNow,
+ @V("sMethodName") String sMethodName
+ );
}
diff --git a/src/main/java/com/xly/milvus/service/MilvusService.java b/src/main/java/com/xly/milvus/service/MilvusService.java
index 7960561..1ecee9e 100644
--- a/src/main/java/com/xly/milvus/service/MilvusService.java
+++ b/src/main/java/com/xly/milvus/service/MilvusService.java
@@ -36,7 +36,7 @@ public interface MilvusService {
* @return long
* @Description 批量插入数据
**/
- long addDataToCollection(String collectionName, String sVectorfiled, String sVectorjson, List