eager_input_streamingの設定でツール入力を高速表示する
Anthropic APIのeager_input_streamingフィールドで、ツール入力をバッファリングなしに配信する設定方法をまとめます。旧betaヘッダーとの互換関係と、効果が出る場面も扱います。
eager_input_streamingで何ができるか
eager_input_streaming は、ツール定義にboolean値を1つ足すだけで、そのツールの入力をサーバー側のバッファリングとJSON検証なしに配信させるフィールドです。長いコードやドキュメントをツール引数として書かせるとき、生成の完了を待たずに断片が届き始めます。
この仕組みはfine-grained tool streamingと呼ばれます。標準のストリーミングは各パラメータをサーバー側で一度バッファリングし、値として妥当だと確認してから返します。eager_input_streaming はこの検証ステップを飛ばすので、大きなパラメータ(ドキュメント全文やコードブロックなど)の最初の断片が届くまでの時間が短くなります。断片は標準のツール利用と同じストリーミングイベントで届くため、受け取り側のコードを大きく変える必要はありません。
標準のストリーミングとeagerの違い
標準(フィールドなし)
サーバーが各パラメータの値を検証してから返します。届いた値は妥当なので扱いやすい一方、大きなパラメータは値が確定するまで画面に出ません。
eager_input_streaming: true
生成中の断片がそのまま届きます。先頭の断片が早く届く代わりに、連結した文字列が有効なJSONになる保証はありません。
向いているのはレイテンシに敏感なアプリケーションです。コーディングエージェントが生成中のファイル内容を画面に流し込みたい場合や、長い記事の下書きをツール引数として書かせて執筆過程を見せたい場合が当てはまります。
1行でeager_input_streamingを有効にする
設定は tools 配列の各ツール定義に eager_input_streaming: true を足すだけです。現行の有効化はツール単位のフィールドで行います(リクエスト単位だったのは旧ヘッダーです)。あわせてリクエスト側で stream: true を指定します。
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 65536,
"tools": [
{
"name": "make_file",
"description": "Write text to a file",
"eager_input_streaming": true,
"input_schema": {
"type": "object",
"properties": {
"filename": { "type": "string" },
"lines_of_text": { "type": "array" }
},
"required": ["filename", "lines_of_text"]
}
}
],
"messages": [
{ "role": "user", "content": "長い詩を書いてpoem.txtに保存して" }
],
"stream": true
}'このリクエストを流すと、filename と lines_of_text の断片が content_block_delta イベントとして生成の途中から届きます。フィールドを付けなかった場合は標準のバッファリングされたストリーミング(各パラメータ値を検証してから返す挙動)のままです。
対応範囲は広く、全モデルがClaude API・Amazon Bedrock・Claude Platform on AWS・Google Cloud・Microsoft Foundryで使えます。ただし対象はユーザー定義ツールのみで、Web検索やコード実行のようなサーバーツールには適用されません。変わるのは配信のタイミングだけで、イベントの種類は標準のtool useストリーミングと同じです。
断片をそのまま見る
公式のCLIタブには、届く断片だけを jq で抜き出す書き方が載っています。ant コマンドにYAMLで同じリクエストを渡し、input_json_delta の partial_json を改行なしで連結して出力します。
ant messages create --stream --format jsonl <<'YAML' |
model: claude-opus-5-5
max_tokens: 65536
tools:
- name: make_file
description: Write text to a file
eager_input_streaming: true
input_schema:
type: object
properties:
filename: {type: string}
lines_of_text: {type: array}
required: [filename, lines_of_text]
messages:
- role: user
content: Can you write a long poem and make a file called poem.txt?
YAML
jq -rj 'select(.delta.type == "input_json_delta") | .delta.partial_json'出力は {"filename": "poem.txt", "lines_of_text": [ のようなJSONの途中の文字列が次々に流れる形になります。フィールドの有無で、この文字列が流れ始めるまでの時間が変わるかどうかを自分のツールで見比べられます。eagerの断片は、標準より長く、単語の途中で切れることが少ない傾向があります。
ストリーミングの公式ページには、現行モデルは input のキーと値を1組ずつ完成させて出すという注記があります。標準のtool useでは、イベントの間に待ちが入ることがあるということです。待ちの長さを比べるときは、この注記も頭に置いておくと測定結果を読み違えにくくなります。
旧betaヘッダーとはどう両立するか
以前は fine-grained-tool-streaming-2025-05-14 というbetaヘッダーをリクエストに付けることで、全ツールの入力ストリーミングを一括で切り替えていました。このヘッダーは今も動きますが、ツールごとのフィールドがヘッダーを置き換える設計に変わっています。優先順位は、ツール1つ1つのフィールドの値で決まります。
リクエストにヘッダーを残したときの各ツールの扱い
フィールドが未設定のツール
ヘッダーの効果が及び、fine-grained streamingが有効になります。
eager_input_streaming: true
ヘッダーの有無にかかわらず有効です。
eager_input_streaming: false
ヘッダーを送っていても、そのツールだけバッファリングされたストリーミングのままです。
複数のツールを1リクエストに並べていて、大きな入力を返すツールと小さな入力を返すツールが混在しているなら、移行の意味は大きくなります。ヘッダー方式では全部まとめて同じ挙動でした。ヘッダーを外すと、フィールド未設定のツールはバッファリングされたストリーミングに戻ります。移行するときは、ヘッダーに頼っていたツールすべてに eager_input_streaming: true を移します。
壊れたJSONが届く前提で組み立てる
断片の組み立て方自体は、eager_input_streaming の有無にかかわらず共通です。違うのは、サーバーがJSONの妥当性を検証しないまま断片を流すため、連結した文字列が有効なJSONにならないケースが起こり得る点です。応答が stop_reason: max_tokens で終わったときも、パラメータが生成の途中で切れて同じ状態になります。
断片を受け取ってから実行するまで
- 1
content_block_start で入れ物を用意する
tool_useブロックの開始イベントではinput: {}という空オブジェクトが届きます。これは配列内の場所を示す目印で、空の文字列を1つ用意して連結先にします。 - 2
input_json_delta の partial_json を連結する
content_block_deltaでtype: "input_json_delta"のイベントが届くたびに、partial_jsonを末尾に足します。初期値がオブジェクトで断片が文字列なのは仕様どおりです。 - 3
content_block_stop でパースを試みる
ブロックの終了イベントで連結結果をパースします。失敗するかもしれないので、
try/catchで必ず守ります。 - 4
失敗したら is_error 付きで差し戻す
パースできない入力は実行せず、
{"INVALID_JSON": "..."}のように1つのキーで包んで、is_error: trueのtool_resultとして返します。
差し戻しの形を、途中で切れた断片で試すと次のようになります。Python 3.12の json モジュールで、3つの断片を連結してパースし、失敗したものを包みました。断片は説明用に作った例です。
import json
frags = [
'{"filename": "poem.txt", "lines_of_text": ["Roses are red",',
' "Violets are blue",',
' "Sugar is sw',
]
acc = "".join(frags)
try:
json.loads(acc)
except json.JSONDecodeError as e:
print(e)
print(json.dumps({
"type": "tool_result",
"tool_use_id": "toolu_xxx",
"is_error": True,
"content": json.dumps({"INVALID_JSON": acc}),
}, ensure_ascii=False, indent=2))出力は次のとおりです。
Unterminated string starting at: line 1 column 81 (char 80)
{
"type": "tool_result",
"tool_use_id": "toolu_xxx",
"is_error": true,
"content": "{\"INVALID_JSON\": \"{\\\"filename\\\": \\\"poem.txt\\\", \\\"lines_of_text\\\": [\\\"Roses are red\\\", \\\"Violets are blue\\\", \\\"Sugar is sw\"}"
}content の中で引用符が二重にエスケープされているのが読み取れます。包む処理を文字列連結で書くと、壊れた入力に含まれる引用符でラッパー自体が壊れます。JSONライブラリで組み立てる理由はここにあります。生の文字列をそのまま返すより、何が起きたのかがClaudeに伝わりやすくなります。差し戻しの詳細はストリーミング中に壊れたJSONが来たときの対処で扱っています。
max_tokens で切れた場合の対応は、stop_reason を見て分けます。生成トークン数を増やして再試行するか、途中までの入力を補修するかの二択になります。
SDKごとの違い
組み立て処理をSDKに任せられるかは言語で差があります。Python・TypeScript・Go・Java・Rubyの公式SDKは、断片を自動で連結して最終的な入力を返すアキュムレータヘルパーを提供しています。PHPのSDKにはこのヘルパーがなく、ドキュメントのサンプルも手動の連結パターンです。
ヘルパーを使っていても、届いた断片をリアルタイムで画面に流す処理は自分で書きます。連結とパースのタイミングを自分で握りたいときや、組み立ての途中で独自の検証を挟みたいときは、ヘルパーのある言語でも手動で組めます。
どのツールに設定すると効くか
すべてのツールで有効化する意味があるわけではありません。効果が体感できるかどうかは、パラメータの生成にかかる時間で決まります。
| ツールの性質 | 効果 | 理由 |
|---|---|---|
| 長文・大きなドキュメントを出力するツール(コード全文の生成、記事の下書き作成など) | 効果大きい | 理由大きなパラメータの先頭断片までの時間が短くなることが、公式の説明する効果 |
短い文字列・数値1つを受け取るツール(get_weather のcityなど) | 効果限定的 | 理由公式が効果として挙げるのは大きなパラメータの先頭断片までの時間 |
| サーバーツール(Web検索・コード実行など) | 効果対象外 | 理由フィールド自体がユーザー定義ツールにしか適用されない |
| Computer use / Browser useのツールセット | 効果対象外 | 理由フィールドはユーザー定義ツール専用で、旧ヘッダーとの併用も拒否される |
公式ページのサンプルには、短い入力の get_weather ツールに eager_input_streaming: true を付けた例もあります。ただし公式が効果として挙げるのは大きなパラメータの先頭断片までの時間なので、短い入力で得られる差は限定的と見込まれます。
自分のツールが表の最上段に当たるかどうかは、まず eager_input_streaming を外した状態でストリーミング付きで動かし、測ります。最初の input_json_delta が届くまでの時間と、content_block_stop までの合計時間を記録します。その差が大きいツールだけに絞って有効化すれば、不正JSONを扱うコードの複雑化を抑えながら体感速度を改善できます。
ツールを追加したり、入力スキーマを変えたりしたときは、同じ手順で測り直します。生成にかかる時間はスキーマや用途によって変わるためです。
ツール定義の数を絞る施策とは軸が別です。Anthropic Advanced Tool UseのToolSearch ToolやProgrammatic Tool Callingは定義そのものを減らす方向です。eager_input_streaming は配信の早さを変えます。両方を組み合わせれば、大規模なツール構成でも待ち時間を抑えやすくなります。ツール呼び出しの往復自体を自動化したい場合はTool Runnerでツール呼び出しループを自動化するも参考になります。
設定したのに効果が出ないとき
設定自体は1行なので、つまずくのは大抵このあたりです。
stream: trueを忘れる:eager_input_streamingはツール側の設定で、リクエスト全体をストリーミングモードにするのは別のパラメータです。片方だけでは効果が出ません。- ヘッダーを送ったのに一部のツールだけ速くならない: あるツールに
eager_input_streaming: falseを明示していないか確認します。 - パースエラーをキャッチしていない: バッファリングされたストリーミングに慣れていると、届いた入力は常に有効なJSONだという前提でコードを書きがちです。フィールドを有効にした瞬間、この前提が崩れます。
max_tokens由来の途中切れとフォーマットエラー由来の不正JSONを区別しない: どちらも症状は「パースできない」です。前者は上限を上げて再試行し、後者はClaudeに書き直させるのが基本です。
まとめ
ツールごとの1行で有効になり、旧ヘッダーは未設定のツールにだけ効くフォールバックとして残ります。検証を飛ばす代償として壊れたJSONを受け取る前提でパースを守り、大きな入力を返すツールに絞って使うと、実装コストに見合った改善を得やすくなります。