背景痛点:交付质量像“抽盲盒”
过去半年,我帮三家客户把大模型客服从 Demo 搬到生产环境,发现一个共同槽点:上线前人工“盲测”都说好,一放量就翻车。
根本原因是缺乏统一标尺——今天用 BLEU,明天用人工满意度,后天干脆看客服坐席有没有被投诉。指标乱 → 版本回滚频繁 → 业务方信心被耗尽。
于是我们把过往踩过的坑梳理成“五维测评体系”,让每一次迭代都有可量化的及格线,而不是靠“感觉”。
技术方案:L1-L5 分级测评框架
先把“好客服”拆成 5 个可测维度:
- 响应准确率(Answer Accuracy)
- 意图识别率(Intent Recall)
- 多轮对话连贯性(Context Consistency)
- 异常处理能力(Exception Recovery)
- 知识库覆盖度(KB Coverage)
每个维度按得分划 5 级,L1 最低(玩具级),L5 最高(金融级)。
举个例子:意图识别率 L3 要求≥85%,L5 则≥95% 且拒识(低置信)<2%。业务方只要说“我们要 L4”,研发就能直接映射到数字,不再鸡同鸭讲。
人工评估 vs 自动化工具速览:
| 维度 | 人工评估 | Rasa Evaluation + 自研脚本 |
|---|---|---|
| 成本 | 高,需标注员 | 一次性脚本,可回归 |
| 速度 | 千条/人日 | 万条/小时 |
| 一致性 | 不同人差 10%↑ | 固定 seed 可复现 |
| 适合场景 | 冷启动、创意型回答 | 回归、 nightly 流水线 |
结论:用自动化跑 90% 的“体力活”,人工只审 Top-K 风险 case,成本立降 70%。
核心实现:让指标真正跑起来
1. 意图识别置信度阈值自优化
以下代码每天凌晨用前一日日志重算阈值,保证 F1 最大。
已加类型注解与异常处理,可直接塞进 Airflow DAG。
# intent_threshold_tuner.py import json, torch, numpy as np from typing import Tuple from sklearn.metrics import f1_score def load_log(log_path: str) -> Tuple[list, list]: """返回文本列表、真实意图列表""" texts, labels = [], [] with open(log_path, encoding="utf-8") as f: for line in f: item = json.loads(line) texts.append(item["text"]) labels.append(item["intent"]) return texts, labels def predict_proba(model, tokenizer, texts): """返回 softmax 后概率矩阵""" model.eval() probs = [] with torch.no_grad(): for t in texts: inputs = tokenizer(t, return_tensors="pt", truncation=True, max_length=128) logits = model(**inputs).logits prob = torch.softmax(logits, dim=-1).cpu().numpy()[0] probs.append(prob) return np.array(probs) def best_threshold(y_true, y_prob, n_search=100): best_f1, best_thr = 0, 0.5 for thr in np.linspace(0.1, 0.9, n_search): y_pred = (y_prob.max(axis=1) > thr).astype(int) f1 = f1_score(y_true, y_pred, average="micro") if f1 > best_f1: best_f1, best_thr = f1, thr return best_thr if __name__ == "__main__": try: texts, labels = load_log("daily_chat.log") prob_matrix = predict_proba(model, tokenizer, texts) thr = best_threshold(labels, prob_matrix) with open("threshold.txt", "w") as f: f.write(str(thr)) print(f"Optimized threshold={thr:.3f}") except Exception as e: print("Threshold tuner error:", e)2. 对话状态机图解
多轮场景最怕“跑飞”。我们用有限状态机(FSM)把订单查询、退换货等拆成独立状态,任何异常都回到“兜底”节点,保证对话不越界。
代码层用 Python 的transitions库,状态与槽位一起快照进 Redis,7 天过期,方便灰度回滚。
生产考量:并发与合规都要硬
1. 200 并发压测脚本
使用 locust,模拟 200 虚拟用户、每秒 5 条消息,持续 15 分钟。关键指标:
- P99 响应 < 1.2 s
- 错误率 < 0.5 %
- GPU 利用率 70-80 %(低于 60 % 说明浪费,高于 85 % 容易抖动)
locust -f stress_test.py -u 200 -r 10 -t 15m --html report.html2. 敏感词 & 数据脱敏
- 敏感词:采用双数组 Trie + 拼音/简码联合匹配,毫秒级。
- 脱敏:正则先抓手机号、身份证,再用同态掩码(保留前三后四),日志落盘即脱敏,避免事后补救。
避坑指南:GDPR 与热更新
1. 对话日志存储合规要点
- 明示同意:用户首次进入弹窗勾选,日志打标记。
- 可撤回:提供 DELETE /chat-history?user_id=xxx 接口,48 小时内清除冷备。
- 数据不出境:若用海外云,务必开 VPC 终端节点,审计记录留 3 年。
2. 模型热更新灰度策略
线上双模型 A/B:
- 默认桶 95 % 流量走旧模型
- 新模型先放 5 % 流量,监控 L1-L5 指标 30 分钟无降级再全量
- 任何核心指标掉 2 % 以上,自动回滚并熔断发版
互动环节:跑一跑你的日志
我们已把测评脚本开源,支持 CSV/JSON 两种格式。
步骤如下:
- 克隆仓库并安装依赖
- 把你的客服日志放到
data/目录 - 运行
python evaluate.py --level L4 --report md - 把生成的 Markdown 报告贴到评论区,一起切磋如何再提 2 % 的准确率!
写在最后
指标不是目的,稳定交付才是。
当你能用“L3 还是 L4”一句话对齐质量,开发和业务就再也不用互相甩锅。
希望这套分级框架和脚本,能让你下一次上线不再“抽盲盒”。如果跑脚本遇到奇葩报错,欢迎带日志截图一起交流,咱们评论区见。