传统服务发布前跑测试,AI 功能却常常只在聊天窗口试几条。模型供应商更新、系统提示改一段、检索索引重建,都可能让某类任务悄悄退化。一次我们优化回答长度,平均满意度看起来上升,引用完整率却下降,研究用户需要自己重新找出处。
后来 Evals 不再是一份实验报告,而是发布流水线的门禁。每一项候选配置都有不可变版本,按离线、影子和线上阶段逐层证明。
图 1:离线评估阻止已知回归,线上预算捕捉分布变化;两者不能互相替代。
候选版本必须完整可重放
type AiReleaseCandidate = {
id: string;
model: string;
modelParameters: Record<string, number | string>;
systemPromptVersion: string;
toolRegistryVersion: string;
retrievalIndexVersion: string;
policyVersion: string;
codeSha: string;
};只写“升级到新模型”无法重放。工具描述、权限策略和索引变化都会改变结果,必须和候选一起冻结。
门禁按风险分层,不用平均分放行
| 层级 | 用例 | 规则 |
|---|---|---|
| G0 确定性 | Schema、权限、工具参数 | 必须 100% 通过 |
| G1 核心任务 | 高频真实任务与引用 | 关键切片不得退化 |
| G2 对抗安全 | 提示注入、越权、秘密 | 任一严重失败阻止发布 |
| G3 质量成本 | 正确性、延迟、token、工具次数 | 在预算内做多目标比较 |
| G4 人工评审 | 边界案例与新能力 | 双盲抽样、记录分歧 |
一个总分可能用文风提升抵消权限回归,这是不可接受的。门禁对关键维度使用硬阈值,其他维度才做权衡。
结果要带统计不确定性
样本量小时,86% 到 88% 可能只是波动。报告展示样本数、置信区间和逐 case 差异;对同一问题使用配对比较,优先看哪些已知案例变好或变坏,而不是只看平均。
失败必须保存输入、候选输出、工具轨迹、评分依据和随机种子/采样参数。否则下一次无法判断是模型随机性、外部工具还是评估器变化。
影子流量验证真实分布
通过离线门禁后,候选在不影响用户的情况下处理一部分脱敏生产请求。它不能执行真实写工具,只能走沙箱或回放录制结果。我们比较任务类型分布、工具计划、延迟、成本和拒答差异。
影子结果仍需遵守数据权限和保留策略,不因为“不展示给用户”就可以无限复制生产输入。
小流量阶段需要自动回退
真实发布使用稳定分桶。线上观察任务成功率、人工接管率、工具失败、权限拒绝、引用打开率、P95 延迟和单任务成本。错误预算快速燃烧或安全指标越界时,控制面把流量切回已知稳定候选。
模型响应不可完全复现,回退指的是配置和流量,不承诺已经发生的外部副作用自动消失。写工具仍需要幂等和补偿。
线上失败回到评估集
用户修正、人工接管和高成本任务进入候选池,经脱敏、聚类和人工标注后成为新 case。生产回流不能直接修改门禁集,避免错误标签和攻击输入污染;数据集升级有版本与评审。
这套门禁不会消除 AI 的不确定性,但让每次发布承担的风险可见。团队讨论从“新模型感觉更好”变成“核心任务无退化,权限用例全过,成本增加 8%,影子流量发现两类新失败”。能够这样交付,Evals 才从研究工具变成软件工程的一部分。
门禁的数据基础是评估集本身的来源和质量——120 个真实问题的构建、分维度评分与防过拟合,我在RAG 评估别再问“感觉怎么样”里记录过;而门禁真正拦下的那类“代码看起来完整”的回归,与AI 辅助编程之后的代码评审关注的是同一件事的两端:生成侧的证据包,和发布侧的回归集。