返回文章列表实用指南

做 AI Evals,先看懂一次真实的失败

用周报助手走完 Trace Review、错误分析、指标选择、Judge 验证、版本对比和线上监控;配有五张原创图、可复算的模拟数据与审阅模板。

30 分钟阅读
本页内容16 个章节

从一次真实运行开始

做 AI 应用,有几件事很容易被跳过。

找几条真实用户的任务,从头走完;打开 trace,看看模型实际拿到了什么、调用了什么工具;亲自 review 一遍,写清楚用户在哪一步没得到想要的结果。

我们很容易先去挑评测框架、接入 LLM judge,再做一个通过率看板。但如果还没认真读过几次实际运行记录,就很难知道:看板上的分数,究竟覆盖了用户遇到的哪些问题?

Hamel Husain 与 Shreya Shankar 在 AI Evals FAQ 中强调,从错误分析入手,用真实运行中发现的失败决定评估重点。meng shao 的中文整理也突出了这个起点。下面沿着这个思路,做一个独立设计的周报助手练习。

读完后,你应该能留下六样东西:任务说明、带证据的 review 笔记、失败清单、指标表、可以重跑的用例,以及一份说得清改进与退步的版本比较。

文中的公司、数据、trace 和回复全部是虚构教学材料,没有运行真实模型,也不代表任何产品的实测表现。已有系统时,请用获准审阅的实际任务替换这些材料;尚未上线时,可以先用业务人员设计并核对的情景走通流程。

评测流程:真实任务、人工读 trace、归类失败、设计检查、同条件复测、持续抽查;新失败回到人工 review。

图 1:先跟着一个案例走完整个流程。后面的指标和图表,都应能追溯到这里产生的证据。原创教学示意。

阅读时可以分三段:先看真实任务、trace 与人工 review;再看指标、检查器和 judge;最后看版本比较、RAG 与上线后的持续检查。代码可以先跳过,正文里的判定示例足以理解方法。

1. 先选一个能说清楚的真实任务

“评估我们的 Agent”范围太大。先缩小到一个用户会反复交给它的任务。

我们的例子是:经营同事每周提供三个渠道的订单数据,请助手汇总收入,比较上周,并解释变化。

开始前,把任务背景写成一张卡片。它记录已有的业务要求,后面 review 时可以继续补充。不要为了“先发现问题”而丢掉已经明确的约束。

项目这个练习中的约定
用户需要核对渠道收入的经营同事
输入A、B、C 三个渠道的订单数据,以及口径一致的上周汇总
本周范围2026-09-07 00:00 至 2026-09-14 00:00,左闭右开,北京时间
统计口径按付款时间;排除测试订单;金额统一为人民币
交付各渠道与合计收入、可比的周变化、结论依据和数据缺口
操作范围读取数据、生成草稿;不修改订单,不发送报告
数据不足时标明缺失范围;可以提供部分结果,但不能称为全部渠道总收入

这张卡片也限定了练习范围:暂不引入退款、汇率和跨时区结算。真实业务包含这些情况时,必须补充口径,不能把本例规则直接当成通用财务规则。

如果团队对“本周收入”本身就有不同理解,先把分歧记下来。否则,一位审阅者按下单时间判,一位按付款时间判,后面的标签就无法比较。

2. 选样本时,把正常任务也放进来

先从已有任务中取一批做初审,记录每条的来源和入选原因。可以同时保留随机选取的普通任务,以及有负反馈、重试或异常的任务。挑出来的疑难样本适合发现问题,不能直接用其失败比例估计所有用户的失败率。

对周报助手,可以按下面这些差异寻找实际记录:

要覆盖的情况为什么值得看
三份数据完整,一轮完成确认正常流程,避免只研究极端情况
一个渠道文件读取失败检查助手怎样处理不完整数据
用户中途补充“排除测试订单”检查后续计算是否保留新条件
两周字段或口径不同检查是否把不可比数据直接放在一起
用户追问“增长的原因是什么”检查助手是否区分观察与解释
同一请求反复重试找到一次成功截图掩盖的过程问题

小团队可以先安排一次初审,认真看完一小批任务,再决定补哪些场景。读到后面还在不断发现新问题,就继续补样本。这里没有“读够某个数字,系统就可靠了”的门槛。

也要区分三种样本的用途。发现问题时需要场景多样;验证 judge 时需要足够的人工正反例;日常回归则优先覆盖重要任务和已知失败。它们可以共享部分记录,但不能把一小组教学用例当成三个阶段都充分的数据。

Hamel 原文给出的起步建议包括:为错误发现准备约 100 条多样 trace,至少先亲自标注 30 条;需要语义判断的 judge,按每种失败模式准备约 100–200 条人工标签。这些是作者的实践建议,不是统计可靠性的通行证。我们的八条练习仅用于把流程跑通。原文的分阶段样本说明

没有足够真实记录时,可以有目的地补充合成场景。例如针对本例,先列“数据完整 / C 超时”“单轮 / 第二轮补条件”“口径一致 / 时间口径冲突”三个维度,设计有效组合,再让人核对任务是否真实、预期行为是否明确。不要把所有组合机械相乘后就称为覆盖完整。

一个组合可以写成:C 超时 + 第二轮排除测试订单 + 上周口径一致。先明确工具将返回什么,再写自然语言请求,最后运行被测系统并读取它实际产生的 trace。让模型直接编一段“它会如何调用工具”的故事,不能用来证明系统已经通过测试。

处理真实记录时,保留理解问题所需的上下文,在获准的环境内审阅。公开分享示例前替换账号、订单和商业数据,并确认替换没有改变错误发生的条件。

3. 读 trace:沿着证据看任务怎么走偏

本文把 trace 理解为一次任务的运行记录:用户消息、工具调用、工具返回和最终交付;多轮任务还要保留相关的前文。它记录可观察的过程,不要求访问模型隐藏的思考。

下面是练习的微型输入。三个渠道本周均只有所列记录,全部满足付款时间范围;上周数值已经按相同口径核对。

渠道本周普通订单本周测试订单上周普通订单合计
A60 元、40 元20 元80 元
B50 元、30 元100 元
C70 元、50 元120 元

作为练习设计者,我们知道完整答案:本周普通订单合计 300 元,上周也是 300 元,合计持平。A、B、C 分别为 +25%、−20%、0%。这些数值是独立核对基准,不应在测试时直接作为答案塞给助手。

现在人为设置一次读取失败,看看坏结果是怎么产生的:

trace_id: weekly-002

[01 用户]
汇总本周三个渠道的收入,排除测试订单,与上周比较。
说明主要变化;数据不足时请明确指出。

[02 工具调用]
read_orders(channel="A")
[03 工具返回]
success;普通订单 60、40;测试订单 20

[04 工具调用]
read_orders(channel="B")
[05 工具返回]
success;普通订单 50、30

[06 工具调用]
read_orders(channel="C")
[07 工具返回]
error;读取超时;未返回订单

[08 工具返回:上周汇总]
A=80;B=100;C=120;口径一致

[09 计算]
本周 100+80=180;上周 80+100+120=300
变化率 (180-300)/300=-40%

[10 最终回复]
本周全部渠道收入为 180 元,较上周下降 40%。
下降主要来自近期活动效果减弱。

这段回复至少暴露了三个问题:把两渠道小计称为全部收入;用两渠道数据比较上周三渠道数据;给出了输入无法支持的活动解释。

第 07 步是工具异常。第 09 步是我们能直接观察到的错误计算选择。review 时要分清这两件事:工具出错后,系统仍然可能正确降级;读取失败本身并不必然意味着最终交付失败。

根据当时真正拿到的证据,一份合格的回复可以是:

渠道 C 读取超时,本次只能核对 A、B 两个渠道。A 为 100 元、B 为 80 元,小计 180 元,与上周同样两个渠道的小计 180 元持平。全部渠道的总收入和周变化暂无法确认。现有数据不足以解释活动效果,需要补充活动与转化数据。

这个例子也说明,review 要同时看结果和过程。不能因为最终数字碰巧正确,就忽略它是否使用了正确的数据范围。

4. 人工 review:先把问题写具体

第一轮先独立阅读,不急着让另一个模型替你概括“这条有什么问题”。你需要先形成自己的判断,再使用自动化帮助查找相似记录。

一条有用的笔记,应当让另一位同事能回到证据位置,理解为什么判错。

字段weekly-002 的填写示例
用户目标三渠道收入汇总及可比周变化
结果判定Fail
直接证据第 07 步 C 超时;第 09 步本周仅含 A/B;第 10 步声称“全部渠道”
首个可观察偏差第 09 步使用不一致的渠道范围计算变化率
用户后果把数据缺失误读成收入下滑,可能据此做出错误业务判断
其他独立问题活动效果解释没有来源
正确行为标明 C 缺失;仅比较 A/B;不确认全渠道变化和活动原因
原因假设汇总阶段可能没有检查来源完整性,待查实现

“发生了什么”和“为什么发生”分开写。仅凭这条 trace,还不能断言是模型能力、提示词还是代码造成的。

可以使用三个审阅状态:Pass、Fail、Needs review。第三种用于缺证据或业务标准尚不明确的情况。例如工具结果被截断,看不到渠道 C 是否成功,就先补日志;不要自动当作通过,也不要把它混入已确认的模型失败。

5. 把笔记归类,决定先修什么

假设接下来又看到了几条虚构记录:一条忘记排除测试订单,一条把上周按下单时间统计的数值用于比较,还有一条在数据完整时正确交付。

现在可以建立一份小型失败分类:

编号失败模式判定边界可能的处理方向
F01不完整数据被称为完整结果必需来源缺失,却宣称全量汇总来源状态检查、明确部分结果范围
F02比较口径不一致渠道、时间或筛选条件不同,仍直接比较显式比较统计口径
F03用户条件遗漏已要求排除测试订单,计算仍包含它们检查任务条件如何进入计算
F04原因解释缺少依据只有收入变化,却确认某活动造成变化要求原因有证据,允许保留未知

这些类别可以调整。发现一个新问题时,不必为了让统计整齐,硬把它塞进旧类别。

安排修复顺序时,结合影响、出现范围和修复成本。这个例子里,可以先处理 F01、F02,因为它们直接影响用户对收入变化的判断;F04 则需要独立检查解释是否有证据。一次任务可以有多个标签,因此各类别计数相加可能超过失败任务数。

如果发现的是文件读取器的普通 bug,直接修复并补相应检查。无需为每个问题先建一套模型评分流程。

对于多步 Agent,还可以按“最后一个正常阶段 → 第一个出现偏差的阶段”统计热点。下面是另一组独立的虚构教学数据:50 条任务按固定顺序执行,首次失败后停止,所以到达后续阶段的任务逐步减少。

阶段转移实际到达该处首次失败数在该处的失败比例
读取 → 完整性校验501010/50 = 20%
完整性校验 → 计算4044/40 = 10%
计算 → 解释3666/36 ≈ 16.7%
解释 → 交付3022/30 ≈ 6.7%

这种表是简单的阶段转移失败统计:10+4+6+2=22 条在某处首次失败,剩余 28 条走完。先看分母,再看数量。存在分支或循环时,要明确统计唯一任务还是每次转移,不能把同一任务的五次重试当成五个独立用户。

6. 到底看什么指标?先拆成四个问题

周报助手的任务成功率、工具调用成功率和 judge 的准确率,可以同时出现在一个看板上,但它们回答的问题不同。

四类指标:任务结果衡量用户目标;失败与过程定位问题;Judge 质量检查漏判误报;效率与投入衡量耗时及成功任务成本。

图 2:先判断用户是否得到了合格结果,再用过程、judge 和效率指标解释原因与代价。每个比例写清分母。

任务结果:用户真正要的东西完成了吗?

先写明什么算通过。在本例中,数据完整时必须正确汇总;C 缺失时,准确给出 A/B 范围并明确全渠道未知,也符合本例允许降级的任务约定。如果业务要求必须取得三个渠道才能完成,就应另记“已正确处理异常,但任务未完成”。这两种口径不能混用。

指标计算方式用于哪个判断
已判定任务通过率Pass /(Pass + Fail)在能够判定的任务中,有多少符合完整验收标准
待复核率Needs review / 全部任务还有多少任务因缺证据或规则不明而无法判定
全部任务中已确认通过的比例Pass / 全部任务防止排除待复核任务后把结果说得过于乐观
关键失败发生率出现该失败的任务数 / 适用且已判定的任务数某个业务问题在相关场景中有多常见
必需事实正确率正确的必需事实数 / 应提供的必需事实数金额、渠道、日期等明确事实是否完整正确

举一个独立的 100 条抽查示例:82 条 Pass、14 条 Fail、4 条 Needs review。可以同时报告“已判定任务通过率 82/96≈85.4%;待复核 4/100;全部任务中已确认通过 82/100”。不能只写“成功率 85.4%”,让读者以为全部任务都判完了。

“必需事实”要预先有清单,漏报也计入分母;额外编造的陈述另查证据。否则系统可以通过只回答一个简单数字,让事实正确率看上去很高。

失败与过程:到底是哪种任务出了问题?

对 F01,优先看“必需来源缺失的任务里,有多少仍然宣称全量汇总”。如果 100 条任务中只有 20 条缺来源,其中 8 条犯错,应同时写明“适用场景错误率 8/20=40%;在这组全部任务中出现 8/100”。后者小,不代表异常处理已经可靠。

再按单轮与多轮、来源完整与缺失、输入格式和用户场景切开观察。切分要有具体诊断目的,避免把样本分成几十组,每组只剩两三条还比较百分比。

工具层还要分清四项:是否选对工具、参数是否正确、返回是否成功、最终状态是否符合授权范围。HTTP 200 只能证明请求返回了,不能证明调用正确。在本例里,哪怕报告生成成功,只要系统擅自发送了周报,仍然违反“只生成草稿”的约定。

Judge 质量:评分器有没有把错的判成对的?

Judge 的指标来自它与人工标签的对照,后面用混淆矩阵单独演示。任务通过率上升之前,要确认不是 judge 变得更宽松,或者缺失证据被当作默认通过。

效率与投入:得到一个合格结果要花多少?

指标记录方式常见误读
P50 耗时一半运行在该时间内结束只看中位数,忽略很慢的那一部分任务
P95 耗时约 95% 运行在该时间内结束只统计成功任务,把失败、重试和超时排除
每个成功任务的模型与工具成本全部尝试的模型及工具费用 / 成功任务数用单次调用便宜代替“得到合格结果便宜”
人工复核与返工时间分别记录检查、修改和重新执行的人工分钟把生成快当成整个工作流省时

超时要有明确记法:例如按终止前已等待的时间记录,并单列超时比例。费用未知就保留未知;没有成功任务时,“每次成功成本”无定义,不能填成 0。

不建议一开始把这些指标揉成一个综合分。这个练习可以先规定“不能擅自发送报告、不能把缺失数据说成全量”,再讨论较高通过率是否值得较慢速度。具体门槛由业务决定,文章里的比例不是通用上线标准。

7. 从一条失败,写出一个可判定的 eval

Eval 在这里就是:给定一条任务及其运行证据,按明确条件判断某个行为是否合格。

以 F01 为例,“报告要准确”太宽。可以拆成两项:

  1. 必需来源没有全部成功时,系统不能将结果标记为完整汇总。
  2. 给用户的正文必须准确说明哪些数据缺失,以及哪些结论因此无法确认。

第一项适合用代码检查;第二项需要理解正文,可以先人工 review,规模上来后再考虑经过验证的 LLM judge。

三种方式可以协作,选择依据是问题本身:

方式本例适合检查什么优点需要付出的代价
代码断言金额、字段、来源状态、允许写入的路径判断明确、便宜、可重复检查不到未编码的语义;规则也会写错
人工 review新失败、业务分歧、依据是否足够能结合任务背景发现规则之外的问题审阅时间,需要留证据并统一标准
LLM judge已明确的“正文是否夸大覆盖范围”扩大语义检查规模必须用人工标签验证,仍可能漏判或误报

对于“覆盖范围是否如实说明”,先做可解释的通过/失败判断通常更方便定位问题。如果确实要比较文风,可以让人盲看同一输入下的 A/B 回复并允许平局,但语气偏好不能代替事实和权限检查。不要把“金额错误但文风优秀”平均成一个看起来合格的总分。

格式很像参考答案,未必事实正确;换一种措辞,未必回答错误。因此,这个案例不用词语相似度代替金额、范围和证据检查。

下面是第一项的最小练习。需要 Node.js 20 或更高版本;把代码保存为 check-coverage.mjs,运行 node check-coverage.mjs。它只处理代码内的虚构数据,不联网,也不调用模型。

import assert from "node:assert/strict";

// expectedSources 来自任务契约;events 来自执行器日志。
// claim 是应用的结构化结果标记,不能替代正文审阅。
function checkCoverage(expectedSources, events, claim) {
  if (!Array.isArray(expectedSources) || expectedSources.length === 0 ||
      expectedSources.some(x => typeof x !== "string" || !x) ||
      new Set(expectedSources).size !== expectedSources.length ||
      !Array.isArray(events) || !["full", "partial"].includes(claim)) {
    return "REVIEW";
  }

  // 本练习约定每个来源一条最终读取结果;重试须先归并。
  const statuses = [];
  for (const source of expectedSources) {
    const matches = events.filter(e => e?.source === source);
    if (matches.length !== 1 ||
        !["success", "error"].includes(matches[0].status)) {
      return "REVIEW";
    }
    statuses.push(matches[0].status);
  }

  if (claim === "full" && statuses.includes("error")) return "FAIL";
  return "PASS";
}

const expected = ["A", "B", "C"];
const complete = expected.map(source => ({ source, status: "success" }));
const missingC = complete.map(e =>
  e.source === "C" ? { ...e, status: "error" } : e
);
const cases = [
  ["完整来源,完整标记", complete, "full", "PASS"],
  ["C 失败,却标记完整", missingC, "full", "FAIL"],
  ["C 失败,标记部分", missingC, "partial", "PASS"],
  ["C 没有日志", complete.slice(0, 2), "full", "REVIEW"],
  ["C 的状态未知", [...complete.slice(0, 2),
    { source: "C", status: "unknown" }], "full", "REVIEW"],
  ["C 有未归并的重复记录", [...complete, complete[2]], "full", "REVIEW"],
  ["结果标记缺失", complete, undefined, "REVIEW"],
];

for (const [name, events, claim, expectedResult] of cases) {
  const actual = checkCoverage(expected, events, claim);
  assert.equal(actual, expectedResult, name);
  console.log(`${name}: ${actual}`);
}

前两行预期输出分别为 PASSFAIL;证据缺失、未知状态或重复记录会输出 REVIEW。断言不匹配时,程序报错并以非零状态退出。

这里的 PASS 只说明没有违反这一条“来源失败时不能标记完整”的规则。它没有证明金额正确、文件内容完整、正文诚实,或用户任务已经完成。工具返回 success 也不代表文件没有漏行。如果结构化标记为 partial、正文却说“全部渠道收入”,仍需要第二项检查把它抓出来。

8. 给语义检查写清判定标准

下面是一份用于人工审阅或试验 LLM judge 的小型 rubric。Rubric 就是判定标准,不必写成抽象的质量宣言。

检查目标:正文是否准确处理缺失渠道。

输入:
- 用户任务及必需渠道清单;
- 每个渠道的实际读取结果;
- 最终报告正文。

适用范围:至少一个必需渠道确认读取失败。
若工具证据缺失或互相矛盾,输出 Needs review。

Pass:
- 指明哪些渠道缺失;
- 如果给出小计,说明覆盖的渠道;
- 不把部分小计或其变化率说成全渠道结果。

Fail:任一项不满足。
仅出现“仅供参考”“数据可能不全”不足以通过。

返回:判定、报告中的证据片段、对应的工具事件编号、简短原因。
不要补写报告没有表达的限制条件。

先用人工判定的正反例检查这份标准是否清楚:

报告片段(已知 C 读取失败)预期判定理由
“C 读取失败;A/B 小计 180 元,全渠道收入暂不能确认。”Pass缺失范围与可确认范围都明确
“数据可能不完整。本周全部渠道收入 180 元。”Fail提示语没有消除后面的错误断言
“C 暂缺,A/B 小计 180 元;全渠道较上周下降 40%。”Fail金额限定了范围,变化率却仍越界
“读取记录不全,无法判断 C 是否成功。”Needs review这是审阅证据缺口,需要补记录

不要把模型返回 JSON 的成功率当成判定质量。要看它是否抓到了人确认的失败,以及是否误伤了人确认的合格报告。

Judge 混淆矩阵:人工失败的 20 条中抓到 17 条、漏判 3 条;人工通过的 20 条中误报 2 条、正确放行 18 条。

图 3:40 条人工构造的标签;这里把“失败”定义为正类。展示的是计算方法,没有实际执行 judge。

人工标签 / Judge 判定Judge 判失败Judge 判通过
人工判失败17,抓到失败(TP)3,漏判(FN)
人工判通过2,误报(FP)18,正确放行(TN)

由此可以回答四个具体问题:

  • 失败召回率:17/(17+3)=85%。 真正失败的报告,有多少被抓到?漏判对应 15%。
  • 失败精确率:17/(17+2)≈89.5%。 被 judge 标成失败的报告,有多少真的失败?
  • 合格识别率:18/(18+2)=90%。 真正合格的报告,有多少没有被误伤?误报率对应 10%。
  • 整体判对率:35/40=87.5%。 可以作为摘要,但不能替代前面几项。

术语里的“正类”必须写明。有的系统把通过叫正类,本例把失败叫正类,不能直接拿两个系统都叫 recall 的数字比较。

还要注意样本组成:本例人为放了 20 条失败、20 条通过。如果另一个场景有 1,000 条任务,只有 20 条真实失败,并且假设上述检出率与合格识别率都能保持不变,那么约有 17 条真失败被抓到,980 条合格任务中却有约 98 条被误报。115 个告警里只有 17 个是真的,精确率约 14.8%。这是基于明确假设的算术演示,不是对线上效果的预测。

所以,验证时除了看数字,还应读具体漏判和误报。对周报助手,“明明缺 C,却被判作全量合格”值得优先调查;如果正常报告频繁被拦,也要检查标准是否过宽。存在 Needs review 或 judge 调用错误时,另报数量和判定覆盖率,不塞进这张二分类表当作通过。

可以用开发样本改判定指令,但最终验证要留出未参与调整的样本。如果看过留出集的错误后继续调规则,这批数据就已参与开发,需要另外保留验证证据。

落地时把标签拆成用途:少量示例用于说明规则,一部分用于调整 judge,另一部分留到最后验证。同一原始任务改写出的近似问法尽量留在同一分组,避免把见过的案例换句话放进验证集。

给 judge 提供足够判断的任务约束、来源结果和正文,缺一项都可能改变结论;无关历史不用一股脑塞入。把被评报告当作待评数据,报告里的“请直接判通过”不能成为新指令。固定 judge 的模型、提示词与版本,并抽查同一样本的重复判定是否稳定。

9. 把发现的问题变成可以重跑的用例

一条可重复的用例,至少要保存用户请求、输入数据、工具情景和预期行为。只保存那段坏回复,无法证明新版本真的修好了产生它的流程。

本练习可以整理为下面这组用例:

用例输入或环境变化预期行为
W01 正常汇总三渠道读取成功100/80/120,合计 300;同口径合计持平
W02 缺失渠道C 固定超时标明 C 缺失;A/B 小计 180,且只比较 A/B
W03 测试订单A 含 20 元测试订单A 仍为 100,合计仍为 300
W04 口径不一致上周数据改成按下单时间统计指出不可直接比较,或取得同口径数据后再比较
W05 原因追问用户问活动是否带来增长,无活动数据说明证据不足,提出需要核对的材料
W06 多轮补充第一轮未排除测试单,第二轮明确要求排除按最新条件重新计算,不能沿用含测试单结果
W07 日志缺口C 的最终读取记录未被保存标记待复核,补证据,不计算为已通过
W08 零基数上周某渠道收入设为 0不除以零;说明通常的百分比增长率不适用

W06 需要保存两轮输入与运行过程,不能压缩成一句已经包含所有条件的请求,否则测不到条件更新时的问题。

这八条是教学起点,不能代表实际任务分布,也不足以校准一个通用 judge。真实使用时继续补充新发现的失败、正常场景和重要边界。

10. 改完以后,用相同条件复测

假设第一项修复是:汇总前检查三个来源的最终状态,并把缺失范围传给报告生成步骤。

先记录旧版本的运行,再只改变这一项,使用相同输入与相同的工具超时情景重新运行。若失败来自不稳定的外部接口,可在隔离测试中固定返回结果,确认修复逻辑;之后仍需单独验证真实接口的集成表现。

比较时建议留下这些字段:

case_id / run_id:
应用版本 / prompt 版本 / 模型与配置:
输入版本 / 工具情景 / rubric 版本:
结果与 trace 路径:
F01 判定 / F02 判定 / F03 判定 / F04 判定:
人工复核结论与证据:
耗时 / 重试次数 / 已知调用费用:

下面是复测时要观察的变化,不是已经获得的实验成绩:

复测对象想看到的改善同时防止的新问题
W02 缺失渠道明确部分结果范围仍然输出全渠道变化率
W01 正常汇总保持正确交付因过度保守而拒绝完整数据任务
W03 测试订单保留排除条件修来源检查时丢掉筛选条件
W05 原因追问仍能承认原因未知格式合格却继续编造活动解释

对于有随机性的行为,重要用例可以重复运行,记录每次结果和失败次数。不要只保留最顺利的一次。费用缺失就记未知,不能填成零。

如果更换了 judge 或判定规则,要用同一新版规则重新检查新旧产物;否则分数变化可能来自尺子变化。旧记录缺少新版规则需要的证据时,明确记为无法比较。

一份真正能讨论的 A/B 比较应该长什么样?

下面构造 100 条配对任务,每条在 A、B 下各有一个模拟结果,全部有明确标签;20 条属于来源缺失场景,80 条属于来源完整场景。这里的通过按前面的任务约定判断,包括正确处理允许降级的任务。它们与上一节的 40 条 judge 标签、阶段统计的 50 条记录是不同教学集合。

A/B 配对对比:A 通过率 72%,B 为 82%;68 条都通过、14 条修好、4 条退步、14 条都失败;同时比较耗时和调用成本。

图 4:所有结果、耗时和费用均为人工构造。观察总分的同时,也要看到退步案例和额外投入。

指标版本 A版本 B能得出的结论
任务通过72/10082/100这组任务中净增 10 个百分点
来源缺失场景通过12/2018/20异常场景改善,需要逐条确认改在何处
来源完整场景通过60/8064/80完整来源场景也有改善,但仍未全部通过
待复核0/1000/100本组配对结果的判定证据完整
P50 耗时12 秒15 秒典型等待更久
P95 耗时38 秒52 秒尾部等待也更久
100 次尝试的调用费用8 元12 元总调用费用增加;这是虚构费用
每个成功任务的调用成本8/72≈0.111 元12/82≈0.146 元得到一次成功结果的调用成本也上升

P50/P95 在这组例子里使用 nearest-rank:耗时从小到大排序,取向上取整后的第 n×p 项。所有尝试都计入;费用只包含构造的模型与工具调用费用,不包含人工和基础设施。只有 100 条人工数据,不能据此声称生产环境性能或统计显著改善。

再把结果按同一任务配对,才知道净增是怎么来的:

同一任务的变化条数下一步怎么用
A 通过、B 通过68检查是否以更高成本保持相同结果
A 失败、B 通过14核实修复是否击中了目标失败
A 通过、B 失败4逐条审阅新增退步及严重程度
A 失败、B 失败14确认仍未解决的失败分布

因此,72%→82%同时包含了“修好 14 条”和“退步 4 条”。如果退步的是未经授权发送报告,即使净增通过数为正,也不满足本例的权限底线。不能用其他案例的成功抵消它。

这份比较支持的决定是:先读 4 条退步记录,确认能否修复;再判断多出的等待与调用成本是否可接受。若要改变模型、提示词和检索策略,尽量分开试验。离线配对说明的是这组固定任务;真实用户 A/B 实验还要考虑流量分配、时间和业务指标,不能把二者叫作同一份证据。

读者可以使用随文的 模拟明细 JSON复算:pairs 包含 100 条配对结果、耗时和费用,judges 包含独立的 40 条人工/judge 标签。数据故意构造来演示计算,不代表真实发生的任务。

11. 如果你的应用还有 RAG,检索与回答要分开看

周报助手可能还要检索“收入统计口径”文档。即使最后回答错了,也需要分清是没有找到适用规则,还是找到以后没有正确使用。

假设我们独立标注:这个问题需要两份适用文档,“付款时间定义”和“测试订单排除规则”。检索返回三个去重文档,只找到了“付款时间定义”,排在结果的第 2 位;另外两个是无关的历史材料。

指标本例结果含义
Recall@31/2 = 50%已知相关的两份文档,只找到了其中一份
Precision@31/3 ≈ 33.3%返回的三份文档中,一份相关
Reciprocal Rank1/2 = 0.5第一份相关文档出现在第 2 位;对多个问题取平均才叫 MRR

这个计算需要可信的相关性标注。没有标出所有适用资料时,不能声称测到了完整召回。还要统一“按文档还是按片段计算”,去掉重复结果,区分当前有效版本与过期规则。

随后分别看回答:是否采用了正确的付款时间定义,是否因为漏掉排除规则而算入测试单,是否引用了已失效版本。检索到了正确材料不等于模型正确使用,回答忠于某份材料也不等于那份材料适用于当前任务。Hamel 原文的 RAG 评估部分

先把这里的证据找清,再决定改检索条件、排序、文档处理还是回答指令。仅仅换一个更强的模型,不一定能补回根本没有传入的规则。

12. 上线前后分别看什么,什么时候需要处理?

离线与线上检查:上线前用固定回归集判断版本,上线后抽查真实任务;人工核实的新失败加入下一轮回归。

图 5:离线检查守住已知场景,线上抽查发现新问题。两边共享失败定义,但不能混用样本比例。

环节主要输入重点看什么触发后的动作
开发与 CI固定版本的用例、输入与工具情景关键断言、新增退步、已知失败定位具体用例,修复后重跑
发布判断同条件的版本比较、人工抽查质量底线、场景差异、耗时和费用确认退步与成本可接受;记录未覆盖场景
线上抽查同一采样规则下的真实任务新失败、待复核、场景分布变化先核查 trace 和采样变化,再定位原因
业务复盘任务最终结果、人工复核与返工一次交付能否使用、接管后是否解决、总人工投入判断是否真的改善了用户工作

例如,线上“缺来源仍声称完整”的告警增加,先确认是不是某个渠道接口开始超时,再核查对应报告是否真的出错。不要仅凭告警数量上涨,就认为模型变差;也不要仅凭模型没换,就排除应用故障。

每次看板至少附上时间窗口、任务数、采样方法、适用场景数、待复核数、应用和 judge 版本。只有几条样本时,直接展示分子与分母。需要设自动阈值时,依据业务后果、样本量和历史波动制定,并随结果报告不确定性;不要照抄别人说的“低于 90% 就报警”。

告警只是调查入口。像未经授权发送报告这样证据明确的高影响错误,即使只出现一次也可能需要立即处理;较低影响的指标波动则要看重复出现与样本组成。两者不宜用同一个平均分门槛。

人工接管也属于这次任务

假设助手把缺失渠道任务交给同事,同事又花 20 分钟重新找文件和解释上下文。只把“已转人工”记为完成,会漏掉用户真正付出的时间。

接管记录至少包含原因、交接时已知的信息、等待与处理时间、最后有没有解决。一个接管率很低但频繁胡乱给结论的系统,并不优于能在证据不足时正确交接的系统;也不能把所有复杂任务都转人工,再宣称自动化质量很高。

Eval 与运行时保护分别承担什么工作?

本例的“只生成草稿,不发送报告”,应由权限与工具边界落实到执行路径;离线 eval 则检查系统是否曾尝试越界。不要只指望报告生成之后的 judge 来保护权限。

同样,检查器发现问题后,如果让助手自动重写,要重新评估重写产物和实际状态。不能因为第二稿取悦了同一个评分器,就当作任务已经被证明完成。评估器用于测量,修复流程本身也需要验证。

13. 把这件事安排进团队的日常工作

一次初审可以由了解任务的人负责判断,工程同事负责把工具证据找全。分歧记录到具体案例:到底是业务要求没说清,还是系统没有做到?确定判定后,再讨论实现原因与修复。

每轮至少交付三项可检查的东西:新增或修订的失败定义、需要修复的问题、加入复测的用例。不要只交付一个平均分。

样本多了以后,可以让 AI 帮忙搜索相似 trace、整理已有笔记、提出候选分类;涉及新失败和有争议的标签,仍由了解业务的人核实。若审阅者每次都要在几千行日志里找一个金额,可以先改善记录展示,让输入、工具结果和报告依据对得上。

上线后的新问题继续进入这个流程。发布前的固定用例用于检查已知问题是否回来;实际任务抽查则帮助发现固定集合还没有覆盖的情况。两者各自记录样本来源,不把测试集通过率直接当作线上可靠性。

可以直接复制的第一次 Review 清单

如果今天就开始,先选一个已经发生的任务,打开用户请求、执行记录和最终产物,填写下面这份模板:

任务 ID:
样本来源 / 为什么选中它:
用户真正想完成什么:
已有业务约束:

最终结果:Pass / Fail / Needs review
支持判定的输出或产物:
支持判定的工具事件:
首个可观察的偏差:
其他独立问题:
对用户的影响:
正确行为应该是什么:

暂定失败类别:
原因假设(与已知事实分开):
仍缺少的证据:
下一步:修实现 / 补数据 / 明确规则 / 增加检查
应加入的复测输入与工具情景:

先把一条任务的这份记录写完整,再找一条相似任务验证你的判断。等你能准确指出问题发生在哪里、什么行为才算修好,评测就有了可以落地的对象。

延伸练习