系列 《评测、可观测与可追溯:怎么知道改了之后更好了》 第 4 / 4 篇 进阶

系列总览 — 为什么这样组织、读它需要什么、读完能做什么

学习地图 — AI 应用工程师学习地图 第 5 / 7 个系列

  1. 评测集——AI 应用的单元测试
  2. 评分——规则、LLM-as-judge 与人
  3. 评什么——单步、RAG、agent 与多轮的指标矩阵
  4. 回归与门禁——CI、供应商静默升级、A/B 与在线评测

上一篇:评什么——单步、RAG、agent 与多轮的指标矩阵

评测集、评分、指标三样齐了,这一篇讲怎么把它们接进发布流程:每次改动(prompt、模型、effort、schema、工具集、检索配置、harness 版本)过一道门禁;供应商在你不动的时候换了模型怎么发现;线上怎么做 A/B 与在线评测;离线分数与在线指标不一致时信谁。

《Prompt 与上下文工程(06):prompt 当代码管——版本、评测、A/B,以及上下文 vs 检索》画了 prompt 的生命周期图(编辑 → 版本 → 门禁 → 灰度 → 生产 → 监控 → 模型升级重跑),这一篇给它完整的实现:门禁的判据、逐条 diff、噪声阈值、探针集、影子模式、A/B 的分流与归因、隐式反馈的种类、离线—在线相关性的校验。

本篇要回答的核心问题是:

门禁怎么设——判据、阈值、逐条 diff、哪些改动触发?1 供应商静默升级怎么发现?2 A/B 与在线评测怎么做,离线与在线不一致时怎么办?3

一、总览

1. 从改动到生产

%% 图:改动从提交到生产的流程:新版本过评测门禁,高风险改动先影子模式,灰度 1–5% 后全量,探针发现漂移再回到门禁
flowchart TB
    CH["改动:prompt · 模型 / effort · schema · 工具集 · 检索配置 · harness 版本 · judge / rubric"] --> V["新版本(不可变)+ 六元组记录"]
    V --> G{"门禁:评测集 × k<br/>分层指标 ≥ 基线 − 噪声阈值<br/>逐条 diff · 成本 / 延迟不退化 · 红队集通过"}
    G -->|"不过"| CH
    G -->|"过"| S["影子模式(高风险改动)<br/>新旧并跑 · 只记录不生效 · 比差异"]
    S --> C["灰度 canary 1–5%<br/>trace 绑定版本 · 在线指标 · 隐式反馈"]
    C -->|"退化"| RB["回滚:标签指回旧版本"]
    C -->|"正常"| P["全量 production"]
    P --> M["监控 + 探针集每日跑<br/>供应商静默升级 · 分布漂移"]
    M -->|"漂移"| G
    AB["A/B:两个版本并行分流<br/>按版本聚合在线指标"] -.-> C

    classDef step fill:#fff7e0,stroke:#c98a00,stroke-width:2px,color:#222
    classDef gate fill:#eef6ff,stroke:#5b8fd6,color:#222
    class CH,V,S,C,P,M,RB,AB step
    class G gate

2. 本文的章节安排

第二章门禁;第三章供应商静默升级的检测;第四章影子模式与灰度;第五章 A/B 与在线评测;第六章离线与在线的关系;第七章实践建议。

二、门禁

1. 什么触发

任何会改变模型行为的东西:prompt 文本、模板结构、few-shot、模型 id 或快照、effort、temperature、schema、工具定义与描述、检索配置(分块、embedding、rerank、k)、上下文管理参数(压缩阈值、卸载阈值)、harness 版本、judge 或 rubric 本身(改了尺子要重量基线)。原则:改动 = 发布,都过门禁。

2. 判据

回归门禁的判据
判据 内容
分层质量 每层的主指标 ≥ 当前生产版本 − 噪声阈值;噪声阈值从 k 次重复的方差算(或基线自身重跑两次的差)
逐条 diff 列出由对变错的用例(含类型与理由)——比总分更有信息;由错变对的也列(确认不是巧合)
形式 格式遵循率、schema 通过率、拒答两半不退化
成本与延迟 每次 / 每任务成本、TTFT、总时长 p50 / p95 不超阈值(或有意为之并记录)
安全 红队集通过率不降
hold-out 门禁以 hold-out 为准,开发集只参考(第一篇)
judge 校准 用作门禁指标的 judge 已过第二篇的协议;judge 版本钉住

3. 噪声阈值

两个版本各跑 k 次的均值差要大于噪声才算变化。粗略:基线自身重跑两次的差就是噪声的量级;或从 k 次的标准差算均值的标准误 \(\sigma / \sqrt{k}\),阈值取两个标准误。评测集小、k 小时阈值大——这是”几十条起、k = 3–5”的代价,也是评测集要长的理由。

%% 图:噪声阈值决定多小的差异算变化——基线 0.870 重跑五次落在 0.855–0.885 之间(标准误约 0.012,两个标准误 0.024);新版本 0.885 看起来涨了 1.5 个点,但落在噪声带里,不能算进步;新版本 0.905 超出噪声带才算真变化;评测集从 50 条长到 200 条,噪声带缩窄一半
%%{init: {"xyChart": {"width": 760, "height": 340, "plotReservedSpacePercent": 60}, "themeVariables": {"xyChart": {"plotColorPalette": "#c0392b, #5b8fd6, #4d9a5c"}}}}%%
xychart-beta
    title "基线五次重跑 vs 两个新版本(通过率)"
    x-axis ["基线 #1", "基线 #2", "基线 #3", "基线 #4", "基线 #5", "新版 A", "新版 B"]
    y-axis "通过率" 0.84 --> 0.92
    bar [0.87, 0.858, 0.882, 0.865, 0.875, 0.885, 0.905]
    line [0.894, 0.894, 0.894, 0.894, 0.894, 0.894, 0.894]

蓝线是基线均值 + 两个标准误(0.894)。新版 A 的 0.885 在线下——是噪声;新版 B 的 0.905 在线上——是变化。

4. CI 的形态

promptfoo、Braintrust、Langfuse 的评测运行、或自写脚本:配置列出评测集版本、prompt 版本、模型、judge、阈值;命令输出分层指标、逐条 diff、成本;阈值不过 CI 失败。运行时间:100 条 × 5 次 × 几秒 = 几分钟到十几分钟(可并行);成本几美元。agent 评测慢(每条几分钟)——用录制回放处理非模型改动(第六篇),只在改 prompt / 模型时全量重跑。

5. 记录

每次门禁的结果是一条记录:六元组(评测集 × prompt × 模型快照 × 工具集 × 检索配置 × judge / rubric)+ 分层指标 + 成本 + 逐条 diff 的链接。它是第六篇”版本 diff”的数据源。

三、供应商静默升级

1. 问题

《模型作为组件(01):失效模式——非确定性、幻觉、上下文与越界,把「模型会出错」拆成七条可检测的性质》与第五篇:同一模型名下的行为会变——供应商更新了快照、改了默认参数、换了 tokenizer、路由到不同的后端(DeepSeek 把 V4-Pro 路由到 V4.1 Flash 的例子)。你没改任何东西,评测分数变了、成本变了、格式遵循率变了。

2. 检测

  • 钉快照:用带日期的模型 id(-2026-06-12 一类)而不是浮动别名;浮动别名的变更不会通知你。
  • 探针集每日跑:几十条对措辞与格式敏感的用例(不是全量评测集)每天定时跑,记分数、格式遵循率、平均输出长度、token 数、TTFT;任一漂出区间告警。它是”模型指纹”。
  • 订阅变更通知:供应商的 changelog、弃用邮件、状态页;《模型作为组件(05):选型——榜单的失真、自己的评测集与供应商的弃用周期》的弃用周期表。
  • 生产指标的突变:格式失败率、平均输出长度、成本 / 请求突然变化,且时间线上没有你的部署——第六篇的版本 diff 对齐会显示”没有对应的变更”。

3. 应对

探针告警后:全量评测确认影响范围;若退化,切到钉住的旧快照(如果还在)或 fallback(《模型作为组件(06):客户端工程——重试、超时、幂等、限流与流式解析》);把这次变更作为一次”外部发布”记录;通知供应商。这是为什么评测集要定期重跑而不只在改动时跑(第一篇)。

%% 图:探针集是模型指纹——几十条对措辞与格式敏感的用例每天定时跑,记分数、格式遵循率、平均输出长度、token 数、TTFT;某天格式遵循率从 99% 掉到 91%、平均输出长度从 210 涨到 290 token,而部署时间线上没有任何变更——这就是供应商静默升级的样子
%%{init: {"xyChart": {"width": 760, "height": 340, "plotReservedSpacePercent": 60}, "themeVariables": {"xyChart": {"plotColorPalette": "#c0392b, #5b8fd6, #4d9a5c"}}}}%%
xychart-beta
    title "探针集每日指标:第 12 天供应商换了东西,你没改任何代码"
    x-axis "天" [1, 3, 5, 7, 9, 11, 12, 13, 15]
    y-axis "格式遵循率 % / 平均输出长度(token ÷ 3)" 60 --> 100
    line [99, 99, 98, 99, 99, 99, 91, 90, 91]
    line [70, 71, 70, 70, 69, 70, 97, 96, 97]

红线格式遵循率,蓝线平均输出长度(÷ 3 以画在同一坐标)。两条线在第 12 天同时跳,部署记录里那天是空的——先查供应商的 changelog,再全量评测确认影响范围。

四、影子模式与灰度

1. 影子模式

高风险改动(换模型、换 harness 版本、改权限策略)在灰度之前多一步:新版本与旧版本并行处理同一批真实请求,新版本的输出只记录不返回给用户;比较两者的差异(judge 或规则)、成本、延迟。真实分布下的对比,零用户风险。代价是双倍的推理成本(只在影子期间)。物理世界的影子模式(第七篇)是同一思想。

2. 灰度

标签发布(《Prompt 与上下文工程(06):prompt 当代码管——版本、评测、A/B,以及上下文 vs 检索》):prod-canary 指向新版本、路由 1–5% 流量、trace 绑定版本;看在线指标与隐式反馈(第五章)一到几天;正常则移 production,退化则移回。灰度期的两个陷阱:缓存冷启动(新前缀第一轮全写,TTFT 与成本高是预期——《Prompt 与上下文工程(05):prompt caching 与上下文的排列》)与样本量(1% 流量的差异要几天才显著)。

3. 回滚

标签指回旧版本,秒级;但要确认旧版本依赖的东西还在(模型快照未下线、工具集兼容、会话状态可读——加密的压缩 item 换模型后不可用,《工具、Agent 与 harness(06):源码级对照——Codex、DeepSeek Harness、Claude Code 与 OpenHarness》)。

%% 图:一次高风险改动的发布路径——离线门禁过了 → 影子模式:新旧版本并行处理同一批真实请求、新版本只记录不返回、比较差异与成本(零用户风险,双倍推理成本)→ 灰度:prod-canary 标签指新版本、1–5% 流量、trace 绑版本、看在线指标一到几天(忽略头几分钟的缓存冷启动)→ 全量;任一阶段退化就把标签移回旧版本,回滚前确认旧快照还在
flowchart TB
    G["离线门禁通过<br/>分层 ≥ 基线 − 噪声 · 逐条 diff · 红队"] --> SH["影子模式(高风险改动)<br/>新旧并跑同一批真实请求 · 新版只记录不返回<br/>比差异 · 成本 · 延迟 · 零用户风险 · 双倍成本"]
    SH -->|"差异可解释"| CN["灰度:prod-canary → 新版本<br/>1–5% 流量 · 按用户固定分组 · trace 绑版本<br/>看在线指标 1–几天(头几分钟缓存冷启动忽略)"]
    CN -->|"正常"| PROD["production 标签 → 新版本,100%"]
    SH -->|"退化"| RB["回滚:标签移回旧版本(秒级)<br/>先确认:旧快照未下线 · 工具集兼容 · 会话状态可读"]
    CN -->|"退化"| RB
    PROD --> MON["监控 + 探针集每日"]
    MON -->|"漂移"| G
    classDef step fill:#fff7e0,stroke:#c98a00,color:#222
    classDef ok fill:#eefaf0,stroke:#4d9a5c,color:#222
    classDef bad fill:#fdecea,stroke:#c0392b,color:#222
    classDef n fill:#eef6ff,stroke:#5b8fd6,color:#222
    class G,SH,CN step
    class PROD ok
    class RB bad
    class MON n

五、A/B 与在线评测

1. A/B

两个版本各打标签、应用按用户或会话随机分流(同一用户固定一组,避免体验跳变)、trace 绑定版本、按版本聚合在线指标;显著性检验(样本量、时长);一次只变一个因素,否则归因不了。A/B 检验的是真实分布下的真实用户行为——离线评测检验的是样本上的质量;两者都要。

2. 在线信号

在线信号的分类
类 信号 含义
隐式 采纳(用了建议、合并了 PR、发送了草稿) 最强的正信号
隐式 修改后使用(改了多少) 部分正确;diff 大小是质量的连续信号
隐式 重试 / 重新生成、追问、放弃、转人工 负信号,强度递增
隐式 引用点击、展开详情、停留 弱信号,与产品形态相关
显式 点赞 / 点踩、评分、文字反馈 稀疏(1–5% 的用户给)、有选择偏差(不满的更爱给)
系统 格式失败、重试次数、拒答率、卫士触发、成本 不依赖用户

每个信号带 trace id 落库(第六篇的反馈绑定)。

3. 在线 judge

抽样把 judge 跑在真实流量上(按比例,成本可控),指标进面板;它覆盖离线集没有的分布,但 judge 在真实分布上的校准要另做(校准集要含真实流量的样本)。用户反馈与 judge 分的一致性是 judge 在线可信度的检验。

六、离线与在线

1. 不一致的四种原因

离线分数涨、在线指标不涨(或反过来):

离线与在线不一致的四种原因
原因 表现 办法
评测集分布过时 离线好的类型在线占比小 从近期流量重采、按在线分布加权
指标选错 离线的 judge 分与用户的采纳不相关 校验相关性;换成与用户行为相关的指标
judge 在线失准 离线校准集与真实分布不同 用真实样本重校
在线噪声 样本量小、季节性、其他改动同期 更长的 A/B、隔离变量

2. 相关性校验

定期(季度)算:离线指标的变化与在线指标的变化在历次发布上的相关性;某个离线指标与在线指标长期不相关,就不该用它做门禁。理想状态是离线评测能预测在线结果——那时门禁才真正有意义。

3. 信谁

短期信在线(它是真的),长期修离线(让它能预测在线)。在线指标有滞后与噪声,离线可重复、便宜、快——两者是不同阶段的工具,不是替代。

%% 图:历次发布上离线指标变化与在线指标变化的散点(示意)——judge 分的变化与用户采纳率的变化基本沿对角线走(相关),说明这个离线指标能预测在线、可以做门禁;ROUGE 的变化与采纳率几乎无关(点散成一团),它就该从门禁里撤掉;季度算一次
%%{init: {"xyChart": {"width": 760, "height": 340, "plotReservedSpacePercent": 60}, "themeVariables": {"xyChart": {"plotColorPalette": "#c0392b, #5b8fd6, #4d9a5c"}}}}%%
xychart-beta
    title "12 次发布:离线指标变化(横轴顺序)与在线采纳率变化(示意)"
    x-axis "发布(按 judge 分变化排序)" ["−4", "−3", "−2", "−1", "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7"]
    y-axis "在线采纳率变化(pp)" -5 --> 8
    line [-3.5, -2, -2.5, -1, 0.5, 0, 1.5, 2, 3.5, 4, 4.5, 6]
    line [2, -1, 3, -2, 1, 0, -1, 2, -2, 1, 0, 1]

红线是 judge 分(校准过)的变化对应的在线变化——单调上升,可以做门禁;蓝线是 ROUGE 的变化对应的在线变化——乱跳,不能。

七、实践建议

  1. 列触发清单:所有会改变模型行为的东西都进门禁,包括 judge 与 rubric 的改动。
  2. 门禁输出四样:分层指标与阈值、逐条 diff、成本延迟、红队通过率;hold-out 为准。
  3. 钉快照 + 探针集每日跑:几十条敏感用例,记分数、格式率、输出长度、TTFT,漂移告警。
  4. 高风险改动先影子再灰度;灰度期忽略头几分钟的缓存冷启动;回滚前确认旧版本依赖可用。
  5. A/B 一次一个因素,按用户固定分组;隐式信号全部带 trace id 落库。
  6. 季度校验离线—在线相关性,不相关的离线指标从门禁里撤。

八、本文小结

  • 改动 = 发布:prompt、模型 / effort、schema、工具集、检索配置、上下文参数、harness、judge / rubric 都触发门禁。
  • 门禁判据:分层质量 ≥ 基线 − 噪声阈值(从 k 次方差或基线重跑差算)、逐条 diff、形式不退化、成本延迟不超、红队通过、hold-out 为准、judge 已校准;结果记六元组。
  • 静默升级:钉快照、探针集每日跑(模型指纹:分数、格式率、长度、token、TTFT)、订阅通知、生产突变对齐时间线;退化切旧快照或 fallback,记为外部发布。
  • 影子模式(并跑只记录,零用户风险,双倍成本)→ 灰度(1–5%,trace 绑版本,忽略缓存冷启动,注意样本量)→ 回滚(移标签,确认依赖)。
  • A/B 一次一因素、按用户固定分组;在线信号隐式(采纳、修改、重试、追问、放弃、转人工、点击)、显式(稀疏有偏)、系统(格式、重试、拒答、卫士、成本),全带 trace id;在线 judge 抽样并另校准。
  • 离线与在线不一致的四原因(分布过时、指标错、judge 失准、噪声);季度校验相关性;短期信在线、长期修离线。

九、自测

  1. 团队改了 judge 的 rubric 让它更严,同一批输出的分数从 4.1 掉到 3.6,于是回滚了上周的 prompt 改动。哪里错了?

    答案

    改 rubric 是改尺子,尺子变了基线要重量——3.6 是新尺子下的分数,与旧尺子的 4.1 不可比,回滚 prompt 没有依据。正确做法:rubric 改动本身走门禁(用新 rubric 重评基线版本与新版本,比两者的差),判据是相对差而不是绝对分。详见第二章。

  2. 没有任何部署,格式遵循率从 99% 掉到 91%,平均输出长度涨 30%。最可能是什么?怎么确认与应对?

    答案

    供应商静默升级(浮动别名指向了新快照,或 tokenizer / 默认参数变了)。确认:探针集今日与昨日的分数与输出长度对比、供应商 changelog、版本 diff 时间线上无本地变更。应对:切到钉住的旧快照或 fallback,全量评测确认影响,记为外部发布,以后用带日期的模型 id。详见第三章。

  3. 换模型是高风险改动。影子模式与灰度各解决什么、各付什么代价?为什么顺序是先影子后灰度?

    答案

    影子:真实分布下新旧并跑、只记录不生效——零用户风险地看差异、成本、延迟;代价是影子期间双倍推理成本。灰度:1–5% 真实用户用新版本——看真实行为(采纳、重试)与隐式反馈;代价是这部分用户承担风险。先影子把明显的问题(格式、成本、大面积差异)拦在用户之前,再用灰度看行为——行为只有真用了才看得到。详见第四章。

  4. A/B 里 B 组的采纳率高 4%,但 B 组同时换了 prompt 与模型。能下结论吗?

    答案

    能说”B 组合更好”,不能归因到 prompt 或模型——一次变了两个因素。若要知道各自贡献,要做 A / A+prompt / A+model / B 的分组,或先固定一个再变另一个。另检查:按用户固定分组了吗、样本量与时长够显著吗、同期有没有其他改动。详见第五章。

  5. 过去六次发布里,离线 judge 分每次都涨,在线采纳率三涨三跌。说明什么?该怎么办?

    答案

    离线指标与在线结果不相关——评测集分布过时、judge 分与用户采纳不相关、或 judge 在真实分布上失准。办法:从近期流量重采评测集并按在线分布加权;把与采纳相关的指标(如引用正确率、任务完成)纳入门禁,撤掉不相关的;用真实流量样本重校 judge;在这之前门禁的结论不能作为上线依据,短期以在线为准。详见第六章。

  1. 触发:任何改变模型行为的东西——prompt 文本与结构、few-shot、模型 id / 快照、effort / temperature、schema、工具定义与描述、检索配置(分块、embedding、rerank、k)、上下文管理参数、harness 版本、judge 与 rubric 本身(改尺子要重量基线);改动 = 发布。判据:分层质量 ≥ 生产版本 − 噪声阈值(阈值从 k 次方差算标准误取两倍,或基线自身重跑两次的差);逐条 diff 列出由对变错与由错变对的用例;格式遵循率、schema 通过率、拒答两半不退化;成本与延迟 p50 / p95 不超阈值;红队集通过率不降;以 hold-out 为准;judge 已过校准且版本钉住。CI 用 promptfoo / Braintrust / Langfuse 或自写脚本,几分钟几美元,agent 评测用录制回放处理非模型改动;每次结果记六元组 + 分层指标 + 成本 + diff 链接。详见第二章。 ↩

  2. 同一模型名下供应商可能更新快照、改默认参数、换 tokenizer、路由到别的后端,你没改任何东西分数、成本、格式率就变了。检测:用带日期的快照 id 而不是浮动别名;探针集(几十条对措辞与格式敏感的用例)每日定时跑作为模型指纹,记分数、格式遵循率、平均输出长度、token 数、TTFT,漂出区间告警;订阅 changelog、弃用通知、状态页;生产指标突变(格式失败率、输出长度、成本 / 请求)在版本 diff 时间线上找不到对应的本地变更。应对:全量评测确认范围,退化则切回钉住的旧快照或 fallback,记为一次外部发布,通知供应商。这是评测集要定期重跑而不只在改动时跑的理由。详见第三章。 ↩

  3. A/B:两版本各打标签,按用户或会话随机且固定分组,trace 绑定版本,按版本聚合在线指标,做显著性检验,一次只变一个因素。在线信号:隐式——采纳(最强正)、修改后使用(diff 大小)、重试 / 追问 / 放弃 / 转人工(负,递增)、引用点击(弱);显式——点赞点踩评分(稀疏 1–5%、有选择偏差);系统——格式失败、重试、拒答率、卫士、成本;全带 trace id 落库。在线 judge 按比例抽样跑真实流量,要用真实样本另校准,与用户反馈的一致性是它的检验。离线与在线不一致的四原因:评测集分布过时(重采、按在线分布加权)、指标选错(校验相关性、换与用户行为相关的)、judge 在线失准(重校)、在线噪声(更长 A/B、隔离变量);季度算历次发布上离线变化与在线变化的相关性,不相关的离线指标撤出门禁;短期信在线、长期修离线让它能预测在线。高风险改动先影子模式(并跑只记录、双倍成本、零用户风险)再灰度(1–5%、忽略缓存冷启动、注意样本量);回滚移标签前确认旧版本依赖可用。详见第四章到第六章。 ↩

这篇对你有用?
系列 《评测、可观测与可追溯:怎么知道改了之后更好了》 第 4 / 4 篇 进阶

系列总览 — 为什么这样组织、读它需要什么、读完能做什么

学习地图 — AI 应用工程师学习地图 第 5 / 7 个系列

  1. 评测集——AI 应用的单元测试
  2. 评分——规则、LLM-as-judge 与人
  3. 评什么——单步、RAG、agent 与多轮的指标矩阵
  4. 回归与门禁——CI、供应商静默升级、A/B 与在线评测

上一篇:评什么——单步、RAG、agent 与多轮的指标矩阵

本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/regression-gates-silent-model-updates-ab-and-online-evaluation.html)的前提下,欢迎各种形式的转载、翻译或商业引用。


COMMENTS

评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。

×