背景与痛点:传统客服系统的局限性
过去很长一段时间,我们团队维护的工单系统全靠关键词+正则规则做应答。用户问“怎么开发票”,规则里没写“开发票”这个同义词,机器人就原地宕机;高峰期并发一上来,人工坐席排队 300+,平均响应 25 分钟,投诉率飙升。总结下来,老系统有三座大山:
- 理解能力弱:只能做字面匹配,稍微换种说法就失效
- 响应慢:规则越堆越多,匹配链路过长,单次请求 600 ms 起步
- 维护成本高:每上新业务,运营同学就要追加几十条正则,回滚率居高不下
引入 NLP 的目标很单纯——把平均响应压到 1 s 内,首响准确率≥85%,同时让运营从“写规则”变成“标数据”,人力释放 50%。
技术选型:BERT vs. RNN 的客服场景 PK
在正式写代码前,我们做了两轮离线实验,对比 LSTM(BiLSTM+CRF)、TextCNN、BERT-base 三种模型在 5 万条客服语料上的效果。结果如下:
- 意图识别:BERT 宏平均 F1 92.3%,LSTM 86.1%,TextCNN 87.9%
- 情感极性(正/负/中):BERT 89.7%,LSTM 81.4%
- 推理耗时(CPU 单核):BERT 280 ms,LSTM 120 ms,TextCNN 90 ms
虽然 BERT 重一点,但客服场景对准确率更敏感,且我们可通过蒸馏 + ONNX 把延迟压到 160 ms,最终拍板:BERT 做意图识别,TextCNN 做情感分析,兼顾效果与吞吐。
核心实现:意图识别 & 情感分析代码示例
下面给出最小可运行版本,基于 PyTorch + Transformers 4.29,已脱敏。假设已有标注数据intent_data.json格式为:
{"text": "我的发票抬头写错了能改吗?", "label": "modify_invoice"}1. 意图识别(BERT 微调)
# train_intent.py import json, torch, random from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizerFast, BertForSequenceClassification, Trainer, TrainingArguments LABEL2ID = {"modify_invoice": 0, "check_logistics": 1, "refund": 2, "human": 3} ID2LABEL = {v: k for k, v in LABEL2ID.items()} class ChatDataset(Dataset): def __init__(self, path): with open(path) as f: self.data = json.load(f) self.tok = BertTokenizerFast.from_pretrained("bert-base-chinese") def __len__(self): return len(self.data) def __getitem__(self, idx): x = self.tok(self.data[idx]["text"], truncation=True, max_length=64, return_tensors='pt') x = {k: v.squeeze(0) for k, v in x.items()} x['labels'] = torch.tensor(LABEL2ID[self.data[idx]["label"]]) return x train_ds = ChatDataset("intent_data.json") model = BertForSequenceClassification.from_pretrained("bert-base-chinese", num_labels=len(LABEL2ID)) args = TrainingArguments( output_dir="./intent_ckpt", per_device_train_batch_size=32, num_train_epochs=3, learning_rate=2e-5, logging_steps=50, evaluation_strategy="no" ) trainer = Trainer(model=model, args=args, train_dataset=train_ds) trainer.train() trainer.save_model("./intent_ckpt")训练 3 个 epoch,loss 降到 0.18 即可停。
2. 情感分析(TextCNN)
# sent_model.py import torch.nn as nn class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim=128, kernel_sizes=[3,4,5], num_classes=3): super().__init__() self.embed = nn.Embedding(vocab_size, embed_dim) self.convs = nn.ModuleList([nn.Conv1d(embed_dim, 100, k, padding=k//2) for k in kernel_sizes]) self.dropout = nn.Dropout(0.5) self.fc = nn.Linear(100*len(kernel_sizes), num_classes) def forward(self, x): x = self.embed(x) # [B, T, E] x = x.transpose(1, 2) # [B, E, T] x = [torch.relu(conv(x)) for conv in self.convs] x = [torch.max_pool1d(c, c.size(2)).squeeze(2) for c in x] x = torch.cat(x, 1) return self.fc(self.dropout(x))用客服场景 2 万条情感语料训练 5 个 epoch,准确率达到 89%,单条推理 25 ms。
3. 在线推理封装
# service.py from transformers import pipeline, BertTokenizerFast, BertForSequenceClassification import torch, time, json intent_model = BertForSequenceClassification.from_pretrained("./intent_ckpt") tok = BertTokenizerFast.from_pretrained("bert-base-chinese") intent_pipe = pipeline("text-classification", model=intent_model, tokenizer=tok, top_k=1, device=0) sent_model = torch.load("sent_cnn.pt", map_location="cpu").eval() def predict(query: str): t0 = time.time() intent = intent_pipe(query)[0]['label'] # 情感模型输入需分词转 id,此处略 sentiment = "neutral" # 伪代码,实际走 TextCNN cost = (time.time() - t0)*1000 return {"intent": intent, "sentiment": sentiment, "latency_ms": round(cost, 1)}把service.py打到 Docker,通过 gRPC 暴露,压测 4C8G 容器,QPS 1200 时平均延迟 160 ms,P99 280 ms,符合 SLA。
性能测试:上线前后对比
| 指标 | 上线前(关键词) | 上线后(NLP) | 提升倍数 |
|---|---|---|---|
| 首响准确率 | 63% | 87% | 1.38× |
| 平均响应时间 | 2300 ms | 160 ms | 14× |
| 人工转接率 | 42% | 19% | -55% |
| 运营维护人日/月 | 18 | 7 | -61% |
数据来自灰度发布两周后的 A/B 对照组,样本 6.4 万会话。
避坑指南:冷启动 & 数据稀疏
- 冷启动:初期标注数据不足,先用 BERT+半监督自训练,把高置信>0.9 的预测结果回灌训练集,三轮后准确率提升 6%。
- 数据稀疏:长尾意图(如“团体批量订购”)样本极少,采用孪生网络做 few-shot,利用 32 条模板即可把召回从 0 拉到 78%。
- 版本回滚:模型灰度时必须支持按用户维度切流,我们利用 Nginx+Consul 做 5% 灰度,一旦线上指标下跌,10 秒内可切回规则基线。
- badcase 闭环:每周拉取 top-200 误分类,运营同学只需在后台点“标为负例”,自动进入下周训练集,持续迭代。
总结与展望
三个月跑下来,NLP 把客服人从重复问答里解放了大半,工单峰值期再没出现“排队 300+”的惨案。下一步准备做三件事:
- 多语言:跨境电商场景,英/西/俄三语混合,考虑 mBERT→XLM-R 升级
- 多轮状态追踪:把会话级意图做成槽位填充,支持“修改收货地址→确认订单号→完成”这种多轮交互
- 端到端生成:在 FAQ 场景试点 GPT 类模型直接生成答案,减少知识库维护量
如果你也在为“规则爆炸”头疼,不妨从意图识别这个小切口试水,先让机器人听懂,再让它答好,效率提升会比想象中来得更快。