开篇:传统客服的“三座大山”
去年双十一,我们给某头部电商做客服压测:
- 人工坐席平均响应 180 s,峰值排队 3 000+ 人
- 规则机器人只能覆盖 47 种标准问,其余全部转人工
- 每万通会话成本 ≈ 1 200 元(含人力、通话、场地)
老板一句话:把响应压到 5 s 以内,成本砍一半。
于是我们把目光投向了开源的 qwen-agent——阿里通义千问对外开放的 Agent 框架,7 B 模型单机 QPS 稳在 30+,意图识别 F1 0.92,比规则引擎高 30 个百分点。下面把踩过的坑、调过的参、跑过的数据一次性摊开。
整体架构:一条请求的三级跳
- 统一网关:Nginx + Lua 做流量染色,按租户切分 qwen 集群
- 对话管理:StateMachine 维护 slot、意图、多轮缓存,超时自动归档
- 知识库:向量库 Milvus + 业务 ES,召回 Top5 文档作为 prompt 上下文
- 模型服务:qwen-7b-chat 通过 vLLM 部署,支持 continuous batching
- 观测:Prometheus 拉取 token 数、首 token 延迟、异常率,Granfana 看板实时报警
核心代码:30 行跑通状态机
以下代码全部在生产环境跑了 3 个月,Python 3.8+,可直接复用。
1. 对话状态机(带超时)
import time from typing import Dict, Optional from dataclasses import dataclass, field @dataclass class Session: uid: str slots: Dict[str, str] = field(default_factory=dict) last_active: float = field(default_factory=time.time) TIMEOUT: float = 300 # 5 min 无交互自动清空 def is_expired(self) -> bool: return time.time() - self.last_active > self.TIMEOUT def update_slot(self, k: str, v: str): self.slots[k] = v self.last_active = time.time()2. 意图识别封装(qwen 版)
from qwen_agent import QwenAgent # pip install qwen-agent class IntentEngine: """单例意图引擎,内部做 prompt 缓存""" def __init(self, model_name: str = "qwen-7b-chat"): self.agent = QwenAgent(model_name) async def predict(self, query: str, history: list) -> str: """ 返回结构化的意图标签,如 refund|shipping|greeting """ prompt = f"历史对话:{history}\n用户:{query}\n意图(仅输出标签):" answer = await self.agent.async_generate(prompt, max_tokens=10) return answer.strip().lower()3. 异步响应处理(FastAPI)
from fastapi import FastAPI app = FastAPI() sessions: Dict[str, Session] = {} @app.post("/chat") async def chat(request: dict): uid = request["uid"] query = request["query"] # 0. 取 or 新建会话 if uid not in sessions or sessions[uid].is_expired(): sessions[uid] = Session(uid=uid) session = sessions[uid] # 1. 意图识别 intent = await intent_engine.predict(query, history=[]) session.update_slot("intent", intent) # 2. 知识召回 + 生成答案 kb_ans = await kb_search(query, top_k=3) final_prompt = f"背景知识:{kb_ans}\n用户问题:{query}" answer = await qwen_agent.async_generate(final_prompt, max_tokens=256) # 3. 更新历史 & 返回 return {"answer": answer, "intent": intent}性能优化三板斧
1. 压测数据对比
| 部署方式 | 并发 | 平均首 token 延迟 | 99th 延迟 | 单轮成本 |
|---|---|---|---|---|
| 单机 1×A10 | 30 | 320 ms | 1.2 s | 0.012 元 |
| 4×A10 分布式 | 120 | 290 ms | 0.8 s | 0.011 元 |
| 规则引擎 | 200 | 50 ms | 120 ms | 0.003 元 |
结论:LLM 方案延迟高一个量级,但可承载复杂意图,成本仍低于人工。
2. 对话上下文压缩
- 历史轮次 > 5 时,用 LLM 自摘要:把 1 500 token 压到 150 token,首 token 延迟降 18%
- 对数值类 slot(订单号、手机号)做正则提取,摘要阶段只保留 key,省 30% token
3. 敏感词过滤
双层策略:
- 前缀树 8 万条敏感词,CPU 0.1 ms 完成匹配
- 若命中,用轻量 BERT 二分类再次校验,降低误杀率到 0.2%
生产环境避坑指南
会话 ID 设计
采用tenant_id + yyyyddd + 8位随机的格式,既能分库分表,又能直接看出业务线+日期,方便灰度回滚。第三方 API 熔断
知识库搜索超时 500 ms 直接熔断,返回兜底文案;连续 5 次失败即开启 30 s 冷却,防止拖垮主链路。模型版本灰度
利用网关流量染色,把 10% 流量切到qwen-7b-v1.1,对比意图 F1 与首 token 延迟,48 小时无异常再全量。回滚只需改 Lua 权重,30 s 生效。
开放讨论
大模型效果越好,参数越大,成本越高;速度越快,显卡越少,效果又打折。你在业务里怎么平衡“成本”与“响应速度”?欢迎评论区贴出你的压测数据和调参经验,一起把 qwen-agent 玩出花儿。