传统服务发布前跑测试,AI 功能却常常只在聊天窗口试几条。模型供应商更新、系统提示改一段、检索索引重建,都可能让某类任务悄悄退化。一次我们优化回答长度,平均满意度看起来上升,引用完整率却下降,研究用户需要自己重新找出处。

后来 Evals 不再是一份实验报告,而是发布流水线的门禁。每一项候选配置都有不可变版本,按离线、影子和线上阶段逐层证明。

AI 功能从固定回归、对抗权限、影子流量到小流量和自动回退的门禁流程

图 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 辅助编程之后的代码评审关注的是同一件事的两端:生成侧的证据包,和发布侧的回归集。