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-flash 对 Is_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 设计三要素:
- 明确 rubric——把「什么算 laundering」用可操作的判据写清(structuring 拆分到申报阈值以下、layering 链式快进快出、mule 多账户汇聚单一受益人等信号);rubric 越具体,裁判方差越小。
- 二元输出——
verdict ∈ {laundering, normal},便于直接进binaryEval;避免「可能/也许/中等风险」这种无法机器消费的模糊档。 - 强制理由——要求输出
reason,既降方差(逼模型走推理链,类似 CoT 效应)又留可审计痕迹(AML 域必须能解释为什么判可疑)。
为什么用 deepseek-v4-flash。 我们的 runner 是 provider-agnostic 的(scripts/run-agent-eval.ts,pnpm 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. 今日实战
- 写
judgeIsLaundering(prompt):rubric 要求输出{verdict: laundering|normal, reason},二元 + 强制理由;解析 JSON 并校验verdict域。 - 从
balanced_300.json取 30 条手验子集(含正负),对每条跑deepseek-v4-flash(key 已配置)。 - 比对 judge
verdict与真Is_Laundering,算 agreement %(一致条数 / 30)。 - 命令侧复用
pnpm eval:agent的 provider-agnostic runner 框架(scripts/run-agent-eval.ts),provider 默认 deepseek,模型 NAMEDdeepseek-v4-flash。 - 把 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-flashid、定价、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.ts(pnpm eval:agentprovider-agnostic runner)、src/aml/groundTruthEval.ts(下游binaryEval)。
SOTA检查 (2026-06 更新)
- 当前模型 id:
deepseek-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 首版数字)。