「zero data retention」(ゼロデータ保持)、すなわちZDRがこの機能にどのように適用されるかについては、APIとデータ保持を参照してください。
細粒度ツールストリーミングは、サーバーサイドのバッファリングやJSON検証を行わずに、Claudeが生成するのと同時にツールの入力をクライアントに配信します。バッファリングのステップを省略することで、ドキュメントやコードブロックなどの大きなパラメータの最初のフラグメントが届くまでの時間が短縮され、フラグメントは標準のツール使用と同じメッセージのストリーミングイベントを通じて届きます。
APIはツールの入力をストリーミングする前にバッファリングや検証を行わないため、部分的または無効なJSONを受け取る可能性があります。停止理由がmax_tokensで終わるレスポンスでは、パラメータが途中で切れることもあります。フラグメントを蓄積し、パースをガードしてください。パースできない入力をClaudeに返す方法については、ツールレスポンスでの無効なJSONの処理を参照してください。
すべてのモデルは、Claude API、Amazon Bedrock、Claude Platform on AWS、Google Cloud、およびMicrosoft Foundryで細粒度ツールストリーミングをサポートしています。使用するには、細粒度ストリーミングを有効にしたいユーザー定義ツールでeager_input_streamingをtrueに設定し、リクエストでストリーミングを有効にします。
eager_input_streamingフィールドはオプションです。trueに設定するとそのツールの細粒度ストリーミングが有効になり、省略すると標準のバッファリングされたストリーミングになります。この場合、APIは各パラメータ値をバッファリングして検証してからストリーミングで返します。例外は、レガシーのfine-grained-tool-streaming-2025-05-14ベータヘッダーをまだ送信しているリクエストで、このフィールドが未設定のツールに対して細粒度ストリーミングが有効になります。ツールごとのフィールドはこのヘッダーを置き換えるものであり、リクエストがまだヘッダーを送信していても、明示的にfalseを指定すればそのツールはバッファリングされたストリーミングのままになります。フィールドの定義についてはツールリファレンスを参照してください。
次の例では、make_fileツールの細粒度ストリーミングを有効にし、Claudeに長い詩を依頼することで、ツール入力がストリーミングされる様子を観察できる十分な大きさになるようにしています:
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=65536,
model="claude-opus-5",
tools=[
{
"name": "make_file",
"description": "Write text to a file",
"eager_input_streaming": True,
"input_schema": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The filename to write text to",
},
"lines_of_text": {
"type": "array",
"description": "An array of lines of text to write to the file",
},
},
"required": ["filename", "lines_of_text"],
},
}
],
messages=[
{
"role": "user",
"content": "Can you write a long poem and make a file called poem.txt?",
}
],
) as stream:
for event in stream:
if event.type == "input_json":
print(event.partial_json, end="", flush=True)
final_message = stream.get_final_message()
print()
for block in final_message.content:
if block.type == "tool_use":
print(f"Complete tool input: {block.input}")すべてのタブでmake_fileツールの細粒度ストリーミングが有効になっています。SDKのタブでは、各入力フラグメントが届いた瞬間に出力し、ストリームが終了したら蓄積された完全な入力を出力します。cURLのタブは生のイベントストリームを表示し、CLIのタブはjqを使ってフラグメントだけを出力します。出力されたフラグメントを結合すると完全なツール入力になるため、Claudeが書き進めるにつれて詩がターミナルを埋めていきます:
{"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", "", "I.", "", "Beneath the vast and star-strewn sky,", "Where silver moonbeams softly lie,", ...
Complete tool input: {"filename": "poem.txt", "lines_of_text": ["The Wanderer's Journey", ...]}eager_input_streamingがない場合、APIは各パラメータ値をバッファリングして検証してからストリーミングで返すため、大きなパラメータについてはClaudeが生成を終えるまで何も出力されません。これを指定すると、Claudeがパラメータを書き始めるとすぐにフラグメントが届き始め、通常はより長く、単語の途中での分割も少なくなります。
蓄積の規約は標準のツール使用ストリーミングと同じであるため、このセクションはeager_input_streamingの有無にかかわらず適用されます。イベント形式については、メッセージのストリーミングのInput JSON deltaを参照してください。細粒度ツールストリーミングで変わるのは、結果について想定できることです。サーバーは検証せずにフラグメントをストリーミングするため、蓄積された文字列は有効なJSONではない可能性があります。
tool_useコンテンツブロックがストリーミングされるとき、最初のcontent_block_startイベントにはinput: {}(空のオブジェクト)が含まれます。これはプレースホルダーです。実際の入力は一連のinput_json_deltaイベントとして届き、それぞれがpartial_json文字列フラグメントを持ちます。完全な入力を組み立てるには、これらのフラグメントを連結し、ブロックが閉じたときに結果をパースします。
SDKがアキュムレータヘルパーを提供している場合(前の例のPython、TypeScript、Go、Java、Rubyのタブがそうです)、これは自動的に処理されます。手動パターンは、ヘルパーのないSDKの場合や、入力の組み立て方を完全に制御したい場合のためのものです。
蓄積の規約:
type: "tool_use"のcontent_block_startで、空の文字列を初期化します:input_json = ""type: "input_json_delta"の各content_block_deltaで、追加します:input_json += event.delta.partial_jsoncontent_block_stopで、蓄積された文字列をパースします以下のSDKの例のように、パースをガードしてください。レスポンスはパラメータの途中でmax_tokensにより停止することもあります。停止理由を確認し、より大きなmax_tokensでリクエストを再試行するか、部分的な入力を修復するかを判断してください。
最初のinput: {}(オブジェクト)とpartial_json(文字列)の型の不一致は意図的なものです。空のオブジェクトはコンテンツ配列内のスロットを示します。デルタ文字列が実際の値を構築します。
client = anthropic.Anthropic()
tool_inputs: dict[int, str] = {} # index -> accumulated JSON string
with client.messages.stream(
model="claude-opus-5",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "Get current weather for a city",
"eager_input_streaming": True,
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
messages=[{"role": "user", "content": "Weather in Paris?"}],
) as stream:
for event in stream:
match event.type:
case "content_block_start" if event.content_block.type == "tool_use":
tool_inputs[event.index] = ""
case "content_block_delta" if event.delta.type == "input_json_delta":
tool_inputs[event.index] += event.delta.partial_json
case "content_block_stop" if event.index in tool_inputs:
raw_input = tool_inputs[event.index]
try:
parsed = json.loads(raw_input)
except json.JSONDecodeError:
# 蓄積された文字列は有効なJSONであることが保証されません。
# このページの「ツールレスポンスにおける無効なJSONの処理」を参照してください。
print(f"Invalid tool input: {raw_input}")
else:
print(f"Tool input: {parsed}")フラグメントへの反応と組み立ては別の関心事です。最初の例では、各フラグメントが届くたびに反応しつつ、アキュムレータヘルパーを使用するタブでは組み立てをSDKに任せています。アキュムレータヘルパーを使用していない場合や、組み立てを完全に制御したい場合は、手動パターンを使用してください。
細粒度ツールストリーミングでは、ツール呼び出しの蓄積された入力が無効または不完全なJSONになる可能性があります。その場合、ツールを実行できないため、代わりに失敗をClaudeに報告してください。ツール結果のcontentはJSONである必要はありませんが、生の文字列を単一のキーを持つJSONオブジェクトでラップすることで、無効なJSONを受け取ったことがClaudeに明確に伝わり、デバッグのために元の入力も保持されます:
{
"INVALID_JSON": "<the unparseable input you received>"
}ラッパーを文字列にシリアライズし、is_errorをtrueに設定したツール結果コンテンツブロックのcontentとして返します:
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"is_error": true,
"content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}無効な入力に含まれる引用符やその他の特殊文字が正しくエスケープされるように、ラッパーは文字列の連結ではなくJSONライブラリを使って構築してください。
コンテキストウィンドウの仕組み、拡張思考とツール使用がどのようにカウントされるか、会話が長くなるにつれてコンテキストを管理する方法を理解します。
テキスト、ツール使用、拡張思考のデルタを含む、Messages APIレスポンスをサーバー送信イベントで段階的にストリーミングします。
tool_useブロックのパース、tool_resultレスポンスのフォーマット、is_errorによるエラー処理を行います。
Anthropicが提供するツールのディレクトリと、オプションのツール定義プロパティのリファレンスです。
Was this page helpful?