背景痛点:电商/金融场景下的三座大山
去年“618”大促,我们团队接到的第一个报警电话来自网关组:客服接口 502 大面积飘红,峰值 TPS 飙到 5200,CPU idle 直接掉到 5%。复盘时我们把问题拆成三块,发现也是大多数智能客服落地时绕不过去的“三座大山”:
- 高并发冲击:大促 30 秒内涌入 5k+ 会话,每条会话还要保持 30 s 心跳,连接数瞬间破 20 w。传统单体服务+Tomcat 默认 200 线程池直接被打穿。
- 多意图混合语句:用户一句“我昨天用花呗买的手机能分期吗,顺便把账单发我”里藏着“分期咨询+账单查询”两个意图,规则引擎写 if-else 到哭,漏判率 18%。
- 状态漂移:支付类对话平均 5.7 轮,用户中途切 App、杀进程再回来,状态机版本号对不上,客服直接答非所问,投诉率飙升。
三座大山压下来,老板只问了一句:“能不能抗住明年双 11?”于是有了这套从架构到模型的完整改造笔记。
架构对比:规则引擎 vs BERT+DST 选型决策树
先放结论:
- 日均会话 <10 w、意图 <30、无多轮——规则引擎+状态机最快 2 周上线。
- 峰值 TPS >3 k、意图可组合、对话深度 >3 轮——直接上 BERT+DST,否则后期重构成本更高。
下面这张表是我们在 50 w 条真实语料上跑出的结果:
| 指标 | 规则引擎 | BERT+DST |
|---|---|---|
| 意图准确率 | 82% | 94.3% |
| 多轮状态保持成功率 | 73% | 96% |
| 峰值 CPU | 92% | 38%(GPU 节点) |
| 需求变更人日 | 5 人/周 | 1.5 人/周 |
| 冷启动耗时 | 0 min | 3 min(可预热) |
决策树如下,按 QPS、意图数、多轮深度三步剪枝即可,不再纠结。
核心实现:三条代码把系统串起来
1. 请求分流:Spring Cloud Gateway 自定义负载
大促最怕单点,我们把“意图识别服务”拆成 8 个 pod,Gateway 层按 uid 一致性做一致性哈希,保证同一用户始终打到同一 pod,省去跨实例状态同步。
# application-route.yml spring: cloud: gateway: routes: - id: intent-service uri: lb://intent-service predicates: - Path=/api/v2/intent filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 3000 redis-rate-limiter.burstCapacity: 5000 - name: ConsistentHash args: key: "#{request.queryParams.getFirst('uid')}"2. 意图识别微服务:BERT 完整推理代码
技术栈:Python 3.9 + FastAPI + transformers 4.27,单卡 T4 可抗 800 QPS,batch=8。
# intent_server.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from transformers import BertTokenizer, BertForSequenceClassification import torch, time, os app = FastAPI() model_path = "/models/bert-intent-v2.1" tokenizer = BertTokenizer.from_pretrained(model_path) model = BertForSequenceClassification.from_pretrained(model_path) # 6 类意图 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device).eval() # 推理模式 class Query(BaseModel): uid: str text: str def preprocess(text: str) -> dict: """分词+截断,返回 torch 输入张量;时间复杂度 O(L),L=token 数""" encoded = tokenizer(text, max_length=128, truncation=True, padding='max_length', return_tensors="pt") return encoded.to(device) @app.post("/predict") def predict(q: Query): start = time.time() inputs = preprocess(q.text) with torch.no_grad(): logits = model(**inputs).logits # O(L) 推理 probs = torch.nn.functional.softmax(logits, dim=-1) intent_id = int(torch.argmax(probs)) cost = (time.time() - start) * 1000 return {"uid": q.uid, "intent": intent_id, "prob": float(probs[0][intent_id]), "rt": cost}启动脚本里加--preload把模型先 load 到显存,避免冷启动 502。
3. 对话状态管理器:Redis 存储设计
DST 要记录每轮槽位、用户意图、置信度,以及是否待确认。采用 Hash + TTL 结构,key=dialog:{uid},field 用 JSON 压缩,TTL 每次写操作刷新,兼顾超时与心跳。
# dst_redis.py import redis, json, time r = redis.Redis(host="redis-cluster", decode_responses=True) def update_state(uid: str, slots: dict, intent: int, confirm: bool): data = { "slots": slots, "intent_path": intent, "last_turn": int(time.time()), "confirm": confirm } r.hset(f"dialog:{uid}", "state", json.dumps(data)) r.expire(f"dialog:{uid}", 600) # 10 min 心跳超时性能优化:压测数据与冷启动预热
压测结果
4C8G 容器 * 8 副本,规则引擎在 5 k TPS 时 RT P99 1.2 s,错误率 6%;切到 BERT+DST 后 RT 降到 280 ms,错误率 <0.5%。GPU 节点成本虽高,但节省 30 台 CPU 机,整体账单反而降 18%。冷启动预热
利用 K8s 的 postStart 钩子,在 pod 真正接流量前向自己发一条“假请求”,触发模型编译与显存分配;同时把最热的 2 k 条历史语料离线推理好,结果写 Redis 缓存,用户首句直接匹配,命中率 65%,基本消灭 3 min 冷启动。
避坑指南:超时、心跳与敏感词
对话超时与心跳
移动端网络抖动大,我们把 HTTP 长轮询改成 WebSocket,心跳包 45 s 一次;服务端 Redis TTL 60 s,留 15 s 缓冲,避免用户切后台再回来被强制清状态。敏感词过滤
敏感词 2 w+ 条,同步过滤会拖慢 RT。采用“异步旁路”:网关层把原文写 Kafka,消费者用 AC 自动机算法(O(n))做扫描,命中的话发“audit”指令给对话管理器,前端实时弹提示,但不阻塞主流程。
# ac_automation.py from ahocorasick import Automaton # pip install pyahocorasick A = Automaton() for w in load_sensitive_dict(): # 2 w 词 A.add_word(w, w) A.make_automaton() def audit(text: str) -> list: return [item for item in A.iter(text)] # O(n) 扫描延伸思考:LLM 时代的下一代客服
规则→CNN→BERT,再往下走,LLM(大语言模型)带来两个新可能:
- Zero-shot 意图:无需标注,直接 prompt 让模型输出结构化的槽位与意图,适合冷启动业务。
- 对话生成即服务:把“答复生成”也交给 LLM,去掉人工模板,A/B 测试显示点击率 +9%。
但成本与合规是最大拦路虎——10 k TPS 下 175 B 模型光推理就要 40 张 A100。我们内部试点“小模型+大模型”级联:先用 110 M 的轻量 BERT 做意图路由,再对 5% 的疑难 Case 调用 LLM,整体 RT 增加 <120 ms,GPU 成本却只涨 15%,算是一条可落地的折中路线。
踩完这些坑,最大的感受是:智能客服不是“算法独角戏”,而是高并发工程+产品体验+运维成本的综合战场。把架构拆分、缓存、预热、心跳这些看似“脏活”的细节做到位,模型准确率才有真正落地的舞台。明年双 11 能不能抗住 1 w TPS?先把 Redis 集群扩到 128 分片再说,算法同学继续去调他的大模型吧。