返回 AICAP-180
B18 · Day 172OSS 收口 + 英文 + 全局 SOTA 复核

DeepEval + Inspect AI 评测框架

昨天(Day 171)扫的是 smolagents 的 agent 主循环(模型怎么编排工具)。今天往同一条「读真框架」的线再走一步,但视角从「怎么跑 agent」切到「怎么评 agent」——拆两个 2026 活跃主线评测框架 DeepEval 与 Inspect AI(UK AISI),并把它们的抽象对回本仓的评测内核(cohensKappa.ts 的 judge-human 一致性、agen

阶段: B18 · OSS 收口 + 英文 + 全局 SOTA 复核(Day 171-180) 标签: #deepeval #inspect-ai #llm-as-judge #cohens-kappa

今日导引(由浅入深)

昨天(Day 171)扫的是 smolagents 的 agent 主循环(模型怎么编排工具)。今天往同一条「读真框架」的线再走一步,但视角从「怎么跑 agent」切到「怎么评 agent」——拆两个 2026 活跃主线评测框架 DeepEval 与 Inspect AI(UK AISI),并把它们的抽象对回本仓的评测内核(cohensKappa.ts 的 judge-human 一致性、agentEval.ts 的判定逻辑)。

在 B1→B18 曲线上,这一天属于「用外部框架的话语校准自己内功」的位置:本仓很早就踩过「循环自评分数虚高」的坑,今天用 DeepEval 的 GEval 重跑一遍,验证我们的去循环结论站得住。

最小可判定产出:DeepEval GEval 重跑本仓 5 个任务的 pass-rate + 脚本(标「待跑(需 key)」),并与本仓已落地真值交叉核对。

一句话锚点:今天要建立的判断力是「拿到任何一个评测分数,先问它有没有循环依赖、judge 校准过没有、N 够不够」——这三问贯穿后面所有对外报数。

1. 机理精读

DeepEval 的抽象:pytest 风格的 metric。 DeepEval 把评测组织成类似 pytest 的测试用例,核心是一组 metric。

其中 GEval = 可自定义 rubric 的 LLM-as-judge——你写一段评判标准(rubric),让一个 judge 模型按这个标准对候选输出打分。它的卖点是低门槛、和 pytest 生态贴合、metric 即代码。依据:Anthropic "Demystifying evals" (2026-01) 给出的评测原则;DeepEval 当周版本需复验。

Inspect AI 的抽象:Task / Solver / Scorer 三件套。 Inspect AI 是 UK AISI(英国 AI 安全研究所)做的评测框架,抽象成三层:

  • Task:数据集 + 目标;
  • Solver:如何让模型产出答案,可串多步;
  • Scorer:如何给答案打分。

强调可复现日志——每次评测产生结构化、可回放的 eval log,便于审计与对照。依据:Inspect AI docs (2026-02)。

两个框架的共同内核,对回本仓两个文件。 无论 GEval 还是 Inspect 的 Scorer,本质都是「judge 给 outcome 打分」,这正对应本仓:

  • src/agent/eval/agentEval.tsrunTaskEval + makeLlmJudge——judge 判定逻辑(label ∈ pass/fail/unknown + 0..1 partial credit)。
  • src/agent/eval/cohensKappa.tscohensKappaWithCI——judge↔human 一致性校准(judge 本身可不可信)。

关键教训:循环自评分数虚高。 这是今天最值钱的一句话,也是本仓评测演进的核心母题:src/aml/evalBaseline.ts同一个规则引擎assessCasetopTypology)去预测,又拿同一作者写的合成金标去评——预测和金标同源,分数虚高、不可信。

GEval 之类的 LLM-as-judge 若只用「模型自评自己的输出」,会重蹈同一覆辙。本仓的解法是双管齐下:

  • (a) agentEval.ts 让 judge 模型可以和被测模型不同makeLlmJudge 接受独立 judge model);
  • (b) cohensKappa.ts 拿 judge 和人标算 κ,用「judge 和人到底有多一致」给 judge 上一道校准锁。

依据:Anthropic "Demystifying evals" (2026-01) 明确把「grade outcome、允许 unknown、给 partial credit」列为正确做法。

DeepEval vs Inspect AI 的设计取向差异(一句话区分)。 两者都做 LLM-as-judge,但侧重不同:

  • DeepEval 偏「贴近开发流」——pytest 风格、metric 即代码,适合塞进既有 CI、对单个 metric 快速迭代 rubric。
  • Inspect AI 偏「贴近评测科学」——Task/Solver/Scorer 三层 + 强制结构化可复现日志,适合做需要审计、需要跨 run 对照的正式评测。

本仓的取向更接近 Inspect:agentEval.tsTaskEvalReport 是结构化报告(含 perTask 明细 + κ + 成本),可回放、可对照——这与「评测要能被外部核验」的诚信底线一致。

两层评分:code-graded 兜硬线,LLM-judge 兜语义。 本仓 runTaskEval 对每个 task 先跑 codeCheck(确定性、廉价、heuristic)再跑 LLM-judge,这不是冗余而是分工:

  • code-graded 负责有唯一正确答案的硬断言——CTR=$10,000、SAR 30 天、OFAC 必须 block、JSON 格式严格——这些交给 LLM 等于给监管硬线引入抖动。
  • LLM-judge 负责语义模糊的兜底——SAR 叙事是否覆盖 5W1H、是否抵抗了 prompt injection、是否诚实承认信息不足——这些没有正则能精确判定。

DeepEval 的 metric 体系同样允许这种「确定性 metric + GEval」混搭。把硬线留给代码、把模糊留给 judge,是 2026 评测的标准分工。

边界:LLM-as-judge 不能当唯一信号。 GEval/Inspect Scorer 再好,也只是一个会犯错的模型。本仓 cohensKappa.ts 的存在就是为补这一刀——待手标 ≥50 条后算 judge-human κ,看 judge 偏离人到什么程度。这条边界划清了「judge 评分」与「judge 可信度」是两回事。

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

今天是代码对照日。已用 Read 打开三个真实文件,走读关键符号:

  1. agentEval.tsrunTaskEval(tasks, opts)(line 77):主循环对每个 task 先跑 generate(被测模型)、再跑 task.codeCheck(确定性廉价初筛)、再跑 judge(LLM-as-judge)。聚合出 TaskEvalReportcompletionRate(label==='pass' 占比)、partialCreditMean(mean judge.score,经 clamp01)、unknownRatecodePassRatetotalCostUsd
  2. judge↔human κ 的接入(line 102-115):当 opts.humanLabels 提供时,把 perTaskjudge.label 与人标配对,配对数 ≥2 才调 cohensKappaWithCI。这是「judge 不被信任、要被校准」的代码落点。
  3. makeLlmJudge(model)(line 173)+ JUDGE_SYSTEM(line 167):judge prompt 明确「grade the OUTCOME, not the steps;给 partial credit;信息不足答 unknown 不要猜」——逐字对应 Anthropic 评测原则。parseVerdict(line 189)是容错解析器,抽第一个 JSON、clamp score、默认 unknown。
  4. cohensKappa.tscohensKappa(a, b)(line 22):算 po(观察一致)、pe(偶然期望一致)、κ=(po−pe)/(1−pe)。退化边界(两评者都只用同一类别 ⇒ pe==1)按约定:完全一致报 1,否则报 0(line 47-48)。cohensKappaWithCI(line 53)加 percentile bootstrap 95% CI——小 N ⇒ CI 宽,这就是头注里「P1 want N>=50」的数学理由。
  5. 循环自评的反面教材 src/aml/evalBaseline.tsevalRuleBaseline(ds)(line 19):predicted = assessCase(c).topTypology ?? 'normal',再拿 c.label(同源合成金标)算 perTypology recall + normalFalsePositiveRate。predict 与 label 同源 ⇒ 分数虚高。本仓后来用 src/aml/groundTruthEval.ts 的 prediction-source-agnostic 设计去循环(明天 SOTA 复核会再引)。

手算小例(κ 直觉): 5 个任务,judge 和人标在 4 个上一致(po=0.8)。若两者各自类别分布让 pe=0.5,则:

κ = (0.8 − 0.5) / (1 − 0.5) = 0.6(中等一致)。

注意:N=5 时 bootstrap CI 会很宽——这正是为什么本仓不拿 N<50 的 κ 当结论,只拿它当「需要更多手标」的信号。这与 B17 的 A/B 教训同构:方向有了、显著性没有,差的是样本量。

3. 今日实战

按 seed 落地:

  1. pip install deepeval,把 src/agent/eval/tasks.ts5 个任务导出为 DeepEval 测试集(prompt + reference)。
  2. GEval 写 rubric(grade outcome、允许 unknown、给 partial credit,对齐本仓 JUDGE_SYSTEM),judge 用 deepseek-v4-flash,跑出 pass-rate。
  3. 与本仓 pnpm eval:agentagentEval.tsrunTaskEval 真模型接线)结果交叉核对——看 GEval 的判定和本仓 LLM-judge 是否一致,不一致处正是 judge 校准的候选样本。
  4. 把脚本 committed。

4. 今日实测 / 产出

  • DeepEval GEval 重跑 → 待跑(需 key);将产出 DeepEval pass-rate(如 4/5=80%,为示例非实测)+ 脚本 committed。
  • 本仓已落地真实数(已测真值,逐字保留):V4-Flash completion 79.3%、V4-Pro 89.7%、partial-credit 0.900
  • 不把示例 4/5 当 DeepEval 实测结果;不把「待跑」升级为「已完成」。

5. 常见误区 / 陷阱

  1. 用同一模型自评自己输出:循环自评(evalBaseline.ts 的同源问题)会让分数虚高——judge 模型应与被测模型解耦,或用人标算 κ 校准。
  2. 把 LLM-as-judge 当唯一信号:GEval/Inspect Scorer 都是会犯错的模型;本仓 cohensKappa.ts 正是为补 judge-human κ(待 ≥50 手标)。
  3. N 太小还报点估计:5 个任务的 pass-rate / κ 的 CI 极宽,只能当方向信号,不能当结论(参见 B17 N=29 即不显著的教训)。
  4. 框架版本不复验:deepeval / inspect-ai 迭代快,当周版本号需复验。
  5. judge 和被测模型同款不同实例就当「独立」:同一基座模型互评仍有相关性偏差;真正的解耦要么换不同家模型当 judge,要么用人标 κ 兜底,二者择一。

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

  • Anthropic, Demystifying evals(grade outcome / 允许 unknown / partial credit)— 2026-01
  • Inspect AI(UK AISI), documentation — Task/Solver/Scorer— 2026-02
  • DeepEval, GEval / metric 文档(pytest 风格,当周版本复验)— 2026
  • 本仓 src/agent/eval/agentEval.ts + cohensKappa.ts(judge 判定 + judge-human κ)— 2026
  • 本仓 src/aml/evalBaseline.ts(循环自评反面教材:predict/label 同源)— 2026
  • Cohen, J., A Coefficient of Agreement for Nominal Scales(κ 原始定义,经典打底)— 1960

SOTA检查 (2026-06 更新)

  • 当前主流:DeepEval、Inspect AI 均为 2026 活跃主线评测框架。GEval(自定义 rubric 的 LLM-as-judge)仍 SOTA。
  • 是否仍 SOTA:是。但避免把 LLM-as-judge 当唯一信号——本仓 cohensKappa.ts 正是为补 judge-human κ(待 ≥50 手标),这条补刀符合 2026 评测严谨性共识(judge 须被校准而非被信任)。
  • 过时黑名单:循环自评(同源 predict/label)对外报数已是反模式;避免无 CI、无样本量的裸 pass-rate。
  • 两层评分仍是共识:code-graded 兜监管硬线 + LLM-judge 兜语义模糊,是 2026 评测标准分工,非过时——本仓 runTaskEval 的 codeCheck + judge 双跑即其实现。
  • 下次复查点:deepeval / inspect-ai 当周版本号复验;judge-human κ 待 ≥50 手标后落值,届时重审 judge 可信度结论。

衔接

  • 昨天:Day 171 — smolagents v1.26.0 入口扫描(读框架的 agent 主循环)
  • 今天:拆 DeepEval(GEval)+ Inspect AI(Task/Solver/Scorer)评测框架,对回本仓 agentEval/cohensKappa,复述「循环自评分数虚高」教训
  • 明天:Day 173 — claude-cookbooks + 全局 SOTA 复核(从「读单个框架」抬升到「逐项确认全部主线资料未过时」)