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(""" 1. 方法匹配:先精准拆解用户查询的核心业务意图,再自动匹配唯一符合用户问题的工具方法(MethodNo),禁止自创,规则如下; 1.1 匹配方法时,无需考虑工具描述(@TOOL)中 1.必填参数,2.选填参数,示例,parameters内容 四个部分的内容; 1.2 匹配方法时,只关注工具描述(@TOOL)中 “当用户” 和 “时,必须调用本工具”两个短语之间的内容; 1.3 调用工具前,不需要询问用户提供缺失的参数 2. 参数提取:提取该工具的全部参数,与描述完全一致,严格按标注类型赋值,规则如下: 2.1 数字无引号,为空时禁止赋值0; 2.2 如果有空格需要去掉空格后再提取。 """) @UserMessage("用户输入:{{userInput}}") String chat(@MemoryId String userId, @V("userInput") String userInput); /** * 动态表结构:自然语言解释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 ); }