意图识别:消息进来该走哪条路
作者:程序员马丁
Ragent AI —— 从 0 到 1 纯手工打造企业级 Agentic RAG,拒绝 Demo 玩具!AI 时代,助你拿个offer。
上一篇讲了 Query 改写,RAG 系统的多轮对话能力已经比较完整了:会话记忆让模型记住了上下文,Query 改写让检索系统也能理解上下文,检索效果大幅提升。
但你把这套系统上线之后,会发现一个尴尬的问题。
用户说“你好,我想咨询一下”,系统拿“你好我想咨询一下”去向量数据库里检索,召回了退货政策、保修条款、配送时效三个 chunk,然后模型基于这些 chunk 回答:“您好,关于退货政策,我们提供七天无理由退货服务……”——用户只是打了个招呼,你给人家讲退货政策?
用户说“我查一下我的年假余额”,系统去知识库里检索年假余额,召回了公司年假制度:正式员工每年享有 10 天带薪年假……,模型回答:“根据公司年假制度,正式员工每年享有 10 天带薪年假。”——用户想知道自己还剩几天年假,不是问年假制度。这个问题应该调 HR 系统的 API,不是查知识库。
问题出在哪?你的 RAG 系统是一根筋的——不管用户说什么,都往知识库里检索。就像一家医院没有分诊台,所有患者来了都直接送去内科,牙疼的、骨折的、做体检的全挤在内科门口。
今天要讲的就是给 RAG 系统装一个分诊台——意图识别与问题路由。用户消息进来,先判断它应该走哪条路:查知识库、调工具、直接闲聊、还是反问用户补充信息,然后把请求分发到对应的处理流程。
为什么需要意图识别
1. 一根筋的 RAG 系统会出什么问题
不做意图识别,所有消息都走 RAG 检索,会遇到三类问题:
| 用户消息类型 | 走 RAG 检索的结果 | 实际应该怎么处理 |
|---|---|---|
| 闲聊(你好、谢谢、哈哈) | 召回不相关的 chunk,模型基于不相关内容强行回答,比不检索还差 | 模型直接回答,不需要检索 |
| 工具调用(查我的订单、我还剩几天年假) | 知识库里没有用户的个人数据,检索不到有用信息,模型说“找不到相关信息” | 调 Function Call / MCP 查实时数据 |
| 模糊问题(有什么推荐的吗、怎么办) | 检索结果分散在多个主题,模型乱答一通 | 反问用户补充信息,明确意图后再处理 |
闲聊走检索,比不检索还差——这一点很多人没意识到。RAG 系统的生成策略里通常有一条规则:“基于以下参考资料回答用户问题”。如果检索到的 chunk 和用户问题完全不相关,模型要么硬着头皮基于不相关的内容回答(答案很奇怪),要么触发兜底回复“抱歉,找不到相关信息”(用户只是说了句你好,你说找不到相关信息?)。
如果不走检索,模型直接回答“你好”,反而更自然。
2. 医院分诊台的类比
意图识别的作用,就像医院的分诊台。
患者来了不是直接看病,先到分诊台:护士问你哪里不舒服,然后告诉你该去哪个科室。牙疼去口腔科,骨折去骨科,做体检去体检中心,不确定什么病的先去全科看看。
RAG 系统也一样。用户消息进来,先经过意图识别这个分诊台:
- 问产品信息、政策规定 → 去“知识库科室”(RAG 检索)
- 查订单、查年假、提交退货 → 去“工具科室”(Function Call / MCP)
- 打招呼、说谢谢 → 去“前台”(直接回复)
- 说不清楚想要什么 → “请您再描述一下症状”(引导澄清)
没有分诊台的医院效率低、体验差;没有意图识别的 RAG 系统也是一样。
四种意图类型
在电商客服场景下,用户消息可以分为四种意图类型。这个分类体系不是固定的——不同业务场景可能有不同的分类方式,但这四类覆盖了大多数场景。
1. 知识检索(Knowledge Retrieval)
用户在问一个知识库能回答的问题。这类问题的特点是:答案存在于你的知识库文档里,是“静态”的、通用的知识。
典型问题:
- iPhone 16 Pro 的退货政策是什么?
- 保修期内屏幕碎了能免费换吗?
- 你们的配送范围覆盖哪些城市?
- 会员等级有什么权益?
处理方式:走完整的 RAG 检索流程——Query 改写 → 向量检索 → 重排序 → 生成答案。
2. 工具调用(Tool Invocation)
用户想查询个人数据、实时信息,或者执行某个操作。这类问题的特点是:答案不在知识库里,需要调用外部系统的 API 才能获取。
典型问题:
- 帮我查一下订单 #12345 的物流状态(查个人订单数据)
- 我还剩几天年假?(查 HR 系统)
- 帮我申请退货(执行操作)
- 今天有什么优惠活动?(查实时数据)
处理方式:走 Function Call / MCP 流程——识别出需要调用哪个工具,传入参数,执行工具,把结果返回给模型生成最终答案。
怎么区分知识检索和工具调用?关键看答案的来源。如果答案在你的知识库文档里(退货政策、保修条款、产品参数),就是知识检索;如果答案需要查数据库、调 API、访问外部系统(某个订单的状态、某个人的年假余额、实时库存),就是工具调用。
3. 闲聊对话(Chitchat)
用户在闲聊、打招呼、表达感谢、开玩笑,不涉及具体的业务问题。
典型问题:
- 你好
- 谢谢你的帮助
- 你是 AI 还是真人?
- 今天心情不好
- 哈哈,你真有趣
处理方式:模型直接回答,不走检索,也不调工具。System Prompt 里定义好闲聊时的回复风格就行。
前面说过,闲聊消息走 RAG 检索反而更差。模型自己就能很好地处理这类对话,不需要知识库的辅助。
4. 引导澄清(Clarification)
用户的问题太模糊,系统无法确定该走哪条路。这时候不应该硬答,而是反问用户补充信息。
典型问题:
- 有什么推荐的吗?(推荐什么?手机?配件?)
- 怎么办?(什么怎么办?退货?维修?)
- 帮我看看(看什么?订单?产品?)
- 出问题了(什么产品?什么问题?)
处理方式:不检索也不调工具,直接返回一个引导性的问题,让用户补充关键信息。
引导澄清不是万能的,不能动不动就反问用户。如果用户的问题虽然简短但意图明确(比如“价格呢”,在聊 iPhone 16 Pro 的上下文中意图很清楚),就不应该反问,而是结合对话历史直接处理。只有在真的无法判断意图时才引导澄清。
5. 四种意图对比
| 意图类 型 | 触发条件 | 处理流程 | 典型示例 |
|---|---|---|---|
| 知识检索 | 问知识库里有的通用知识 | Query 改写 → 检索 → 生成答案 | 退货政策是什么 |
| 工具调用 | 查个人数据 / 实时信息 / 执行操作 | Function Call / MCP | 查我的订单状态 |
| 闲聊对话 | 打招呼 / 感谢 / 闲聊 | 模型直接回答 | 你好、谢谢 |
| 引导澄清 | 问题模糊、信息不足 | 反问用户补充信息 | 有什么推荐的 |
实际业务中,意图分类体系可能更细。比如电商场景可能拆出“投诉”“催单”“比价”等专门的意图类别。但核心思路不变:先分类,再路由。从这四种基础类型开始,根据业务需要逐步细化。
意图识别的三种方案
知道了要分哪几类意图,接下来的问题是:怎么判断一条用户消息属于哪个类别?
1. 规则匹配方案
最简单直接的方案:用关键词和正则表达式判断意图。
public class RuleBasedClassifier {
/** 闲聊关键词 */
private static final Set<String> CHITCHAT_KEYWORDS = Set.of(
"你好", "您好", "谢谢", "感谢", "再见", "拜拜",
"哈哈", "嗯嗯", "好的", "收到", "明白了"
);
/** 工具调用关键词 */
private static final Set<String> TOOL_KEYWORDS = Set.of(
"我的订单", "查订单", "订单号", "物流状态",
"年假", "余额", "申请退货", "提交",
"修改地址", "取消订单"
);
public String classify(String query) {
// 完全匹配闲聊关键词(短消息)
if (query.length() <= 6 && CHITCHAT_KEYWORDS.contains(query)) {
return "chitchat";
}
// 包含工具调用关键词
for (String keyword : TOOL_KEYWORDS) {
if (query.contains(keyword)) {
return "tool";
}
}
// 消息太短且不是闲聊,可能需要澄清
if (query.length() < 5) {
return "clarification";
}
// 默认走知识检索
return "knowledge";
}
}
规则匹配的优缺点很明显:
| 维度 | 表现 |
|---|---|
| 速度 | 极快(微秒级) |
| 准确率 | 低——只能匹配写死的关键词,覆盖不了用户的各种表达方式 |
| 成本 | 零(不调模型) |
| 维护成本 | 高——每发现一个 bad case 就要加一条规则,越加越多越加越乱 |
规则方案最大的问题是无法理解语义。“帮我看看剩几天假”没有命中“年假”关键词,就会被错分为知识检索。“能退吗”——是问退货政策(知识检索)还是想发起退货(工具调用)?规则无法区分。
规则方案适合做第一层快速过滤——把最明确的意图快速识别出来(“你好”就是闲聊,“查订单 #12345”就是工具调用),不确定的交给大模型处理。
2. 大模型分类方案
用大模型做意图分类,核心是设计好分类 Prompt。
2.1 分类 Prompt 的设计
你是一个意图分类助手。根据对话历史和用户的最新消息,判断用户的意图类别。
意图类别定义:
1. knowledge - 知识检索:用户在询问产品信息、政策规定、操作指南等知识库中的内容。
示例:"iPhone 16 Pro 的退货政策是什么""保修期多久""配送范围覆盖哪些城市"
2. tool - 工具调用:用户想查询个人数据、实时信息,或执行某个操作。
示例:"查一下我的订单状态""帮我申请退货""我还剩几天年假"
3. chitchat - 闲聊对话:用户在打招呼、感谢、闲聊,不涉及具体业务问题。
示例:"你好""谢谢""你是AI吗""今天心情不好"
4. clarification - 引导澄清:用户的问题太模糊,缺少关键信息,无法确定意图。
示例:"有什么推荐的""怎么办""帮我看看""出问题了"
判断规则:
- 结合对话历史判断,相同的话在不同上下文中意图可能不同
- 如果用户的问题涉及"我的""查一下"等个人化表述,通常是工具调用
- 如果问题在问通用的规则、政策、产品信息,通常是知识检索
- 只有在真的无法判断意图时才分类为 clarification
- 以 JSON 格式输出,格式为:{"intent": "分类结果", "confidence": 置信度}
- 不要输出 JSON 以外的任何内容
对话历史:
{history}
用户最新消息:{query}
几个设计要点:
- 每种意图都有明确定义和示例:这是 Few-shot 的典型用法,给模型足够的参考。定义要写清楚边界——什么算知识检索,什么算工具调用,不能让模型 猜
- 强调结合对话历史判断:同样一句“能退吗”,在不同的对话上下文中意图不同。第一轮对话时大概率是问退货政策(知识检索),前面在聊某个具体订单时大概率是想发起退货(工具调用)
- 输出 JSON 格式:方便程序解析。加 confidence 字段可以在置信度低时触发兜底策略
- clarification 要谨慎使用:Prompt 里明确说“只有在真的无法判断时才分类为 clarification”,避免模型动不动就要求澄清
2.2 分类效果与边界情况
大模型分类的准确率通常在 90% 以上,但有一些边界情况需要注意:
| 边界情况 | 用户消息 | 可能的分类 | 正确处理方式 |
|---|---|---|---|
| 上下文相关 | 能退吗?(前面在聊某个订单) | 工具调用 | 结合对话历史判断,这里是想退货,不是问退货政策 |
| 知识 vs 工具模糊 | iPhone 16 Pro 有货吗? | 取决于系统设计 | 如果你有实时库存 API,分为工具调用;如果没有,分为知识检索 |
| 确认回复 | 好的(前一轮助手问“确认退货吗”) | 工具调用(确认操作) | 结合上下文,这是对退货操作的确认 |
| 多意图 | 查一下退货政策,顺便帮我看看订单到哪了 | 知识检索 + 工具调用 | 可以取主要意图,或者拆分处理(参考 Query 改写的多意图拆分) |
边界情况是必然存在的。不要追求 100% 的分类准确率,而是要做好兜底策略——分类错了不要出大问题就行。比如把闲聊误分为知识检索,最多是检索了一下但没找到相关内容,模型给一个兜底回复,不会造成严重后果。但如果把工具调用误分为闲聊,用户想退货你回了个“好的呢”,那就有问题了。
大模型分类方案的优缺点:
| 维度 | 表现 |
|---|---|
| 速度 | 较慢(500~3000ms) |
| 准确率 | 高——能理解语义,能利用对话历史 |
| 成本 | 每次分类消耗一次模型 API 调用(用小模型可以控制成本) |
| 维护成本 | 低——加新意图只需修改 Prompt,不需要写代码 |
3. 混合方案(推荐)
规则匹配速度快但准确率低,大模型分类准确但有延迟和成本。把两者结合起来,就是混合方案:规则做第一层快速过滤,大模型做第二层精确分类。
流程是这样的:

规则层只处理高置信度的明确场景:
- 短消息且完全匹配闲聊关键词(“你好”“谢谢”)→ 直接判定为闲聊
- 包含明确的工具触发词且带具体参数(“查订单 #12345”)→ 直接判定为工具调用
- 其他所有情况 → 交给大模型分类
这样做的好处是:
- 大部分闲聊消息不需要调模型:用户打招呼、说谢谢这种高频场景,规则就能搞定,省了一次 API 调用
- 复杂场景有模型兜底:语义模糊、上下文相关的消息,交给大模型判断,准确率有保障
- 成本可控:实际场景中,闲聊和明确的工具调用大约占 30%~40% 的消息量,这部分都可以被规则层拦截
三种方案的对比:
| 维度 | 规则匹配 | 大模型分类 | 混合方案 |
|---|---|---|---|
| 速度 | 极快(微秒) | 较慢(300~3000ms) | 快(规则命中时微秒,未命中时 300~3000ms) |
| 准确率 | 低(60%~70%) | 高(90%+) | 高(90%+) |
| Token 成本 | 零 | 每次请求都消耗 | 仅未命中规则时消耗(节省 30%~40%) |
| 维护成本 | 高(不断加规则) | 低(改 Prompt) | 中(规则 + Prompt 两套) |
| 推荐场景 | 不推荐单独使用 | 消息量小、对延迟不敏感 | 生产环境(推荐) |
实际项目中,混合方案是最稳的选择。规则层用来拦截最明确的意图,大模型层处理剩下的复杂场景。随着业务发展,你可以逐步丰富规则层的覆盖范围,减少大模型调用的比例。
问题路由的架构设计
1. 路由器模式
意图识别完成后,需要一个路由器把请求分发到不同的处理流程。整体架构是这样的:

路由器的职责很简单:拿到意图分类结果,调用对应的处理器。每个处理器各自负责自己的逻辑,互不干扰。
用 Java 代码表示,路由器的核心逻辑就是一个 switch:
public String route(String intent, List<Message> history, String query) throws IOException {
switch (intent) {
case "knowledge":
// 知识检索路径:Query 改写 → 检索 → 生成答案
return handleKnowledgeRetrieval(history, query);
case "tool":
// 工具调用路径:Function Call / MCP
return handleToolInvocation(history, query);
case "chitchat":
// 闲聊路径:直接调模型生成回答
return handleChitchat(history, query);
case "clarification":
// 澄清路径:返回引导问题
return handleClarification(history, query);
default:
// 兜底:默认走知识检索
return handleKnowledgeRetrieval(history, query);
}
}
default 分支走知识检索是一个安全的兜底策略。知识检索路径本身就有兜底机制——如果检索不到相关内容,模型会说“抱歉,找不到相关信息”。这比走错其他路径(比如误调了工具)要安全得多。
2. 意图识别在 RAG 完整链路中的位置
把意图识别加入后,完整的 RAG 链路变成了这样:

注意意图识别的位置:在会话记忆之后,Query 改写之前。
为什么在会话记忆之后?因为意图识别可能需要对话历史来做判断。用户说“好的,帮我退了吧”,如果不知道前面在聊什么,无法判断这是工具调用(确认退货操作)。