在真实业务里把一个 Agent 从“能跑”做到“敢上线”意图识别是绕不过去的命门。我自己维护过一套工业级 Agent 意图识别分层漏斗核心思路很朴素把用户的一句话按“确定性从高到低”拆成多层漏斗先用规则、再用语义向量召回、最后才上大模型每层只处理自己能吃下的部分摸不准就往下一层丢。这么做不是为了炫技而是为了解决一个真问题——大模型确实聪明但在线上环境里聪明不等于可控更不等于省钱。这篇文章会把整套分层漏斗的设计思路、每层的关键实现、阈值怎么调、上线后我踩过的坑全部摊开来讲。适合正在做 Agent 应用、客服机器人、智能助手想从 Demo 走向线上生产环境的人参考。看完你会明白为什么要“规则优先、向量居中、大模型兜底”也会知道每一层到底该做到什么程度才算合格。1. 为什么工业级 Agent 上来就得谈分层漏斗1.1 一股脑全上大模型Demo 很爽上线就崩最早我也做过最朴素的方案用户发一句话直接拼进 Prompt 丢给大模型让它输出意图和参数。Demo 阶段确实惊艳什么“帮我查一下上个月的电费”“我要改发票抬头”都能轻松识别准确率看起来有 95%。但一旦放到工业环境里问题立刻暴露。首先是成本。主流大模型按 Token 计费一次调用如果有系统提示词、历史对话、用户问题、候选意图列表上下文轻松过万 Token。一天十万次请求光是识别意图这个环节月成本就够买一台不错的服务器。这还只是意图识别后面还有工具调用、结果生成、记忆写入每一环都在烧钱。其次是延迟。大模型推理受排队、上下文长度影响P95 延迟很容易冲到 3 秒以上。在线交互场景里用户等不了三秒。更麻烦的是大模型偶尔会“自由发挥”把无关问题强行塞进某个意图甚至编造出用户根本没提的参数。这种不可控性在面向真实用户的系统里是很致命的。所以我的结论很简单大模型不是不能用而是不能“什么都用它”。它应该作为漏斗的最后一道判断层而不是唯一的判断层。分层漏斗的本质就是让每一类问题都流向成本最低、确定性最高的处理环节。1.2 工业级约束成本、延迟、可解释性和可审计性工业级系统跟 Demo 最大的区别在于它有一堆显性的限制条件。成本有预算上限延迟有 SLO服务等级目标出问题要能复盘审计时要能说清楚“这条请求为什么被判成这个意图”。举一个很实际的例子。金融或电商场景里如果用户的意图被判错可能会导致后续工具调用执行了错误的操作。这时候如果整个判断过程是一个黑盒大模型你说不清它为什么选了这个意图问题就很难收敛。而分层漏斗每层的判断逻辑是可以解释的规则层命中就是命中向量层得分多少就是多少大模型层有完整的 Prompt 和输出内容。每一层都有记录出了问题能一层一层回放。这也是我坚持做分层的原因。不是说分层能让准确率从 90% 涨到 99%而是分层让系统在成本和风险上变得可控。你有能力知道哪一层出了问题然后针对性地修哪一层而不是把整个 Prompt 推倒重来。1.3 漏斗的本质确定性优先越往后越贵可以这样理解漏斗不是一个复杂的算法而是一条“成本递进”的处理流水线。第一层是规则引擎几乎零成本毫秒级返回完全可解释第二层是向量检索基于 Embedding 计算相似度成本很低中等可解释第三层才轮到 LLM成本最高能力最强但需要严格约束输出最后一层是兜底专门处理前几层都搞不定的长尾问题。这就像一个医院的分诊台。头疼脑热先由护士分诊确定是感冒就直接引导到普通门诊护士拿不准的才叫值班医生来看再复杂的才进专家会诊。你不可能让所有病人一进门就直接见专家那样专家累死医院也亏死。Agent 意图识别也是同一个道理。我在设计漏斗时还遵循一条铁律确定性优先。凡是规则能覆盖的绝不让给模型凡是向量能解决的绝不让给大模型。这样既能保证核心高频路径的稳定性又能把大模型的算力留给真正需要“理解”的场景。2. 漏斗整体设计与分层选型2.1 四层结构总览我这套漏斗一共四层每一层负责不同的输入范围和任务类型。先看总览再细说每一层为什么这样设计。层级名称核心手段成本延迟覆盖目标L1规则层正则、意图模板、槽位抽取极低毫秒级高频、高确定性场景L2语义检索层Embedding、向量相似度低10-50ms常见说法、近似表达L3LLM 推理层大模型 结构化输出高1-3s复杂歧义、多轮上下文L4兜底层澄清策略 / 默认意图低即时未知意图、防御性拒绝值得注意的是L1 到 L3 是递进关系不是并列关系。每层结束后判断是否还需要继续往下走完全由“置信度”决定。后面我会专门讲阈值怎么设。2.2 为什么规则层放在最前确定性大于智商很多技术人觉得规则层太“土”正则和模板这玩意儿没有任何智能含量。但恰恰是这种“没有智能”的东西才配得上放在最前面。原因有三点快、稳、便宜。规则层可以用纯字符串匹配也可以用预编译正则还可以配合一些简单的上下文标志位。它的响应速度是纳秒到微秒级别在大流量场景下基本不增加任何负担。规则层的判断结果具有完全确定性同一个输入永远得到同一个输出这在测试和回归中是极大的优点。我在规则层主要覆盖以下三类内容第一类是命名固定、表达单一的高频指令比如“查余额”“充值”“绑定手机号”第二类是强业务关键词组合比如“我要开发票”其中的关键词和词序相对稳定第三类是黑名单和禁忌词这类内容不适合交给模型做柔性理解直接拦截反而更安全。规则层的代价是“碎”需要持续维护词表。但它放在漏斗最前面命中率通常能挡住 30% 到 50% 的线上流量对整体成本的贡献非常可观。省下的钱足够养两个人来维护规则库。2.3 为什么向量检索层在中间低成本召回规则层挡不住的那部分流量就是用户表达方式有变化、但核心意图明确的场景。比如“你们的发票能开给个人吗”“我要个抬头改成个人的发票”这两种说法词面完全不同但意图都是“开票信息修改”。这种场景用向量检索做召回效果比规则好得多而且成本比大模型低一个数量级。实现上也很直白。先把候选意图做成一个意图库每个意图包含多条同义说法用 Embedding 模型向量化构建索引。用户来了把他的问题向量化去意图库里做相似度检索得分靠前的就是候选意图。这一层的核心参数是“阈值”。阈值设高了召回率低大量请求会漏到 LLM 层阈值设低了会把近似但不相关的意图拉进来导致后续误判。我通常的做法是跑一遍真实历史数据画出得分分布然后在“漏判率”和“错判率”之间找平衡点。这个点不是拍脑袋定的是拿数据说话的。2.4 为什么 LLM 层放第三只做判断不做检索LLM 层的定位是处理向量检索拿不准的内容。它不是用来做海量召回的而是用来做精细推理的。一个常见误区是为了让 LLM 更聪明把整个意图库和全部文档都塞进 Prompt。这样做不仅浪费 Token而且会让模型困惑因为无用信息太多反而降低了判断质量。在工业级漏斗里LLM 层收到的输入应该是“高价值上下文”用户的原始问题、对话历史摘要、以及向量检索给出来的 2-3 个候选意图。它的任务是从候选里选一个或者判断“都不对”。这个定位让它变成了一个判断题而不是填空题既降低了推理难度也大幅减少了幻觉空间。我实际测试下来把候选意图控制在 2-3 个比直接让模型从 50 个意图里选准确率能高出接近 10 个百分点。而且输出 Token 少因为模型只需要回答“选哪个”加一句简要理由Token 成本自然降下来了。2.5 要不要第四层兜底未知意图也是一种结果很多系统只设计了前三层LLM 判断不了就直接报错。但工业级系统里“无法识别”本身就是一个应该被显式管理的意图而不是一个异常。L4 兜底层专管三类场景用户说的话完全离开了预定义的意图范围问题涉敏或明显超出业务边界用户需求合法但当前系统不具备处理能力。比如用户问“你吃饭了吗”“今天天气怎么样”在一个纯粹的售后助手系统里这既不应该硬塞给某个业务意图也不应该让大模型自由发挥而是要走预设的应对路径。兜底层的核心策略是“澄清优先于猜测”。我一般会给用户抛一个澄清选项您是想咨询 A、B 还是 C这样既避免了大模型瞎猜导致的错误路由也给了用户一个二次表达的入口。3. 各层关键实现与参数细节3.1 规则层实现正则、意图模板、槽位抽取规则层不只是简单匹配关键词它还要负责抽取槽位Slot因为后续工具调用需要参数。比如用户说“帮我查一下水电费”意图是“查账单”但槽位是“水电费”还是“燃气费”必须抽出来。我的做法是维护一个意图模板库每个模板包含触发词、必含词、可选词、排除词以及槽位抽取规则。举个简化例子{ intent: query_bill, triggers: [查, 看, 查询, 账单, 费用], required: [账单, 费用], excluded: [历史账单, 已结清, 退款], slots: { bill_type: [水电, 燃气, 物业, 宽带, 全部] } }一条用户消息进规则层先匹配意图模板命中之后再抽槽位。如果触发词全命中但槽位缺失可以进入槽位追问流程。这样规则层不仅能识别意图还能预填参数减少后续 LLM 层的压力。但这个层有个必须注意的坑排除词列表一定要维护。比如用户说“我不要看账单”这段文本里包含“账单”和“看”如果不加排除词规则层就会误判成查询意图。我在线上出过一次很尴尬的事故用户说“别再给我发账单短信了”直接被规则层判定为“查账单”还给用户念了一长串账单明细。后来我养成了习惯所有规则模板都必须配排除词。3.2 语义检索层实现Embedding 与阈值语义检索层并不复杂但工程细节比较讲究。首先是 Embedding 模型选型工业场景基本会考虑两点维度适中比如 768 或 1024 维支持批量推理。模型本身要对中文分句效果好尽量做到行业内通用语料上不偏科。意图库的构建也影响很大。我给每个意图准备 10 到 20 条典型说法这些说法不是随机写的而是从真实用户日志里筛出来的高频表达。然后用 Embedding 模型全部向量化存进向量数据库。线上每次请求把用户问题向量化做 Top-K 检索K 一般取 3 或 5。相似度得分需要定一个“双阈值”逻辑。我习惯设一个高阈值和一个低阈值高于高阈值直接判定为该意图低于低阈值直接送入 LLM 层落在中间则带着候选意图一起送给 LLM 层做确认。这样既不放过高置信度的快速通道也不放过低置信度的模糊场景。实际调参可以用历史日志跑分。把一周的用户问题全部跑一遍向量检索画出命中得分分布再看得分在 0.85 以上的请求中有多少是真实的正确意图。这个过程中我踩过最大的坑是不同 Embedding 模型输出的分数尺度不一样换模型时必须重新标定阈值不能沿用旧阈值否则准确率会整体漂移。3.3 LLM 推理层实现结构化输出与成本控制LLM 层的核心是“让它输出可解析的 JSON而不是自由文本”。我在 Prompt 里会明确告诉模型这是意图判断任务候选意图只有这几个你必须从候选中选择一个如果都不符合输出 unspecified。输出格式必须是一个 JSON 对象包含 intent、score、reason 三个字段。{ intent: modify_invoice_title, score: 0.93, reason: 用户询问发票抬头是否可以修改为个人, slots: { title_type: 个人 } }这里 score 不是让模型算概率它更多是给监控系统一个信号。模型自己给的置信度和真实准确率并不完全对齐但至少能帮我们观察趋势。真正的“置信度”还是要靠最终的执行反馈来校准。成本控制方面我有几个实用做法第一系统提示词固定下来并缓存不要每次重复编码相同的大段说明第二尽可能压缩历史对话只保留最近两轮左右的信息更早的对话用摘要代替第三如果规则层和向量层已经给出了高度一致的答案LLM 层可以直接跳过。换句话说LLM 不应是每请求必经的环节而应是在前两层拿不准时才被激活。还有一个非常重要的点LLM 层的输出必须做 schema 校验。模型偶尔会输出多余字段或者把原因写成一整段话导致解析失败。我会用校验器强制校验不合法就重新调用一次或者降级到 L4 兜底。3.4 层间路由置信度阈值与回退策略整条漏斗的骨架是一个路由函数。我用一段简化代码来展示它的判断逻辑def route_intent(query, context): # L1 规则层 result rule_engine.match(query) if result and result.confidence 0.98: return result.intent, result.slots, rule # L2 语义检索层 candidates vector_search(query, top_k3) if candidates[0].score 0.85: return candidates[0].intent, candidates[0].slots, vector if candidates[0].score 0.75: return None, candidates, clarify_or_llm # L3 LLM 推理层 return llm_judge(query, context, candidates), llm # L4 兜底层 return fallback_strategy(query), fallback这段逻辑有几个细节值得展开。第一L1 命中后并不是百分百直接返回还要校验槽位是否齐全不齐全就进入追问流程。第二L2 落在中间区间时我不会直接送 LLM而是先看用户之前是否已经明确表达过意图如果有可以跳过候选流程直接走历史意图。第三L3 输出 unspecified 时会落到 L4L4 可以主动发起澄清而不是简单回复“听不懂”。回退策略也要考虑服务异常。如果 LLM 层超时或返回解析失败系统应该降级到向量层最高分结果或者直接走兜底话术。绝不能因为模型故障导致整个链路跟着崩掉。3.5 面向工业级的监控度量漏斗上线后必须建立一套分层监控指标。我日常看三类数据第一类是各层流量占比规则层占多少、向量层占多少、LLM 层占多少、兜底层占多少第二类是各层准确率抽样每天随机抽一定比例请求做人工标注第三类是成本和延迟按层统计平均耗时和 Token 消耗。一旦 LLM 层流量占比突然上升通常意味着规则层或向量层的覆盖率在下降。比如业务上新功能时新说法没有及时补充进规则和意图库流量就会大幅涌向 LLM 层这往往是成本暴涨的前兆。我会给每个层配一个环比波动告警超过阈值立刻去查。4. 一个真实意图的完整流转过程4.1 场景用户说“发票抬头能改成个人吗”为了把整个漏斗讲透我用一个真实高频场景走一遍流程。某售后助理 Agent支持开发票、改抬头、查水电费、申请售后等八个业务意图。现在用户发来一句发票抬头能改成个人吗。这句话包含“发票”“抬头”“个人”几个关键词但没有明确的业务动词规则层很难直接判定是“开发票”还是“改抬头”。规则层命中率不高于是快速放行进入语义检索层。向量层拿这句话算相似度候选结果大概是modify_invoice_title改抬头0.82、create_invoice开发票0.74、invoice_info发票查询0.66。我的高阈值是 0.85低阈值是 0.75所以 0.82 落在中间区间。系统不会直接判定而是把这两个候选带着上下文一起送进 LLM 层。4.2 漏斗各层的实际命中情况LLM 层拿到候选后结合对话历史判断用户想改的是“发票抬头”而不是新开发票。模型返回 intentmodify_invoice_titleslots{title_type: 个人}reason 是“用户咨询发票抬头能否修改为个人抬头”。这样最终结果就是意图是修改发票抬头参数是修改为个人抬头。后续工具调用会打开发票抬头修改流程并向用户确认当前抬头的具体情况。整个过程里规则层虽然没直接命中但也没花多少成本向量层完成了一次快速召回LLM 层只用了很少的 Token 做了一次二选一确认。相比直接把整句话喂给大模型自由发挥这个流程的判断质量更稳成本也更低。4.3 阈值调参与回归样例我调阈值时不会只盯着单个意图而是会准备一组回归样例包含不同难度的用户表达。简单样例是“改抬头到公司”“发票开个人”中等样例是“这个名字能换吗”“能开成XX吗”困难样例是“帮我处理一下别开公司名”这种需要结合上下文的话。用回归样例跑完一遍统计各层的分布和准确率再微调阈值。比如把向量层高阈值从 0.85 降到 0.82会有一部分原本落在中间区间的简单样例直接命中向量层省掉 LLM 调用但如果降太多错误命中也会跟着涨。所以每次调完都必须看整体准确率和成本两条曲线只看一条就会出问题。5. 常见问题与排查技巧实录5.1 规则层误命中怎么收规则层最常见的故障是“关键词太宽”。比如“我要投诉”这句话触发词里有“投诉”但用户后续可能是在夸某位客服也可能在描述投诉流程。关键词匹配会直接命中投诉意图把用户带进投诉流程。我的解决办法是三层第一规则层只收表达非常固定的高频场景含糊说法一律不接第二所有规则必须有排除词把“不要”“别”“怎么投”“投诉电话是多少”这类排除掉第三规则层命中后要抽槽位槽位抽不齐就触发追问而不是直接执行动作。线上维护规则库我养成了一个习惯每周导出规则层误命中的样本人工扫一遍发现有共性的表达就往排除词表里加。一个月下来规则层的误判率能肉眼可见地往下走。5.2 向量召回阈值飘了怎么办向量层阈值的“漂移”问题比规则层更隐蔽。比如某天新意图上线旧意图库的相似度分布被新向量挤压导致大量原本 0.83 分左右的请求掉到 0.79直接流入 LLM 层。表面上准确率没变但成本可能翻倍。我排查这类问题时第一步看各层流量占比的变化趋势第二步看特定意图的得分分布。如果发现某个意图的分布整体左移说明它的向量表达可能和新意图混淆了需要重新调整该意图的同义说法或者修改新意图的表述让两类向量在空间中分得更开。另外一个高频坑上线新的 Embedding 模型后必须重新跑一遍回归样例并重标阈值。不同模型的向量空间分布差异很大旧阈值基本不可沿用这是我吃过亏以后总结出来的硬经验。5.3 LLM 层出现幻觉意图LLM 层幻觉的表现是模型编造了一个不在候选里的意图并且模棱两可地给出一个高置信度分数。比如候选只有修改抬头和开发票模型却输出“查询发票”。这在单意图任务里属于明显的越界行为。我的应对手段有三个。第一Prompt 里把候选意图写死并明确禁止创造新意图第二输出必须走 JSON Schema 校验凡是 intent 字段不在候选集合里的一律重试第三原因字段要求模型引用用户原文片段不能凭空概括。即便这样模型偶尔还是会“自信地胡说”所以我在下游工具调用前还会做一次合法性检查确保意图是系统支持的。5.4 兜底层成了垃圾处理厂兜底层如果流量占比过高系统体验会变得很差。用户发现机器人总是反问“您想问什么”就会开始烦躁。兜底流量高往往不是兜底层的问题而是前几层没接住。排查思路是先看兜底层的样本把这些样本按内容聚个类。如果发现某类表达频繁出现说明这里应该新增意图或规则如果发现用户表达都集中在某个新业务方向说明产品侧有需求变化意图库需要扩容。我有一次发现兜底流量连续两周上涨聚类之后发现全是用户问“如何开发票”但当时意图库里开发票的说法太少导致向量检索得分一直偏低。补了一批真实表达之后兜底占比立刻降了 5 个百分点。5.5 意图漂移新热词和产品变化怎么应对线上业务会持续演化今天的高频表达下个月可能就变了。比较典型的是产品更名、优惠活动关键词、平台规则调整。这类变化往往不会马上反映在意图库里导致漏斗短期内准确率下滑。我的应对是建立一个“意图漂移看板”每天统计每个意图的错误路由率和平均置信度观察曲线变化。一旦某个意图的平均置信度连续几天走低就去人工看日志判断是不是表达方式变了及时补充同义说法或调整规则词表。6. 漏斗之外框架、Agent 生态与工程落地点6.1 LangChain、Dify、CrewAI 这类框架解决什么做分层漏斗时很多人会问要不要直接用 LangChain、Dify 这类 Agent 框架或者用 CrewAI 做多 Agent 编排。我的观点是框架解决的是“Agent 编排与集成”的问题而漏斗解决的是“意图判断的工程质量”问题两者不是替代关系而是配合关系。比如你可以用 LangChain 的 Router 去做基础的意图路由用 Dify 的可视化工作流去编排多轮对话用 CrewAI 去拆解多个子 Agent 的分工。但真要上工业级生产还是要在这些框架外面包一层自己的漏斗和监控体系。框架给你的是一套灵活的工具而不是一套免费的准确率和成本保障。实际项目中我见过很多团队直接用框架里的默认路由把用户消息一股脑喂给模型结果上线后成本失控。框架本没有错但要清楚它的边界它能帮你快速搭建链路但工程上的调优、监控、回归测试还是要自己下功夫。6.2 多 Agent、记忆与技能在漏斗里的位置现在 Agent 生态里经常提到记忆Memory、技能Skill、工具Tool、多 Agent 协作。这些组件和漏斗的关系是什么简单说意图识别是它们共同的前置门槛。多 Agent 系统里每个子 Agent 的入口都需要自己的意图识别而不是把用户问题全局分发给所有 Agent。主入口漏斗先把用户意图路由到对应业务域子 Agent 内部再做更细粒度的意图判断。这本质上是一个双层漏斗。记忆模块则会在第三层增强上下文如果用户在两轮前说“我要改发票抬头”当前说“还有备注也一起换”模型需要结合记忆才能准确理解“一起换”的含义。技能和工具注册表可以为意图识别提供候选动作列表让 LLM 层知道当前系统“能做什么”避免它生成系统根本不支持的意图。6.3 关于 Agent 安全与边界做意图识别漏斗时安全边界是一个容易被忽略的工程点。用户可能会在对话中试图让 Agent 执行超出业务范围的操作或者试探系统权限边界。工业级系统不能把这些问题全部交给大模型去“自觉”。我的做法是在规则层加一道独立的拦截层针对明显越界或违规的表达直接拒绝不进入后续业务意图判断。这层规则必须独立于普通意图规则单独维护、单独审计、单独告警。在 L3 层Prompt 里也会明确标注系统能力范围超出范围的请求统一路由到兜底策略。系统还要有明确的沙箱概念工具执行要经过权限校验和参数校验。意图识别只负责判断用户想做什么而“能不能做”“做到什么程度”必须由独立的授权和沙箱机制决定。这一步不能省。6.4 从零搭建一条自己的漏斗学习路线建议如果你是新手想从零搭一条工业级漏斗我的建议是先别碰 Agent 框架而是把这几件基本功练好第一先把规则层做精学会正则、词表、意图模板和槽位抽取第二再学 Embedding 和向量检索理解相似度分数和阈值的关系第三然后才学怎么给大模型写 Prompt尤其是结构化输出和候选集约束。很多人一上来就看多 Agent 编排、高级技能、记忆网络反而在最基础的意图识别工程上翻了车。意图识别是 Agent 的地基地基不稳后面搭什么都白搭。我面试工程岗时经常会问对方如果线上意图识别准确率下降了你从哪一层的哪个指标开始查能答清楚这个问题的候选人往往才是真正做过系统的人。个人体会这套漏斗我迭代了将近一年最大的体会是工程上没有一步到位的“智能”只有一层一层垫起来的“可靠”。规则层很土但它稳向量层很轻但它快LLM 层很强但它贵兜底层很笨但它让系统有了退路。每一层单独看都不完美合在一起才像一个能上生产环境的 Agent 系统。最后再分享一个小技巧每次改完阈值或规则别只看准确率涨了没有一定顺手看一眼各层流量占比和平均延迟。很多问题不是功能上跑不通而是资源上慢慢耗尽等你反应过来时成本已经超了。把监控埋好把回归样例留好这个漏斗才能陪着你的业务一起长大。
阅读完成 · 觉得有帮助?