背景痛点:客服被“问爆”的日常
- 闲鱼每天高峰时段咨询量可达3000+ 条/小时,其中 60% 是“还能便宜吗”“包邮吗”这类重复问题,人工客服来回粘贴模板,手指都快抽筋。
- 平台活动大促时,响应延迟飙升到2-3 分钟,买家等不耐烦直接流失,卖家也投诉“官方客服不回我”。
- 新人客服培训周期至少两周,离职率却高达 30%,公司一年在人力上烧掉大几十万,老板天天喊“降本增效”。
一句话:用纯人肉扛高并发,既贵又慢,还容易出错。
技术选型:规则引擎 vs 机器学习
| 维度 | 规则引擎(if-else) | 机器学习(Rasa+FastAPI) |
|---|---|---|
| 开发速度 | 原型快,一周能跑 | 首版 2-3 周,但后期可迭代 |
| 维护成本 | 规则>500 条后人看不懂 | 意图、槽位集中管理,可回测 |
| 泛化能力 | 换个问法就挂 | 模型可自动扩句,鲁棒性高 |
| 性能 | 单机 qps 高,但逻辑臃肿 | 异步+缓存,线上 2000 qps 稳 |
| 部署 | 直接打包脚本 | Docker+CI,一次配置复用 |
结论:
- MVP 验证阶段可以先用 100 条规则顶住,但正式对外服务必须上 ML 方案。
- 本文直接给生产级组合:Rasa 3.x + FastAPI + Redis + PostgreSQL,一次到位。
整体架构图
核心实现拆解
1. 用 Rasa NLU 搞定意图识别
闲鱼场景常见意图(共 18 类):
ask_price:还能便宜吗ask_postage:包邮吗ask_defect:有瑕疵吗bargain:砍价deliver_time:多久发货
训练数据格式(nlu.yml 片段):
version: "3.1" nlu: - intent: ask_price examples: | - 最低多少 - 能便宜20吗 - 少30立刻拍训练命令:
rasa train --fixed-model-name xianyu_bot训练完在results/intent_report.json看宏平均 F1,>0.92 即可上线;不够就继续扩句。
2. 基于 Redis 做对话状态跟踪(DST)
多轮砍价场景示例:
用户:最低多少?
Bot:亲,可以给您减 10 元哦。
用户:再少 10 元?
Bot:抱歉,已经是最低价啦。
需要记录上一轮已优惠金额,否则就穿帮。Redis key 设计:
chat:{user_id}:{item_id} -> hash - intent_stack # 意图栈,json 列表 - discount # 已优惠金额 - ttl # 过期时间戳核心代码(utils/dialog_state.py):
import json import redis from typing import Dict, Any POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, db=1, decode_responses=True) rdb = redis.Redis(connection_pool=POOL) def get_state(user_id: str, item_id: str) -> Dict[str, Any]: key = f"chat:{user_id}:{item_id}" raw = rdb.hgetall(key) if not raw: return {"intent_stack": [], "discount": 0, "ttl": 0} raw["intent_stack"] = json.loads(raw["intent_stack"]) return raw def set_state(user_id: str, item_id: str, data: Dict[str, Any], ex=600): key = f"chat:{user_id}:{item_id}" data["intent_stack"] = json.dumps(data["intent_stack"]) pipe = rdb.pipeline() pipe.hset(key, mapping=data) pipe.expire(key, ex) pipe.execute()时间复杂度:O(1),空间 O(n) n 为槽位数量,可忽略。
3. 集成闲鱼 API 实时查商品
官方未开放公开 API,可用“内部运营接口”或RPA 方案(登录态 Cookie + 签名算法)。为避坑,建议:
- 统一封装
xianyu_client.py,所有外部调用走同一函数,方便加缓存、降级。 - 对热点商品做 30 秒 Redis 缓存,减少重复请求。
- 异常兜底:接口超时 1.2 秒 → 返回“客服暂时无法获取商品信息,请稍后再试”。
核心片段:
import httpx import asyncio from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)) async def fetch_item(item_id: str) -> dict: url = f"https://g-ali.xianyu.cn/item/{item_id}" async with httpx.AsyncClient(timeout=1.2) as client: r = await client.get(url, headers=signed_headers()) r.raise_for_status() return r.json()代码示例:对话管理核心(FastAPI 端点)
文件main.py,带异常捕获与日志:
import logging from fastapi import FastAPI, HTTPException from pydantic import BaseModel from utils.dialog_state import get_state, set_state from rasa import get_intent # 自己封的推理函数 logging.basicConfig(level=logging.INFO) logger = logging.getLogger("xianyu_bot") app = FastAPI(title="闲鱼智能客服") class ChatReq(BaseModel): user_id: str item_id: str text: str @app.post("/chat") async def chat(req: ChatReq): try: state = get_state(req.user_id, req.item_id) intent = await get_intent(req.text) # 调用 Rasa NLU state["intent_stack"].append(intent) # 根据意图生成回复 answer = craft_answer(intent, state) # 更新状态 set_state(req.user_id, req.item_id, state) logger.info(f"uid={req.user_id} iid={req.item_id} intent={intent}") return {"answer": answer} except Exception as e: logger.exception("chat error") raise HTTPException(status_code=500, detail="系统开小差,请稍后再试")craft_answer里写策略即可,比如砍价超过上限就拒绝,并记录state["discount"]。
性能优化:异步 IO 让并发起飞
- FastAPI 原生支持 async,IO 等待阶段线程不会阻塞,单容器 4U8G 可跑到2500 qps(压测脚本:wrk -t8 -c200 -d30s)。
- Rasa NLU 推理默认是 CPU 串行,可开启
SANIC_WORKERS=4多进程,或把模型转成 ONNX + ONNXRuntime-GPU,延迟从 180 ms 降到 40 ms。 - Redis 用 pipeline 批量读写,减少 RTT;另外把热点 key 放到本地 LRU 缓存(如
cachetools.TTLCache),命中率 95% 时 QPS 再翻一倍。
避坑指南:生产踩坑血泪史
意图识别准确率掉至 70%
原因:口语化、错别字。
解法:- 用
Rasa NLU的RegexFeaturizer+CountVectorsFeaturizer组合,先把数字、邮费、便宜等关键词做正则强化。 - 收集线上日志,每周人工标注 500 条,增量学习,准确率回升到 92%。
- 用
对话超时
用户 10 分钟没回,Redis key 自动过期,回来继续问“还能便宜吗”,Bot 忘了之前优惠。
解法:- 过期后把状态持久化到 PostgreSQL,用户再说话时先查库恢复,体验无感。
敏感词过滤
买家发“微信”“QQ”想绕开平台,不拦会被处罚。
解法:- 采用双数组 Trie + AC 自动机,敏感词 1 万条,单次过滤 <2 ms;命中后直接回复“平台外联系方式禁止发送哦~”。
可扩展方向
多语言
把 Rasa pipeline 里的LanguageModelFeaturizer换成bert-base-chinese+xlm-roberta-base并行,前端带lang=auto参数,根据用户输入首句自动检测语言,路由到对应模型。情感分析
在 NLU 后加一层sentiment字段,愤怒值 >0.7 直接转人工,并高亮“用户情绪激动”,客服提前准备安抚话术,差评率可降 15%。语音输入
接入阿里一句话识别,把 ASR 文本直接送进现有/chat接口,回复再用 TTS 播放,适合“开车党”挂闲鱼。
部署小贴士
- Docker 化:Rasa、FastAPI 各一个容器,用
docker-compose编排;nginx 做反向代理与限流。 - CI/CD:GitHub Actions → 阿里云镜像仓库 → ACK 集群滚动升级,平均 3 分钟完成热更新。
- 监控:Prometheus + Grafana,核心看仨指标:
- intent_latency
- cache_hit_ratio
- escalate_rate(转人工比例)
小结
整套方案跑下来,客服响应时间从 90 秒降到 12 秒,人力节省 40%,大促再也不是“全员通宵回消息”。
如果你也维护电商客服,被重复问题折磨到怀疑人生,不妨按本文步骤先搭个 MVP,再逐步迭代。
下一步,试试把情感分析和多语言加进去,让机器人不仅会回消息,还会“读空气”,那就真的离“无人客服”不远了。