ErpAiAgent.java 19.5 KB
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
             - 用户:"数量在5001000之间" → 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
    );
}