response ステージがエージェントがそれに作用する
前にそれを剥がすか書き換えます。強制決定はすべてのプロバイダで同一です — 同じ
ルール、同じ判定、同じイベント。異なるのは、ファイアウォールがストリーミングされた
ツール呼び出しに作用した後にクライアントが見るワイヤーの形です。OpenAI chat、
OpenAI Responses API、ネイティブ Claude /v1/messages がそれぞれツール呼び出しを
異なってフレーム化するからです。
このページはそれらの顧客が観測可能な違いへの焦点を絞った注記です。ルール言語を
再文書化はしません — ファイアウォールルールを参照 —
ステージモデルも同様で、ステージでカバーされています。
3 つすべてが共有する内部の保持・再組み立てメカニズムについては、
ストリーミング内部を読んでください。
1. なぜファイアウォールのプロバイダストリーミングがワイヤーで異なるか
非ストリームレスポンスでは、ファイアウォールは返信全体を一度に見て決定します。 ストリームでは、モデルのツール呼び出しは断片のシーケンスとして到着します — 1 つの フレームに名前、引数 JSON はさらに多くにまたがって少しずつ。判定は完全な呼び出し (名前と完全な引数)を必要とし、ツール呼び出しの断片は一度転送されると撤回 できません。 そのためすべてのプロバイダでゲートウェイは同じことをします:通常のコンテンツを ライブでストリームさせ、呼び出しが完全に組み立てられるまでツール呼び出しフレームを 保持します。ストリーム終了時に各組み立てられた呼び出しを評価し、生き残ったもの のみを — そのプロバイダ独自のイベント形で — 発行します。テキストは決して停滞しません。 ツール呼び出しフレームのみが保持されます。アシス
タントコンテンツ、推論、role フレームはライブで変更なくストリームされます。保持は
最初のツール呼び出し断片からそのターンの終わりまで適用されます — そのためチャットのみの
レスポンスはファイアウォールがアタッチされていないのと全く同様にストリームされます。
2. OpenAI chat completions
/v1/chat/completions では、ツール呼び出しはインデックスでキー付けられた
delta.tool_calls 断片としてストリームされます。ゲートはそれら(とレガシーの
delta.function_call 形)を保持し、終了フレームで、生き残った呼び出しをゼロから
再インデックスして発行し、続いて finish フレームを発行します:
| 結果 | クライアントが受け取るもの |
|---|---|
| allow | 元の保持されたフレーム、バイト単位 — 真の通過。 |
| sanitize | 引数が書き換えられた 1 つの tool_calls デルタ、それから finish_reason: "tool_calls"。 |
| deny(一部の呼び出し) | 生き残った呼び出しのみ、それから finish_reason: "tool_calls"。 |
| deny(すべての呼び出し) | ツール呼び出しなし、それから finish_reason: "stop" — ターンはモデルがテキストで答えることを選んだように見えます。 |
3. OpenAI Responses API
ネイティブの/v1/responses ストリームは独自のイベントモデルを持ちます — ツール
呼び出しは response.output_item.added で開き、response.function_call_arguments.delta
断片をストリームし、response.output_item.done で完了する function_call アイテム
です。ファイアウォールは done、呼び出しが完全になる最初のポイントで評価します:
allow → バッファされたイベントがそのままフラッシュ
allow → バッファされたイベントがそのままフラッシュ
呼び出しがクリアされると、アイテムの
added / argument-delta / done イベントが
変更なく発行されます。sanitize → アイテムシェル + 書き換えられた done
sanitize → アイテムシェル + 書き換えられた done
added シェルがストリームされ、それから引数がリダクト版である done が — 元の
argument-delta 断片はドロップされるため、リダクトされていない値が決してあなたに
到達しません。deny → アイテムがどこからも削除
deny → アイテムがどこからも削除
バッファされたイベントはドロップされ、deny されたアイテムは、クライアントが最終
状態を構築する終端の
response.completed オブジェクトからもフィルタアウトされ
ます — 決して実行されなかった呼び出しへのぶら下がった参照はありません。4. ネイティブ Claude /v1/messages
ネイティブの Anthropic ストリームは異なる獣です:コンテンツはインデックス付きの
ブロックとして到着します — content_block_start → content_block_delta
(input_json_delta 断片)→ content_block_stop — stop_reason を運ぶ
message_delta で閉じられます。ファイアウォールは最初の tool_use ブロックから保持し、
それぞれを評価し、生き残ったブロックを連続したインデックスで再構築するため、剥がされた
ブロックがインデックスのギャップを残しません。
Claude 固有の手がかりは stop_reason です。すべての tool_use ブロックが deny される
場合、tool_use の stop_reason はクライアントに決して到着しないツール呼び出しを
約束してしまう — そのためゲートウェイはそれを end_turn に書き換えます:
tool_use ブロックを連続して再インデックスして保ち、
stop_reason: "tool_use" をそのまま残します。
5. 具体例 1 つ
同じルールはすべてのプロバイダで同じ決定を生成します — クライアントが読むワイヤーの 形のみが異なります。response ステージで一度作成します:
rm -rf 呼び出しを
deny します。クライアントが観測するもの:
| ワイヤー | 完全な剥がし後の終端シグナル |
|---|---|
| OpenAI chat | finish_reason: "stop" |
| OpenAI Responses | response.completed からアイテムが不在 |
| ネイティブ Claude | stop_reason: "end_turn" |
6. プロバイダをまたいで一定であるもの
ワイヤーは異なります;コントラクトは異なりません:- 判定とルールはワイヤーに依存しません。
allow/audit/deny/sanitizeは すべてのプロバイダで同じことを意味します。判定を参照。 - サニタイズはツール呼び出しの引数のみを触ります。ツールが返すコンテンツは 決して触りません — すべてのワイヤーで。 レスポンスのサニタイズを参照。
- allow は真の通過です。 ファイアウォールが何も作用しないとき、保持されたフレームは 正確なアップストリームバイトとして再生されます — 再バッチなし、プロバイダ固有の フィールドの損失なし。
- シャドウモードはどこでも適用されます。 それをオンにすると、保持されたツール
呼び出しは常に生き残り(
auditに格下げ)、ポリシーがトラフィックを変える前に プロバイダをまたいだその影響を測定できます。 シャドウモードを参照。
7. これがどこに収まるか
ストリーミング内部
すべてのプロバイダが共有する保持・組み立て・再組み立てメカニズム。
ステージ
なぜストリーミングされたツール呼び出しの強制が
response サーフェスに存在するか。判定
ストリーミングされた呼び出しが解決するプロバイダに依存しない決定。
レスポンスフィルタリング
モデルが発行するツール呼び出しを、ストリームかどうかに関わらずゲート。
