第 05 章:意图分类与路由入口¶
上一讲:Milvus 索引机制与基本操作
下一讲:检索策略与动态计划
本讲目标¶
- 理解意图识别在 RAG 链路中的关键作用
- 掌握"规则优先 + LLM 补充"的混合分类策略
- 读懂
classify_intent()的六步判断顺序及其设计理由 - 理解 Source 自动推断的机制
本讲地图¶
本图对应本讲功能闭环,展示从输入到本讲交付物的主干路径。节点与主项目代码文件和函数保持一致,后续章节消费的能力只作为交付边界出现。
图 1:第 05 讲功能闭环地图¶
flowchart TD
C05_DEMO["demo_intent.py<br/>main()"]
C05_RULES["规则配置<br/>get_rule_config()"]
C05_SCENARIO["场景解析<br/>resolve_scenario()"]
C05_SOURCE["source 推断<br/>score_source_matches() / detect_source_boundary()"]
C05_DIRECT["直答意图<br/>classify_direct_intent()"]
C05_ROUTE["入口路由<br/>decide_route()"]
C05_FAST["FAQ 快路径前置判断<br/>should_try_faq_fast_path()"]
C05_CLASSIFY["检索意图<br/>classify_intent()"]
C05_OUT{{"章节输出<br/>RouteDecision"}}
C05_DEMO --> C05_SCENARIO
C05_DEMO --> C05_ROUTE
C05_RULES --> C05_FAST
C05_SCENARIO --> C05_SOURCE
C05_SOURCE --> C05_ROUTE
C05_ROUTE --> C05_DIRECT
C05_ROUTE --> C05_FAST
C05_FAST --> C05_CLASSIFY
C05_DIRECT -->|"直答分支"| C05_OUT
C05_CLASSIFY -->|"检索分支"| C05_OUT
style C05_DEMO fill:#F8FAFC,stroke:#64748B,stroke-width:2px
style C05_RULES fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style C05_SCENARIO fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
style C05_SOURCE fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style C05_DIRECT fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style C05_ROUTE fill:#F5F3FF,stroke:#7C3AED,stroke-width:2px
style C05_FAST fill:#FEF3C7,stroke:#D97706,stroke-width:2px
style C05_CLASSIFY fill:#DBEAFE,stroke:#2563EB,stroke-width:2px
style C05_OUT fill:#DCFCE7,stroke:#16A34A,stroke-width:2px
节点与代码对齐¶
| 节点 | 对齐文件 | 函数/对象 | 本章职责 |
|---|---|---|---|
| demo_intent.py | scripts/demo_intent.py |
main() |
命令行验证入口,构造 query、history 和 source_filter。 |
| 规则配置 | qa_core/config/rules.py |
get_rule_config() |
读取 config/rules.toml 中的 FAQ fast path 触发词和查询变体规则。 |
| 场景解析 | qa_core/scenarios/registry.py |
resolve_scenario() |
加载当前业务场景、source 白名单、业务域和联系方式。 |
| source 推断 | qa_core/scenarios/boundary.py |
score_source_matches() / detect_source_boundary() |
根据场景 source_patterns 推断问题所属资料分类。 |
| 直答意图 | qa_core/intent/classifier.py |
classify_direct_intent() |
识别空问题、问候、感谢、越界和短句转人工。 |
| 入口路由 | qa_core/pipeline/steps.py |
decide_route() |
决定 direct_answer、source_boundary 或 retrieval。 |
| FAQ 快路径前置判断 | qa_core/pipeline/steps.py |
should_try_faq_fast_path() |
短标准问答只标记为适合优先查 FAQ;真实 FAQ 命中从第 09/10 章的 Pipeline 上下文中调用 Milvus FAQ collection。 |
| 检索意图 | qa_core/intent/classifier.py |
classify_intent() |
进入检索前输出 FOLLOW_UP、FAQ_QUERY 或 KNOWLEDGE_QUERY。 |
| 章节输出 | qa_core/pipeline/route.py |
RouteDecision |
给第 06 章的检索计划提供稳定输入。 |
本讲项目交付闭环¶
这一讲不是只讲“什么是意图分类”,而是要把在线问答链路的第一个业务判断模块落到项目代码里。完成本讲后,需要能清楚回答三个问题:用户问题进来后先被分成哪类、为什么按这个顺序判断、这个分类结果会怎样影响后面的检索和生成。
| 项目交付项 | 说明 |
|---|---|
| 核心模块 | qa_core/intent/classifier.py |
| 核心函数 | classify_intent()、infer_source()、_classify_with_llm() |
| 输出对象 | IntentResult |
| 下游衔接 | 第 6 讲 build_retrieval_plan()、第 7 讲 rewrite_query_if_needed()、第 10 讲 RAG Pipeline |
| 验证入口 | tests/test_intent_and_scenarios.py、tests/test_retrieval_and_prompt.py |
本讲实现完成后的代码结构:
flowchart TB
subgraph IntentModule["qa_core/intent"]
Classifier["classifier.py<br/>classify_intent()<br/>规则优先 + LLM 兜底"]
Category["question_category.py<br/>infer_question_category()<br/>问题类别判断"]
end
subgraph ScenarioModule["qa_core/scenarios"]
Registry["registry.py<br/>场景配置加载"]
Boundary["boundary.py<br/>source pattern 匹配"]
end
subgraph Downstream["下游模块"]
Strategy["retrieval/strategy.py<br/>根据 IntentResult 生成 RetrievalPlan"]
Rewrite["pipeline/rewrite.py<br/>根据 requires_rewrite 改写追问"]
Prompt["prompts/selector.py<br/>根据意图和类别选择 Prompt Profile"]
end
Classifier --> Strategy
Classifier --> Rewrite
Classifier --> Prompt
Registry --> Classifier
Boundary --> Classifier
Category --> Strategy
闭环验证方式:
验证时重点看四类结果:问候/越界/人工客服是否直接返回,追问是否标记 requires_rewrite=True,FAQ 问法是否优先走 FAQ_QUERY,流程/制度/文档类问题是否走 KNOWLEDGE_QUERY。
第一部分:前置知识 — NLU 中的意图识别¶
1.1 什么是意图识别¶
在自然语言理解(NLU)中,意图识别(Intent Classification) 是判断用户"想干什么"的技术。
1.2 传统做法 vs 本项目做法¶
传统 NLU 意图分类(如 Rasa、BERT 分类器): - 训练一个专门的分类模型 - 需要标注训练数据 - 意图类型固定,新增意图需要重新训练
本项目做法(规则 + LLM): - 高频确定场景用正则规则(快、稳定、零成本) - 模糊场景用 LLM 结构化输出(灵活、无需标注数据) - 不需要训练任何模型
1.3 意图识别在 RAG 中的特殊角色¶
在 RAG 系统中,意图识别不只是贴标签。它直接影响后续所有决策:
第二部分:六种意图类型¶
| 意图 | 触发场景举例 | 后续行为 |
|---|---|---|
| GREETING | "你好"、"在吗" | 直接返回问候语,不检索 |
| FOLLOW_UP | "那审批呢"、"费用呢" | 先改写再检索,提高直出阈值 |
| KNOWLEDGE_QUERY | "入职流程有哪些步骤" | FAQ+文档都查,更多文档上下文 |
| FAQ_QUERY | "API 限流怎么办" | FAQ 优先,降低直出阈值 |
| HUMAN_SERVICE | "客服电话"、"转人工" | 直接返回联系方式 |
| OUT_OF_SCOPE | "怎么买彩票" | 直接拒答 |
第三部分:六步判断顺序(核心设计)¶
classify_intent() 的判断顺序是刻意设计的,不是随意排列。每一步都有它的理由。
flowchart TD
Start(["classify_intent(query, history, scenario)"]) --> S1
S1{"1️⃣ 问候规则<br/>'你好/在吗/你是谁'"} -->|"✅ 命中"| G1["GREETING<br/>confidence: 1.0<br/>直接返回问候语"]
S1 -->|"❌ 未命中"| S2
S2{"2️⃣ 越界检测<br/>'彩票/赌博/攻击'"} -->|"✅ 命中"| OOS["OUT_OF_SCOPE<br/>confidence: 0.95<br/>直接拒答"]
S2 -->|"❌ 未命中"| S3
S3{"3️⃣ 人工客服<br/>'客服电话' + len≤18"} -->|"✅ 命中"| HS["HUMAN_SERVICE<br/>confidence: 0.9<br/>直接返回电话"]
S3 -->|"❌ 未命中"| S4
S4{"4️⃣ 追问检测<br/>history存在 + 代词/短句"} -->|"✅ 命中"| FU["FOLLOW_UP<br/>confidence: 0.8<br/>requires_rewrite=True"]
S4 -->|"❌ 未命中"| S5
S5{"5️⃣ 强规则<br/>FAQ关键词 / 标准问法"} -->|"✅ 命中"| SR["FAQ_QUERY / KNOWLEDGE_QUERY<br/>confidence: 0.82-0.84"]
S5 -->|"❌ 未命中"| S6
S6["6️⃣ LLM 结构化输出<br/>IntentLLMDecision Pydantic"] --> LLM["LLM 返回意图 + 置信度<br/>校验 source 白名单"]
G1 --> Result["IntentResult → QAService"]
OOS --> Result
HS --> Result
FU --> Rewrite["先改写再检索"]
SR --> Result
LLM --> Result
style G1 fill:#ECFDF5,stroke:#059669
style OOS fill:#FEF2F2,stroke:#DC2626
style HS fill:#FFFBEB,stroke:#D97706
style FU fill:#EFF6FF,stroke:#2563EB
style SR fill:#ECFDF5,stroke:#059669
style S6 fill:#F8FAFC,stroke:#64748B
意图到检索策略的映射¶
flowchart LR
subgraph Intents["意图类型"]
I1["GREETING"]
I2["OUT_OF_SCOPE"]
I3["HUMAN_SERVICE"]
I4["FOLLOW_UP"]
I5["FAQ_QUERY"]
I6["KNOWLEDGE_QUERY"]
end
subgraph Strategy["检索策略"]
S_skip["跳过检索<br/>直接返回"]
S_rewrite["先改写 → 再检索<br/>提高阈值 0.78"]
S_faq["FAQ 优先<br/>降低阈值 0.62<br/>减少文档候选"]
S_knowledge["扩大搜索<br/>增大 doc_top_k<br/>更多上下文"]
end
I1 --> S_skip
I2 --> S_skip
I3 --> S_skip
I4 --> S_rewrite
I5 --> S_faq
I6 --> S_knowledge
style Intents fill:#EFF6FF,stroke:#2563EB,stroke-width:2px
style Strategy fill:#ECFDF5,stroke:#059669,stroke-width:2px
这张图是整个检索决策链的起点。意图识别不只是"贴标签"——标签一旦确定,后续的检索行为就完全不同了。六种意图对应四种检索策略,每一条映射都有明确的设计理由:
策略一:跳过检索(GREETING / OUT_OF_SCOPE / HUMAN_SERVICE)
这三种意图的共同特征是"答案不依赖知识库"。问候就是打招呼、越界就是拒答、人工客服就是返回电话号码——它们不需要查 Milvus,不需要调 LLM 生成,甚至不需要构建上下文。这个策略的价值在于:省掉一次无意义的检索 + LLM 调用。在实际运行中,这三类问题占所有请求的 5-10%,每次都跳过能显著降低延迟和成本。
策略二:先改写再检索(FOLLOW_UP)
追问的问题是"不完整的"。"那审批呢"这四个字如果不结合历史,任何人都看不懂。所以 FOLLOW_UP 不能直接检索——必须先调用 LLM 把省略的主语和背景补全("入职流程中的审批步骤是什么"),再用改写后的问题去检索。threshold=0.78 是本项目当前的保护阈值,不是行业标准:它比基础 FAQ 直出阈值更谨慎,用来过滤"看起来有点相关但其实不相关"的结果。生产环境应通过 FAQ 误直出率、召回率和人工评测集继续校准。
策略三:FAQ 优先(FAQ_QUERY)
FAQ_QUERY 的问题形态通常是短问题 + 标准问法(如"xxx 怎么办""xxx 流程是什么"),这类问题大概率在 FAQ 库中有精确匹配。策略参数 threshold=0.62 是本项目为了“FAQ 标准答案优先”设置的相对宽松阈值,不是通用标准。它成立的前提是 FAQ 质量高、问题覆盖稳定、误直出率可控。同时 doc_top_k 减少到较少候选,因为 FAQ 已经覆盖了答案,不需要召回大量文档。
策略四:扩大搜索(KNOWLEDGE_QUERY)
知识咨询类问题("入职流程有哪些步骤""如何申请预算")答案通常分散在多个文档片段中,需要 LLM 对多个 chunk 做综合和归纳。所以 doc_top_k 会增大——召回更多候选给 Reranker 筛选,保证 LLM 有足够完整的上下文。这类问题对"漏召回"非常敏感:少召回一个关键段落,LLM 就可能生成不完整的答案。
映射规则的实际代码在 qa_core/retrieval/strategy.py 的 build_retrieval_plan() 中,它接收 IntentResult 作为输入,输出一个 RetrievalPlan 对象(包含 faq_top_k、doc_top_k、threshold、use_rerank 等字段),后续的检索环节只使用 Plan 中的参数,不再关心原始意图。
完整源码解读¶
步骤 1:问候和身份问题最先处理
为什么最先处理:问候是最常见、最确定的场景。不需要任何上下文或外部信息。放在最前面可以最快返回,避免无意义的后续处理。
步骤 2:越界检测
为什么必须有这一步:越界问题如果进入 RAG 流程,LLM 可能从知识库片段中拼出看似相关但不应回答的内容。安全拦截必须在任何检索之前完成。
OFF_TOPIC_HINTS 匹配的关键词:彩票|赌博|股票内幕|色情|违法|攻击|破解|黑客入侵
步骤 3:人工客服短句
为什么有长度限制(≤18 字符): - "客服电话"、"转人工" → 短句,确实在要联系方式 - "客服电话打不通怎么办,我在线等了一个小时了" → 长句,可能是在抱怨或问怎么联系客服的流程,不应直接截断
步骤 4:追问检测
追问的两个触发条件:
1. 代词或追问词开头:那|这个|那个|它|上面|刚才|还有|审批呢|费用呢
2. 问题很短(≤8 字符):极短的问题在有历史上下文时更可能是追问
requires_rewrite=True 意味着后续流程会先结合历史改写问题,再进入检索。
步骤 5:强规则判断
这是规则最丰富的一层,见第四部分详解。
步骤 6:LLM 兜底
只有前面五步都无法确定时,才调用 LLM。对于大多数高频业务问题,前五步已经覆盖。
第四部分:强规则设计详解¶
4.1 _strong_rule_domain_intent()¶
4.2 各规则的关键词设计¶
FAQ_HINTS — 触发标准问答检索:
这些词通常是具体的、有标准答案的问题。例如"API 调用失败怎么办"→ 大概率 FAQ 里有标准答案。
FAQ_QUESTION_SHAPE_HINTS — 标准问答的问法形态:
这些词表示用户在问一个具体的"How-to"类问题,这类问题通常有标准答案。
DIRECT_FAQ_SHAPE_HINTS — 口语化的短问答形态:
这些是真实业务场景中更口语化的问法,比如"那隐蔽工程验收资料呢"、"API 限流导致接口失败怎么排查"。
KNOWLEDGE_HINTS — 触发知识文档检索:
这些词表示用户需要综合性的知识解答,不一定是 FAQ 能覆盖的。
4.3 置信度阶梯¶
注意置信度的设计:0.82 → 0.83 → 0.84
这不是随意设置的。随着条件越来越具体(有 source 推断 + 短问题 + 特定问法),置信度逐步提高,但仍然留有余地(不设为 1.0 或 0.95)。这是因为规则毕竟是规则,总有一些边缘情况可能误判。
第五部分:LLM 结构化分类¶
5.1 何时调用 LLM¶
只有当前面五步规则都无法确定时,才调用 LLM:
- 问题较长,无法用简单关键词判断
- 没有推断出 source
- 问题表达模糊但仍在业务范围内
5.2 实现代码¶
5.3 白名单校验的重要性¶
LLM 可能会返回不在场景配置中的 source 名(比如把 hr 识别为 human_resources)。如果不校验就直接拼入 Milvus 过滤表达式,会导致运行时查询错误。这里丢弃无效值并使用规则推断的 source 作为兜底。
5.4 LLM 失败的处理¶
这是一个重要的设计选择:LLM 失败必须暴露依赖问题,不能静默改用低配规则路径。如果 LLM 不可用,意图识别可能不准,后续的检索策略、Prompt 模板都会偏——与其让用户看到一个可能错误的答案,不如在意图识别阶段就报错。
第六部分:Source 自动推断¶
6.1 为什么需要 Source 推断¶
用户在页面上提问时,不一定手动选择业务分类。系统需要自动判断"入职流程有哪些步骤"属于 HR 分类,"API 限流怎么办"属于 IT 分类。
前端也有业务分类下拉框,但很多用户不会手动选择。自动推断可以作为默认值,也可以作为前端选择的补充。
6.2 infer_source() 实现¶
当前项目是多业务场景知识问答平台,source 会参与 Milvus 过滤表达式和数据隔离,必须来自当前场景的 valid_sources。新增业务分类时,只修改 scenario.toml 的 valid_sources 和 source_patterns,不要在 Python 主链路里堆业务硬编码。
6.3 场景级 Source Pattern 的优先级¶
第七部分:IntentResult 与下游联动¶
7.1 IntentResult 的所有字段¶
7.2 各字段的下游影响¶
本讲实践闭环¶
| 项目 | 内容 |
|---|---|
| 本讲类型 | 项目实现 |
| 实践产物 | qa_core/intent/classifier.py 意图分类模块 |
| 是否进入最终项目 | 是 |
| 验收方式 | 运行意图分类相关单测或用典型问题手动验证 |
| 后续落点 | 第 6 讲根据意图生成检索计划,第 10 讲进入 Pipeline 主流程 |
通过标准:问候、转人工、越界可直接处理,知识类/FAQ/追问类问题能输出稳定 IntentResult。
本讲从 0 到 1 实现闭环¶
本讲在在线问答链路中的位置如下。在线主链路会先执行 decide_route(),把问候、转人工、越界和 FAQ 精确命中这类确定性分支先收口;只有 route=retrieval 时,才进入本讲的检索类意图识别,决定后续是 FAQ 查询、知识查询,还是需要结合历史做追问改写。
flowchart TD
Q["用户问题"] --> Route["查询路由 decide_route()<br/>direct_answer / faq_exact / retrieval"]
Route -->|"direct_answer"| Direct["直接返回<br/>问候/转人工/越界"]
Route -->|"faq_exact"| FAQExact["FAQ 精确直出<br/>intent=FAQ_QUERY"]
Route -->|"retrieval"| Source["source 自动推断<br/>读取 scenario.toml"]
Source --> R1["规则 1:GREETING<br/>问候/自我介绍"]
R1 --> R2["规则 2:OUT_OF_SCOPE<br/>越界/风险话题"]
R2 --> R3["规则 3:HUMAN_SERVICE<br/>短句转人工"]
R3 --> R4["规则 4:FOLLOW_UP<br/>有历史 + 省略追问"]
R4 --> R5["规则 5:强领域规则<br/>FAQ/知识关键词 + 问法"]
R5 --> Hit{"前 5 条规则<br/>是否命中?"}
Hit -->|"是"| Result["IntentResult"]
Hit -->|"否"| Struct["规则 6:LLM 结构化兜底<br/>IntentLLMDecision"]
Struct --> Validate["source 白名单校验"]
Validate --> Result
Result --> Branch{"是否 direct_answer<br/>或 requires_rewrite?"}
Branch -->|"direct_answer"| Answer["直接返回<br/>跳过检索"]
Branch -->|"requires_rewrite"| Rewrite["第 7 讲<br/>追问改写"]
Branch -->|"普通查询"| Next["第 6 讲<br/>生成 RetrievalPlan"]
实现完成后,相关代码结构应该是下面这张图:
flowchart LR
subgraph Core["qa_core"]
subgraph Intent["intent"]
Init["__init__.py<br/>导出意图模块"]
Classifier["classifier.py<br/>意图类型<br/>IntentResult<br/>规则优先 + LLM 兜底"]
end
Schemas["schemas.py<br/>下游请求/响应结构<br/>承接 IntentResult"]
end
subgraph Tests["tests"]
IntentTest["test_intent_and_scenarios.py<br/>规则路径<br/>source 推断<br/>场景边界"]
end
Classifier --> Schemas
IntentTest -. 验证 .-> Classifier
Step 1:先定义意图分类的输出结构¶
目标:不要只返回一个字符串,而是把后续链路需要的决策一起返回。
来源:真实代码节选,见 qa_core/intent/classifier.py。
设计解释:
direct_answer:问候、越界、转人工可以直接返回,不进入 RAG。requires_rewrite:追问类问题必须先补全上下文。suggested_source:下游会变成 Milvussource == "hr"这类过滤条件。reason:进入 Trace 和诊断面板,说明为什么这么判断。
Step 2:实现 6 步规则优先路径¶
目标:高频、确定、低风险的问题先用规则处理,只有前 5 条规则都无法判断时才调用 LLM。
来源:真实代码逻辑压缩版,对应 qa_core/intent/classifier.py::classify_intent() 和 _strong_rule_domain_intent()。
规则顺序不能随便换:
| 顺序 | 规则 | 为什么放在这里 |
|---|---|---|
| 1 | GREETING | 最快返回,不需要任何业务检索 |
| 2 | OUT_OF_SCOPE | 安全拦截必须靠前,避免进入 LLM 或检索 |
| 3 | HUMAN_SERVICE | 只识别短句,避免长文本中的“客服”误触发 |
| 4 | FOLLOW_UP | 必须有历史,否则“费用呢”无法判断指代对象 |
| 5 | 强领域规则 | FAQ/知识类高频问题用规则节省 LLM 调用 |
| 6 | LLM 兜底 | 只处理长尾和模糊表达,成本最高所以放最后 |
强领域规则内部又分为 4 类:
来源:真实代码逻辑压缩版,对应 qa_core/intent/classifier.py::_strong_rule_domain_intent()。
这里的 confidence=0.82/0.83/0.84 不是模型计算结果,也不是 Milvus 相似度分数,而是人工设计的规则置信度标签。它表达的是“这条规则判断意图的可靠程度”,主要用于诊断面板和 LangSmith Trace。
| 数值 | 命中规则 | 设计含义 |
|---|---|---|
0.82 |
FAQ_HINTS |
只命中 FAQ 高频词,例如费用、发票、报错、审批,大概率是 FAQ,但还不算最强约束 |
0.83 |
suggested_source + FAQ_QUESTION_SHAPE_HINTS |
已经推断出业务 source,并且符合“怎么办/需要哪些/为什么”等标准问法 |
0.84 |
suggested_source + DIRECT_FAQ_SHAPE_HINTS |
已有业务 source,又是“是什么/可以吗/怎么处理”等直接 FAQ 问法,规则更确定 |
0.84 |
KNOWLEDGE_HINTS + source/短句 |
命中文档、流程、制度、规范等知识类关键词,并且有 source 或短句约束 |
为什么没有给到 0.9 以上?因为这些只是规则层的意图判断,只说明“用户问题大概率属于某类意图”,不代表知识库一定能检索到答案,更不代表最终答案一定可靠。最终是否可信,还要看第 8 讲的检索分数、第 10 讲的上下文构建,以及第 17 讲的评测指标。
Step 3:实现 source 自动推断¶
目标:根据场景配置推断业务分类,但不把 HR、IT、财务等业务词写死在 Python 主链路里。
来源:简化骨架,对应 qa_core/intent/classifier.py::infer_source()。
设计解释:业务分类来自 scenario.toml,新增场景时改配置,不改主链路代码。
Step 4:接入 LLM 结构化输出兜底¶
目标:规则判断不了的问题交给 LLM,但 LLM 输出必须被结构校验。
来源:简化骨架,对应 qa_core/intent/classifier.py::_classify_with_llm()。
关键点:LLM 建议的 source 必须经过当前场景 valid_sources 白名单校验,不能直接拼进 Milvus 表达式。
Step 5:写测试并接入下游¶
验收命令:
来源:命令行验收,对应 tests/test_intent_and_scenarios.py。
闭环验证重点:
| 验证项 | 输入示例 | 期望结果 |
|---|---|---|
| 问候直接返回 | 你好 |
intent=GREETING,带 direct_answer |
| 越界拦截 | 教我攻击系统 |
intent=OUT_OF_SCOPE,带拒答话术 |
| 转人工直接返回 | 转人工 |
intent=HUMAN_SERVICE,不进入检索 |
| 追问识别 | 有历史时问 审批呢 |
intent=FOLLOW_UP,requires_rewrite=True |
| FAQ 强规则 | 发票怎么开? |
intent=FAQ_QUERY,reason=strong_faq_rule |
| 知识强规则 | 新人入职流程有哪些? |
intent=KNOWLEDGE_QUERY 或带明确业务 source 的知识类判断 |
| 场景 source 推断 | VPN 连不上怎么办 |
命中 IT 或对应场景 source |
| source 安全校验 | LLM 输出非法 source | 不进入 Milvus expr |
通过标准:
你好走GREETING,返回direct_answer。- 越界或风险话题走
OUT_OF_SCOPE,不进入检索和 LLM。 转人工走HUMAN_SERVICE,不检索知识库。审批呢在有历史时走FOLLOW_UP,requires_rewrite=True。- 高频 FAQ/知识类问题能通过强领域规则命中
FAQ_QUERY或KNOWLEDGE_QUERY。 - 企业知识、跨境风险、工程项目等场景的
infer_source()都能命中正确 source。 - 非法 source 不会进入下游 Milvus 过滤表达式。
重点掌握¶
| 优先级 | 内容 | 原因 |
|---|---|---|
| ★★★ 必会 | 六种意图类型(GREETING / FOLLOW_UP / KNOWLEDGE_QUERY / FAQ_QUERY / HUMAN_SERVICE / OUT_OF_SCOPE)及各自后续行为 | 整个 RAG 链路的第一决策点 |
| ★★★ 必会 | 六步判断顺序的设计理由:问候(最快返回)→ 越界(安全拦截)→ 人工客服(短句限制)→ 追问(指代消解)→ 强规则(高频覆盖)→ LLM(兜底) | 每一步的位置都有工程考量 |
| ★★★ 必会 | "规则优先 + LLM 补充"策略:高频确定用规则(快、零成本),模糊用 LLM(灵活) | 核心设计哲学,避免 LLM 过度依赖 |
| ★★ 理解 | IntentResult 各字段的下游影响(direct_answer → 跳过检索、requires_rewrite → 改写、suggested_source → Milvus 过滤) | 理解意图识别如何串联后续模块 |
| ★★ 理解 | 置信度阶梯设计(0.82→0.83→0.84)和原因 | 体现不同意图的风险分层 |
| ★★ 理解 | Source 自动推断(infer_source):只读取场景 TOML source_patterns,不写业务硬编码兜底 | 理解"不选分类也能自动匹配"的实现 |
| ★★ 理解 | LLM 结构化输出(with_structured_output → IntentLLMDecision)和白名单校验 | LLM 输出不可信,必须验证 |
| ★ 了解 | 关键词正则的设计思路(FAQ_HINTS、KNOWLEDGE_HINTS 等) | 了解设计模式即可 |
本讲小结¶
- 意图识别不只是贴标签,它决定后续全部检索和生成策略
- 判断顺序至关重要:问候→越界→人工客服→追问→强规则→LLM,每一步都有理由
- 规则优先 + LLM 补充:高频确定用规则(快、稳、省钱),模糊用 LLM(灵活)
- LLM 失败即暴露,不静默切换到规则路径
- Source 推断只查场景 TOML source_patterns,顺序即优先级,不在主链路代码中保留业务分类硬编码
- LLM 输出的 source 必须经白名单校验,防止无效值进入 Milvus 表达式
- 所有判断都有
reason字段,确保可解释和可追踪
下一讲:检索策略与动态计划 — RetrievalPlan、动态阈值、不同问题类别的特殊保护