EvalArc のレポートフォルダーを引き渡す:入力の複製、記録されたオプション、evalarc verify で確認できる範囲

diff・eval-health・hillclimb-review が入力の複製とオプションを保存、evalarc verify がオフラインで再計算。終了コードの読み方も。

フロー図:タイトルの下に 1 から 4 の番号付きの塗りつぶした円が左から右へ矢印でつながっている。下の文字は順に、--output で入力のバイト複製と params.json を保存、verify が結果とハーネスの複製の 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 のモジュール docstring と docs/verification.md によると、フォルダーには次のものが入ります。

  • 入力のバイト複製:結果ファイル、split.json、cases.json、そしてスキャンされたハーネスファイル。ハーネスファイルは harness/0000、harness/0001……の順に保存され、それぞれのファイル名、スキャンラベル、SHA-256 が params.json に記録されます。ハーネスの複製が合計 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 をレポートに記録された入力の識別情報と比較します。ハーネスの複製は params.json の記録と比較します。絶対パス、.. を含むパス、シンボリックリンクはすべて拒否されます。
  3. 記録されたオプションで compute_diff、compute_health、compute_hillclimb のいずれかを呼び出します。モジュール docstring によれば、レポートを書くコマンドと 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 は、リポジトリ同梱のサンプルデータから 3 つのフォルダーを作ります。テストはコマンドラインのエントリー関数 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

テストは毎回新しい一時ディレクトリに書き出します。この 3 つのコマンド自体の終了コードは確認せず、続く 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

テストはさらに、3 つの結果すべての files に params.json が含まれること、out/climb/hillclimb.json を直接渡しても 0 が返ること、hillclimb-run の例の出力フォルダーを verify に渡しても 0 が返ることもアサートしています。

終了コード 0・1・2 は何に答えるか

終了コード条件答える問い
0(既定)再計算の結果が保存されたレポートと一致記録は自己矛盾がないか。ゲートが不合格でも、eval health が警告を出していても 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 になります。これがまさに要点で、「レポートは改変されていない」と「レポートの結論が合格である」は別のことです。

テストはまた、フォルダーを 1 か所ずつ改変し、verify がそれを検出することをアサートしています。

改変テストでの操作テストのアサーション
判定の書き換えhillclimb.json の 2 番目のステップの decision を keep に変更終了コード 2、エラーに “differs from the recomputed” を含む
入力の複製の書き換えdiff フォルダーの current.json にある最初の "C" を "I" に置換終了コード 2、エラーに “differs from the input the report recorded” を含む
ハーネスの複製の書き換え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 に対応づける実装、そして上記のテストは、いずれもこれを 3 種類のフォルダーに拡張しています。

制約と注意点

  • verify の出力にある scope が範囲を明示しています。保存された入力のバイトと記録されたオプションから再計算し、保存されたレポートと比較するだけで、元のツールの採点は再実行せず、入力の出所を認証せず、HTML と Markdown は確認しません。
  • 結果ファイルの複製の期待ハッシュは保存されたレポートに、ハーネスの複製のハッシュは params.json に記録されており、どちらも同じフォルダー内にあります。ハッシュ検査は結果ファイルとハーネスの複製の変更を検出します。split、cases、またはオプションの変更は、再計算の出力が変わる場合にだけ検出されます。入力・ハッシュ・レポートをまとめて整合するように書き換えられた場合は防げません。ドキュメントの言葉では、示し合わせた捏造でも内部的には矛盾がないことがあり、実行の証拠が必要なときは信頼できる採点を再実行すべきです。
  • 比較では evalarc_version が無視され、再計算には検証する側がインストールした実装が使われます。作成側と検証側のバージョンが同じかどうかは別途確認が必要で、保存された入力のバイトはそれを示しません。
  • 上記の終了コードとエラーメッセージはテストのアサーションであり、本サイトがテストを実行して観察した結果ではありません。本記事は性能、実際のユーザー、完全なセキュリティ監査については何も主張しません。
  • 本記事は commit 8b134e2 に基づきます。以降のバージョンでは、コマンド、フィールド、終了コードが異なる可能性があります。

リンク