きめ細かいツールストリーミング

Claude API エンジニアリング 8分で読めます

きめ細かいツールストリーミングは、Claudeが生成したツール入力を、サーバ側でのバッファリングもJSON検証も挟まずに、生成した端からクライアントへ届ける機能です。大きなパラメータの最初の断片が届くまでの時間が短くなるため、レイテンシが重要な用途で効きます。

きめ細かいツールストリーミングとは

通常のツール使用のストリーミングでは、APIは各パラメータの値をいったんサーバ側でバッファし、JSONとして妥当かを検証してからクライアントへ返します。安全ですが、パラメータが大きいほど最初の1文字が届くまで待たされます。ファイル1本ぶんのテキストやコードブロックをツール入力として渡す場合、この待ち時間がそのまま体感の遅さになります。

きめ細かいツールストリーミングは、このバッファと検証を省きます。断片は標準のツール使用と同じinput_json_deltaイベントで流れてくるため、受け取り側の実装は大きく変わりません。変わるのは「届いた断片を連結した結果が、妥当なJSONである保証がなくなる」という一点です。

公式ドキュメントは、この点を明確に警告しています。APIが検証しないため、部分的あるいは不正なJSONを受け取る可能性があります。また、レスポンスがmax_tokensで打ち切られた場合、パラメータの途中で切れることもあります。断片を蓄積し、パースを必ずガードで囲むことが前提の機能です。

有効にする方法

Claude API、Amazon Bedrock、Claude Platform on AWS、Google Cloud、Microsoft Foundry のいずれでも、すべてのモデルがこの機能に対応しています。有効化の方法は、きめ細かいストリーミングを使いたいユーザー定義ツールにeager_input_streamingtrueで指定し、リクエスト側でストリーミングを有効にするだけです。

eager_input_streamingは省略可能なフィールドです。trueを指定するとそのツールでだけ有効になり、省略すると従来どおりのバッファ付きストリーミング(APIが各パラメータ値をバッファし検証してから返す)になります。

例外がひとつあります。旧来のfine-grained-tool-streaming-2025-05-14ベータヘッダを依然として送っているリクエストでは、このフィールドが未設定のツールについてもきめ細かいストリーミングが有効になります。ツール単位のフィールドはこのヘッダを置き換えるもので、ヘッダを送っているリクエストであっても、明示的にfalseを指定したツールはバッファ付きのままになります。

{
  "model": "claude-opus-4-8",
  "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", "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?"}
  ],
  "stream": true
}

Python SDKであれば、ツール定義に"eager_input_streaming": Trueを足したうえでclient.messages.stream(...)を使い、event.type == "input_json"のときにevent.partial_jsonを出力すれば、断片が届くたびに画面へ流れていきます。eager_input_streamingを付けない場合、大きなパラメータはClaudeが生成し終えるまで何も表示されません。付けた場合は、パラメータの生成が始まった時点から断片が届き、しかも1つあたりの断片が長く、単語の途中で切れることが少なくなります。

入力デルタの組み立て

断片を組み立てる手順は、きめ細かいストリーミングの有無にかかわらず同じです。変わるのは結果に対して何を仮定できるかだけです。

tool_useのコンテンツブロックがストリーミングされるとき、最初のcontent_block_startイベントにはinput: {}という空のオブジェクトが入っています。これはプレースホルダで、実際の入力はinput_json_deltaイベントの列として届き、各イベントがpartial_jsonという文字列の断片を運びます。完全な入力を組み立てるには、これらの断片を連結し、ブロックが閉じたところでパースします。

蓄積の契約は次の3ステップです。

  1. content_block_starttype: "tool_use"のとき、空文字列を初期化する(input_json = ""
  2. content_block_deltatype: "input_json_delta"のたびに追記する(input_json += event.delta.partial_json
  3. content_block_stopで、蓄積した文字列をパースする

このパースは必ずガードしてください。加えて、レスポンスがパラメータの途中でmax_tokensに達して止まることもあります。停止理由を確認し、max_tokensを上げて再試行するのか、部分的な入力を修復するのかを決める必要があります。

初期のinput: {}がオブジェクトで、partial_jsonが文字列という型の不一致は意図的な設計です。空のオブジェクトはコンテンツ配列の中の位置を示すための枠であり、実際の値はデルタの文字列が組み立てます。

Python、TypeScript、Go、Java、Ruby のSDKにはアキュムレータのヘルパが用意されており、この組み立てを代行してくれます。手動のパターンが必要になるのは、ヘルパのないSDKを使う場合か、組み立て方を自分で完全に制御したい場合です。なお「断片に反応すること」と「断片を組み立てること」は別の関心事で、断片が届くたびに画面へ出しつつ、組み立て自体はSDKのヘルパに任せる、という構成も可能です。

不正なJSONの扱い

きめ細かいツールストリーミングでは、あるツール呼び出しについて蓄積した入力が、不正または不完全なJSONになっていることがあります。その場合はツールを実行できないので、失敗をClaudeへ返します。

ツール結果のcontentはJSONである必要はありませんが、生の文字列を単一のキーの下にJSONオブジェクトとして包むと、「不正なJSONを受け取った」ことがClaudeにとって曖昧でなくなり、デバッグのために元の入力も保持できます。公式が示す形は次のとおりです。

{
  "INVALID_JSON": "<the unparseable input you received>"
}

このラッパーを文字列へシリアライズし、is_errortrueにしたツール結果コンテンツブロックのcontentとして返します。

{
  "type": "tool_result",
  "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
  "is_error": true,
  "content": "{\"INVALID_JSON\": \"<the unparseable input you received>\"}"
}

ラッパーは文字列連結ではなく、使っている言語のJSONライブラリで組み立ててください。不正な入力の中に引用符やその他の特殊文字が含まれていた場合に、正しくエスケープされる必要があります。

使いどころと注意点

この機能が効くのは、ツール入力が大きく、かつ最初の断片が早く届くことに意味がある場面です。典型例は、ファイルの中身やコードブロックをツール入力として生成させ、それをそのまま画面へ流したいケースです。逆に、入力が小さいツール(都市名を1つ渡すだけ、といったもの)では、バッファの有無による差はほとんど体感できません。

採用を判断するときのトレードオフは明確です。得られるのは初動のレイテンシで、引き換えに払うのは「受け取った入力が妥当なJSONである」という保証です。この保証を失う以上、パースのガード、停止理由の確認、不正入力をClaudeへ返す経路の3つを実装する必要があります。これらを用意しないまま有効にすると、稀に発生する不正JSONでアプリケーションが落ちることになります。

また、ツール単位で設定できる点は運用上重要です。すべてのツールで有効にする必要はありません。大きなパラメータを生成するツールにだけeager_input_streamingを付け、それ以外は既定のバッファ付きのままにしておけば、検証を失う範囲を最小限に抑えられます。

この記事は公式ドキュメントに基づく非公式の日本語解説です。仕様は更新されることがあるため、実装前に公式ドキュメントで最新の内容をご確認ください。