ツール領収書
ツールレシートは、ツールの実行結果が正常にランタイムから返されたことを証明する暗号学的な証明です。レシートを有効にすると、正常に実行されたツールに対して、呼び出しとその結果に対する HMAC-SHA256 ダイジェストが付与されます。このダイジェストはツール実行結果のテキストに追加され、会話の一部としてモデルに返されます。
現実的な結果は次のとおりです。モデルは実行していないツールを実行したと説得力を持って主張することはできず、ツールの結果を捏造することもできません。そのような主張は、欠落した、あるいは無効なレシートを生成します。
脅威モデル
LLMはストリングジェネレーターです。デフォルトでは、実際には行っていないツール呼び出しを語る(「git log を実行したところ、最新のコミットは…」)ことや、ツール呼び出しの結果をでっち上げる(呼び出しがタイムアウトしたにもかかわらず「天気APIによると72°Fです」)ことを防ぐものは何もありません。自律性を持つエージェントにとって、これは単なる正確性の問題にとどまらず、否認可能性の問題でもあります。
ツールレシートは、可能な限り低コストな構造でそのギャップを埋めます。すなわち、揮発性のインメモリキーを用いた対称MACです。
ここでいう「ephemeral」の意味。 チャネルサーバーのパスは、チャネルランタイムコンテキストの存続期間中、1つのHMACキーをメモリに保持します。ダイレクトエージェントターンのパスは、ターンごとに新しいレシートスコープを作成します。どちらの場合も、キーはメモリ内に留まり、モデルには送信されず、セッションをまたぐ永続的な検証子ではありません。
出典: Basu, A. (2026). “Tool Receipts, Not Zero-Knowledge Proofs: Practical Hallucination Detection for AI Agents.” arXiv:2603.10060。
動作方法
-
レシートが有効な場合、ランタイムはアクティブなレシートスコープ用に256ビットのインメモリキーを作成します。チャネルサーバーのパスではこのキーを
ChannelRuntimeContextに保持し、直接ターンのパスではそのターン用に新しいスコープを作成します。このキーがディスクに書き込まれたり、モデルに送信されたり、ログに記録されたりすることは一切ありません。 -
各ツールの実行が成功した後、ランタイムは次を計算します:
receipt = HMAC-SHA256(key, tool_name || args || result || timestamp) -
領収書は、ツール結果のテキストに以下のように追加されます:
[receipt: zc-receipt-<timestamp>-<base64url-digest>] -
ツール結果(領収書を含む)がモデルにフィードバックされます。
モデルは会話履歴内のすべてのレシートを参照できます。ユーザーに生成するテキスト内でそれらをそのまま出力することも可能です。しかし、_新しい_有効なレシートを生成することはできません。HMACにはセッションキーが必要ですが、モデルはそれを持っていないからです。
レシート形状
zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA
^ epoch seconds ^ base64url(HMAC-SHA256 digest)
zc-receipt- プレフィックスは、リーク検出器がそれらを削除しないようにするために存在します(レシートは表面化しても安全であり、機密情報を含んでいません)。
領収書が検出するもの
| シナリオ | 領収書なし | 領収書あり |
|---|---|---|
| モデルはツールを実行したと主張したが、実際には実行されなかった | 検出不可能 | レシートなし:捏造が露見 |
| モデルが実際の呼び出しに対して結果を生成する | 検出不可能 | 検証時のHMAC不一致 |
| モデルが自身が行った成功した呼び出しを否定する | 検証不可能 | 会話内のレシートは、署名済みの結果が発行されたことを証明します |
| モデルが妥当なレシート文字列を生成する | 妥当 | HMAC検証に失敗しました |
レシートがやらないこと
- テキスト出力を制限しないでください。 モデルは、ツール呼び出しに関連しないことを言うことができます。
- ツール使用を強制しないでください。 レシートはツールが呼び出された場合にのみ生成され、「モデルが何かを調べるべきだったのに、事前の知識から回答した」といった問題には役立ちません。
- ブロックされた呼び出しや失敗した呼び出しは対象外です。 承認拒否、タイムアウト、ブロックされた呼び出し、失敗したツールの戻り値は、オブザーバビリティまたは監査イベントであり、レシートを伴うツール結果ではありません。
- レシートスコープをまたがないでください。 レシートキーは一時的なものなので、あるスコープで生成されたレシートは、そのスコープがなくなった後は検証できません。
- 1つのチャネルランタイムコンテキスト内で、チャネルや会話を互いに分離しないでください。 そのコンテキスト内のチャネルサーバーの会話はキーを共有します。脅威モデルは、クロスチャネルの偽造ではなく、ランタイム内のLLMによる捏造を対象としています。
- バックグラウンドまたはデタッチされたデリゲートのスポーンには拡張しないでください。 ユーザーのターンからデタッチするバックグラウンドおよび並列のデリゲートスポーン(
background: true)は、ターンごとのコレクターがそれらのスポーンの完了前にレンダリングされるため、ユーザーに表示されるブロックにレシートを表示しません。同期的なデリゲートのサブエージェント内のレシートはキャプチャされます。
領収書を表示する
デバッグログで
sh
RUST_LOG=zeroclaw_runtime::agent=debug zeroclaw daemon
出力:
DEBUG Tool receipt generated tool=shell receipt=zc-receipt-1774604899-fVRG...
ユーザーに表示される返信
[agent.tool_receipts] show_in_response = true の場合、返信には末尾にブロックが含まれます:
Here's the weather in Istanbul: 16°C, sunny.
---
Tool receipts:
weather: zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA
LLMの出力において
モデルはコンテキスト内でレシートを参照するため、ツールの結果を説明する際にそれらをそのまま出力することがあります。リークディテクターは、このエコー出力が機能するように zc-receipt-* トークンを変更せずに通過させるよう設定されています。ランタイムとモデルの両方がレシートブロックを含む場合、ユーザーには2つが表示されます。チャネル固有のフォーマットルールを使って一方を取り除いてください。
設定
セキュリティプロパティ
- 一時的なインメモリキー。 アクティブなレシートスコープ内にのみ保持されます: チャネルサーバーパスはチャネルランタイムコンテキストを使用し、直接ターンパスはターンごとのスコープを使用します。永続化されず、ログに記録されず、モデルのコンテキストにも含まれません。長期ストレージを侵害しても得るものはありません。
- 標準的なMACプリミティブ。 Rustエコシステムからの
hmacとsha2。 - 無視できるオーバーヘッド。 ツール呼び出しごとに1 ms未満。
- 外部依存関係は追加されません。
どの領収書が_ではない_
- ZK 証明ではありません。ランタイムは鍵を持っているため、レシートを検証できます。第三者は検証できません。
- 会話ハッシュとクロス署名されていません。以前の会話を改ざんしても、その後のレシートは無効化されません(レシートは計算された呼び出しのみをカバーします)。
- 承認ゲートの代替ではありません。レシートは呼び出しが行われたことを証明しますが、その呼び出しが適切だったかどうかを決定するものではありません。
現在の状態
| 機能 | ステータス |
|---|---|
| 成功したツール結果のHMAC生成 | 出荷済み |
| ツール結果の成功時に領収書を追加しました | 出荷済み |
| レシートのデバッグログ | 出荷済み |
show_in_response | 出荷済み |
| システムプロンプトの指示をエコー受信 | 出荷済み |
| 永続的な監査データベースのレシート | 計画済み |
| クロススコープのレシート検証 | 計画されていません(一時的なキーの設計を参照) |
関連項目も参照してください
- セキュリティ → 概要
- 自律性レベル: レシート対象となる呼び出しが発生するかどうかを決定するポリシー層
- リファレンス → 設定: 生成された設定リファレンス