---
title: Approval
description: Tool call の手前に人を立て、自分の UI から決着させる。
sidebar:
  order: 3
---

モデルがそうすべきだと判断したからという理由で起きてはならない tool call があります — 返金する、メールを送る、レコードを削除する。そうしたものについては、run が一時停止して尋ねます。

Approval は session ログ上の 2 つの event の組なので、決める人がライブのストリームを見ていても、1 時間後に session を開いても、同じように動きます。

## 2 つの event

ゲートされた call が来ると、run は `approval_requested` を追記して待ちます。

| フィールド | 内容 |
| --- | --- |
| `call_id` | 判断の対象となる call |
| `server` | その tool がどのサーバーのものか |
| `tool` | Tool 名 |
| `arguments` | モデルがそれを呼び出そうとしている引数 |
| `args_hash` | 再発火の際に照合される同一性 |

`arguments` が要求 event に載っているのは意図的です。決める人は、対応する `tool_call` を探してログをさかのぼらなくても、自分が何を判断しているのかを見られます。

決着は `approval_resolved` として追記され、`decision`（`allow` または `deny`）、モデルに拒否理由を伝える任意の `deny_message`、そして `resolved_by` を持ちます。

## UI を作る

1. **要求を監視する**

    すでに session のストリームを追尾している client
    は、他の event と同じように `approval_requested`
    を受け取ります。その event 自身のフィールドから描画してください。

2. **実際に何が尋ねられているかを示す**

    `arguments` はそのまま描画してください。approval
    ゲートの価値は、要約ではなく本物の call を人が見ることにあります。

3. **決着させる**

    要求が示した `call_id` に対して判断を投稿します。

    ```http
    POST /v1/sessions/{session_id}/approvals/{call_id}
    ```

    ```json
    { "decision": "deny", "message": "Refunds over $500 need a manager." }
    ```

    `allow` なら call が発火し、その `tool_result` が続きます。`deny`
    なら、モデルは拒否を — 設定していればあなたのメッセージとともに —
    受け取り、そこからターンが続きます。

`message` は `deny` だけのものです。`{"decision": "allow"}` と一緒に送ると、無視されるのではなく拒絶されます。allow がモデルの見ない誘導文をひそかに運ぶことはありません。

判断は速やかに。call を保持している broker は次のティックであなたの判断を読み、engine にそのまま答えます。その保持時間を過ぎると run は再キューされて call を発火し直します — `args_hash` はそれを照合するために存在します。

## 拒否は失敗ではない

拒否された call は正常な結果であって、エラーではありません。モデルはそう告げる tool 結果を受け取って先へ進みます。だからこそ `deny_message` を書く価値があります。「承認されませんでした」ではモデルは推測するしかありませんが、「$500 を超える返金にはマネージャーの承認が必要です」なら次に何をすべきかが伝わります。

:::note
判断がログに載るので、別のログを突き合わせなくても session の監査証跡は完結します。誰が、どの引数について、何を決着させたかが、作業そのものと同じ順序付きストリームに並びます。
:::
