前阵子有个Agent项目上线后别的指标都挺正常就意图识别这一环每天晚上被线上告警折磨。问题场景很统一用户把一句话说得稍微丰满一点——“我周一上午那个预约能帮我改到下午吗”——主模型就开始犯迷糊要么把“改预约”识别成“新建预约”要么直接丢掉后半句的意图。换到传统客服语义理解里这种句子属于基本功但在Agent场景里意图识别的复杂度被放大了好几个量级。后来我把整个识别链路重构成了一套工业级Agent意图识别分层漏斗问题才算彻底解决。这篇文章我想把这套方案完整拆开聊一聊。它本质上做的事情很简单不再让一个“全能大模型”去硬扛所有意图识别请求而是用“便宜、快、确定”的手段先处理掉绝大多数请求把少数疑难杂症留给昂贵但强大的模型。听起来像老生常谈的架构分层但落地之后我发现这套漏斗解决的不只是成本和延迟还顺便把准确率、可解释性、安全防护全都理顺了。适合正在做AI Agent、AI客服、自动化助手或者准备把Agent从Demo推向生产环境的同学参考。1. 为什么工业级Agent需要一个意图识别漏斗1.1 单一LLM做意图识别的三个难以忍受的痛点先说说最直接的动机。很多人第一次搭Agent意图识别直接用大模型跑比如“system prompt里写一句‘你是意图识别助手请输出用户意图’然后拿GPT-4/Claude一顿输出”。 Demo阶段这么搞完全没有问题但放到生产环境三个问题会接踵而至。第一个是成本。想象一个日活10万的Agent前台假设平均每个用户每天触发5次意图识别每次请求的输入输出加一起大概消耗2000 token一天就是10亿token。按大模型API的价格粗算一天大几千甚至上万块的调用成本一个月下来足够买几台高配GPU服务器了。这在商业项目里是不可持续的。第二个是延迟。大模型推理的P99延迟通常在2到4秒之间这在聊天场景还能勉强接受但放在电商客服、售后处理、智能硬件指令控制这类场景里用户等4秒才得到“正在处理”的反馈基本等于体验事故。我实测过如果把意图识别和后续的Agent执行串行起来端到端时长轻松突破10秒。第三个是稳定性这个最隐蔽也最致命。大模型是概率输出温度稍微调一点、prompt稍微变一下同一个句子可能给出不同意图标签。更麻烦的是它面对复杂的多意图句子容易“上下文丢失”——你把用户历史对话塞进去之后它可能被前面的内容带偏。还有幻觉问题模型会在不确定的时候硬着头皮猜一个意图而且猜得理直气壮。这三个问题叠加起来导致单一模型方案在工业级场景里既贵又慢还不可控。1.2 分层漏斗的核心思想先粗筛再精排说白了分层漏斗的思路并不新鲜传统搜索系统里的“召回-粗排-精排”就是这个套路只不过我们把它搬到了意图识别上。核心假设是线上请求的意图分布是极度不均匀的20%的高频意图可能占了80%的流量比如“查订单”“退款”“改地址”“问物流”这类确定性很强的请求。这些请求完全不需要大模型出场用规则或者轻量分类器就能搞定。漏斗的基本结构分为三层最底层是规则与关键词引擎用确定性匹配秒杀高频、固定模式的请求中间层是轻量意图分类模型处理那些规则覆盖不到但语义模式相对稳定的表达最顶层才是LLM语义精排专门处理模糊、长尾、需要上下文或多意图拆分的复杂请求。每一层都有一个“置信度闸门”信不过就往下放行绝不在某一层硬答。这套结构带来的收益很直接成本和延迟曲线大幅下降同时因为每一层都可以针对自己的请求分布做专项优化整体准确率反而比单一模型更高。更难得的是前两层的决策过程是规则和特征出了问题可以直接复盘定位这在生产环境里是救命级别的能力。1.3 什么样的项目适合上这套漏斗也不是所有Agent都要上分层漏斗。如果你做的是一个内部工具类Agent一天就几百次调用对响应时间也不敏感那直接让大模型识别意图完全没问题没必要为了架构而架构。但如果你的Agent要对外服务、日活高、意图种类多、用户表达口语化随意或者对响应延迟和成本有硬性要求那分层漏斗基本是必选项。另外一个判断标准是看意图体系本身。如果你定义的意图集合超过30个并且意图之间存在嵌套关系比如“退款”下面还有“退款进度”“退款方式”单一分类器很难拍得准这时候漏斗天然适合你——因为每一层可以只负责一部分意图子集复杂度被拆开了。我见过不少项目是上了漏斗之后才把原本一团乱麻的意图体系慢慢梳理清楚的。2. 漏斗各层的详细设计与实现逻辑2.1 第一层规则与关键词引擎规则引擎是漏斗的地基它的任务是用确定性的手段抓住高频、模式固定的意图。这里说的“确定性”非常重要正则表达式和关键词匹配是100%可预期的命中就是命中没命中就是没命中没有概率问题也没有幻觉问题。实现上我推荐用Aho-Corasick自动机来做关键词匹配。它能把一大堆关键词一次性编译成一个状态机匹配时间复杂度O(n)扫一遍文本就能找出所有命中的词条。配合正则表达式做模式级匹配比如订单号、手机号、日期时间的提取可以把“槽位填充”也一并做掉。实际操作中我通常是先给每个意图建一个“意图词典”每条意图收集50到100条高频种子表达比如“退”“退货”“退款”“不想要了”“七天无理由”都归到“退货申请”这个意图下。然后对输入文本做归一化——转小写、去停用词、纠正常见错别字比如“号”和“好”在语音输入里的混用再交给匹配器。这里有个特别容易踩的坑规则不要写得太宽。我早期做过一个案例“退”这个字被当成退款意图的关键词结果用户说“我不退步了继续按原方案执行”系统直接识别成退款申请闹了个大笑话。规则层必须做“上下文约束”比如“退”必须出现在“退货/退款/退掉/退一下”这类组合词里才算数单字命中一律不采信。规则层的设计原则是宁缺毋滥少命中几个没关系后面还有第二层第三层兜底但命中一定要准。2.2 第二层轻量意图分类模型第一层没接住的表达不代表都是复杂意图大部分只是“换了个说法”而已。比如“冰箱制冷效果不太行”和“冷藏室总结冰”这两句话没有任何公共关键词但语义上指向同一个意图。这种泛化能力就得交给轻量意图分类模型。我常用的方案是蒸馏后的BERT比如distilbert或者更轻的TextCNNembedding。输入一句话输出一个意图标签分布。为什么不用大模型因为分类任务本质上是个“降维”任务轻量模型在单一分类任务上的准确率完全够用而推理延迟只有几毫秒成本几乎可以忽略。部署上用ONNX Runtime或者TensorRT推理单张GPU卡能扛住几千QPS这在漏斗里承担了绝大多数“规则漏网之鱼”。但这层有个天生的短板它对多意图句子、需要上下文推断的句子处理不好。这不是模型垃圾而是任务设计问题。“帮我查一下订单顺便改一下收货地址”这种句子分类器无论怎么训练都很难稳定地输出两个意图它更适合的任务是“判断这句话最可能属于哪一个意图类目”。所以我对第二层的要求很克制只做单意图分类置信度低于阈值就放行到第三层绝不让它强行充当全能选手。训练数据方面第一版可以用历史日志加人工标注也可以让LLM离线批量生成同义改写样本把单个意图的种子表达扩充到几百甚至上千条。这一步成本不高但对召回率的提升非常明显。2.3 第三层LLM语义精排与Agent路由真正复杂的请求最终会落到LLM这一层。它要解决的问题包括多意图拆分、指代消解、复杂槽位抽取、以及规则层和分类层完全没见过的长尾表达。这一层的设计关键不是模型的提示词写得多花哨而是输出约束和不确定性处理。我强烈建议用结构化输出schema来约束LLM的返回结果无论是函数调用function calling还是JSON Schema约束都可以。比如让模型严格输出四类字段domain业务域、intent意图名、slots槽位对象、confidence置信度。注意最后这个“意图未知”类别我见过太多项目把这一项省掉了导致模型在不确定时硬猜一个意图——这是线上bad case最大的来源之一。一定要在提示词里明确告诉模型信息不足时可以输出intent: unknown这比猜错要好得多。另外第三层可以承担多意图拆分任务。识别出句子里有多个意图后拆分成多个结构化结果分别路由到不同的子Agent去执行。比如“帮我查一下订单顺便改一下收货地址”这句话第三层会输出两个意图结果一个是查订单一个是改地址系统再根据这两个结果做Agent编排。这一步是单一模型方案最难做好的地方因为在分层漏斗里前两层已经过滤掉了大量简单请求第三层的输入都集中在“复杂case”上多意图的比例会非常高值得专门优化。2.4 层与层之间的衔接与反馈闭环分层漏斗不是把三层简单拼起来就完事了层与层之间的衔接逻辑才是工业级和Demo级的分水岭。核心有三个机制。第一个是置信度闸门。每一层都必须定义“什么时候放行什么时候下放”。规则层没有置信度概念只要命中就算过但要注意“部分命中”的情况——比如只匹配到半句意图要判断是否整句意图清晰轻量分类模型要设置信度阈值分类器输出低于阈值就走下一层LLM层也要设一个“unknown比例监控”如果某个时间段内unknown输出比例超过正常值说明线上出现了数据漂移需要告警。第二个是熔断降级。LLM层是高危依赖一旦外部API超时或者限流漏斗不能整体瘫痪。我实际的做法是LLM层超时后自动降级用轻量分类模型的结果加模板回答顶上如果分类模型也不可用就退回规则层只回复确定性的高频意图。原则是“宁可答得简陋不能没有响应”。第三个是数据回流。第三层LLM的高质量判定结果不能只留在日志里吃灰要定期抽出来经过人工抽检审核之后作为第二层轻量模型的训练数据。我见过不少团队做分类模型训练时到处找公开数据集其实最优质的数据就藏在自己的漏斗日志里。每跑一轮回流第二层的覆盖率就高一点落到第三层的流量就少一点整体成本继续下降。这个“AI训练AI”的迭代才是漏斗长期越跑越省的核心动力。3. 实操过程分层漏斗的搭建与调优3.1 技术选型与框架集成先讲讲我推荐的技术栈。规则引擎不需要任何重型组件纯CPU就能跑用Python的regex库加上自实现的Aho-Corasick自动机即可或者直接用pyahocorasick这个库五行代码编译词典。轻量分类模型我选distilbert加一个分类头finetune之后导出ONNX格式跑ONNX Runtime推理单条延迟稳定在5毫秒以内。LLM层接外部API或者私有化模型但前面要包一层统一的接口方便随时切换。集成方式上有一个很关键的建议把意图识别模块做成独立的微服务通过HTTP/gRPC对外提供接口而不是把漏斗逻辑写死在业务代码里。这么做的好处是意图识别模块可以独立扩缩容、独立发版、独立做压测不会因为主业务的频繁迭代而跟着抖动。对外接口可以设计成简单直接的样子入参是用户当前文本加可选的对话历史出参是识别结果包括命中层级、意图标签、槽位、置信度、耗时等。我实际给出的JSON Schema会是这样{ type: object, properties: { hit_layer: {type: integer, enum: [1, 2, 3]}, domain: {type: string}, intent: {type: string}, slots: {type: object}, confidence: {type: number}, unknown: {type: boolean} }, required: [hit_layer, intent, confidence] }这个schema不管哪一层返回都一样业务侧完全不用关心到底用了规则还是模型还是LLM只需要消费结果。3.2 阈值怎么定多级阈值与降级策略阈值设计是整个漏斗最微妙的部分调得太高会误杀调得太低会误判。我根据自己的经验给出一个初始参考值大家可以根据自己的数据分布再调。轻量分类模型的置信度阈值我通常设置在0.6到0.75之间。低于0.6时基本可以断定分类器在硬猜往下放高于0.75时大胆用出错概率很小。中间地带要看业务容错度如果识别错了代价很高比如触发一次退款操作阈值就往高了调如果错了还可以让用户二次确认阈值可以适当放低。规则层的“阈值”不是置信度而是触发条数。我一般要求至少两个独立关键词命中或者一个强模式比如订单号正则命中才把规则层的结果作为最终意图。单关键词命中只作为候选交到第二层再判断一下。LLM层的特殊之处在于它本身已经是“最高裁判”没有更低层级可以下放所以它要处理的是“不确认就不输出”。我通过提示词要求模型在置信度低于0.7时输出intent: unknown然后系统走兜底策略——让用户换一种说法重新描述或者转人工。把这个比例监控起来如果超过10%说明前两层漏了太多本应拦截的请求问题出在漏斗上半段而不是LLM本身。降级策略上我给LLM层设了800毫秒的超时阈值超时就自动走第二层结果。第二层可用性依赖GPU资源我会给它配置熔断器连续错误超过20次就摘掉一切退回规则层。这个策略虽然保守但保证系统在最坏情况下仍然能响应高频意图。3.3 性能与成本实测数据我拿一个客服场景的项目做参考跟大家分享一组实测量级数据。这个项目有32个一级意图、120多个叶子意图日请求量在60万次左右流量分布比较典型高频10个意图占了65%请求量。三层漏斗的流量占比大概是规则层拦掉35%轻量分类层吃掉45%落到LLM层的只有20%左右。推理延迟上规则层平均低于1毫秒分类层平均5毫秒LLM层P50在800毫秒左右、P95在2.5秒。但注意因为LLM只处理20%的请求全链路的加权平均P95延迟降到了大约400毫秒和单一模型方案动辄2秒以上的体感完全不在一个级别。成本方面对比更夸张。纯用大模型方案一天60万次请求假设每次平均消耗1500 token日消耗约9亿token按市面主流大模型API的价格一天成本在万元以上量级。上了分层漏斗之后80%的请求落在规则和分类模型上这两层的边际成本几乎可以忽略LLM只处理12万次请求日成本降到两千块左右整体节省80%以上。同时意图识别的整体准确率从单一模型的88%提升到了94%主要还是靠减少“模型硬猜”的比例。需要说明的是这些数字来自特定业务场景不能当普适值用但量级是有参考价值的。你的场景里只要高频意图集中度没有那么极端漏斗依然会明显优于单一模型。3.4 日志、埋点与标注回流这是工业级方案和玩具Demo的最大分水岭。如果漏斗没有记录“为什么命中/为什么没命中”的日志那它和黑盒没有区别。我在每个请求的日志里至少记录这些字段请求ID、命中层级、每层置信度、每层耗时、最终意图、槽位结果、用户是否二次纠正。这些数据进了数据仓库既用来日常排查问题也用来做标注回流的原料。标注回流的流程是这样的每周从LLM层处理过的请求里随机抽一批配合用户后续行为比如是否追问“我说的是另一个意思”做人工标注把标注好的结果加到第二层分类模型的训练集里重新finetune上线评估再观察漏斗流量分布。这样跑两三个月你会发现规则层覆盖率在上升分类模型的能力在变强落到LLM层的请求比例持续下降。我见过长期维护比较到位的项目LLM流量占比能压到10%以内——这个“漏斗变浅”的过程就是整个系统持续降本的直接体现。4. 常见问题与排查技巧实录4.1 常见问题速查表现象根因处理方向同一个意图换种说法就识别不了规则层词典覆盖不足或分类层训练数据泛化不够用LLM批量扩充同义表达重新训练分类模型一句话里有两个意图只命中一个前两层被设计为单意图输出让低层只负责单意图拦截多意图请求主动放行到LLM层漏斗第三层流量占比越来越高前两层准确率或覆盖率在退化优先查规则词典是否有线上新词未收录再看标注回流是否中断分类模型置信度普遍很低训练数据分布和线上真实分布不对齐加强日志回流用线上数据重新采样训练集LLM输出大量unknown提示词约束不够或用户表达确实过于模糊把unknown结果加入分析看看是否有高频新意图需要新增单条请求耗时偶发飙高LLM层超时设置太长或没有熔断收紧LLM超时阈值配置熔断器降级到第二层4.2 意图漂移与数据分布变化线上意图分布不是一成不变的业务活动、节假日、产品改版都会导致意图分布剧烈变化。比如电商大促期间“优惠券计算”“满减规则”这类意图会突然暴增而平时占比很高的“退款申请”反而被稀释。如果漏斗前两层的词典和训练数据没有跟上这些新热点意图就会大量涌向LLM层导致成本和延迟突然抬升。我的做法是建立“意图分布周对比”监控把每层触达率、每个意图的请求占比按周做环比超过设定波动幅度就告警然后在漂移期之前准备好临时规则和专项训练数据。比如大促前两周就开始收集“优惠券”相关的种子表达提前扩充规则词典让漏斗前两层在流量高峰到来之前做好承接。这种事前准备比事后紧急调优要省太多力气。4.3 容易被忽略的安全与合规细节意图识别层往往是用户输入进入Agent系统的第一道门所以它天然承担着安全过滤的职责。我在实际项目里吃过亏刚开始只关注识别准确率忽略了对恶意输入的粗过滤结果有人在输入框里塞了一段prompt注入文本试图让Agent执行未授权的操作。从那以后意图识别服务在进入漏斗之前都会先过一道内容安全过滤检测文本长度、检测重复字符、匹配已知的恶意模式特征、识别疑似提示注入的句子结构。另外不要把所有用户输入都原封不动地塞进LLM的上下文里。意图识别只需要当前句子加有限的对话历史就够了超过一定长度的历史要裁剪涉及身份证号、手机号、地址等隐私信息要脱敏否则这些敏感内容会跟着上下文一起进入LLM的输入端带来数据合规风险。很多团队把精力花在识别准确率上却忽略了这一层数据安全设计容易被审计一票否决。4.4 调试面板把漏斗每一层都变成白盒最后分享一个我觉得价值最高的工程细节给漏斗做一个可视化调试面板。在这个面板里输入一个句子可以看到它走过了哪些层、每一层的命中候选是什么、置信度是多少、为什么放行或拦截、最终结果是什么。遇到bad case时一眼就能定位问题出在哪一层——是规则词典缺词分类模型置信度不够还是LLM提示词约束失效。这套面板的开发成本不高但对排查效率的提升是几何级的。它让意图识别漏斗真正变成了一个白盒系统而不是几个人围着日志文件猜来猜去。写在最后的实操体会这套分层漏斗我前前后后迭代了大半年最大的感受是它最值钱的地方不是省了多少钱、降了多少延迟而是让整个Agent的行为变得可预期、可调试、可演进。规则层给你确定性分类层给你泛化能力LLM层给你复杂场景的兜底三层各司其职每一层都可以独立优化和回归测试。根据我个人经验再分享一个小技巧漏斗上线之后不要急着追求“LLM流量占比越低越好”。前期让它多接一些请求没关系因为只有LLM处理过的case才包含高质量标注信号。先让它跑出数据再利用回流慢慢把流量压下来节奏才健康。如果你一上来就疯狂调规则去拦截请求会把很多本应暴露的问题掩盖掉后面会花更长的时间去还这笔技术债。
阅读完成 · 觉得有帮助?