AI Agentに実装を任せる。
別のAgentにレビューさせる。
問題があれば修正し、もう一度レビューする。
こうした開発フローは、もうそれほど珍しくなくなりました。
私自身もAgentへかなりの作業を任せています。
そこで気になったのが、ログの量ではなく「何を根拠に次の工程へ進んだのか」 でした。
Agentのログは残っている。レビュー結果も残っている。
しかし、
- その結果は、正確には何に対するレビューだったのか
- Agentを実行する前から、その対象と条件は決まっていたのか
- 失敗した試行は残っているのか
- 結果が出たあとから、都合よく意味を結び直せないか
は別の問題です。
そこで作っているのが、Rust製OSSの EvidenceRegistry です。
現在のリリースは v0.3.0。
v0.3.0ではRust Coreに加えて、AI Agentの実行をReview経路へ接続する AI Agent Evidence Binder が入りました。
EvidenceRegistry全体を作った背景や設計思想については、Zennの「AI Agentの出力は、何を証拠として受け入れる?」で書いています。
この記事ではv0.3.0のBinderに絞ります。
中でも中心にあるのは、
「Agentを起動してからレビュー記録を作るのではなく、Review Requestを先に固定する」
という設計です。
結果が出てから「何のレビューだったか」を決めたくない
単純なAgent workflowなら、次のような構造でも動きます。
Agentを起動
↓
レビュー結果を取得
↓
ログを保存
↓
「これは対象Xへのレビューだった」と記録
ただ、この順序には気になる点があります。
レビュー結果が存在してから、その結果の意味を決められる余地がある。
たとえば後から、
- どのsourceを対象にしたレビューだったか
- どのReview Requestへの回答だったか
- どのPolicyで評価するか
を決められるなら、ログ自体が本物でも、
「最初からその条件でレビューした」ことにはなりません。
EvidenceRegistry v0.3.0のBinderでは、この順序を逆にしています。
Review Requestを固定してから、Agentを起動する
Binderの流れを単純化すると、こうなります。
Target
↓
Freeze
↓
Review Request
↓
dispatch intent を保持
↓
──────── ここで初めてAgentを起動
↓
Agent execution
↓
Output Capture
↓
Review Result
↓
Policy
↓
Admission
↓
cold inspect
ここで使っている言葉を先に簡単に説明すると、
Freeze
レビュー対象を固定したものです。あとからcaptureされたAgentの出力とは別物として扱います。
Review Request
そのFreezeを対象として、何をレビューするのかを保持した依頼です。
Review Result
そのRequestに結び付けられた、構造化されたレビュー結果です。
Policy
そのResultをこのlaneでどう評価するかというルールです。
Admission
Policyを適用した結果として得られる、そのlaneでのdispositionです。Agentが CLEAN と書いたことやprocess exit 0そのものではありません。
Binderは、まずレビュー対象をFreezeします。
次に、その対象に対するexact Review RequestをStoreへ記録します。
さらに run は、どのprocessをどの条件で起動しようとしているかというdispatch intentを、process spawnより先に保持します。
必要なintentを保持できなければ、Agentを起動しません。
つまり、
「Agentがこう答えた。ではこれはRequest Aへの回答だったことにしよう」
ではありません。
先に、
「Request Aはこれ。Targetはこれ。この実行を行う」
まで固定してからAgentを起動します。
私がEvidenceRegistryで特に重要だと思っているのは、この境界です。
Agentの回答と「受理」も分ける
Agentがレビューを終えると、Binderはその出力をcaptureします。
controlled routeでは review.txt とstrictな submission.json が必須です。
自由文のレビューを別のLLMへ渡して「たぶんPASSと言っています」と解釈させるのではなく、submissionを決められたschemaとしてparseします。
そのResultは、実行前に記録したReview Requestへ結び付きます。
その後に初めてPolicyとAdmissionがあります。
Agentが CLEAN と書いたことと、そのResultがPolicyを満たしてaccepted Admissionになったことは別です。
さらに、acceptedになったことも「Agentのレビュー内容が真実だった」という意味ではありません。
READMEではこの境界を、
STRUCTURALLY_VALID != AUTHORITATIVELY_VALID
そして、
Policy acceptance != semantic truth
と明示しています。
失敗したattemptも含めて何が残るのか
v0.3.0には、4種類の結果を意図的に発生させるcontrolled fake lifecycleがあります。
| attempt | 内容 | process exit | Result記録 | disposition | failure stage |
|---|---|---|---|---|---|
a |
process failure | 7 | false | not_completed | process |
b |
malformed submission | 0 | false | not_completed | submission |
c |
Policy rejection | 0 | true | rejected | - |
d |
clean | 0 | true | accepted | - |
最後の d がacceptedになっても、a、b、c は消えません。
同じsampleを別processから binder inspect すると、公開READMEに記録されている実観測では、
- 4 attempts
- 2
ObservedFailure - 2
HistoricalReferencesValidated - 1 Request
- 2 Results
- 1 accepted Admission
- 1 rejected Admission
- 0 independent reviews established
が確認されています。
predecessorも null → a → b → c の順で保持されています。
ここで independent reviews established: 0 なのも重要です。
レビュー履歴が複数残っていることと、独立レビューが成立したことを同一視しません。
最後の成功だけを現在の状態として見せるのではなく、そこへ至るattempt chain自体を残します。
raw stdoutはprivate run evidenceとして保持する設計なので、ここでは公開READMEに掲載されている実観測値だけを記載しています。
実Agentでは、もっと分かりやすい失敗が実際に起きた
fake lifecycleより面白いのは、v0.3.0公開後に行った実Agentでのbounded observationです。
Codex CLI
Windows x86_64上で、Binderのgeneric literal-argv routeからCodex CLIを実行しました。
最終attemptはprocess exit 0 で完了し、review.txt とstrict submission.json を生成。
Result、Output Freeze、accepted Admissionまで記録され、別processからのcold inspectionも完了しました。
しかし、その前に2回失敗しています。
1回目は、promptに禁止されたcontrol characterが含まれていて、Binderのargv preflightで拒否。
2回目はCodex自体は起動しましたが、global optionを exec subcommandの後ろへ置いていたため、Codex CLIのargument parsingで失敗。
その後、別の明示的なattemptとして引数を修正し、完了しました。
つまり履歴は単なる「Codexレビュー成功」ではありません。
preflight failure → CLI argument failure → 明示的な後続attempt → completed
です。
Claude Code
Claude Codeでも似たことが起きました。
最初のattemptは、CLIが未ログインだったため失敗。
次のattemptではauthentication後に実model executionまで到達しましたが、caller-selected budget capで停止。
その後、より高いcapを設定した別の明示的なpredecessor-linked attemptが承認され、lifecycleが完了しました。
こちらは、
未ログイン → real model execution到達後budget cap → 明示的な後続attempt → completed
という履歴です。
最後の成功だけ保存していれば、どちらも単なる「Agentレビュー成功」に見えます。
しかし、どうそこへ到達したのかまで残すと、かなり違う情報になります。
なお、このobservationによってCodex CLIやClaude CodeがEvidenceRegistryのbuilt-in / formally qualified adapterになったわけではありません。
v0.3.0のgeneric managed-process routeを使った、限定されたWindows上のreal-agent observationです。
レビュー対象と、レビュー後の出力も別物
Binderではもう一つ、時間的な境界を分けています。
レビュー前に固定したTarget Freezeと、レビュー後にcaptureしたAgent Output Freezeは別物です。
Agentが後から生成した review.txt や submission.json が、元のReview RequestのTargetだったことにはなりません。
これは当たり前に見えます。
でも、Agent workflowが長くなり、生成物を次のAgentが読み、さらに新しいartifactを生成し始めると、この「いつ存在していたものなのか」はかなり重要になります。
Binderでは、post-review outputを元のTargetへ遡及させません。
もっと任せるために、境界を残す
EvidenceRegistryを作った目的は、AI Agentを信用しないためではありません。
むしろ、もっと仕事を任せるためです。
Agentへ任せる範囲が小さければ、人間が頭の中で「何を頼んだ」「何が返った」「なぜ次へ進んだ」を覚えていられます。
しかし、
実装Agent → Review Agent → 修正 → 再Review → 別環境でqualification → 別Reviewer
くらいまで進むと、大量のログがあるだけでは足りません。
必要なのは、
何を根拠として、どの判断を受理し、次の工程へ進んだのか
を区別して残すことです。
一方で、EvidenceRegistryが記録していても、それだけで、
- Reviewerの推論が正しい
- Reviewer本人のidentityが証明された
- すべての欠陥を発見した
- 複数レビューが独立している
- sandboxが安全だった
- 外部deploymentが許可された
とは言えません。
EvidenceRegistryが扱っているのは、固定されたTarget、Request、Result、Policy、Admissionと、その履歴の関係です。
記録した範囲より大きな主張をしないこと自体も、この仕組みの一部です。
v0.3.0を読む・試す
v0.3.0はsource-only releaseです。
prebuilt binaryやcrates.io packageはありません。
Rust Coreに加えて、AI Agent Evidence Binder companionが含まれています。
Binder implementation treeはWindows x86_64、Linux x86_64、macOS arm64でnative qualificationされています。
ただし、この3 OS qualificationはBinder implementationとdeterministic fixture routeについてのもので、Codex CLI / Claude Codeのreal-agent observation自体はWindows上の限定された記録です。
まずbuildせずに読むなら、次の順が分かりやすいと思います。
README.mddocs/RELEASE-NOTES-v0.3.0.mddocs/AGENT-BINDER-DESIGN.mddocs/BINDER-REAL-AGENT-OBSERVATIONS-v0.3.0.md
実際に試す場合は、公開repositoryをcloneしてからannotated v0.3.0 tagを固定します。
git clone https://github.com/DwarfM42/EvidenceRegistry.git
cd EvidenceRegistry
git fetch origin tag v0.3.0
git checkout --detach v0.3.0
git rev-parse "v0.3.0^{tag}" "v0.3.0^{commit}" HEAD 'HEAD^{tree}'
git status --short
tag object、resolved commit、HEAD、source treeがGitHub Releaseの記録と一致することを確認してからbuildします。
READMEには、実Agentを使わずに init → run → inspect を確認できるcontrolled fake lifecycleもあります。
最初からCodexやClaude Codeを接続する必要はありません。
おわりに
Agent時代の開発で必要なのは、ログをたくさん残すことだけではないと思っています。
EvidenceRegistryで特に固定したかったのは、順番でした。
結果が出てから「これは何の結果だったのか」を決めるのではなく、
Targetを固定する
↓
Review Requestを固定する
↓
dispatch intentを保持する
↓
Agentを起動する
↓
結果をcaptureする
↓
Resultを元Requestへ結び付ける
↓
Policy / Admission
↓
全attemptをcold inspectする
という順序にする。
Agentへ任せる仕事が増えるほど、
「何をやったか」だけでなく、「何を依頼した結果として、それを受け取ったのか」
が重要になると思っています。
EvidenceRegistry v0.3.0では、そこをAI Agent Evidence Binderとして、実際のAgent実行までつなぎました。
もっとAgentへ任せたい。でも、後から判断の根拠が分からなくなるのは困る。
そんなところに引っ掛かる方がいたら、EvidenceRegistryを覗いてみてもらえるとうれしいです。