ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

工业级Agent意图识别:四层漏斗架构设计与落地实践

工业级Agent意图识别:四层漏斗架构设计与落地实践 1. 从一次线上事故说起为什么单层意图识别扛不住真实流量去年下半年我接手了一个对话式Agent系统的重构上线第一周就出了个不大不小的事故。用户问“帮我查一下上个月电费”系统把它路由到了“闲聊”分支回了一句“好的呢请问还有什么可以帮您”。用户当场就炸了连续发了三条“你是不是傻”。事后复盘问题出在意图分类器上——单层模型把“查电费”这个动作意图和“上个月”这个时间修饰混在了一起置信度只有0.41低于阈值就被兜底逻辑吞掉了。这件事让我彻底想明白一个道理单层意图识别在真实业务里就是个伪命题。真实用户的query从来不是教科书里的干净样本它带着口语、省略、多意图嵌套、领域漂移甚至还有情绪。你用一个模型去扛所有情况要么阈值调高导致大量误拒要么阈值调低导致路由错乱。工业级Agent要的不是“一个更准的模型”而是一套分层漏斗式的意图识别架构——让不同复杂度、不同确定性的query在不同层级被处理把算力花在刀刃上把准确率堆在关键路径上。这篇内容我想把整套分层漏斗的设计思路、每一层的职责边界、层与层之间的交接协议、以及我在实际压测中踩过的坑完整地摊开讲一遍。适合正在做Agent路由、对话系统意图分发、或者多技能编排的同行参考。不管你是刚接触Agent开发的新手还是已经在调LLM路由的老手应该都能从里面找到能直接抄作业的部分。2. 分层漏斗到底分几层职责边界与流量分配2.1 为什么是四层而不是三层或五层先说结论我最终落地的是四层结构规则层、轻量分类层、LLM语义层、兜底澄清层。这个层数不是拍脑袋定的是被流量分布和成本曲线逼出来的。我统计过我们系统连续两周的query分布大约35%的query可以被规则直接命中比如“查话费”“退订”“人工”这类高频短指令40%的query用轻量分类模型就能搞定比如“我想改一下套餐”“流量用超了怎么办”剩下25%才是真正需要LLM深度理解的复杂query多意图、指代消解、隐含条件。如果全部走LLM单次路由成本会翻四倍以上延迟从80ms飙到600ms如果只做两层复杂query的准确率会掉到70%以下。三层的问题在于规则层和LLM层之间跨度太大中间那40%的“中等复杂度”query没有合适的处理层要么被规则误伤要么被LLM过度处理。五层的问题在于层间交接开销太大每多一层就多一次序列化和上下文传递而且维护成本指数上升。四层是成本、延迟、准确率三者的帕累托最优点。层级处理占比平均延迟单次成本准确率目标规则层35%5ms接近099%轻量分类层40%30-50ms极低92%LLM语义层25%400-800ms较高95%兜底澄清层触发式200ms中不追求准确率追求不误判2.2 每一层的核心职责不是“分类”而是“过滤”很多人设计分层漏斗时容易犯一个错把每一层都当成一个独立的分类器每层都输出一个完整意图。这是错的。分层漏斗的本质是逐层缩小候选空间每一层的核心职责是“过滤掉不属于自己处理范围的query”而不是“给出最终答案”。规则层的职责是高精度拦截它只处理那些模式极其固定、几乎不可能有歧义的query。命中就出结果不命中就往下走绝不勉强。轻量分类层的职责是高召回粗分它把query映射到几个大的一级意图域比如“查询类”“办理类”“投诉类”“闲聊类”允许一定误差因为后面还有LLM层兜着。LLM语义层的职责是精细消歧处理指代、省略、多意图拆分、领域术语映射。兜底澄清层的职责是安全网当所有层都无法给出高置信度结果时主动向用户发起澄清而不是瞎猜。这个职责划分带来的一个关键设计原则是每层的输出不是“意图标签”而是“意图标签置信度是否继续下传”的三元组。只有置信度超过该层阈值且明确标记“终止”的query才会被拦截否则一律下传。这样做的目的是防止某一层“过度自信”导致误拦截。2.3 层间交接的协议设计别让上下文在传递中丢失层与层之间传递的不是原始query字符串而是一个结构化的路由上下文对象。这个对象里至少包含原始query、归一化后的query、已命中的规则ID、轻量分类的top3意图及分数、对话历史摘要、用户画像标签、当前会话状态。我踩过的一个坑是早期版本里轻量分类层只把top1意图传给LLM层结果LLM层丢失了“第二候选”的信息导致一些边界case无法纠正。后来改成传top3并且把每层的分数都带上LLM层在做最终判断时可以参考前序层的“犹豫程度”。比如轻量分类层给出“查询类0.52、办理类0.48”LLM层看到这个接近的分数就会知道这里存在歧义需要更仔细地分析。注意层间上下文对象要做字段级版本控制。我们有一次升级轻量分类层新增了一个字段但LLM层的解析代码没同步更新导致线上大量query在交接时抛异常。后来强制要求所有层间对象的schema变更必须走版本号并且做向后兼容。3. 规则层被低估的高精度拦截器3.1 规则不是正则堆砌而是模式树一提到规则层很多人脑子里浮现的就是一堆正则表达式。我早期也是这么干的写了三百多条正则维护起来想死。后来改成了模式树结构根节点是意图域中间节点是关键词组合叶子节点是具体的动作模板。举个例子“查话费”这个意图模式树是这样的根节点“查询类”下面挂“费用查询”子节点再下面挂“话费”“余额”“欠费”三个关键词叶子。匹配时先走树的分支剪枝只有关键词命中才进入下一层判断。这样比逐条正则匹配快得多而且新增意图只需要在树上加节点不用改匹配引擎。规则层的命中条件我设了三条硬性要求query长度小于15个字、不包含指代词这个、那个、它、不包含多意图连接词并且、还有、顺便。三条全满足才走规则匹配否则直接下传。这个门槛看起来严但实测下来规则层依然能吃掉35%的流量因为高频短指令天然就符合这些条件。3.2 规则的优先级与冲突消解规则之间会打架。比如“我要退订流量包”既命中“退订”规则又命中“流量包”规则。我的处理方式是给每条规则打优先级分数分数由“历史命中准确率 × 业务权重”计算得出。退订类规则的业务权重高因为涉及用户权益所以优先级高于查询类。冲突消解的策略是取优先级最高的规则但如果两条规则分数差距小于0.1则都不命中直接下传。这个“犹豫即下传”的原则贯穿整个漏斗是我认为最重要的设计哲学之一。宁可让LLM层多处理一点也不要在规则层做出模棱两可的判断。3.3 规则层的冷启动与热更新规则层最大的优势是可解释、可干预。线上出问题时运营同学可以直接看到是哪条规则命中了改起来也快。但规则层最大的坑是冷启动时规则覆盖不全以及业务变化时规则过期。我的做法是规则层支持热更新配置存在独立的配置中心修改后秒级生效不需要重启服务。同时建了一个规则命中监控看板每天统计每条规则的命中次数、后续转化率、以及被LLM层“纠正”的比例。如果某条规则的纠正率超过15%就自动告警提示这条规则可能过时了。实操心得规则层不要追求大而全要追求“准而稳”。我见过太多团队把规则层写成了一本百科全书结果维护成本比LLM还高。规则层只处理那些“闭着眼睛都不会错”的query剩下的交给后面。4. 轻量分类层小模型的大作用4.1 为什么不用BERT-base而用蒸馏小模型轻量分类层的选型我纠结了很久。BERT-base准确率确实好但推理延迟在CPU上要200ms以上GPU上也要50ms而且并发一上来显存就爆。最后我选的是6层蒸馏模型参数量只有BERT-base的40%在GPU上单次推理12msCPU上35ms准确率相比BERT-base只掉了3个百分点。这个取舍的逻辑是轻量分类层的目标不是“最准”而是“在可接受的准确率下把大部分简单query快速分流”。它分错了没关系LLM层会兜住。但如果它太慢整个漏斗的延迟优势就没了。实测下来蒸馏模型top3输出阈值下传的策略让轻量层的有效准确率即“高置信度输出中正确的比例”达到了94%以上。4.2 训练数据的构造负样本比正样本重要轻量分类层的训练数据构造有个反直觉的点负样本的质量比正样本更重要。因为这一层要处理的是“中等复杂度”query它最大的风险不是分不对而是“把不该自己处理的query强行分类”。我的做法是正样本用业务日志里的真实query按一级意图域打标负样本专门构造“边界query”——比如那些应该下传给LLM层的多意图query、带指代的query、领域术语密集的query。负样本的标签是“下传”训练目标是让模型学会说“这个我处理不了”。这个思路让轻量层的“下传准确率”从78%提升到了91%。具体来说就是模型在面对复杂query时top1意图的置信度会明显偏低从而触发下传条件。4.3 阈值设定动态阈值比固定阈值靠谱固定阈值0.70.8我试过都不行。因为不同意图域的分数分布不一样“查询类”的分数普遍偏高“投诉类”的分数普遍偏低。用同一个阈值要么查询类误拦截要么投诉类全下传。后来改成了按意图域分别设定阈值并且阈值是动态调整的每天根据前一天的“拦截后用户满意度”和“LLM层纠正率”自动微调。比如某个意图域的LLM纠正率上升了说明轻量层在这个域上判断不准阈值就自动调高让更多query下传。意图域初始阈值调整依据调整幅度查询类0.72LLM纠正率±0.03/天办理类0.78用户二次确认率±0.02/天投诉类0.65人工介入率±0.04/天闲聊类0.85会话终止率±0.01/天5. LLM语义层把大模型用在刀刃上5.1 Prompt设计不是让LLM分类而是让LLM做结构化抽取LLM层最容易犯的错是直接问模型“这个query属于哪个意图”。这样做的问题是模型的输出不稳定而且无法处理多意图。我的做法是让LLM做结构化抽取从query中抽取“动作”“对象”“条件”“指代消解结果”四个要素然后由后置的规则引擎根据抽取结果映射到意图。举个例子query“帮我把上个月那个超出的流量包退掉”。LLM抽取结果是动作退订对象流量包条件上个月超出部分指代消解“那个”指代上文中提到的“流量包”。后置规则引擎拿到这四个要素就能精确路由到“退订-流量包-历史账单”这个意图节点。这个设计的优势是LLM的输出是可验证的。如果抽取结果不完整或矛盾后置引擎可以拒绝路由触发澄清。而且抽取结果可以缓存相同模式的query不用重复调用LLM。5.2 少样本示例的选择要覆盖“难例”而不是“常见例”Few-shot示例的选择直接决定LLM层的表现。我见过很多团队放的都是“查话费”“改套餐”这种简单例子结果LLM在遇到复杂query时完全不知道该怎么处理。我的做法是示例集里至少60%是难例——多意图嵌套的、带指代的、有省略的、领域术语密集的。比如“我昨天那个投诉怎么还没处理顺便帮我看下话费”。这个query包含两个意图投诉进度查询话费查询示例里就要展示如何拆分。另外示例的顺序也有讲究。我把最难的例子放在最后因为LLM对末尾示例的注意力权重更高。实测下来这个细节让复杂query的抽取准确率提升了4个百分点。5.3 输出解析的容错LLM会胡说你得接得住LLM层的输出必须做严格的结构化校验。我定义了JSON schema要求LLM必须按格式输出任何字段缺失或类型错误都视为解析失败触发下传或澄清。但LLM有时候会“创造性”地输出一些schema里没有的字段或者把枚举值写错。我的处理是宽容解析严格校验。解析时允许额外字段存在但校验时只认白名单里的字段和枚举值。如果关键字段动作、对象缺失直接判定为解析失败。注意LLM层的超时时间要设得比平均延迟高但比用户忍耐阈值低。我设的是1.2秒超过就降级到兜底澄清层。不要为了等LLM而让用户干等体验比准确率更重要。6. 兜底澄清层不猜直接问6.1 什么情况下触发澄清兜底澄清层的触发条件有三类所有层都无法给出高置信度结果、层间结果冲突且无法消解、LLM层解析失败或超时。触发后不是直接说“我不明白”而是根据已有的部分信息生成一个有针对性的澄清问题。比如用户说“那个东西帮我弄一下”规则层和轻量层都无法处理LLM层抽取出的动作是“弄”对象缺失。这时候澄清层会问“您是想办理还是查询呢可以具体说一下吗”而不是笼统地说“请重新描述”。6.2 澄清问题的生成策略澄清问题的生成我用了模板槽位填充的方式。模板库覆盖了常见的缺失槽位组合缺动作、缺对象、缺条件、多意图冲突每个模板有若干变体避免重复问同一句话让用户烦躁。如果连续两次澄清都无法确定意图第三次就转人工。这个兜底策略很重要因为有些query就是机器处理不了的硬撑只会让用户更生气。6.3 澄清层的“学习”机制每次澄清后用户的回复会被记录下来作为难例样本回流到轻量分类层和LLM层的训练集里。这样澄清层不只是“兜底”还是整个漏斗的数据飞轮入口。我统计过上线三个月后澄清触发率从最初的12%降到了5.8%就是因为大量难例被前序层学会了。7. 压测与调优那些只有跑起来才知道的事7.1 并发下的层间背压分层漏斗在低并发下表现很好但一上压测就暴露问题LLM层是瓶颈当大量query同时下传到LLM层时请求排队延迟飙升进而导致上游超时重试形成雪崩。我的解决方案是层间背压降级给LLM层设一个并发上限超过上限的query直接走快速降级路径——用轻量层的top1结果一个保守的置信度折扣先给出一个“可能正确”的响应同时异步调用LLM做二次确认如果LLM结果不同再通过会话消息纠正。这个设计有点激进但实测下来用户感知很好大部分情况下快速降级的结果是对的即使错了纠正消息也能及时到达。7.2 缓存策略哪些能缓存哪些不能规则层的匹配结果可以全量缓存因为规则是确定性的。轻量分类层的输出可以按query归一化后缓存命中率大概30%。LLM层的输出不能直接缓存因为同一个query在不同对话历史下意图可能不同。但LLM层的结构化抽取结果可以按“query对话历史摘要”做缓存命中率大概15%。缓存失效策略我用的是LRUTTLTTL设的是24小时因为业务规则和用户画像每天都会变。7.3 监控指标别只看准确率分层漏斗的监控不能只看端到端准确率要分层看。我建了一套指标体系每层的拦截量、下传量、拦截准确率、下传后LLM纠正率、层间交接耗时、澄清触发率、澄清后解决率。其中最重要的指标是**“LLM纠正率”**——即轻量层拦截后LLM层如果也处理了同一个query给出的结果与轻量层不一致的比例。这个指标直接反映轻量层的“过度自信”程度。我设的告警线是10%超过就说明轻量层需要重新训练或调阈值。监控指标正常范围告警阈值处理动作规则层拦截准确率99%97%检查规则冲突轻量层LLM纠正率8%10%调高阈值或重训LLM层解析失败率3%5%检查prompt和schema澄清触发率8%12%分析难例分布层间交接P99耗时20ms50ms检查序列化开销8. 几个反直觉的经验教科书不会告诉你的第一个反直觉的点规则层越“笨”越好。我见过团队把规则层写得极其复杂支持模糊匹配、同义词扩展、甚至简单的语义相似度。结果规则层变得不可解释、不可维护而且经常误拦截。后来我强制要求规则层只做精确匹配和有限的关键词组合反而效果更好。规则层的价值在于“确定性”不在于“智能”。第二个反直觉的点LLM层不是越强越好。我们试过用更大的模型准确率确实提升了2个百分点但延迟翻倍成本翻三倍。而且大模型在小样本场景下容易“过度思考”把简单query复杂化。最后我们用的是中等规模的模型配合精心设计的prompt和示例性价比最高。第三个反直觉的点澄清不是失败是数据采集。早期我把澄清触发率当成负面指标拼命想降低它。后来想通了每一次澄清都是一次高质量的标注——用户亲口告诉你他想要什么。这些数据回流后整个漏斗会越来越准。现在我甚至会主动在低置信度时触发澄清而不是硬猜。第四个反直觉的点层数不是越多越好但层间的“犹豫传递”越多越好。每一层都要把自己的“不确定”明确传递给下一层而不是强行给出一个确定答案。这种“犹豫传递”机制让LLM层能够感知到前序层的困难从而调整处理策略。9. 落地清单从零搭一套分层漏斗的最小步骤如果你现在要从零开始搭一套分层漏斗我建议按这个顺序来先做日志分析统计query的长度分布、意图分布、高频模式。这一步决定了你的规则层能覆盖多少流量。搭规则层只做精确匹配和简单关键词组合优先级冲突用“犹豫即下传”原则处理。训轻量分类层用蒸馏小模型重点构造负样本边界query输出top3置信度。接LLM层prompt做结构化抽取而非直接分类示例集里难例占60%以上。建兜底澄清层模板槽位填充连续两次失败转人工。压测重点测LLM层的并发瓶颈和层间背压。建分层监控重点看LLM纠正率和澄清触发率。建数据飞轮把澄清记录和LLM纠正记录回流到训练集。这套东西我从零搭到稳定运行花了大概六周其中前两周全花在日志分析和规则层上。很多人会跳过第一步直接上LLM结果就是规则层覆盖不全、轻量层训练数据不对、LLM层prompt没针对性整个漏斗效率极低。最后分享一个我在实际调优中的小技巧给每一层加一个“影子模式”。新版本上线时不直接替换旧版本而是让新旧版本同时跑对比输出差异。差异大的query自动记录下来人工review后再决定是否切换。这个机制帮我们避免了好几次线上事故尤其是轻量层模型更新时影子模式能提前发现“过度自信”的问题。
返回列表