离线评测涨了 3 个点,上线后用户投诉变多——这几乎是每个模型团队都会遇到一次的落差。它不是"评测做错了",而是离线与线上量的根本不是同一件事。架构视角要做的,是把这个落差拆成可命名的来源,并在发布流程里为每个来源留一道闸。
1. 落差的五个来源
| 来源 | 机制 | 表现 |
|---|---|---|
| 数据分布 | 评测集来自旧流量或人工编写 | 新场景、新话术上直接退化 |
| 评测集污染 | 训练数据里混入了评测题 | 离线分数虚高,线上断崖 |
| 指标与业务错位 | 指标可算,但不是用户在意的东西 | 分数涨、留存跌 |
| 交互效应 | 多轮对话、上下文、工具返回改变行为 | 单轮评测无法覆盖 |
| 用户适应 | 用户学会绕过或改写提问 | 上线一段时间后指标缓慢下滑 |
五个里只有第二个是"技术错误",其余都是测量设计问题。因此对策不是更努力地调模型,而是让测量本身分层。
2. 评测集的四个要求
| 要求 | 具体做法 | 缺失后果 |
|---|---|---|
| 代表性 | 从真实流量分层抽样,标注来源与时间 | 分数与现实脱节 |
| 难度分层 | 简单/中等/困难各占一定比例,分开报 | 平均分掩盖困难档崩溃 |
| 标注一致性 | 双人标注 + 仲裁,抽检一致率 | 噪声大到无法判断版本差异 |
| 可扩充 | 按场景标签可持续追加,版本化 | 上线半年后评测集过期 |
{"id":"q-001","input":"...","reference":"...","tags":["refund","multi-turn"],"difficulty":"hard","source":"prod-2026-03","split":"golden"}
{"id":"q-002","input":"...","reference":null,"tags":["open-ended"],"difficulty":"medium","source":"synthetic","split":"dev"}
"自己造题自己考"的陷阱:出题人知道模型会怎么答,题目会不自觉地朝模型擅长的方式写。缓解手段是让出题与调优由不同人负责,并把一部分题目锁进只读的 golden 集,任何人不得用于迭代。
3. 指标的适用边界
| 指标 | 适用 | 失效条件 |
|---|---|---|
| 准确率 | 类别均衡、单标签 | 类别不平衡时被多数类主导 |
| F1 / Macro-F1 | 需要兼顾查准查全 | 类别极多且样本极少时方差巨大 |
| BLEU / ROUGE | 参考译文高度相似 | 开放生成、允许多种正确表达 |
| LLM-as-judge | 大规模、开放式 | 位置偏差、长度偏好、对自家模型偏心 |
| 人工评分 | 小样本、高价值判断 | 成本高、评分者间一致性难保证 |
生成任务里 BLEU 类指标与人类判断的相关性通常只在"翻译/摘要且参考唯一"时成立,拿来评对话或创作类任务,等于用错的尺子量。LLM-as-judge 必须做校准:与人工评分比对若干条,确认排序一致再扩大使用;同时固定 judge 的提示与模型版本,否则指标会随 judge 一起漂移。
4. A/B 与灰度在模型场景的特殊性
| 特殊性 | 机制 | 应对 |
|---|---|---|
| 同一用户多次请求不一致 | 每次请求随机分流,同一人多轮对话被切到两个版本 | 按用户/会话稳定哈希分流 |
| 实验污染 | 用户看到旧版本输出后再评价新版本 | 以会话为实验单位,不跨版本续聊 |
| 指标延迟 | 转化、留存类指标要等数天 | 先看代理指标,再看终局指标 |
| 交互反馈 | 用户行为随界面变化,不是纯模型效果 | 固定 UI,只变模型 |
import hashlib
def bucket(user_id: str, salt: str = "exp-2026-11", mod: int = 100) -> int:
# 稳定分流:同一用户恒定落在同一桶,跨请求一致
return int(hashlib.sha256(f"{salt}:{user_id}".encode()).hexdigest()[:8], 16) % mod
实验单位的选择决定结论有效性:按请求分流测的是"平均输出质量",按用户分流测的是"体验变化"。两者结论经常不一致,报告里必须写清用的是哪一种。
5. 生成任务的分段评估
无唯一答案的任务,整体打分几乎没有信息量。做法是按维度拆开,各维度独立定义可判定的标准:
| 维度 | 判定方式 | 常见失败 |
|---|---|---|
| 事实正确性 | 与检索片段比对、可核查引用 | 看似合理但无据 |
| 完整性 | 是否覆盖必答子问题清单 | 漏条件、漏例外 |
| 格式约束 | 结构化解析 + 字段校验 | JSON 截断、字段缺失 |
| 多轮一致性 | 与前文约束不冲突 | 自我矛盾、遗忘设定 |
| 长文连贯 | 分段摘要与全文主题比对 | 前后段落重复或漂移 |
{"rubric_version":"r3","dims":[
{"name":"faithfulness","weight":0.4,"check":"every claim has a retrieved span"},
{"name":"completeness","weight":0.3,"check":"all required_subquestions covered"},
{"name":"format","weight":0.2,"check":"schema_valid == true"},
{"name":"consistency","weight":0.1,"check":"no contradiction with prior turns"}
]}
分段评估的额外收益是可定位:总分掉了,能看出是事实性还是完整性,而不是只知道"变差了"。
6. 回归防护:黄金集、版本对比与显著性
三个动作缺一不可:固定黄金集、同条件跑新旧版本、判断差异是否超出噪声。
import random
def bootstrap_ci(a, b, n=2000, alpha=0.05):
diffs = []
for _ in range(n):
sa = [random.choice(a) for _ in a]
sb = [random.choice(b) for _ in b]
diffs.append(sum(sb)/len(sb) - sum(sa)/len(sa))
diffs.sort()
return diffs[int(n*alpha/2)], diffs[int(n*(1-alpha/2))] # 区间跨 0 即不显著
小样本上的 1~2 个点差异经常落在置信区间内。没有显著性判断的"提升"只是随机数,把它写进发布说明,会在下一次无法复现时透支团队信任。
7. 发布策略与版本管理
| 策略 | 能看到什么 | 风险 |
|---|---|---|
| 影子流量 | 真实输入下的输出差异,不影响用户 | 看不到用户反馈 |
| 小流量灰度 | 真实反馈与业务指标 | 需要稳定分流与足够样本 |
| 全量切换 + 回滚开关 | 速度最快 | 回滚窗口短,故障面大 |
release:
model: gpt-x@2026-10-01 # 模型快照,不用 "latest"
prompt: p-2026-11-03-r2 # 提示模板版本
index: kb-2026-11-05 # 检索索引快照
rollback_to: {model: gpt-x@2026-09-15, prompt: p-2026-10-20-r1, index: kb-2026-10-28}
可回滚的前提是三元组一起版本化。只回滚模型而索引已更新,等于换了个系统,问题往往依旧。
8. 线上监控看什么
| 指标 | 为什么看 | 异常信号 |
|---|---|---|
| 失败率(含超时、限流) | 最先反映依赖与容量 | 突增且集中某模型/某地域 |
| 拒答率 | 安全策略或上下文缺失 | 与提示版本变更同步上升 |
| 人工介入率 | 兜底成本 | 缓慢上升常是先兆 |
| 每请求成本 | token 与调用次数 | 提示变长、重试增多 |
| 输出长度分布 | 截断与啰嗦 | 长度骤降 = 截断,骤升 = 失控 |
| 用户行为代理指标 | 复制、重问、放弃 | 重问率上升是最可靠的坏信号 |
8.1 评测的组织成本
评测不是写完脚本就结束的事项。三类隐性成本决定它能否长期存在:
| 成本 | 来源 | 控制手段 |
|---|---|---|
| 标注 | 双人标注 + 仲裁,困难样本耗时最长 | 优先标注"模型分歧样本",不平均用力 |
| 评测运行 | 大规模跑分要消耗同等的推理与人工预算 | 分档运行:dev 集每日、golden 集发版前 |
| 维护 | 场景变化导致题目过期、口径漂移 | 设题集 owner,按季度复审标签与参考 |
发版前必跑:golden 集(只读)+ 回归集(近两版失败样本)
日常跑:dev 集(快速反馈,允许噪声)
不跑:把 golden 集塞进每次迭代——它会迅速被"过度拟合"成调优目标
判据只有一条:评测集的使用频率与它的可信度成反比。用得越频繁的集合,越容易被当作优化目标,越早失去测量能力。
9. 三个版本维度的组合爆炸
模型、提示、检索索引各自迭代,组合数是乘积。正交化的做法是一次只动一个维度,其余冻结:
| 变更 | 必须冻结 | 观察重点 |
|---|---|---|
| 换模型 | 提示、索引 | 输出长度、格式合规、成本 |
| 改提示 | 模型、索引 | 拒答率、完整性 |
| 重建索引 | 模型、提示 | 召回质量、引用准确率 |
三者同时变更时,任何指标变化都无法归因。发布记录里写明"本次只动了哪一维",比写"整体优化"有用得多。
10. 下线与回滚的判断依据
回滚应当有预设阈值,而不是开会决定:核心指标劣于基线超过置信区间、失败率超过约定上限、成本涨幅超过预算比例、或出现新类别的严重错误(如幻觉引用)。触发即回滚,事后再分析——把"是否回滚"变成判断题,通常会在争论中错过窗口。
排错
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 离线高、线上低 | 评测集污染或分布不符 | 换新抽样集复测,检查训练数据是否含题 |
| 指标涨但用户不满 | 指标与业务目标错位 | 引入行为代理指标,重定优化目标 |
| A/B 结论反复 | 按请求分流、样本不足 | 改按用户分流,先算所需样本量 |
| 同一用户答案风格突变 | 分流不稳定 | 用稳定哈希,锁会话 |
| 版本对比无法归因 | 多维同时变更 | 冻结其余维度,重跑对照 |
| 上线两周后缓慢下滑 | 用户适应、输入分布漂移 | 建立周期性重采样与漂移监控 |
边界
| 场景 | 不必做的事 | 理由 |
|---|---|---|
| 无评测集 | 直接上线 | 没有基线就无法判断回归,先建最小黄金集 |
| 低频任务(日均个位数) | A/B 实验 | 样本量永远不够,改用人工抽检 |
| 内部工具、用户是同事 | 复杂指标体系 | 人工抽检 + 反馈渠道更有效 |
| 一次性批处理 | 在线灰度 | 无在线流量,改为离线对比 + 抽样复核 |
| 提示微调(改错别字级) | 全量评测 | 成本高于风险,抽查即可 |
自测
- 五个落差来源里,哪些属于测量设计问题而非模型问题?
- 为什么"自己造题自己考"会系统性高估?如何缓解?
- BLEU 类指标在什么条件下才与人类判断相关?
- 按请求分流与按用户分流,测的分别是什么?
- 显著性判断缺失时,1~2 个点的"提升"意味着什么?
- 模型、提示、索引三者为何必须一起版本化?