限时福利领取


背景痛点:电商/金融场景下的三座大山

去年双十一,我们团队负责的智能客服在 0 点 10 分瞬间飙到 2300 TPS,直接把网关打挂。复盘后,我们把痛点收敛成三条:

  1. 高并发对话:2000+ TPS 只是开胃菜,峰值时还要保持 P99 响应 < 300 ms,否则用户就转人工,客服成本翻倍。
  2. 方言与噪声:华南地区粤语、闽南语混杂,普通话带口音,ASR 误识别率 18%,直接把“查询余额”听成“查询鲍鱼”,意图识别全崩。
  3. 合规校验:金融场景要先过“反洗钱敏感词”过滤器,再调风控接口,任何一次越权回复都可能被监管罚款。

这三座山不铲平,AI 客服就只能做“演示产品”。

技术选型:Rasa、Dialogflow 还是自研?

我们先用 10 万条真实对话做了离线评测,结果如下:

框架 意图 F1 多轮 DST 准确率 二次开发自由度 私有部署成本
Dialogflow 0.91 0.78 低(黑盒) 按调用收费,贵
Rasa 3.x 0.89 0.82 低,但 GPU 推理慢
自研 + Transformer 0.94 0.87 一次性投入,可控

结论

  1. 峰值并发下 Dialogflow 的 SLA 无法保证,且金融数据出域合规风险高。
  2. Rasa 的 DIET 分类器在 CPU 上 latency 高,2000 TPS 需要 60+ 节点,成本反超自研。
  3. 自研基于 BERT-mini + 蒸馏,单卡 T4 可扛 1200 QPS,F1 反而更高,于是拍板自研。

核心实现一:对话状态机(Python 版)

状态机要解决两件事:状态持久化(服务重启不丢上下文)与超时清理(防止内存泄漏)。代码如下,符合 PEP8,时间复杂度 O(1) 单次读写。

# state_machine.py
import time
import redis
from typing import Dict, Optional

class ChatSession:
    def __init__(self, redis_client: redis.Redis,
                 session_id: str, ttl: int = 1800):
        self.r = redis_client
        self.sid = session_id
        self.ttl = ttl

    def get_state(self) -> Optional[Dict]:
        data = self.r.hgetall(f"ds:{self.sid}")
        if not data:
            return None
        return {k.decode(): v.decode() for k, v in data.items()}

    def set_state(self, state: Dict) -> None:
        key = f"ds:{self.sid}"
        pipe = self.r.pipeline()
        pipe.hset(key, mapping=state)
        pipe.expire(key, self.ttl)
        pipe.execute()

关键点

  • Redis Hash 存储整轮对话状态,单次 pipeline 原子写,latency < 5 ms。
  • TTL 30 min,超时自动淘汰,避免僵尸会话占内存。

核心实现二:Kubernetes 弹性伸缩图解

我们把无状态服务(意图识别、FAQ 检索)与有状态服务(DST、会话存储)拆成两套 Deployment,配合 HPA 与 Cluster-Autoscaler:

k8s 架构

流量路径

  1. Ingress-Nginx 按 X-Session-Id 做一致性哈希,保证会话亲和性/Session Affinity。
  2. Pod 侧暴露 /readyz 探针,只有模型热更新完成才置 true,防止流量打到正在加载权重的副本。
  3. HPA 指标自定义:QPS + GPU 利用率双阈值,QPS>800 或 GPU>70% 即扩容,缩容冷却 180 s,避免抖动。

生产考量:敏感信息脱敏 & AB 流量分配

1. 对话日志脱敏

金融客服常出现“身份证号、银行卡号、CVV”。我们采用正则+命名实体联合方案:

import re, spacy
nlp = spacy.load("zh_core_web_sm")

SENSITIVE_PATTERNS = [
    (r"\d{16,19}", "[BANK_CARD]"),
    (r"\d{6}\*\*\*\*\d{4}", "[ID_MASK]"),
]

def desensitize(text: str) -> str:
    for pattern, mask in SENSITIVE_PATTERNS:
        text = re.sub(pattern, mask, text)
    doc = nlp(text)
    for ent in doc.ents:
        if ent.label_ in ("PER", "MONEY"):
            text = text.replace(ent.text, f"[{ent.label_}]")
    return text

脱敏在网关侧 sidecar 完成,不落盘原始日志,审计部直接拉脱敏后的 JSON,合规通过。

2. 模型 AB 测试

新模型想灰度 10% 流量,我们在 Ingress-Nginx 用 canary-by-header 实现:

nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "x-model-version"
nginx.ingress.kubernetes.io/canary-by-header-value: "v2"

客户端随机会话里 10% 带 x-model-version: v2,Prometheus 对比两组“意图置信度分布+解决率”,两周后无显著下降即全量。

避坑指南:三次踩坑血泪史

  1. 未配置会话亲和性
    现象:同一会话前后两句打到不同 Pod,DST 读不到上一轮状态,答非所问。
    解决:Ingress 加 nginx.ingress.kubernetes.io/upstream-hash-by: "$http_x_session_id"

  2. GPU 内存泄漏
    现象:TensorRT 引擎未显式 destroy(),24 h 后显存占满,新 Pod 起不来。
    解决:在 __del__ 里加 torch.cuda.empty_cache(),并用 livenessProbe 每 12 h 重启一次。

  3. Redis 大 Key 造成阻塞
    现象:单条会话状态 > 1 MB(用户把 20 轮对话全带上下文),bgsave 时延迟飙到 2 s。
    解决:状态分层,只保留最近 5 轮,历史存 S3,读时按需懒加载。

代码规范 & 复杂度说明

  • 所有 Python 代码通过 black --line-length 79 强制格式化。
  • 意图分类前向推理 O(n) n=token 数,< 128 时 latency 稳定 28 ms。
  • DST 状态机读写 Redis O(1),内存占用与轮数成正比,上限 5 轮后淘汰,空间可控。

互动挑战:把 P99 延迟再降 30 ms

当前链路:Ingress → 意图识别 → DST → 答案召回 → 脱敏 → 返回,P99 280 ms。
挑战任务

  1. Fork 示例代码,尝试用 异步批处理 把“意图+答案召回”并行化。
  2. 在 4 核 8 G 的 K8s Pod 里压测,目标 P99 < 250 ms,CPU 增长 < 10%。
  3. 提交 PR,附 locust 报告截图,我们将送出自研 BERT-mini int8 量化权重一份。

结尾体验

整套方案上线后,双十一峰值 2500 TPS 零事故,平均响应 220 ms,粤语口音识别错误率从 18% 降到 7%,监管脱敏审计一次通过。
当然,客服场景变化快,下周可能又要适配“视频客服”新需求,路还长,继续迭代。

限时福利领取


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐