解析首跑结果
在 B1→B18 整条能力曲线里,B1 解决的是最底层的问题:怎么判一个 AI 输出对不对、贵不贵。
阶段: B1 · evals 方法论 + tokenization 起步(Day 1-10) 标签: #evals #failure-taxonomy #attribution #agent-eval
今日导引(由浅入深)
在 B1→B18 整条能力曲线里,B1 解决的是最底层的问题:怎么判一个 AI 输出对不对、贵不贵。
Day 1-5 已经把方法论铺到能跑——task/trial/outcome 三层(Day 1)→ 三种 grader(Day 2)→ BPE 原理(Day 3)→ OpenRouter 接入(Day 4)→ harness 首跑出数(Day 5)。
Day 5 跑出了第一份自产报告(completion% + cost + commit),但报告只是原料,不会自己告诉你下一步该改什么。
今天的任务就是把这份原料「读懂」——把失败逐条归因到一个稳定的失败分类法(failure taxonomy),统计 top-3 失败类型及各自占比,让「失败」从一团噪声变成可迭代的结构化信号。
最小可判定产出:一张失败归因表(top-3 类型 + 各自占比%)。明天起转去手写 BPE 编码器(Day 7-9),所以今天也是 evals 方法论这条支线的一个临时收束点。
1. 机理精读
读自产报告,本质是做「错误分析(error analysis)」,不是看一个总分。
Day 5 的 agentEval.ts 给出的 completionRate(源码字段名,注释写 fraction with judge.label === 'pass')是一个聚合数——它告诉你「整体多少任务过了」,但不告诉你为什么没过的那些没过。
一个 79% 的 completion 和另一个 79% 可以是完全不同的两个系统:一个是 21% 都栽在工具超时上,另一个是 21% 全是幻觉。这两种 79% 的修法南辕北辙。
所以读报告的第一性原理是:把聚合数拆回到单条失败,再把单条失败归到可复用的类别。 没有这一步,迭代就是盲目的。
读一份报告要盯三件事,三件事对应三种迭代动作:
- 哪些 task fail —— 定位到具体任务 id,看是不是集中在某一类任务(比如全是 multi-hop layering 任务挂,说明检索/推理深度不够)。
- cost 分布 —— 哪几条吃掉大头。Agent 任务的 cost 长尾极重:少数任务因为多轮 tool-call + 长 context 会吃掉总成本的大半。只看
totalCostUsd均值会被长尾骗。 - 判定噪声来源 —— 是 code grader 漏判(确定性检查覆盖不到的语义错),还是 LLM-judge 抖动(同一条输出两次判分不一致)。这一条决定你该信哪个 grader,也呼应 Day 2 的「三种 grader」。
为什么必须归因到结构化类别才能迭代?
因为没有类别,你只能「fail 了就重跑」——而重跑对系统性错误(比如 prompt 缺一段约束)毫无帮助,只会把同样的错以同样的概率再犯一遍。
结构化 taxonomy 把「这条错了」升级成「这条属于 hallucination / 这条属于 format_violation」,于是修法立刻清晰:
- critical 类先修;
- code-checkable 的类用确定性断言兜底(不浪费 LLM-judge 预算);
- 纯语义类才动 prompt 或换模型。
关键权衡:taxonomy 的颗粒度。 太粗(只有「对/错」)等于没归因;太细(每个错一个类)无法统计、无法复用。
本仓 failureTaxonomy.ts 选了 6 类,并给每类配了 severity(critical/high/medium)做排错优先级——这是经验上「够用且能进看板」的颗粒度。
出处方法论是 Hamel Husain + Shreya Shankar 的 evals 工作流:人工错误分析 → open coding(开放编码)→ axial coding(轴向编码)→ LLM-as-judge → 代码型检查(Lenny's Newsletter 2025-09 / hamel.dev evals-faq)。「先人工开放编码、再归并成稳定类别」正是结构化 taxonomy 的来历。
与相邻概念的边界:failure taxonomy 不是 grader(Day 2)。
- grader 判「过没过」,taxonomy 判「没过是哪种没过」;
- outcome(Day 1)是单任务的达标线,taxonomy 是跨任务的失败聚类。
三者串起来才是一条完整的 eval 闭环:outcome 定线 → grader 打分 → taxonomy 归因 → 迭代。
2. 推导 / 手算 / 代码走读
今天 seed 指向 src/aml/failureTaxonomy.ts(已在 repo,18.7KB),是确定性的分类 schema,非 LLM。真实符号走读:
FailureClassId(type):6 个稳定 id ——tool_failure/hallucination/context_pollution/retrieval_miss/format_violation/typology_misjudge。注释明确「进 CI / 看板的 key,勿改名」,这就是归因表的列。FAILURE_TAXONOMY(readonly 数组):6 个FailureClass对象,每个带definition(是什么)、decisionRule(人工/代码据此归类,可执行可复现)、severity、可选exampleCaseId。exampleCaseId的妙用:typology_misjudge的exampleCaseId: 'C049',注释说这是金标 v1 第 1 个 normal 案件(现金密集型商户),已知会被规则误报为 structuring——把「已知坑」钉死在代码里,便于回归定位。severity三档 +SEVERITY_WEIGHT(Record):critical:3 / high:2 / medium:1,给排错优先级提供可排序的数值权重。- 谁是 critical:
tool_failure(下游工具抛错/超时/返回结构不合法 → 无有效产物)与hallucination(引用案件交易集中不存在的 txId/金额/对手 → 凭空捏造证据)。AML 场景 hallucination 最危险,因为 SAR 必须可溯源到真实交易。 isBenignNoise(memo, channel)(工具谓词):channel === 'card'直接判良性;否则看 memo 是否在['工资代发','账单代扣','日常消费']里。这是context_pollution类的判定底座——识别「真实存在但不该引用」的噪声交易。suggestFailureClasses(c, assessment, sar)(建议器):注释诚实写「非自动判定,仅给标注者建议」。它把 4 条 decisionRule 串起来返回可能命中的classId[]:- ①
sar.citedTxIds.some(id => !txIds.has(id))→hallucination; - ② 引用了
isBenignNoise的交易 →context_pollution; - ③
sar.sections.length < 5→format_violation; - ④
assessment.topTypology ?? 'normal'≠c.label→typology_misjudge。
- ①
- 关键边界:真正的 critical 判定走
evalChecks的确定性检查,suggestFailureClasses只做 UI 提示。把建议器当判官就错了。 FailureLabel(interface):人工标注落盘契约,字段含annotator: 'human' | 'llm-judge',注释引「LLM 模拟用户是不可靠代理 (2026-01),关键评分保留人工」——与 Day 2 的 self-preference 警示一脉相承。
走读结论:归因不是拍脑袋,而是有可执行 decisionRule 兜底的。
其中 hallucination / context_pollution / format_violation / typology_misjudge 这 4 类有 code 侧近似判定;retrieval_miss 精确判定「需金标证据集」(源码注释直说,目前只有「citedTxIds 非空 + 段落完整」的下界守卫)。
6 类的修法分流(归因后立刻知道怎么改):
tool_failure(critical)→ 不是 prompt 问题,是工程问题:加重试/超时/契约校验,归 SRE 而非提示词。hallucination(critical)→ 收紧「只能引用证据集内 txId」的约束,或加cited_tx_exist代码型 grader 硬卡。context_pollution(high)→ 用isBenignNoise在喂给模型前过滤噪声渠道,做 context engineering(呼应 B5)。retrieval_miss(high)→ 调规则引擎召回 / recall@k,属检索层,不是生成层。format_violation(medium)→ 纯确定性代码检查兜底(evalChecks),完全不用 LLM-judge 预算。typology_misjudge(high)→ 分类层语义错,动 prompt/few-shot 或换更强模型。
为什么 format_violation 是 medium 而 hallucination 是 critical? 因为前者「结构错但可一眼发现、可代码兜底」,后者「内容假但表面合规、会骗过人工复核」——AML 场景里「看起来对的假证据」比「明显坏的格式」危险得多,这是 severity 排序的金融语义依据。
3. 今日实战
把 Day 5 的失败任务逐条归因到 failureTaxonomy.ts 的 6 类,统计 top-3:
- 打开 Day 5 落到
agent-evals/reports/的自产报告,列出所有judge.label !== 'pass'的任务(或离线 fixture 下 code-grader 判负的任务)。 - 对每条失败任务,跑
suggestFailureClasses拿候选类,再人工核对decisionRule定档(建议器只给候选,最终归类是人工动作——见上文边界)。 - 按
FailureClassId计数,算每类占比% = 该类条数 / 总失败条数,按SEVERITY_WEIGHT给同占比的类排序,取 top-3。 - 落表到
dayN-evals.md旁,列classId | count | pct% | severity | 代表 caseId。
4. 今日实测 / 产出
- 失败归因表(top-3 类型 + 各自占比%):依赖 Day 5 报告。
- 若仅离线 fixture 跑通,则先对 code-grader 可判的失败归因,LLM-judge 相关失败标「待跑(需 key)」。 占比按实际归类结果填,不臆造。
failureTaxonomy.ts已在 repo(6 类 +suggestFailureClasses+isBenignNoise,确定性 schema,非 LLM)。- 状态:归因表为本日人工产物(待按 Day 5 实际报告落数);其中 LLM-judge 维度待跑(需 OPENROUTER_API_KEY)。
5. 常见误区 / 陷阱
- 「fail 了就重跑」无归因循环:对系统性错误无效,只会重复犯错。必须先归类再决定修法。
- 把
suggestFailureClasses当自动判官:它注释明写「仅给标注者建议」,critical 判定走evalChecks。误用会把 UI 提示当成判定结论。 - 只看
completionRate/totalCostUsd均值不看分布:agent cost 长尾极重,少数任务吃掉大头,均值会骗人。 - 把
retrieval_miss当已能精确判定:源码注释说精确判定需金标证据集,当前只有下界守卫,别夸大。
6. 学习资源(每条带 YYYY-MM)
- Hamel Husain + Shreya Shankar《AI Evals FAQ》(hamel.dev / evals-faq, 2026-01) — 三种 grader + self-preference 偏差,本批次 Day 2/6 共用。
- Hamel + Shreya, error analysis 工作流(open coding → axial coding → LLM-judge → code check, Lenny's Newsletter 2025-09)— 本仓
failureTaxonomy.ts头注引用的方法论出处。 - Anthropic《Demystifying evals》(2026-01) — outcome-over-process,Day 1 主线,归因表的 outcome 判据来源。
- 本仓
src/aml/failureTaxonomy.ts(2026-06,repo 内)— 6 类失败 schema +decisionRule+suggestFailureClasses。
SOTA检查 (2026-06 更新)
- 当前主流:结构化 failure taxonomy 归因是 2026 agent-eval 的主流做法(与 Hamel/Shreya 的 error-analysis 工作流一致),仍 SOTA,有效。
- 过时黑名单:避免「fail 了就重跑」无归因循环;避免只报单一 accuracy 总分而不拆失败类别(已被 outcome + rubric + taxonomy 多维评测取代)。
- 下次复查点:是否需新增 2026 agent 失效模式类别——如 tool-call 幻觉(模型臆造不存在的工具/参数)、long-context 上下文丢失。当前 6 类覆盖 AML Copilot 场景,但 agent 域半衰期 ~6 个月,下次复查随 B3(agent loop)和 B4(multi-agent)实测扩类。
衔接
- 昨天:Day 5 — 跑通 eval harness(
agentEval.tscompletionRate+cohensKappa.tsκ,离线 fixture 出数)。 - 今天:把 Day 5 的失败逐条归因到 6 类 failure taxonomy,统计 top-3 占比,让失败从噪声变成可迭代的结构化信号。
- 明天:Day 7 — byte-level 编码器 (1),转去手写 BPE 三件套(vocab/merge 表/encode 主循环),把 Day 3 的原理落成
tokenizer.ts代码。