把 EvalArc 报告文件夹交给别人复核:输入副本、记录的选项与 evalarc verify 的边界

EvalArc 的 diff、eval-health、hillclimb-review 用 --output 保存输入字节副本和全部选项,evalarc verify 离线重算并比对报告。讲清退出码 0、1、2 的含义与边界。

流程图:标题下四个带编号 1 到 4 的实心圆从左到右用箭头相连,下方文字依次为:--output 写出输入字节副本与 params.json;verify 核对结果与 harness 副本的 SHA-256 是否与记录一致;用同一组 compute_* 函数按记录的选项离线重算;与保存的报告比较,忽略 created_at 等,任一步不一致则退出码 2。底部是来源说明。
从 –output 写出报告文件夹到 evalarc verify 复核 diff、eval-health、hillclimb-review 报告的步骤,依据 commit 8b134e2 的 evidence.py 与 docs/verification.md 绘制;–require-accepted 的退出码 1 不在图中。

EvalArc 的 diff、eval-health、hillclimb-review 和 hillclimb-run 加上 --output 时,会写出一个自包含的报告文件夹:每个输入文件的字节副本,加一份记录了全部选项的 params.json。收到文件夹的人可以运行 evalarc verify,离线重算一遍,确认保存的报告确实是从这些输入和选项算出来的。本文依据固定在 commit 8b134e2 的实现、文档和回归测试,讲文件夹里存了什么、verify 怎样重算、退出码 0、1、2 分别回答什么问题,以及这项检查证明不了什么。

本站的 EvalArc:分数变高了,智能体哪里退步了 介绍过这个工具,复核一次模型升级:完整计划匹配从 15/24 升到 19/24,EvalArc 门禁为什么仍然拒绝 讲过怎样从发布记录重算一次升级判定。本文讲的是另一件事:把自己算出的报告交给别人时,对方怎样核对它没有被改过、也没有和输入对不上。下文的行为描述都来自源码和测试文件([O],项目自己的代码),测试断言是代码写明的预期,本站没有运行这些测试。

文件夹里保存了什么

按 evidence.py 的模块说明和 docs/verification.md,文件夹包含:

  • 输入的字节副本:结果文件、split.json、cases.json,以及被扫描过的 harness 文件。harness 文件依次存成 harness/0000、harness/0001……,每一项在 params.json 里记录文件名、扫描标签和 SHA-256。harness 副本合计超过 16 MiB 时,写入直接报错。
  • params.json:schema 为 evalarc.report-params.v1,带 kind(diff、eval-health 或 hillclimb-review)和这次运行的每个选项。例如 diff 的 max_cost_ratio、cost_metric、require_generalization、leak_min_chars;eval-health 的 saturation、min_effect、ordered、plan_attempts;hillclimb-review 的 objective、stall_after、min_steps、labels。
  • 保存的报告本身:分别是 diff.json、health.json 或 hillclimb.json。

复制输入时,代码会核对读到的字节与预期的 SHA-256 一致,不一致就报“文件在读取过程中变了”。文件夹里的路径都是相对路径,所以可以移动或归档;报告记录的是原始文件名,副本改了名也能验证。

verify 怎样重算

evalarc verify 收到一个目录,或者其中的 diff.json、health.json、hillclimb.json 时,先看有没有可识别的 params.json,有就按报告文件夹处理。步骤如下:

  1. 检查 params.json 的 schema 和 kind,并确认报告的 schema 与 kind 对应。
  2. 读取每份结果副本,把它的 SHA-256 与报告里记录的输入身份比较;harness 副本则与 params.json 里的记录比较。绝对路径、含 .. 的路径和符号链接一律拒绝。
  3. 用记录的选项调用 compute_diff、compute_health 或 compute_hillclimb。模块说明称,生成报告的命令和 verify 调用的是同一组 compute_* 函数,所以两边不会各算各的。
  4. 去掉各层的 created_at 和 evalarc_version 字段,再把重算结果与保存的报告比较;有任何不同就失败。

通过时输出 schema 为 evalarc.verification.v1 的结果:verified、kind、passed,每类报告各自的摘要(diff 是 gate_passed 和 blocking_changes,eval-health 是 healthy 和 warnings,hillclimb-review 是 recommendation 和每一步的 decisions),读过的每个文件的 SHA-256 和字节数,以及一段 scope 说明。

用仓库示例走一遍

回归测试 tests/test_report_verify.py 用仓库自带的示例数据生成三个文件夹。测试是直接调用命令行入口函数 main([...]);下面是对应的命令行写法,在固定提交的仓库根目录下运行:

git clone https://github.com/noteflowai/evalarc.git
cd evalarc
git checkout 8b134e2374333adb54ace0aa81f7068e1c3b6881

这几份文件没有给出安装步骤。检出代码只固定了示例数据和源码,不会固定 PATH 上的 evalarc;请自己确认所用的 evalarc 来自同一提交,否则结果可能不同。

evalarc diff examples/results-diff/inspect/baseline.json \
  examples/results-diff/inspect/current.json \
  --held-out examples/results-diff/inspect/split.json \
  --harness examples/results-diff/inspect/harness \
  --max-cost-ratio 2 --cost-metric duration \
  --output out/diff

evalarc eval-health examples/results-diff/inspect/baseline.json \
  examples/results-diff/inspect/current.json \
  --cases examples/results-diff/inspect/cases.json \
  --min-effect 0.05 --ordered --plan-attempts 4 \
  --output out/health

# 测试按文件名顺序传入 results/ 下以 0 开头的 JSON
evalarc hillclimb-review examples/hillclimb-review/results/0*.json \
  --held-out examples/hillclimb-review/split.json \
  --objective cost \
  --harness examples/results-diff/inspect/harness \
  --output out/climb

测试每次都写到新的临时目录。它不检查这三条生成命令自己的退出码,只断言随后 verify 的结果:

evalarc verify out/diff --json      # 测试断言:退出码 0,kind=diff,passed=false
evalarc verify out/health --json    # 测试断言:退出码 0,kind=eval-health,passed=false
evalarc verify out/climb --json     # 测试断言:退出码 0,kind=hillclimb-review,passed=true

evalarc verify out/diff --json --require-accepted     # 测试断言:退出码 1
evalarc verify out/health --json --require-accepted   # 测试断言:退出码 1
evalarc verify out/climb --json --require-accepted    # 测试断言:退出码 0

测试还断言三份结果的 files 里都有 params.json;直接传 out/climb/hillclimb.json 也返回 0;把 hillclimb-run 示例的输出文件夹交给 verify 同样返回 0。

退出码 0、1、2 分别回答什么

退出码条件回答的问题
0(默认)重算结果与保存的报告一致记录是否自洽。门禁拒绝、健康检查报警告时也是 0
1(加 --require-accepted)记录一致,但 passed 为假结论是否可以接受
2输入、选项或报告对不上,或文件夹无效证据本身有矛盾或残缺;JSON 输出 verified: false

verify.py 把 fully_resolved 和 accepted 都设为同一个 passed;文档称 --require-resolved 沿用同样的退出码约定,但本文依据的 tests/test_report_verify.py 没有测试它在报告文件夹上的行为。

三类报告的 passed 含义不同。diff:门禁通过,并且要求了泛化检查时它也通过,设置了成本上限时成本门禁也通过。eval-health:healthy 为真,文档的说法是没有警告。hillclimb-review:merge_recommended 为真,即建议合并。测试里的 diff 和 eval-health 文件夹默认验证得到 0,加上 --require-accepted 就是 1:这正说明“报告没被改过”和“报告的结论是通过”是两回事。

测试还逐一改动文件夹,断言 verify 能发现:

改动测试的做法测试断言
改判定hillclimb.json 第二步的 decision 改成 keep退出码 2,错误含 “differs from the recomputed”
改输入副本diff 文件夹里 current.json 的第一个 "C" 改成 "I"退出码 2,错误含 “differs from the input the report recorded”
改 harness 副本harness/0000 改写成 clean prompt退出码 2,错误含 “harness file that was scanned”
改选项eval-health 的 params.json 里 saturation 改成 0.5退出码 2
移动文件夹把 climb 文件夹移到别处退出码 0

文档 docs/verification.md 在“Consistency, acceptance and resolution”一节仍写着 --require-accepted 只适用于 suite,这是早先的说法。同一文档讲报告文件夹的一节,以及 verify 把 passed 映射为 accepted 的实现和上面的测试,都把它扩展到了这三类文件夹。

局限 / 注意

  • verify 输出的 scope 写明了范围:从保存的输入字节和记录的选项重算,再与保存的报告比较;不重跑来源工具的评分,不认证输入的来源,不检查 HTML 和 Markdown。
  • 结果副本的预期哈希记录在保存的报告里,harness 副本的哈希记录在 params.json 里,两者都在同一个文件夹中。哈希检查可以发现结果副本和 harness 副本的改动;split、cases 或选项的变化,则只有在改变重算结果时才会被发现。它仍挡不住有人把输入、哈希和报告一起改成一致的样子。文档的原话是:协调一致的伪造也可以是内部自洽的;需要执行证据时,应重新运行可信的评分。
  • 比较时忽略 evalarc_version,重算用的是验证方安装的实现。生成方与验证方的版本是否一致,要另外确认;保存的输入字节不能证明这一点。
  • 上文的退出码和错误信息是测试断言,不是本站运行测试观察到的结果。本文也不涉及性能、真实用户或完整的安全审计。
  • 本文依据 commit 8b134e2,之后的版本在命令、字段或退出码上可能不同。

链接