
意图识别核心实现逻辑第一部分用户问题进来后的第一件事 — 清洗用户输入的问题往往不干净。比如用户可能会说你好请问报销流程是什么这里面你好请问对我们来说都是噪音。所以我们第一步做了一个叫 normalize_user_query 的方法它做的事情很简单——用正则表达式把你好您好请问想问一下这类礼貌性前缀剥掉只留下真正的业务问题。但有一个细节如果用户说的就是一句你好剥完之后是空的这时候我们会保留原文因为这就是一句问候后面有专门的逻辑处理它。第二部分路由决策 — 先拦能拦的清洗完之后问题进入我们的路由决策层核心方法是 decide_route。这个方法的思路是能在检索之前解决的问题就不要浪费资源去检索。它会按优先级依次检查三件事第一关问候直回系统会用一组预定义的模式去匹配比如你好您好hihello在吗你是谁这类纯问候。一旦命中系统直接返回一段欢迎语比如你好我是企业知识助手可以帮你查询制度、流程、FAQ 和文档资料根本不需要去检索任何文档。这个逻辑在 classify_direct_intent 方法里它是我们整个系统的第一个守门人。第二关越界拒回有些问题明显不在我们的业务范围内比如帮我预测彩票怎么破解系统这类涉及违法、攻击、色情的内容。我们用一组关键词正则去检测命中后直接回复这个问题超出了问答范围我无法提供帮助。这个安全拦截必须在任何检索之前完成不能给 LLM 任何机会去发挥。第三关转人工检测如果用户说我要转人工给我客服电话而且整句话不超过 18 个字系统会直接返回人工支持的联系方式。这里有个细节——限制 18 个字是因为如果用户说我要转人工帮我处理一下报销的问题这种其实是带着业务问题的不应该被拦截应该让它正常进入检索流程。以上三关只要命中任何一关系统就直接返回答案后面所有流程都不走了。 第四关分类边界检测 如果前三关都没命中还有一道检查。我们的系统支持多个业务分类比如人事、财务、IT。用户可能在前端选了人事分类但问的其实是报销需要哪些材料这明显是财务的问题。这时候 detect_boundary_answer 方法就会检测到这个不匹配。它内部会调用 detect_source_boundary这个方法会用每个分类配置的正则模式去匹配问题内容计算一个分数——命中次数乘以 10 加上匹配文本长度再减去分类的优先级索引。如果算出来用户选的分类得分低另一个分类得分高系统就会提示用户这个问题更像属于财务分类当前选择的是人事请切换分类后再查询。这四关都过了说明这个问题确实需要检索路由层才会返回进入检索链路。第三部分精细意图分类 — 搞清楚用户到底想问什么进入检索链路后我们不是直接就去搜而是先做一次更精细的意图分类。这个逻辑在 classify_intent 方法里。为什么需要意图分类因为不同类型的问题检索策略完全不同。比如报销需要什么材料这种 FAQ 类问题可能 FAQ 库里就有标准答案直接返回就行不需要翻文档。但如果是SOP 流程具体是什么这种知识类问题就需要从文档里找多段信息拼出答案。意图分类的执行顺序1.是否是追问类第一步先排除追问。如果用户之前问过出差流程是什么现在又问费用呢这种追问省略了关键信息系统需要先做问题改写把费用呢改写成出差费用怎么算这种完整问题才能去检索。追问的判断依据是有历史对话并且当前问题以那这个他们继续还有这类词开头或者问题特别短——不超过 8 个字。第二步如果系统能推断出问题属于哪个业务分类比如通过 infer_source 方法推断出报销属于财务再结合关键词和句式做领域规则分类。这个逻辑在 _strong_rule_domain_intent 方法里它的规则是递进的2.递进型置信度问题分类只要问题里出现了费用价格报错发票审批这类高频业务词就判定为 FAQ 查询规则置信度 0.82。 如果还知道业务分类问题又比较短不超过 32 个字并且带有怎么办需要什么有哪些这种标准问法置信度提高到 0.83。 如果带有是什么可以吗能不能这种更直接的问法置信度进一步提高到 0.84。 如果命中了文档流程制度规范这类知识库关键词并且知道业务分类或者是短句就判定为知识查询置信度 0.84。 以上都不命中就兜底为知识查询但置信度只有 0.6——表示我也不太确定先按知识查询处理。 这里的置信度不是概率它表达的是规则判断有多确定。这个分数后面会直接影响检索策略的松紧程度。第四部分构建检索策略 — 怎么搜、搜多少、能不能直接答意图分类完成后进入我们整个系统最核心的部分——检索策略构建核心方法是 build_retrieval_plan。这个方法不执行检索它只生成一份检索执行计划告诉下游该怎么搜、搜多少、能不能直接返回 FAQ 答案。计划一旦生成就不可修改下游按计划执行就行。先提取问题特征——风险类表格类在构建计划之前系统会先判断两个特征第一个是风险类别由 infer_question_category 方法判断。费用类涉及钱、合规类涉及法规、排障类设备故障、总结类需要归纳整理还是普通问题。不同风险类别的检索策略不同——费用类答错了可能造成经济损失合规类答错了可能面临监管处罚所以这两类的检索策略最保守。第二个是是否为表格查询由 is_table_query 方法判断。如果问题里出现表格清单台账明细这类词说明用户可能在问表格相关的内容。表格有个特殊问题——不同表格在语义空间里长得很像都是清单字段列名但实际内容完全不同。比如验收项清单和付款节点清单向量检索会觉得它们很像但内容完全不一样。所以表格类查询会禁用模糊匹配只允许精确匹配。然后从默认参数出发逐层收紧。参数锥形收紧检索策略的核心设计思想叫参数锥形收紧——从一组宽松的默认参数出发每一层规则根据自己的关注点收紧参数后层不能放宽前层的约束只能继续收紧。第一层默认基参由 _base_params 方法生成。FAQ 和文档各召回 20 条FAQ 直出门槛 0.72匹配分超过 0.72 就直接返回 FAQ 答案不走 LLM最终给 LLM 4 个片段。如果是短句不超过 20 个字FAQ 召回数会提高到 30因为短句在向量检索中歧义很大报销两个字可能匹配到几十条不相关的 FAQ需要更大的候选池让后面的精排器去筛选。第二层意图分支由 _intent_rules 方法根据意图类型选择不同策略 如果是FAQ 查询文档召回减半到 10 条FAQ 能覆盖的问题不需要翻太多文档FAQ 直出门槛从 0.72 降到 0.640.72 减去 0.08 的折扣但绝对不低于 0.62 的地板价。策略核心是大胆直出节省成本。 如果是知识查询文档召回扩大到至少 24 条给 LLM 的片段数增加到至少 5 个。策略核心是多捞文档让 LLM 有足够素材组织答案。如果是追问FAQ 和文档召回都扩大因为追问省略了信息需要更多候选去匹配但 FAQ 直出门槛提高到 0.78。策略核心是多搜但别轻易直出因为问题本身信息不完整。第三层短问题保护如果问题不超过 20 个字且不是追问文档候选上限被限制在 12 条短句噪声大太多候选反而引入干扰FAQ 直出门槛提高到 0.78短句匹配到的 FAQ 不可靠。第四层规则分数保护由 _rule_score_guard 方法实现。如果前面的意图分类置信度很低低于 0.70说明系统也不太确定这个问题属于什么类型这时候会采取最保守的策略——完全禁止模糊 FAQ 直出只允许精确匹配文档召回扩大到 24 条直出门槛提到 0.86。如果置信度在 0.70 到 0.82 之间门槛提高到 0.82 但不禁止模糊匹配。0.82 以上则不施加额外保护。第五层风险类别由 _category_rules 方法实现。费用类的直出门槛提到 0.84合规类提到 0.86——这是最高的因为合规类答错的后果最严重。排障类和总结类不提门槛因为排障类 FAQ 通常有标准 SOP直出是安全的总结类本身不太涉及 FAQ 直出的概念。所有风险类别都会扩大文档召回到 24 条上下文片段增加到至少 6 个。第六层表格偏好如果是表格类查询文档召回扩大上下文片段增加到至少 7 个并且强制只允许精确匹配——原因前面说过表格内容语义相似但实际不同模糊匹配太容易误导。 最终输出 所有层处理完后生成一个不可变的 RetrievalPlan 对象包含 16 个字段覆盖检索开关、召回数量、直出门槛、上下文控制等所有参数。下游拿到这个计划就按计划执行不需要再做任何判断。而且计划里有一个 reason 字段记录了完整的决策链路。比如 faq_first_short_query_guard_pricing_guard 表示这次检索经历了FAQ 优先策略 → 短问题保护 → 费用类保护三层调整。通过日志可以直接回溯为什么这次用了这些参数。第五部分FAQ 直出 — 能快就快检索计划生成后系统按计划执行 FAQ 向量检索和文档向量检索。这里有一个关键机制FAQ 直出。当 FAQ 检索的最高匹配分超过计划中设定的门槛时系统会直接返回 FAQ 的标准答案跳过文档检索和 LLM 调用。这样做的好处是响应速度快、成本低、答案质量稳定因为是人工维护的标准答案。但直出是有条件的而且条件会被层层收紧 默认门槛 0.72 FAQ 意图降到 0.64但不低于 0.62 短问题提高到 0.78 低置信度问题禁止模糊直出 费用类提高到 0.84 合规类提高到 0.86表格类完全禁止模糊直出核心原则就是越不确定越保守越确定越积极。第六部分知识查询 — 该稳就稳如果 FAQ 没有命中或者门槛太高没达到直出条件系统就走完整的 RAG 链路——把 FAQ 和文档的检索结果合在一起经过精排器重新排序过滤掉低分噪声截取排名靠前的几个片段拼成上下文喂给 LLM由 LLM 生成最终答案。知识查询类问题会拿到更多的文档片段至少 5~7 个因为这类问题往往需要多段证据拼合才能给出完整答案。