返回 AICAP-180
B14 · Day 135外部 ground-truth AML eval

LLM judge 校准

Day 134 把均衡评测集 balanced_300.json 备好了,但喂给谁判?答案是一个 LLM judge。今天做 judge 校准(calibration):设计一个 rubric 明确、二元输出、强制理由的 judge prompt,用 NAMED deepseek-v4-flash 对 Is_Laundering 做二分类,并在小手验子集上比对 judge 输出与真标签的一致率。

阶段: B14 · 外部 ground-truth AML eval(Day 131-140) 标签: #llm-as-judge #rubric #calibration #deepseek-v4

今日导引(由浅入深)

Day 134 把均衡评测集 balanced_300.json 备好了,但喂给谁判?答案是一个 LLM judge。今天做 judge 校准(calibration):设计一个 rubric 明确、二元输出、强制理由的 judge prompt,用 NAMED deepseek-v4-flashIs_Laundering 做二分类,并在小手验子集上比对 judge 输出与真标签的一致率。

它在 B14 的位置是「把外部标签 + 独立预测器真正接上」——Day 131 论证要解耦,Day 132-134 备好外部标签,今天补上独立预测器这一端。至此「解耦」的两条腿(外部标签 + 独立判别)才算都到位。

放到 B1→B18 能力曲线上:LLM-as-judge 是贯穿整套 AICAP eval 的核心装置,今天把它从「agent 任务打分」迁移到「AML 二分类」并做校准,是把通用 judge 范式落到具体高风险域的实操。最小可判定产出:judge prompt v1 + 30 条手验子集的校准一致率(agreement %)。注意:judge-human κ(≥50 hand labels)仍 gated,今天只做 30 条一致率初校。

1. 机理精读

judge 为什么需要 rubric。 LLM judge 若只给一句「这可疑吗?」,输出会自由发挥、方差大、不可解析。校准的前提是把 judge 约束成确定性接口。Anthropic《Demystifying evals》(2026-01) 的 judge 设计三要素:

  1. 明确 rubric——把「什么算 laundering」用可操作的判据写清(structuring 拆分到申报阈值以下、layering 链式快进快出、mule 多账户汇聚单一受益人等信号);rubric 越具体,裁判方差越小。
  2. 二元输出——verdict ∈ {laundering, normal},便于直接进 binaryEval;避免「可能/也许/中等风险」这种无法机器消费的模糊档。
  3. 强制理由——要求输出 reason,既降方差(逼模型走推理链,类似 CoT 效应)又留可审计痕迹(AML 域必须能解释为什么判可疑)。

为什么用 deepseek-v4-flash 我们的 runner 是 provider-agnostic 的(scripts/run-agent-eval.tspnpm eval:agent),默认 provider 为 deepseek(China-friendly、便宜)。src/agent/config/providers.ts 第 35-36 行明确:

defaultChatModel: 'deepseek-v4-pro',
defaultSubAgentModel: 'deepseek-v4-flash',

judge 这种高频、轻量的二分类任务用 flash(efficient 档,src/agent/shared/cost.ts 第 15 行:input $0.14/M、output $0.28/M)最划算;需要更强判别时可切 pro(第 14 行:input $0.435/M、output $0.87/M)。模型经 OpenRouter/官方均可调用。

什么是校准(calibration)。 校准 = 在一个有真标签的小子集上,比对 judge 的 verdict 与真 Is_Laundering一致率(agreement %)。它回答「这个 judge 靠不靠谱」——如果 judge 在 30 条手验子集上和真标签只有 60% 一致,那它产出的 recall/precision 全不可信,prompt 要重写。一致率高才说明 judge 可作为 Day 136 全量 300 条裁判的预测源。

校准 vs 评测的边界。 今天的 30 条是校准集(验 judge 本身),不是 Day 136 的 300 条评测集(验系统能力)。两者必须分开,否则又回到「用同一批数据既调 prompt 又报结果」的循环陷阱(Day 131 的病换皮回来)。校准集小、用来调 prompt;评测集大、调完后一次性跑、出最终数字。这条纪律和机器学习里「validation set 调超参、test set 只跑一次」是同构的。

关键诚实边界:一致率 ≠ κ。 30 条一致率是粗校准,不是 judge-human Cohen's κ。κ 要扣除随机一致的成分(chance agreement),且需 ≥50 条人工标注(hand labels)才稳。在均衡集上随机瞎猜的期望一致率就有 50%,所以裸一致率会系统性高估。今天只产 30 条一致率,judge-human κ 仍 gated(依赖更多人工标注落盘)——这是诚信边界,不得把 30 条一致率包装成 κ。

三类「标签 vs 预测」对照中,今天测的是哪一对(区分三个一致性度量,别搞混):

度量比对双方本任务状态
judge–truth 一致率(今天)judge verdict vs AMLworld 真 Is_Laundering待跑(需 key),30 条
judge–human κjudge verdict vs 人工标注gated(≥50 hand labels 未就绪)
human–human κ(标注质量)标注员 A vs 标注员 B不在本批次范围

今天只动第一行,且只到「一致率」粒度(不到 κ)。第二行的 κ 之所以重要,是因为 AMLworld 是合成数据——judge 和合成真值一致,不完全等于 judge 和「真实专家判断」一致;judge–human κ 才能回答后者。但它 gated,所以今天诚实地只报 judge–truth 一致率,并标注「合成真值,非专家真值」。

2. 推导 / 手算 / 代码走读

judge prompt v1 设计(要新建 judgeIsLaundering:核心是约束输出契约 {verdict: 'laundering' | 'normal', reason: string}。rubric 段落列出判据:

  • 结构化拆分(structuring):多笔金额略低于申报阈值(如 $10,000)、短时间密集;
  • 链式快进快出(layering):资金到账后迅速多跳转出、留存极短;
  • 多账户汇聚(mule_network):多个来源账户汇入单一受益人、各源账户行为同步。

judgeIsLaundering(prompt) 的职责:组装 system+user prompt → 调 deepseek-v4-flash → 解析 JSON → 校验 verdict 域(非 {laundering, normal} 即报错)→ 返回。

一致率手算:设 30 条手验子集,judge 与真标签在其中 24 条相同,则 agreement = 24/30 = 80.0%。这是原始一致率,不扣随机基线(扣随机就是 κ,今天不做)。若想直觉感受 κ:均衡集随机一致率 ≈ 0.5,则 κ ≈ (0.80 − 0.50)/(1 − 0.50) = 0.60——但这只是示意,真 κ 需 ≥50 hand labels,今天不报。

下游对接(真实代码,src/aml/groundTruthEval.ts:judge 输出的 verdict 映射成 predicted(laundering→可疑类,normal→'normal'),与真标签组成 {label, predicted},喂给 binaryEval(items, 'normal')(第 38 行,pure+tested)——这是 Day 136 的事。runner 框架复用 pnpm eval:agent 的 provider-agnostic 跑法(scripts/run-agent-eval.ts,第 8-9 行示例命令)。

rubric 输出契约的反例对照(解释为什么二元 + 强制理由是硬约束):

judge 输出可消费?问题
{verdict: 'laundering', reason: '3 笔 $9,800 在 2 小时内分拆'}二元 + 具体理由,可直接进 binaryEval
"这看起来有点可疑,但也可能是正常工资发放"自由文本、无二元字段、无法机器解析
{risk: 'medium'}三档/连续档,无法折叠成 suspicious/normal,且缺 reason
{verdict: 'maybe'}verdict 越域,应被解析层 fail-fast 拒绝

只有第一行能进下游管线——这就是为什么 judgeIsLaundering 的解析层必须校验 verdict ∈ {laundering, normal} 并要求 reason 非空,越域即报错而非静默吞掉。

为什么强制 reason 还能提质而不只是留痕:要求模型先写理由再下结论,等价于在 prompt 层强制了一段隐式 CoT。实证上,「解释后判定」比「直接判定」在边界案例上方差更小——因为模型被迫把判据落到具体交易特征上,而不是凭整体语感拍脑袋。在 AML 这种边界案例(如「大额但有合理商业背景」)占比高的场景,这一点尤其关键。

已有的真实模型数字(可参照,但非本任务数字):在 agent 任务完成度评测里,deepseek-v4-flash completion 79.3%deepseek-v4-pro 89.7%(judge=pass)。这是已落地的真实数字,可作为 flash vs pro 能力差的参照——但它不是 AML judge 的一致率,AML judge 的 30 条 agreement 需本日跑出后才有,不得挪用 79.3%/89.7% 冒充。

3. 今日实战

  1. judgeIsLaundering(prompt):rubric 要求输出 {verdict: laundering|normal, reason},二元 + 强制理由;解析 JSON 并校验 verdict 域。
  2. balanced_300.json30 条手验子集(含正负),对每条跑 deepseek-v4-flash(key 已配置)。
  3. 比对 judge verdict 与真 Is_Laundering,算 agreement %(一致条数 / 30)。
  4. 命令侧复用 pnpm eval:agent 的 provider-agnostic runner 框架(scripts/run-agent-eval.ts),provider 默认 deepseek,模型 NAMED deepseek-v4-flash
  5. 把 prompt v1 + 30 条一致率落盘,诚实标注「这是粗校准一致率,非 κ;judge-human κ 仍 gated」。

4. 今日实测 / 产出

  • 状态待跑(需 key 跑)
  • 产出(待跑后):judge prompt v1 + 30 条校准一致率数字(agreement %)。
  • 现状:key 已配置,但 30 条手验子集依赖 Day 134 的真实数据落盘balanced_300.json);judge-human κ(≥50 hand labels)仍 gated
  • 可参照的已有真实数字:V4-Flash completion 79.3% / V4-Pro 89.7%(judge=pass)——agent 任务完成度,本任务 AML 一致率。
  • 铁律:30 条 AML agreement 需本日跑出,不预填;不得把一致率包装成 κ,不得挪用 79.3%/89.7%。

5. 常见误区 / 陷阱

  • judge 不给 rubric / 不强制理由:输出自由发挥、方差大、不可解析——必须二元 + 强制 reason。
  • 把校准集和评测集混用:用同一批数据既调 prompt 又报结果,退回循环陷阱——30 条校准、300 条评测严格分开。
  • 把 30 条一致率当 κ:一致率不扣随机基线,κ 才扣;且 κ 需 ≥50 hand labels。今天只有一致率,κ 仍 gated。
  • 用 legacy deepseek-chat/deepseek-reasoner:2026-07-24 退役,prompt/脚本必须用 v4 id(deepseek-v4-flash/deepseek-v4-pro)。

6. 学习资源(每条带 YYYY-MM)

  • Anthropic《Demystifying evals》(2026-01)——judge 设计三要素(rubric / 二元输出 / 强制理由)。
  • DeepSeek-V4 模型卡 / API 文档(2026-04)——deepseek-v4-pro / deepseek-v4-flash id、定价、legacy 退役公告(2026-07-24)。
  • Zheng et al.《Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena》(arXiv:2306.05685, 2023-06)——LLM-as-judge 方法与位置/冗长等偏置(概念背景)。
  • 本仓代码:src/agent/config/providers.ts(第 35-36 行 v4 默认模型,已 Read)、src/agent/shared/cost.ts(第 14-15 行 v4 定价)、scripts/run-agent-eval.tspnpm eval:agent provider-agnostic runner)、src/aml/groundTruthEval.ts(下游 binaryEval)。

SOTA检查 (2026-06 更新)

  • 当前模型 iddeepseek-v4-pro / deepseek-v4-flash 为当前 id(providers.ts 第 35-36 行已落)。
  • 退役告警legacy deepseek-chat/deepseek-reasoner 于 2026-07-24 退役,prompt/脚本须用 v4 id。
  • 方法论:《Demystifying evals》(2026-01) 的 judge 设计仍 SOTA;rubric + 二元 + 强制理由是当前主流。
  • 过时黑名单:避免无 rubric 的自由文本 judge;避免用退役 deepseek id;避免把模型自评当外部标签(reward-hacking 风险)。
  • 合成真值的诚实标注:AMLworld 是合成数据,judge–truth 一致只证明「judge 与合成规则一致」,要再标「非专家真值,judge–human κ 仍 gated」。
  • 下次复查点:执行当周用 WebSearch 复核 deepseek v4 id / 定价 / 退役时间线是否变动;Day 136 全量裁判前确认 judge 一致率达标(否则先重写 prompt)。
  • 是否仍 SOTA(judge 范式):rubric + 二元 + 强制理由的 judge 设计 2026-06 仍是主流,无替代;位置/冗长偏置的去偏建议(如随机化、双向打分)可作增强但非必需。

衔接

  • 昨天:Day 134 — loader 与去偏(流式 + 分层平衡抽样导出 balanced_300.json)。
  • 今天:设计 rubric/二元/强制理由的 judge prompt,用 deepseek-v4-flash 对 30 条手验子集做二分类并算一致率(κ 仍 gated)。
  • 明天:Day 136 — 混淆矩阵与指标(对 balanced_300 全量跑 judge,喂 binaryEval 出 TP/FP/TN/FN + recall/precision/FPR 首版数字)。