背景痛点:传统客服的三大“老大难”
去年双十一,我们老系统被 12w 并发直接打挂:CPU 飙到 90%,平均响应 3.8 s,意图识别准确率只剩 62%。复盘下来,问题集中在三点:
动态上下文处理
规则引擎靠正则+关键词,用户换种说法就懵;多轮对话里一旦插一句“等等,我先确认收货地址”,后续流程直接断片。多轮对话状态维护
状态机写死 147 个节点,每加一个活动就要硬编码,分支爆炸;内存里只存当前节点 ID,历史消息被 GC 掉,用户回退到上一题时系统一脸懵。知识更新成本
运营每周上新 FAQ,开发配合发版 2 天;一旦遇到紧急舆情,只能热补丁,结果把旧逻辑冲掉,回滚都来不及。
一句话:规则系统像“刻舟求剑”,越迭代越沉。
技术对比:规则 vs 传统 NLP vs 大模型
先上硬数据。我们在同一批 1.2w 条真实语料上做了离线评测,结果如下:
| 维度 | 规则引擎 | 传统 NLP (BERT+CRF) | 大模型 (7B+LoRA) |
|---|---|---|---|
| 意图准确率 | 68% | 82% | 94% |
| 平均响应时间 | 180 ms | 350 ms | 220 ms |
| 训练/标注人日 | 5 | 25 | 8 |
| 新增知识耗时 | 2 d | 1 d | 10 min |
| 多轮一致性 (人工评) | 55% | 70% | 91% |
说明:
- 大模型方案用 4-bit 量化,GPU 显存占用 5.2 GB;BERT 方案 1.2 GB,但推理要过 3 段 pipeline,延迟反而更高。
- 训练人日含标注+微调,规则引擎看似最少,可后期维护呈指数级上涨。
架构设计:一张图看清分层
核心三层:
LLM 微调层
- 底座选 7B 开源基座,用 LoRA 降秩=64,学习率 2e-4,3 epoch 就能收敛。
- 训练语料 8w 条,覆盖 200 个意图,负样本用“相似问法不同意图”硬挖掘,提升边界区分度。
对话管理层
- 基于 LangChain 的 ConversationBufferWindowMemory,窗口=5,保证上下文不超限。
- 状态持久化到 Redis,TTL=30 min,支持用户跨端继续聊。
知识检索层
- 向量库用 Milvus 2.3,HNSW 索引,ef=128,top-k=5,召回<50 ms。
- 文本块按 512 token 滑动切分,重叠 50 token,兼顾召回与精度。
关键代码:带缓存的 RAG 流程
# rag_chain.py from typing import List from langchain.vectorstores import Milvus from langchain.llms import LlamaCpp from langchain.chains import ConversationalRetrievalChain from langchain.cache import RedisCache import redis, hashlib, time EMBED_MODEL = "BAAI/bge-base-zh-v1.5" LLM_PATH = "/models/7B-q4_0.gguf" REDIS_URL = "redis://cache:6379/0" class RagService: def __init__(self): self.cache = RedisCache(redis_client=redis.from_url(REDIS_URL)) self.llm = LlamaCpp(model_path=LLM_PATH, n_ctx=4096, temperature=0.2) self.db = Milvus( embedding_function=EMBED_MODEL, collection_name="faq_v1", connection_args={"host": "milvus", "port": "19530"} ) self.chain = ConversationalRetrievalChain.from_llm( llm=self.llm, retriever=self.db.as_retriever(search_kwargs={"k": 5}), memory=self._build_memory(), return_source_documents=True ) def _build_memory(self): from langchain.memory import ConversationBufferWindowMemory return ConversationBufferWindowMemory(k=5, input_key="question") def ask(self, session_id: str, question: str) -> str: """单次问答入口,带缓存,复杂度 O(1) 命中时""" key = hashlib.md5(f"{session_id}_{question}".encode()).hexdigest() if (cached := self.cache.get(key)) is not None: return cached st = time.time() resp = self.chain({"question": question}) self.cache.set(key, resp["answer"], ttl=300) print(f"cost={time.time()-st:.2f}s") return resp["answer"]要点:
- 缓存 key 拼接 session_id,防止用户 A 吃到用户 B 的答案。
- 向量检索 + LLM 生成总耗时 ~220 ms,缓存命中降到 8 ms。
生产考量:高并发下的“保命”策略
- 流量控制
采用令牌桶,桶容量=500,速率=100/s,超出直接返回 429,防止 GPU 被打满。
# limiter.py import time, threading from typing import Optional class TokenBucket: def __init__(self, rate: int, capacity: int): self._rate = rate self._capacity = capacity self._tokens = capacity self._lock = threading.Lock() self._last = time.time() def acquire(self, need: int = 1) -> bool: with self._lock: now = time.time() delta = now - self._last self._tokens = min(self._capacity, self._tokens + delta * self._rate) self._last = now if self._tokens >= need: self._tokens -= need return True return False- 敏感词过滤
引入开源“敏感词检测 SDK”,支持自定义词库 1.8w 条,检测耗时 <2 ms;若命中,直接降级到“人工客服”分支,避免风险外溢。
避坑指南:我们踩过的 3 个深坑
微调数据污染
早期把测试集误混进训练集,结果线下准确率 98%,上线 74%。
解决:训练前做 hash 去重,留 10% 会话级隔离测试集,确保同问句不会跨集合。API 超时设置
默认 60 s 超时,GPU 偶尔卡顿导致连接堆积,把后端线程池打满。
解决:Nginx 层 3 s 熔断,重试 1 次;LLM 侧改 streaming 输出,首 token 延迟>1 s 直接返回“正在思考,请稍等”占位符。对话状态存储
曾用 MySQL 存 JSON,字段长度 4 k,用户发长语音转文本后爆字段,直接写入失败。
解决:改 Redis List,每轮对话 LPUSH,LTRIM 保持最新 10 条,单条上限 1 k,整体内存<100 MB。
代码规范小结
- 所有函数写类型注解(Python 3.10+)
- 关键路径加
try/except,异常打 Sentry,附带 trace_id - 时间复杂度>1e6 处写注释,例如向量检索
O(log n),暴力循环O(n^2)直接重构
延伸思考:成本与速度的跷跷板怎么玩?
大模型效果爽,账单也酸爽。我们内部做过量化对比:
| 方案 | 首 token 延迟 | 单卡 QPS | 月租(8卡A100) |
|---|---|---|---|
| FP16 | 280 ms | 8 | 12w |
| 8-bit | 220 ms | 12 | 9w |
| 4-bit + GQA | 180 ms | 18 | 6w |
| 4-bit +投机解码 | 120 ms | 25 | 6w |
投机解码用 1B “小模型”打草稿,7B 大模型并行验证,延迟再降 30%,但代码复杂度翻倍。
问题来了:你的业务愿意牺牲多少准确率换成本?能否接受 2% 的意图识别下降,换来每月省 6w 预算?欢迎动手实验不同量化方案,把结果留言交流。
把大模型搬进客服不是“一键成神”,更像给老火车换发动机:轨道得重新铺,信号系统得升级,乘客还得重新教。走完这一圈,我们意图识别提升 40%,响应延迟降 30%,但真正的收益是——运营同学终于可以在周五下班前 10 分钟搞定上新,而不用拉着研发加班到深夜。技术价值,大概就藏在这些不再被提起的夜晚里。