电商客服意图识别实战:规则+小模型+LLM三层混合架构详解
聊意图识别之前,先讲个真实场景。我接手过一个电商客服项目,日均消息量在十万级,用户进来第一句话大概率是“在吗”“发货没”“怎么退”“有优惠吗”——就这几板斧。一开始团队拍脑袋想直接用大模型,说效果好、泛化强,结果一算账:十万日活消息,每轮调用最快也要三百毫秒,单条成本虽然看着不贵,但叠加并发高峰和上下文重复处理,一个月账单直接超出预期。更麻烦的是延迟,用户问一句“发货没”,等两秒才弹答案,情绪马上就上来了。后来我们换了一套思路,把意图识别拆成三层:规则层打底、小模型做主路、大模型兜底,也就是常说的“规则+小模型+LLM三层混合架构”,效果立竿见影——P99延迟从一千二降到两百毫秒以内,单条调用成本降了九成以上,准确率还稳中有升。这篇文章就把这套架构从设计到落地的完整过程盘一遍,重点讲清楚每层该放什么、不该放什么,以及那些不踩一遍根本不知道的坑。
这套方案适合谁?如果你在做客服机器人、工单分派、私域运营、甚至任何涉及文本意图分类的场景,只要消息量大、意图种类多、预算有限,都可以直接参考这套思路。如果你只是好奇大模型怎么用在实际业务里,这篇文章也能回答一个核心问题:大模型不是万能的,也不是唯一解,它只是拼图里的一块。
1. 为什么是三层,而不是端到端一个大模型
1.1 单一大模型在业务场景里的三个真实问题
很多人一听到“意图识别”,第一反应就是“这不是LLM最擅长的事吗”。确实,大模型在理解复杂语义、处理口语化表达上的能力,规则和小模型都追不上。但放到真实的电商客服场景里,只靠大模型会撞上三堵墙。
第一堵墙是延迟。电商客服对响应速度的要求其实非常苛刻,用户发完消息,超过1秒没动静,很多人就会重复发或者直接转人工。而大模型生成式响应天生是慢的,即使只是做一次意图分类,带着整段历史上下文去调一次接口,往返时间也在几百毫秒到一两秒之间。当并发量一起来,排队等待更会让延迟不可控。
第二堵墙是成本。别只看单次调用的token价格,真实业务里要跑通一条完整的对话链路,往往不止调一次模型。意图识别要调一次,识别完还要触发后续的回答生成,又要调一次。一个十万级日活的场景,按每条消息两三次调用算,一个月下来是一笔相当可观的账单。而且随着业务量增长,这个数字还会线性往上走。
第三堵墙是稳定性。大模型有一个很要命的问题:不稳定。同样的输入,上午可能返回“退款”,下午可能返回“退货退款”,两次都算对了,但格式不一样,下游逻辑处理起来就麻烦。更不用说幻觉问题——模型把语义理解偏了,会得出一个逻辑自洽但完全错误的分类结果,这种错误在客服场景里往往会造成用户投诉。
1.2 三类技术的边界条件对比
与其争论“哪个模型最强”,不如老老实实承认:规则、小模型、大模型各有自己的舒适区,根本不存在谁替代谁的问题。我习惯用一个比喻:规则层是值班保安,小模型层是前台接待,大模型是店长。日常问题保安和前台就处理了,只有搞不定的疑难杂症才需要店长上。
- 规则层:对确定性强的表达最擅长。“发货没”“到哪了”“物流”这些词一出现,基本连猜都不用猜。适合做第一道过滤器,秒回且零成本。
- 小模型层:能处理一定程度的语义变化,但只擅长“见过”的表达。通过意图分类或序列标注模型,把用户输入映射到一个固定类别集合上。速度快、成本低、稳定可控。
- 大模型层:负责复杂、模糊、多轮关联的语义理解。适合做兜底或深层理解,比如用户绕了一大圈其实是想要补偿,这种“潜台词”小模型很难学会。
三层架构的核心逻辑不是技术炫技,而是用合适的工具处理合适的问题,把最贵的资源留给最少数量的请求。说白了就是让绝大多数简单问题在低层就结束,只有少数复杂问题才逐层上报到最贵的模型那里。
1.3 三层架构的业务价值不只是省钱
算完账你会发现,三层架构最大的价值其实不是省钱,而是让系统的延迟、成本、准确率变得可预期。规则层是确定性的,小模型层是概率性的但可控,大模型只负责处置那批“必须上强度”的case。这样每一层的资源消耗都是可估算的,不会出现某个月账单突然暴涨的情况。
另外还有一个隐藏好处:可解释性。如果哪类意图识别效果不好,你可以直接从三层里定位问题。规则漏接了,就补规则;小模型分错了,就加样本;大模型抽风了,就调prompt或者回流badcase。每一层都有针对性的优化方案,而不是面对一个黑盒干瞪眼。
2. 规则层:最廉价也最容易被低估的第一道防线
2.1 规则层到底放什么内容
规则层的定位是处理高置信度、低语义复杂度、高频出现的意图。它不是用来覆盖全量场景的,而是用来做“一刀切”的,切得越准越好。放得过多,规则之间会互相打架;放得过少,又起不到分流作用。我一般按三个标准来挑选放进规则层的意图:频次高、表达模式固定、误判代价低。
拿电商客服场景举例,几个典型的规则层选手:
- 物流查询:命中“发货”“物流”“快递”“到哪了”“多久到”等,直接判定为物流意图。
- 退换货:命中“退货”“换货”“退款”“七天无理由”等关键词组合。
- 开发票:命中“发票”“开票”“抬头”等。
- 人工客服:命中“人工”“转人工”“真人”“客服”等,直接转接,不废话。
这里面有个小技巧:不要把单个词作为触发条件,要尽量用短语或者词组合。比如单看“退”字,它可能是“退货”也可能是“不退”;但“退货”两个字一起出现,基本没跑了。规则层的命中门槛设得高一些,宁可不命中交给下层,也不要错误命中把用户带跑偏。
2.2 规则表达的三种写法
实际工程里,规则层的实现一般有三种写法,从简到繁都有:
第一是关键词匹配。最直接,遍历一段文本看是否包含某个词或短语。实践中常用的是把关键词和同义词扩展成数组,比如“快递”“快件”“物流”“包裹”“到哪了”,谁先命中谁赢。实现成本最低,但需要配合词序和上下文判断,不然容易误伤。
第二是正则表达式。适合匹配带结构、带模式的表达,比如“订单号:123456”或“单号是ABC123”这种。正则的优点是精准,缺点是写起来费劲、容易漏,维护也是噩梦。我一般只在需要匹配强结构化信息时才用,比如订单号、手机号、地址。
第三是布尔逻辑组合。用AND/OR/NOT组合关键词,比如“退款 AND NOT 退款政策”这类过滤逻辑。结合否定词的排除,能显著降低误判率。实践中我会在判定“退款”意图时排除“退款政策”“退款到账时间”这种咨询类表达,因为它们虽然包含关键词,但想要的不是退款操作而是政策说明。
2.3 规则层的三个致命误区
规则层看着门槛低,其实处处是坑。
第一个坑是规则爆炸。今天加一个关键词,明天加一个同义词,规则表越滚越长,最后自己都忘了哪条规则在生效。我的建议是每条规则必须带生效时间和添加理由,并且定期用线上日志做复盘,把半年以上零命中的规则清理掉。
第二个坑是误判。规则层是硬匹配,一旦触发就跳过后续所有环节,如果规则太宽泛,很容易把用户导向错误的流程。比如用户发一句“你们的发货速度怎么这么慢”,如果只看到“发货”两个字就命中了物流查询,那就完全错了——用户显然在抱怨而不是查询。这就是为什么规则层必须设置高门槛,关键词至少要组合到“表达确定性很强”才让过。
第三个坑是无法自学习。规则是人写的,意味着人想不到的表达它永远覆盖不到。同一个意思,十个用户有十种说法,即使调了半天规则,覆盖率也上不去。这也是为什么必须有下一层小模型来兜住,规则层做的是“稳准狠”的清除工作,不是大而全的覆盖。
3. 小模型层:整个架构的性价比核心
3.1 为什么用小型分类模型而不是继续堆规则
当你发现规则层把最简单的那批意图过滤掉之后,剩下的流量依然庞大,而且表达方式五花八门,规则怎么也覆盖不完。这时候就是小模型登场的时机。
我在项目里用的方案是意图分类模型,输入一段用户文本,输出预定义的意图标签。和规则层相比,它最大的优势是能泛化:同一个意思换十种说法,只要训练数据里见过类似表达,模型大概率能把它归到正确的类别里。和LLM相比,它的优势是快、便宜、稳定:固定模型结构和参数,输入输出都是定长的,推理延迟可以压到几十毫秒,单次调用的成本约等于零,而且是完全可控的。
小模型的定位不是替代规则层,而是填补规则层覆盖不到的中间地带。规则层像是精确制导炸弹,小模型则是覆盖面积更广的区域火力,两者互为补充。
3.2 模型选型:从FastText到BERT变体
选型大概率是大家最关心的部分。直接说结论:如果意图数量少(比如十个以内)、句子结构简单,用FastText性价比最高,训练快、部署轻、CPU上就能跑得飞快。如果意图数量多(几十个)、句子表达复杂,建议用BERT-small或DistilBERT这类蒸馏过的预训练模型做微调,准确率能比FastText高一个数量级。
我实际用的是BERT-small中文预训练模型做微调,隐藏层只有6层,参数量大概是BERT-base的一半不到。在电商客服这个场景里,它的准确率已经能到93%以上,推理延迟在CPU上用ONNX Runtime跑大概30毫秒,完全够用。
给你一份比较直观的模型选型参考:
| 模型 | 参数量 | 推理延迟(CPU) | 准确率(相对) | 适用场景 |
|---|---|---|---|---|
| FastText | ~百万级 | 5ms以内 | 基准 | 意图少、句子短、资源极度受限 |
| TextCNN | ~千万级 | 10ms左右 | 比FastText高5%-10% | 中等规模意图分类,部署简单 |
| BERT-small | ~千万级 | 30ms左右 | 比TextCNN高3%-5% | 意图多、语义复杂、需要泛化 |
| BERT-base | ~亿级 | 100ms+ | 比BERT-small高1%-2% | 对准确率要求极高,不差钱 |
注意,准确率的提升是边际递减的。从FastText到BERT-small,准确率提升明显;从BERT-small到BERT-base,提升就非常有限了,但延迟和显存开销涨上去好几倍。在电商客服这个场景里,真的没必要上BERT-base,性价比最优解是BERT-small这个级别。
3.3 训练数据的坑:从标注到清洗
小模型的效果七成靠数据,三成靠调参。而数据处理这一步,恰恰是最多团队翻车的地方。
首先是标注标准要统一。多人标注同一个句子,可能有人标“退货”,有人标“退款”,还有人觉得是“售后”。我的做法是:写一份标注规范文档,把容易混淆的意图边界讲清楚,并定期做标注一致性抽检,不一致率超过5%就打回重标。这一步看起来繁琐,但能省掉后面无数麻烦。
其次是类别不平衡问题。电商场景里,“物流”意图的数量可能是“投诉”意图的几十倍。如果不做处理,模型会学成一个“只会认物流”的偏科生。我的处理方式是:先按原始比例训练一版看基线,然后用欠采样或过采样把每个类别的样本量拉平再训练,最后选择在测试集上表现更好的那版。另外,对样本量特别少的意图,可以借用LLM做数据增强——让大模型基于现有的少量标注样本改写同义表达,生成更多训练数据。
再一个坑是数据泄漏。做训练集和测试集切分时,一定要按用户而不是按句子切分。同一个用户的几十条消息,如果一部分进了训练集、一部分进了测试集,模型其实是变相“见过”了测试数据,跑出来的准确率会虚高。切分的时候要保证同一用户的所有句子里,要么全在训练集,要么全在测试集。
3.4 模型训练的工程细节
训练流程其实很常规,但有几个细节值得单独说。
第一是文本预处理要克制。很多人习惯上来就做去停用词、繁简转换、标点清洗,但BERT这类预训练模型本身对噪声有很强的鲁棒性,过度清洗反而会丢掉上下文的语义线索。我实际只做两件事:把全角字符转成半角,把URL和订单号这种纯结构信息替换成占位符,比如“订单号123456”变成“订单号[MASK]”。这样模型学的是“订单号”这个词本身的语义,而不被具体的数字干扰。
第二是类别数量控制在20个以下。小模型的分类能力是有限的,并不是像大模型那样什么都能做。如果你发现意图类别超过30个,互相之间还有交叉,就要考虑是不是把意图体系设计得太细了——比如“不喜欢”和“不合适”在业务上也许根本不需要区分成两个意图。把意图合并到业务动作可区别的粒度,模型压力小,效果也更好。
第三是置信度阈值必须做校准。分类模型对每个意图都会输出一个概率值,你不可能百分之百信任它。我一般设一个0.6的置信度线:大于这个值就采用模型结果,小于这个值就交给下一层。阈值不是拍脑袋定的,是对验证集做概率分布分析后选出来的。原则很简单:宁可让模型说“不确定”,也好过它强行分一个错的,把用户带进错误流程,后面要花十倍的精力来挽回。
4. LLM层:兜底复杂场景的“终审法官”
4.1 LLM在所有层里到底负责什么
LLM层的定位不是取代规则和小模型,而是处理那些前两层搞不定的长尾请求。这些请求的特点是:表达绕、隐含意图、多轮关联、需要一定程度的推理。典型场景比如:
- “我上周买的那个蓝色的耳机,当时有活动送的赠品到现在还没收到,我想问问怎么回事”——这里既有“物流”又带着“投诉”性质,小模型大概率会分错。
- “你们家东西质量也太差了吧,我才用两天就坏了,你们自己说怎么办”——这句话没有一个明确的关键词,但意图是“售后投诉”加“补偿倾向”。
- “我看直播间里说下单备注会送赠品,我怎么没收到啊”——这属于“优惠/赠品咨询”,但表达方式和常规咨询差异很大。
这类消息占比通常在10%到15%之间,但恰恰是这部分决定了客服体验的成色。规则层和小模型追求的是“快”,LLM层追求的是“准”和“懂”。
4.2 Prompt设计的四条实战经验
LLM层本质上是用上下文学习来做少样本分类,prompt写得好不好,直接决定准确率上限。我踩过很多坑之后总结出四条经验。
第一条是给出明确的角色和任务定义。不要只写“对下面的句子做意图分类”,要让模型有代入感。我习惯这样开头:“你是一名资深的电商客服质检专员,负责判断用户的真实意图。用户的输入可能包含抱怨、提问、索要补偿等多种诉求,你需要从给定的意图列表中选择最匹配的一个意图。”
第二条是意图类别要带定义和示例。不能让模型猜“售后”和“投诉”有什么区别,要把每个意图配上一句话解释和两个典型示例。比如“售后:用户主动要求处理商品问题,包括退款、换货、维修、索要赔偿等。示例:‘这个坏了能换吗’‘我要申请退款’”。这一步相当于在做少样本学习,示例的质量直接决定模型的理解质量。
第三条是要求模型输出结构化结果。我要求模型严格按照固定格式输出,比如“{"intent": "售后", "confidence": "high"}”,并说明如果意图不在列表中,就输出“{"intent": "other", "confidence": "low"}”。输出结构化之后,下游代码处理起来就非常顺滑,不需要解析一堆自然文本。
第四条是把判断依据也问出来。我会在prompt里加一句:“请简述你的判断依据,不超过20字。”这一步看着多余,其实对badcase分析极其有用。当模型分错的时候,你能从它的判断依据里看出是prompt写得不够清楚,还是这个case本身太模糊。有了判断依据,后续优化prompt不是盲人摸象。
4.3 成本控制的三个手段
很多人担心LLM层成本失控,实际上做好三件事就能把它管住。
第一是入口控制。只有规则层没命中、小模型置信度又不足的句子才会进到LLM层,这一层天然就是少数流量。我实测下来,十万条用户消息里能走到LLM层的只有一万条左右。只要把前面的门槛设好,LLM层的量级就压住了。
第二是输入裁剪。很多case其实不需要带完整的多轮上下文,截取当前句加前一轮就足够了。上下文长度直接决定token消耗,能塞200字就绝不塞2000字。我在实际中会做一轮“裁剪优先级”设计:当前问句 > 上一轮用户消息 > 上一轮机器人回复 > 更早历史。裁到差不多够用就停,不为理论上的完整性多花token。
第三是缓存和降级。同一个用户用同样的表达反复提问,第一次查LLM,后面直接缓存结果。另外要预留降级方案:当LLM接口超时或出现异常时,自动降级到小模型结果,哪怕置信度不足也先顶上,总比给用户一个错误的好答复要强。线上稳定比单次准确性更重要。
4.4 大模型的“幻觉”怎么防
即使prompt写得再完美,LLM也有概率抽风,分出一个明显离谱的结果。我见过最经典的一次:用户问“你家怎么发货这么慢”,模型竟然分到了“赞美”。为了防止这种离谱结果挡住正确识别,我在LLM层后面加了一个结果校验器——一个轻量的关键词过滤器,检查模型输出的意图和输入文本之间有没有基本的语义重合。如果完全没有重合,直接判定为无效结果。这个校验器不追求找出所有错误,只负责拦住最离谱的那一批。它不完美,但很实用。
5. 三层架构的落地:从流程图到工程实现
5.1 完整请求链路设计
抛开抽象讨论,直接看一套生产可用的落地链路。当一个用户消息进来,请求会依次经过以下节点:
- 数据接入:消息进入服务,做基础清洗(繁简转换、URL替换、字符归一化)。
- 规则层判定:走规则引擎,如果命中高置信度规则,直接产出意图结果并结束;未命中则进入下一层。
- 小模型层判定:调用意图分类模型,如果输出概率高于阈值(比如0.6),直接产出结果;低于阈值则进入下一层。
- LLM层兜底:构造prompt(包含当前消息、历史上下文、意图列表及示例),调用大模型接口获取结构化结果,执行结果校验。
- 兜底保护:如果LLM超时或校验失败,回落到“other”意图,走兜底话术或转人工。
- 结果入库:无论哪一层命中,最终结果都统一写入日志,用于后续效果分析和badcase回捞。
需要特别强调的是这个链路内部的超时控制。我把每一层的时间预算都设得很严格:规则层5毫秒,小模型层50毫秒,LLM层500毫秒。一旦某层超时,立即放弃本层结果,要么降级到上一层结果,要么直接用兜底方案。绝不能因为一个慢请求让整条链路的用户体验被拖垮。
5.2 置信度设计与动态调优
三层架构里,置信度机制是核心粘合剂。每一层都要输出一个“我有把握吗”的信号,这个信号的质量直接决定了整体效果。
规则层的置信度由匹配条件决定:命中的关键词越多、模式越复杂,置信度越高。小模型层用softmax概率值作为置信度。LLM层则通过prompt让模型自己输出confidence等级,并结合校验器的检查结果做校准。
实操上我建议给每一层都预设一个最低置信度门槛,并定期用线上数据做动态调整。比如小模型的0.6阈值,如果发现测试集里0.5到0.6区间内有大量正确样本,就往下调到0.55;如果发现这个区间全是错误样本,就往上调到0.65。这个调整过程要和badcase分析配合做,它的核心是回答一个问题:“我们到底可以多信任这个模型?”
5.3 兜底策略的三道保险
即使有三层架构,线上依然会遇到各种没法预料的输入。我准备了三个兜底手段,按优先级排:
第一个是转人工。当所有层的置信度都低,或者LLM直接判定为“other”意图时,第一时间转人工客服,并且在转交记录里附带意图识别链路的信息,让人工客服能快速接手。
第二个是小模型高置信度强制覆盖。当LLM层超时或校验失败,但小模型层置信度在0.4到0.6之间(不够单独出结果但也没有明显冲突)时,我可以选择用小模型结果顶上去。这个策略的前提是下游话术足够通用,即使分错也不会出大问题。
第三个是通用澄清话术。当确实无法判断意图时,机器人会回复一句“抱歉,我没太理解您的意思,您可以换一种说法吗?或者直接回复‘人工’转接人工客服。”这个话术不需要任何模型能力,但能有效减少用户重复输入带来的负面体验。
三道保险的逻辑是一以贯之的:系统不知道答案时,不要装作知道,更不要让用户承担错误后果。
6. 线上踩坑实录:四个最常见的问题
6.1 规则层误判率飙升,根源是规则太宽
我们曾经把规则层的关键词做得很宽,觉得多匹配一条是一条的赚,结果误判率直接翻倍。核心问题是“发票”这个词,用户发“发票怎么还不开啊”,本意是催促,但命中“发票”关键词后被当成“开发票”意图,跳到了开票流程。排查之后我们把规则改成短语级别匹配,只用“开发票”“开票”“发票抬头”这类完整词组触发,误判率才降下来。规则层宁可漏接不要错接,漏接了下面还能补救,错接了用户直接流失。
6.2 小模型训练集切分错误导致指标虚高
有一版模型训练完,测试集准确率95%,上线后直接跌到86%。复盘发现数据切分时按句子随机切,同一个用户的多轮对话分散在训练集和测试集里,模型在训练的时候已经见过了测试数据的上下文风格,导致评估指标严重虚高。后来改成按用户ID做切分,准确率的预估值才回归真实水平。
6.3 LLM返回格式突变导致解析崩溃
大模型接口升级后,有一阵连续收到格式不符的返回,我们在代码里直接报了空指针异常。后来在解析逻辑里加了对异常情况的捕获,同时把解析失败的情况全部降级到小模型结果。这件事给我的教训是:大模型的输出格式只能当协议,不能当约定,下游解析必须有兜底,否则它的一次抽风就是你的一次线上事故。
6.4 兜底话术被用户吐槽“像机器人”
转人工和澄清话术我们一开始写得特别机械——“抱歉,我未能理解您的意图,请重新描述。”用户反馈极差。后来改成了口语化表达:“不好意思,刚才没太听明白,您能换个说法吗?或者回复‘人工’也行,我帮你转人工客服。”同一套逻辑,体验提升一个档次。技术链路再完善,最后面对用户的还是那一句话,值得多花一点时间打磨。
7. 未来扩展:这套架构还能怎么长
三层架构不是终点,它更像一个成长骨架,可以在不改变整体结构的前提下持续扩展。
一个明显的方向是从意图识别走向情感识别。把“情感倾向”作为和意图并列的输出,比如用户带着愤怒情绪来投诉,和心平气和来咨询,对应的服务策略应该不同。小模型层可以轻松扩展一个情感分类任务,LLM层也可以在prompt里增加情感判断字段。
另一个方向是从单轮分类走向多轮会话状态管理。目前这套架构处理的是单条消息的意图,但真实客服场景里,用户往往要经过多轮对话才把诉求说清楚。可以把每一轮的意图识别结果累加成一个“会话状态”,并结合状态决定下一步动作。这个扩展对规则层和小模型层改动不大,主要是增加一个状态机层。
还有一个值得尝试的方向是用户画像加权。把用户的会员等级、历史订单记录、售后次数等信息作为特征输入,理论上可以帮助系统更准确地判断意图强度,比如黑钻会员说“我有点不满意”,可能比普通用户说“我要投诉”更值得认真对待。这些信息对规则层没有帮助,但可以作为小模型层的输入特征,或者作为LLM层prompt的一部分。
8. 写在最后的实操心得
三层架构这套东西,理论听起来不复杂,真正的难度在于坚持“层与层之间不越界”的原则。规则层就想好怎么把简单问题切干净,别跃跃欲试想处理复杂语义;小模型层就老老实实提高泛化能力,别想着零样本泛化到一切场景;LLM层就努力把兜底做稳,不要什么都让它来做。每一层把自己那一亩三分地耕好,整体效果自然不会差。
我实际跑下来的数据是:规则层能拦截掉约40%的流量,小模型层能处理约50%,LLM层负责剩余的10%。每十万条消息,真正调用LLM的只有一万条左右,这个比例让成本完全在可控范围内。
最后再分享一个小技巧。无论你使用哪种模型,一定要把badcase回流机制做成自动化。每天定时从线上日志里捞取预测错误样本,去重后自动进标注池。每周做一次标注和重训。这个闭环一旦跑起来,系统的意图识别准确率会是稳步上升的,而不是永远停留在你上线那一刻的水平。很多团队搭好了架构就跑路不管了,三个月后效果下滑却怪架构不对,其实只是没有让它持续进化而已。
电商客服意图识别做到最终,拼的从来不是单个模型的智商,而是整个系统的工程控制力。希望这篇文章能让你少走一些弯路。