意图识别混合架构实践:规则+小模型+LLM如何落地客服系统
我们接手的第一个电商客服意图识别项目,目标很朴素:把客服会话里的用户诉求自动归类,然后路由给对应处理流程。当时团队内部有一种声音,说直接用LLM就行,毕竟ChatGPT出来以后大家都在玩few-shot,感觉什么都能干。但实际上线之后很快就发现问题——大模型不是不好用,是贵、慢、还不稳定,高峰期并发一上来,既不经济也不可控。
后来我们反复调优,最终稳定下来的方案,是一套“规则+小模型+LLM”的三层混合架构。这个方案既不是炫技,也不是图省事,而是被成本和准确率倒逼出来的结果。这篇文章我会把整个架构的拆解思路、每一层的定位与实现、层与层之间的仲裁逻辑,以及线上运行时踩过的坑,原原本本写出来。不管你是做客服系统、做对话机器人,还是正在给传统软件加AI能力,这篇内容应该都能给你一些可落地的参考。
1. 三层混合架构的整体设计思路:为什么意图识别不能只靠大模型
1.1 低成本高并发是客服场景的硬约束
电商客服意图识别与普通文本分类最大的不同,是请求量和延迟要求都接近“基础设施”级别。大促期间一个客服机器人后端可能每秒要处理几千次消息,每个消息又要在一个会话周期内快速响应,否则用户感知到的就是“机器人反应很慢”。LLM在这种量级下非常吃力,不管是API成本还是推理延迟,都会成为瓶颈。
更关键的是,客服意图识别并不需要每个请求都用最强模型。大量用户消息非常固定,比如“我要退货”“改一下地址”“发我发票”,这类消息用正则在毫秒内就能搞定,用大模型反而是杀鸡用牛刀。架构设计的第一原则,就是不要让高成本能力处理低难度请求,而是要按难度分层路由。
1.2 规则准确率并不低:一个让人意外的对比数据
我见过不少团队一开始就跳过规则,直接用深度学习模型,理由是“规则维护麻烦”。但实际我们在项目初期做了一个对比测试:用2000条真实客服消息,分别用规则系统、小模型和大模型识别意图。结果规则系统在确定性意图上的准确率达到98%以上,小模型在常见意图上的F1值在91%左右,而大模型在单独评测时虽然效果好,但加上延迟和成本之后,性价比极为难看。
这让我意识到,规则不是“过时”的技术,而是整个架构里准确率最高、成本最低的一环。问题是它的覆盖范围窄,无法处理语义变化和未知表达,所以需要小模型和LLM去补足召回。三层架构的本质,是用“确定性优先+模型兜底”的思想,把准确、便宜、快三者尽可能同时做到。
1.3 三层架构的定位与关键分工
我在设计这套架构时,给每一层都定了一个非常明确的职责,互不越界:
- 规则层:只处理高确定性意图,匹配逻辑完全透明,准确率要无限接近100%,允许覆盖范围小。
- 小模型层:处理高频、模式相对固定但表达有变化的意图,通过分类模型给出带置信度的预测结果。
- LLM层:作为兜底回应复杂、复合、长尾或模糊的用户意图,牺牲部分成本换取更好的泛化能力。
这里有一个容易被忽视的点:三层之间不是简单的“从上到下走一遍”,而是每层都独立运行、并行计算,最后由一个仲裁模块决定最终结果。为什么要并行而不是串行?因为规则和小模型都能在几十毫秒内出结果,串行会增加整体延迟,而并行之后,LLM是唯一可能拖慢响应的大头,仲裁模块可以等小模型和规则出结果后先返回,如果某个请求LLM还没返回,就先走其他两层的结论,保证整体时效。
1.4 降级链路的核心理念:宁可错过,不可错杀
在设计层与层的承接关系时,我们坚持一个原则:某一层识别不出意图不要紧,可以由下一层来补。但某一层一旦错误识别,后面就很难纠正。所以规则层我们要求“宁可漏,不可错”,小模型层则设置较高的置信度阈值,低于阈值的全部交给LLM。
这套理念落地到系统里就变成了一个降级链路:规则层没命中,交给小模型;小模型置信度不足,交给LLM;如果LLM也判断不出来,最终会回落给“建议转人工”。整套链路在语义上是逐级泛化的,但每一级都保住了自己范围内的精确性。
2. 规则层:正则表达式与关键词系统的工程化落地
2.1 规则层到底能识别哪些意图:低成本高并发,覆盖高确定性场景
规则层适合处理的意图,基本都有相同特征:表达方式有限,关键词确定,或者有很明显的实体特征。比如退款、退货、换货、修改地址、申请发票、取消订单、催发货、物流查询,这些意图里的关键词和短语几乎是刚性的。
在实际业务里,我们还把一部分“操作类”意图纳入了规则层,比如用户发一个订单号、发一个手机号、发一串快递单号,这本身不是意图,但结合上下文它就是“我要查订单/查物流”的意图。这种能力用模型做容易过度泛化,用规则做反而精准。
2.2 规则引擎的工程架构:多模匹配、规则数据结构、性能优化
很多人谈到规则,脑子里就是一句简单的if关键词in message,但真正客服场景下的规则引擎远比这个复杂。首先是性能问题:如果系统有100个意图,每个意图有几十条规则,整体就是一个上千条正则的匹配器,单条用户消息逐条编译匹配,CPU开销会非常大。我们的做法是用Aho-Corasick多模匹配算法先做一遍关键词预筛,把所有规则里的核心关键词(比如“退”“发票”“物流”“地址”)构建成AC自动机,消息进来先用AC自动机扫一遍,命中了哪几个关键词,再只去匹配相关的少量正则。
这套设计把单条消息的匹配耗时从几毫秒降到了几十微秒,高峰期也能扛得住。第二个考虑是规则数据结构,我们用JSON配置规则,每个意图下面包含include、exclude、priority三部分:include是正向关键词或正则,exclude是排除词,priority用于处理“催发货”和“咨询发货时效”这类容易混的意图。exclude非常重要,比如用户说“我不想退货了”,如果规则只看“退货”关键词,就会误判成退货意图,但一旦包含“不”“取消”这类词,就应该排除掉。
2.3 到期日、订单号、物流单号:确定性场景的规则设计
我举一个具体的规则案例。用户消息是“麻烦帮我看看这个订单到了没,订单号是JD123456789”,规则层的处理逻辑是:先匹配“到了没”“订单号”等核心词,命中物流查询意图;接着用订单号的正则提取实体,交给下游服务去查物流。这里的核心正则大概是:
import re
pattern = re.compile(r'([A-Z]{0,4}\d{8,15})')
order_id = pattern.search(message)
# 排除掉类似手机号、日期等误提取
if order_id and not re.search(r'\d{11}', message):
extracted_id = order_id.group(1)
当然这只是示意,真正上线的规则要处理很多边界情况,比如用户漏了字母、订单号和手机号混在一起、消息里出现两个数字编号等等。我们在规则层做过一个统计,只要订单号规则设计得好,识别准确率非常高,而且规则层的预测完全可解释,出了问题可以瞬间定位到是哪个规则匹配错了。
2.4 规则无法覆盖的场景:需要小模型出场
规则层最大的问题是语言表达的多样性。用户可能说“我要退钱”“钱怎么退回来”“这个商品不想要了”,如果硬用关键词,前者落在退款意图,后两者可能一个都匹配不上。或者用户说“你们的物流也太慢了,都一周了还没送到”,这句话没有“催发货”里的“催”字,也没有“物流查询”里的“查”字,但它的意图非常明确是催发货。
这类表达有固定模式但充满变体的场景,就轮到小模型出场了。规则层解决的是“事情是什么就是什么”,小模型层解决的是“八种说法其实是一件事”。
3. 小模型层:文本分类模型从训练到上线的完整链路
3.1 选型:有监督文本分类是核心问题
小模型层本质就是一个有监督的短文本分类器。选型上我们对比过FastText、TextCNN和蒸馏后的BERT,最终线上用的是蒸馏后的BERT变体,因为客服消息短、上下文信息少,FastText虽然快但太依赖词面特征,面对同义改写很吃力;而完整BERT在服务化之后延迟在几十毫秒量级,对于客服场景完全可接受。
我们用的蒸馏模型参数量在30M-50M之间,int8量化后推理一条消息大概在10-20毫秒。这个量级已经足够覆盖大多数电商服务场景,如果再往上堆参数量,收益不大,成本却直线上升。
模型结构上,就是在Transformer编码器之上接一个多层感知机分类头,输出各个意图类别的概率分布。训练时的标签体系,需要和规则层、LLM层的输出保持统一,否则仲裁模块会处理得很痛苦。
3.2 数据如何来:标签体系设计、样本构造、人工标注
我一直认为,小模型层真正的工作量不在模型,而在标签体系和样本。我们按一线客服的分类习惯,把意图先划分为三层:一级类目(售后、售前、物流、支付、其他),二级类目(退款、换货、补差价、开发票、催发货……),三级类目是真正输入模型的标签,一般控制在30-50个左右。标签粒度如果太细,类别之间边界会模糊,小模型学不清楚;如果太粗,下游路由又不够精准。
样本来源主要有两个渠道:一是清洗历史客服会话记录,抽取出用户的首条消息或独立消息,按标签体系做人工标注;二是基于规则层积累的已知样本反向挖掘,比如规则命中“退款”“退货”的消息,可以抽出部分做人工复核后作为训练样本。这里有一个很重要的操作:不要直接用规则的结果作为小模型的标注数据。规则层有偏差,直接把偏差喂给模型,会让模型继承规则层的错误。
标注数据量方面,我们的实践经验是,每个意图至少1000条标注样本,冷启动阶段实在没有,可以先500条,但训练后要密切监控类别召回,低于阈值就补充样本。整体标注成本比较高,所以我们的标注平台会优先标那些规则层覆盖不到的样本,最大化利用标注预算。
3.3 训练细节:类别不均衡、标注噪声、阈值校准
短文本分类训练中最坑的三件事,我逐个说。第一是类别不均衡,电商场景里“催发货”和“开发票”的样本量能差几十倍,我们用过重采样、焦点损失和类别权重,最后的结论是焦点损失效果最稳,但前提是基础学习率要调低一档,否则容易训不收敛。
第二是标注噪声,客服会话里的用户消息经常缺字少词、有错别字、混合表情,标注员之间的一致性本身就是一个问题。我们每批数据会抽10%做双人标注,用Cohen's Kappa算一致性,如果Kappa低于0.7,这批数据要打回重新标。
第三是阈值校准。模型的预测概率不能直接用,因为softmax输出普遍过于自信。我们会在验证集上做一个阈值搜索,对每个意图找一个独立阈值,这个阈值的标准是:在保证精确率不低于95%的前提下,尽量提高召回。低于阈值的预测结果直接判为低置信度,送LLM兜底。
3.4 上线与服务化:ONNX部署、批处理
小模型上线我们用ONNX Runtime部署,因为它在CPU上的推理性能比直接用PyTorch好不少。当时测试的时候,一个会话里并发几十条消息,ONNX Runtime可以稳定压在30毫秒以内。服务化方面,我们封装成了一个独立的意图分类服务,输入是一句用户消息和可选的会话上下文,输出是各个意图TopK的置信度。
这个服务还支持批处理,收到一批消息后,不是逐条推理,而是把文本padding到同一个batch里一起过模型,我记得当时在GPU上的batch吞吐大约是单条的4-5倍。不过客服场景如果消息到达时间不均匀,批处理反而会增加延迟,所以要设置一个最大等待窗口,比如5毫秒,超过窗口就必须立即推理。
3.5 小模型的致命弱点:为什么它撑不起复杂场景
小模型虽然便宜又快,但它有两个致命弱点。第一是完全无法应对训练集里没见过的意图,长尾请求一来,它可能把置信度均摊到几个相近类别上,这时候阈值机制就起作用了——低置信度会被送去LLM。第二是它对否定和条件语义理解很弱,比如“如果发货了就取消退款申请,没发货就不用”,这种句子有先后条件,有语义转折,用分类模型很容易识别成普通的“取消退款”。
这类复杂请求,规则做了太武断,小模型做又能力不足,就只能靠LLM了。
4. LLM兜底层:从“大模型什么都能干”到“什么才需要大模型干”
4.1 哪些意图必须由LLM兜底
根据线上统计,会落到LLM的请求大概占总流量的10%-15%。具体来说,有四类典型请求必须由LLM处理:
- 多意图复合消息。比如“怎么退货顺便帮我开个发票”,这同时包含退货和发票两个意图,传统分类模型只能输出一个主类别,LLM可以拆解出多个意图并排序。
- 带条件的意图。比如“如果明天还没到货我就退款”,核心意图不是退款而是催发货+潜在退款意向,LLM能理解条件语义。
- 隐含意图。用户说“我买了三件衣服就只送来两件,你们怎么回事”,这里面隐含了少发货投诉和补发查询两个意图,单一分类很难抽出来。
- 低频长尾意图。比如咨询售后政策、询问优惠券叠加规则、要求补差价、投诉客服态度,这些样本少,专门训一个分类器成本太高。
4.2 Prompt与结构化输出
LLM层的核心工程是提示词设计。我们使用的不是开放式问答,而是强制结构化输出,否则下游没法解析。一套比较稳定的提示词模板大致是这样的:
你是一个电商客服意图识别引擎。请分析用户消息的意图。
输出要求:
1. 以JSON格式输出
2. 包含字段:intents(数组)、primary_intent(主意图)、entities(对象)、summary(一句话总结)
3. intents数组中每个元素包含name和confidence字段,confidence取值0到1
4. 如果消息意图不明确,primary_intent输出"UNKNOWN"
示例:
{"intents":[{"name":"REFUND","confidence":0.9}],"primary_intent":"REFUND","entities":{"order_id":"JD123"}}
实体提取也很重要,客服场景里的订单号、商品名、金额、日期、地址,都是高频实体。LLM抽取实体比规则法和小模型都强,但偶尔会漏,所以我们在提示词里专门加了一步“自查”,要求模型先抽取实体,再检查是否和原文完全匹配,如果不匹配就重新抽取,这个多一轮自我修正的步骤,确实能把实体抽取的准确率往上提几个点。
4.3 评测:提示词A/B测试
提示词不是写一次就能一劳永逸的。我们会定期从线上抽样500条消息,同时用当前线上提示词和候选提示词跑一遍,逐条对比输出,重点看三类错误:意图识别错误、实体抽取遗漏、JSON格式不合法。格式不合法在LLM输出里非常常见,很多模型在复杂指令下会输出多余的说明文字,所以我们的解析模块有容错,会尝试用正则把JSON片段抠出来,还抠不出来就只能判为UNKNOWN。
这里要提醒一下,评测的时候不要只看单轮结果,还要看意图分布是否偏移。我们曾遇到过提示词微调后整体准确率上升,但“退款”意图被大量误判的情况,因为新版提示词里增加了一个误导性示例,导致模型过度偏好退款类输出。所以评测指标里一定要加上同一批样本的混淆矩阵对比。
4.4 LLM的两大问题:延迟、幻觉、成本
LLM层的痛点非常直白。第一是延迟,大模型单次推理动辄1-3秒,这还不包括排队时间,所以LLM层绝不能阻塞主请求流程。我们的处理方式是:主流程先返回规则或小模型的结论,LLM作为一个并行任务在后台计算,如果发现小模型结果和LLM结果不一致,再把不一致的消息存入日志做离线分析。只有那些低置信度且无法靠规则兜底的请求,用户侧才会感知到等待。
第二是幻觉,模型会一本正经地编造实体或意图,比如把不存在的订单号抽出来。我们的对抗策略是在提示词里明确“只能输出原文中出现的实体”,并且在解析层做二次校验,实体值必须在原始消息字符串里能匹配到。
第三是成本,这个不多说,大家都有体感,降本的方法无非是减少调用量、用小模型替代简单请求、以及用缓存命中重复问题。我们在缓存上的优化效果非常明显,电商客服里重复表达的比例远超想象。
5. 三层之间的仲裁逻辑与线上避坑清单
5.1 优先级与置信度仲裁
仲裁模块是整个架构的大脑。它的输入是三个来源的输出,输出是最终意图。最粗的策略是“规则 > 小模型 > LLM”,但实际运行下来会有几个问题。
第一种情况是多意图合并。用户既催发货又要开发票,规则层命中了“开发票”,小模型输出“催发货”置信度0.85,LLM输出两个意图。仲裁逻辑不能直接取优先级高的规则结论,而需要把三个预测做一次合并,保留所有高置信度的意图。我们在意图定义里专门加入了“COMBINED_XX_AND_YY”的复合意图标签,把高频出现的组合固化下来,这类样本再回流到小模型的训练集里。
第二种情况是低置信度仲裁。小模型输出“退款”置信度只有0.55,LLM还没返回,这时候不能直接给“退款”结论。我们的策略是:如果置信度低于0.3,直接走人工;如果0.3-0.7之间,等LLM结果;如果上面0.7,可以直接返回。这个阈值可以按业务要求调节,退款这类敏感意图我们阈值会提高,物流咨询这类低风险意图阈值可以放低。
5.2 短文本的不确定性
客服消息天然很短,有的甚至只有一个词,比如“发票”或“物流”。这种短文本对三层的挑战都很大。规则层看到“发票”可以命中开发票意图,但如果用户发的是“不要发票”,规则就会误判,所以短文本必须结合上下文判断。
我们的做法是:在仲裁模块里设置一个上下文窗口,如果当前用户消息过短(比如少于5个字),会结合前两轮用户消息和客服回复一起送入小模型和LLM。规则层则不直接处理这种短消息,宁可多给小模型一点机会。
上下文接入之后,小模型的准确率有了明显提升,因为客服对话里用户经常会回答客服的追问,单看当前消息完全不知道意图。比如客服问“您的订单号是多少?”用户回复“JD123456”,这时候应该识别为“提供订单信息”,而不是任何业务意图。
5.3 灰度效果与前置验证
这套三层架构并不是一上线就完整的,它经历了几个阶段的迭代。最开始只有规则层,识别率大概在50%左右;后来加入了小模型,覆盖率提升到85%;最后加入LLM兜底,整体意图识别准确率在评测集上能达到97%左右,在线转人工率下降了约三成。
这里要说一个有价值的经验:任何时候都不能直接全量切换。规则层需要小模型补足的时候,我们是先让模型在“影子模式”下运行一周,也就是模型预测结果只写入日志,不影响线上路由。一周后统计影子结果和规则结果的差异,人工抽检差异样本,确认模型的错误率可接受后再灰度切量。加了LLM之后也是一样,并且LLM的灰度期更长,因为它的不确定性更高。
5.4 踩坑清单:线上运行半年后总结的五个高频问题
最后这条避坑清单,是我觉得比上面所有代码和配置都更有价值的部分,每一条都是真金白银买来的经验。
一、规则层的词表会慢慢腐烂。业务人员会不断新增商品、优惠券名、活动名,如果规则词表没有定期更新机制,召回会越来越差。我们的做法是每周自动跑一批新对话,检测规则未命中的高频词,人工审核后加入词表。
二、小模型对否定词非常敏感。训练数据里如果“不要”“不需要”这类否定样本太少,模型会把“我不要发票”识别成“开发票”。解决方法是构造一批“否定+原意图词”的负样本,并且给否定词加权重。
三、LLM返回的JSON永远比你想象的更脏。模型偶尔会在JSON外面包一层Markdown代码块标记,偶尔会输出中文键名,解析层必须做兼容。我见过一个团队就因为这个,LLM识别全链路成功率一直卡在90%上不去。
四、标签体系不能完全照抄客服分类法。客服人员分得细,但模型学不了那么细的边界,尤其是那些靠人工经验和语境判断的类别。标签合并和分裂一定要基于样本实测结果,而不是基于直觉。
五、三层架构要有一个统一的全链路评测集。我们维护了3000条全手工标注的评测数据,每次任意一层改动,都必须全量跑一遍这三层,观察整体指标变化。很多时候某一层指标涨了,整体却跌了,原因就是层与层之间的输出分布变了。
这套架构跑下来,我最大的感受是,技术选型永远要服务于业务约束。LLM再聪明,也替代不了规则层的廉价精确;小模型再快,也理解不了条件句;而如果只有规则,客服意图识别注定只能覆盖一小半场景。所以我一直认为,混合架构不是过渡方案,而是一种长期稳定的工程形态。如果你正在做类似的项目,建议先从规则层建起,再逐层叠加,而不是一上来就上大模型。每一步都能带来可量化的业务收益,每一步也都会让你对现有系统有更深的理解。
更多推荐



所有评论(0)