背景痛点:传统客服系统为何“听不懂人话”
过去两年,我先后接手过三套客服系统的重构。每次上线前,业务方最焦虑的问题几乎一模一样:
- 用户说“我要退钱”,系统却理解成“我要退款”,结果把用户引到售后工单,而售后只能处理退货,导致投诉飙升。
- 同一句话在不同轮次出现,机器人像金鱼一样“秒忘上文”。例如先问“我订单在哪”,再问“那什么时候到”,系统直接回答“请问您想查询哪个订单”,体验瞬间崩塌。
- 用户甩一张截图,传统规则引擎只能回“暂不支持图片”,活生生把问题踢给人工,夜间值班同学叫苦连天。
归根结底,老系统靠“关键词+正则”堆规则,意图歧义、上下文丢失、多模态处理三大硬伤越积越深。维护同学每天盯着 3 万条日志人工扩写规则,依旧挡不住 20% 的误识别。
技术对比:规则、Seq2Seq、BERT+RLHF 谁更扛打
为了把误识别率从 20% 降到 5% 以下,我们拉了三套方案在测试集(5.2 万条真实对话)上做横向对比,结论直接上图:
核心数据如下:
| 维度 | 规则引擎 | Seq2Seq+Attention | BERT+RLHF |
|---|---|---|---|
| 意图识别准确率 | 79.3% | 86.7% | 93.1% |
| 平均响应 | 120 ms | 450 ms | 380 ms |
| 维护成本/季度 | 18 人日 | 6 人日 | 3 人日 |
| 新增意图工作量 | 新增正则+回归测试 | 重训模型+标注数据 | 人工排序 200 条样本喂 RLHF |
结果一目了然:规则引擎维护噩梦,Seq2Seq 生成式容易“胡言乱语”,而 BERT+RLHF 在准确率、响应、维护成本取得相对均衡,成为后续架构底座。
核心实现:BERT+RLHF 落地三部曲
1. 意图识别模型 Fine-tuning
我们采用bert-base-chinese做底座,把 21 个业务意图(退货、改签、发票等)当成多分类任务。训练数据 1.8 万条,来自客服日志脱敏+人工纠偏。关键代码如下(已加类型标注与异常捕获,符合 PEP8):
# intent_model.py from transformers import BertTokenizer, BertForSequenceClassification from torch.utils.data import Dataset, DataLoader import torch from typing import List, Tuple class IntentDataset(Dataset): def __init__(self, texts: List[str], labels: List[int], tokenizer, max_len: int = 128): self.texts = texts self.labels = labels self.tokenizer = tokenizer self.max_len = max_len def __len__(self) -> int: return len(self.texts) def __getitem__(self, idx: int) -> dict: encoding = self.tokenizer( self.texts[idx], truncation=True, padding='max_length', max_length=self.max_len, return_tensors='pt' ) item = {key: val.squeeze(0) for key, val in encoding.items()} item['labels'] = torch.tensor(self.labels[idx], dtype=torch.long) return item def train_epoch(model, dataloader, optimizer, device) -> float: model.train() total_loss = 0 for batch in dataloader: try: input_ids = batch['input_ids'].to(device) attention_mask = batch['attention_mask'].to(device) labels = batch['labels'].to(device) outputs = model(input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss loss.backward() optimizer.step() total_loss += = loss.item() except RuntimeError as e: torch.cuda.empty_cache() continue return total_loss / len(dataloader)训练 3 个 epoch,准确率即可达到 92%+。把模型推到torchserve做推理,单次 forward 平均 180 ms,满足 <500 ms 的 SLA。
2. 对话状态跟踪(Dialog State Tracking/DST)
上下文靠 Redis 存储,键设计遵循cid:{conversation_id}:slot,值用 Hash。示例结构:
Key: cid:10086:slot ├─ intent: "return" ├─ order_id: "123456" ├─ retry: 0 └─ ttl: 1740每轮对话先读 Redis,若无数据则初始化默认 slot;模型推理后更新对应字段,并设置 30 min 过期,兼顾内存与体验。核心读写函数:
# dst_redis.py import redis from typing import Optional, Dict, Any r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True) def get_slot(cid: str) -> Dict[str, Any]: data = R.hgetall(f"cid:{cid}:slot") return data or {"intent": None, "retry": 0} def update_slot(cid: str, kv: Dict[str, Any], ttl: int = 1800) -> None: key = f"cid:{cid}:slot" R.hset(key, mapping=kv) R.expire(key, ttl)3. 异步处理架构(Celery+RabbitMQ)
为了让耗时任务(如图片 OCR、工单创建)不阻塞主流程,我们引入 Celery 做异步队列。整体链路:
用户消息 → Flask Gateway → 意图模型(同步)→ 若需异步 → 推送 Celery → Worker 处理 → 回调 API → 推送消息
部署时把 Worker 与模型推理容器分离,CPU 型任务放 Worker,GPU 型放 TorchServe,避免抢占。压测显示,异步改造后 95th 延迟从 1.2 s 降到 380 ms。
避坑指南:生产踩坑三板斧
对话超时重试的幂等性
移动端弱网场景会重复发相同消息,我们给每条用户消息生成msg_id,进入网关先查 Redis 是否已处理,若命中直接返回缓存结果,避免重复扣费或重复建单。敏感词过滤的 DFA 算法优化
早期用in关键字循环,性能惨不忍睹;换成 Deterministic Finite Automaton/DFA 后,单次匹配降到 0.03 ms,敏感词库 1.2 万条也稳得住。GPU 冷启动预热
TorchServe 默认懒加载,首次请求超时 8 s。我们在 CI 阶段加一条“假请求”做 warm-up,并在 Kubernetes readinessProbe 里检测/models/bert状态,确保流量接入前模型已驻留显存。
性能验证:ab 压测数据一览
硬件:4C8G*3 Gateway,1×A10 GPU,TorchServe 单卡,Redis 6.2 集群。
命令:ab -n 50000 -c 200 -p body.json -T application/json http://gateway/chat
结果:
- 并发 200,QPS 512,平均响应 380 ms
- 错误率 0.8%(全部来自超时重试,已幂等)
- GPU 利用率 62%,Redis 读 QPS 1.1 w,CPU 65%
对比老规则引擎(同并发):
- QPS 120,响应 120 ms,但错误率 6.3%,且意图识别准确率仅 79%
- 维护规则 1 万+,新增需求需 3 天测试回归
代码规范与可维护性
- 统一 Black 格式化,行宽 88,强制类型标注,CI 阶段 mypy 检查
- 所有接口返回封装为
schemas.ResponseModel,字段变动自动生成 OpenAPI 文档 - 异常捕获后写
structlog,索引到 Elasticsearch,方便追踪上下文 - 单元测试覆盖 ≥85%,意图模型用
pytest+hypothesis做属性化测试,确保新增意图不出现回退
延伸思考:大模型时代客服架构往哪走
提示工程即服务
把 Prompt 托管到独立仓库,支持灰度回滚,解决“改一句提示全量上线”的痛点。大小模型协同
轻量 BERT 做意图+槽位,大模型(如千亿级)做“兜底闲聊/复杂推理”,通过置信度门控路由,兼顾成本与体验。实时个性化
引入用户画像流式特征,把最近 30 天订单、浏览记录注入 Prompt,实现“千人千面”回答,但注意隐私脱敏与动态脱敏。多模态统一
图片、语音、文档统一 Tokenize,走同一套 Transformer,降低维护割裂感。GPU 资源调度将更细粒度,可能演进到 Serverless GPU。
写在最后
整套流程跑下来,最大的感受是:别再堆规则了,真心堆不动。把 BERT+RLHF 这套深度方案落地后,误识别率从 20% 降到 5%,夜间人工介入下降 40%,运维同事终于不用凌晨三点起床加正则。希望这篇实战笔记能帮你少踩几个坑,早日让客服机器人“听懂人话”。