1. 背景痛点:高并发下的“客服雪崩”
去年双十一,我们内部客服系统被瞬间流量打爆:平均响应从 800 ms 飙到 4 s,用户疯狂点“人工客服”按钮,结果人工队列也一起瘫痪。复盘时把问题拆成三类:
- 并发瓶颈:Tomcat 默认 200 线程池,Intent-SVC 同步调用外部翻译接口,线程被阻塞,CPU 空转。
- 状态混乱:多轮对话用 session 存 Redis,结构是扁平 KV,没有版本号,A 用户覆盖 B 用户上下文,机器人开始“胡言乱语”。
- 意图漂移:规则引擎 3000 条正则,优先级靠“人肉”编号,新活动上线要通宵调权重,准确率 72% 原地踏步。
结论:如果效率问题不解决,智能客服就是“智能添堵”。
2. 技术选型:规则、传统 NLP 与 BERT 的“三角恋”
| 维度 | 规则引擎 | TF-IDF+FastText | BERT+微调 |
|---|---|---|---|
| 训练数据 | 不需要 | 1 w+ 标注 | 5 k+ 标注即可 |
| 意图类扩展 | 加一条规则 | 重训模型 | 增量微调 30 min |
| 响应时间 | 1 ms | 20 ms | GPU 20 ms / CPU 80 ms |
| 可解释性 | 高 | 中 | 低,需日志回溯 |
| 多语言 | 写正则写到哭 | 需分词 | 原生多语言 |
决策:核心意图识别走 BERT,FAQ 命中用规则兜底,再包一层 Redis 缓存,GPU 机器只负责推理,其余模块无状态,可横向扩容。
3. 核心实现
3.1 Spring Boot 骨架:JWT + 限流
@RestController @RequestMapping("/api/v1/bot") public class ChatController { @RateLimiter(name = "chat", fallbackMethod = "limitFallback") // 基于 resilience4j @PostMapping("/chat") public Reply chat(@RequestHeader("Authorization") String jwt, @Valid @RequestBody ChatReq req) { // 1. 验签换用户 id Long uid = JwtHelper.parse(jwt); // 2. 状态机加载上下文 DialogState state = stateService.load(uid); // 3. 意图识别 Intent intent = intentService.predict(req.getText()); // 4. 状态迁移 state = state.handle(intent, req); // 5. 保存回 Redis stateService.save(uid, state); return Reply.build(state.getAnswer()); } public Reply limitFallback(String jwt, ChatReq req, Exception ex) { return Reply.busy(); // 返回“系统繁忙,请稍候” } }3.2 对话状态机 UML 与代码
状态图概括:
Idle → AwaitingName → AwaitingPhone → Idle
任何时刻可进入 Fallback,超时 30 s 自动退回 Idle。
public enum State { IDLE, AWAIT_NAME, AWAIT_PHONE, FALLBACK } public class DialogState { private State state = State.IDLE; private int retryCounter = 0; // 防无限循环 public DialogState handle(Intent i, ChatReq req) { switch (state) { case IDLE: if (i == Intent.ORDER) return askName(); break; case AWAIT_NAME: if (i == Intent.PROVIDE_NAME) collectName(req); return askPhone(); case AWAIT_PHONE: if (i == Intent.PROVIDE_PHONE) return confirm(); else if (++safeCounter > 3) return fallback(); } return this; } }4. 性能优化
4.1 压测报告(JMeter 2000 TPS)
- 90% 响应 < 120 ms
- 99% 响应 < 220 ms
- CPU 占用 68%,GPU 占用 42%,网卡未打满
- 错误率 0.02%,均为超时熔断触发
4.2 Redis 管道批量回写
对话里每次要拉取 3~5 个 KV(上下文、用户标签、商品信息)。若用同步 get,一次 5 ms,累加到 25 ms。改用 pipeline:
List<Object> ans = redisTemplate.executePipelined((RedisCallback<String>) conn -> { conn.get(uidCtx); conn.get(uidTag); conn.hGet(prodKey, prodId); return null; });RT 降至 4 ms,整体吞吐 +18%。
5. 避坑指南
- 循环熔断:状态机里加
safeCounter,连续 3 次无法解析意图→强制FALLBACK→返回“转人工”。 - 敏感词过滤:别在 for 循环里
Pattern.compile,预编译后放ConcurrentHashMap,实测 1 w 次匹配从 900 ms 降到 40 ms。 - GPU 与 CPU 混布:推理服务打标签,K8s 调度保证 GPU 节点只跑模型 Pod,避免 JVM 抢占显存触发 OOM。
6. 延伸思考:大模型时代的客服架构
BERT 只能解决“意图分类”,下一轮对话把上下文拼成 512 字符,长一点就截断。GPT-3.5/4 的 4 k~32 k 窗口,理论上可把整通对话一次性喂进去,省掉状态机。但企业落地要权衡:
- 成本:GPT-4 每 1 k tokens 0.03 USD,1 w QPS 月账单 > 30 w;
- 时延:生成式模型平均 1.2 s,远高于分类模型 100 ms;
- 安全:需本地部署 + 知识蒸馏,防止核心数据出境。
设想“双层架构”:
- 轻量 BERT 做路由,把 80% 简单咨询直接分流;
- 复杂个案走本地 LLM,外挂向量检索(Faiss+Embedding),答案可控可追溯;
- 对实时性要求高的订单、支付,仍用状态机兜底,保证毫秒级响应。
如此既享受大模型泛化能力,又不被成本与合规绑架。
把系统从“能用”做到“抗得住”,最大的感受是:智能客服的瓶颈往往不是模型准不准,而是工程细节——线程、缓存、超时、降级,一环掉链子,前端就“转圈圈”。先把链路压稳,再谈算法惊艳,用户才会真正感到“智能”而不是“智障”。