Claude Media
並列ツール呼び出しを増やすシステムプロンプトの書き方

並列ツール呼び出しを増やすシステムプロンプトの書き方

Claudeが複数ツールを同時に呼ぶ確率を上げる、システムプロンプトの2段階の書き方と、平均ツール呼び出し数(average tools per message)で効果を測る方法をまとめます。

Claudeは既定で並列に複数ツールを呼ぶが、確実ではない

Claude 4以降のモデルは、独立した複数の操作が必要な場面では、追加の指示なしでも1回の応答に複数のtool_useブロックを含めます。2つの都市の天気を聞かれれば、天気取得ツールを2回、同じ応答の中で呼び出すのが基本挙動です。

ただし「基本的に起きる」ことと「毎回起きる」ことは別です。履歴の組み立て方やプロンプトによって、独立した操作のはずなのに逐次の呼び出しになることがあります。

並列化の価値は往復回数にあります。3つの独立したツールを順番に呼ぶと、Claudeへの往復が3回発生し、そのたびにネットワーク遅延と生成時間が積み上がります。1回の応答にまとめて呼べれば、往復は1回で済みます。呼び出しの多いワークフローほど差が出ます。

並列化を狙う前に、そのツールが並列向きかを見る

システムプロンプトを調整する前に、対象のツールが並列実行に向くかを見ておきます。Messages APIはツールの実行順序を規定しません。同時に走らせるか、出てきた順に走らせるかは、呼び出し側の判断です。

くらべる

並列にしてよいツールと、直列に残すツール

独立した読み取り

並列向き

複数ファイルの読み込みや複数都市の天気取得のように、副作用がなく、実行順が結果に影響しない操作です。並列化を促すプロンプトはこの種のツールに向けます。

副作用・共有状態・依存

直列に残す

書き込み・送信・課金が発生する呼び出し、共有状態を読み書きする操作、前の結果を次の入力に使う操作です。順序や排他制御が結果の正しさに関わります。

Claudeが並列で呼ぶと決めても、実際に同時に走らせるのは呼び出し側のコードです。Pythonなら、応答のtool_useブロックをまとめてasyncio.gatherに渡せば、遅いツールが1つあっても全体の待ち時間はその1つ分で済みます。直列に残したいツールだけをforで順に回す書き分けも、同じ応答の中でできます。

import asyncio
 
async def run_tools(response, handlers):
    calls = [b for b in response.content if b.type == "tool_use"]
    results = await asyncio.gather(*(handlers[c.name](c.input) for c in calls))
    return [
        {"type": "tool_result", "tool_use_id": c.id, "content": str(r)}
        for c, r in zip(calls, results)
    ]

副作用のあるツールが混在する構成でプロンプトだけを強めると、直列にしたい呼び出しまで同時に出てくる恐れがあります。

システムプロンプトの一文と強い指定タグの効果差

もっとも手軽な対策は、システムプロンプトに1文を足すことです。公式が示している標準の一文は次の内容です。

For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.

これで並列化率が足りないときに使うのが、より強い指定の<use_parallel_tool_calls>タグです。具体例と「独立した読み取り専用の複数コマンドは必ず並列で実行する」という指示を含みます。タグごと、下の全文をそのままシステムプロンプトに入れます。

<use_parallel_tool_calls>
For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially. Prioritize calling tools in parallel whenever possible. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. When running multiple read-only commands like ls or list_dir, always run all of the commands in parallel. Err on the side of maximizing parallel tool calls rather than running too many tools sequentially.
</use_parallel_tool_calls>

2つの違いは具体性です。標準の一文は方針を述べるだけで、タグ版は「3ファイルなら3呼び出し」「ls系は全部並列」という具体例まで書いています。まず標準の一文を足し、それでも伸びないときにタグへ切り替える2段階で試すと、手戻りが出ません。

ユーザーメッセージの書き方でも並列率は変わる

システムプロンプトに手を入れられない場面では、ユーザーメッセージ側の書き方も並列化に影響します。「パリの天気を教えて。ロンドンも」のような逐次の列挙を、同時実行を明示する文に変えます。

Agent SDKでカスタムツールを定義している場合、ツールの実装を変えなくても、呼び出し元のプロンプト設計だけでこの挙動は変えられます。ハンドラに手を入れる前に、プロンプト側の調整から試すほうが変更のコストは低く済みます。

平均ツール呼び出し数(average tools per message)で効果を測る

プロンプトを変えたら、並列化が増えたかを数値で確かめます。指標は「ツール呼び出しを含むメッセージ1件あたりの平均ツール呼び出し数」です。

tool_call_messages = [
    msg for msg in messages if any(block.type == "tool_use" for block in msg.content)
]
total_tool_calls = sum(
    len([block for block in msg.content if block.type == "tool_use"])
    for msg in tool_call_messages
)
avg_tools_per_message = (
    total_tool_calls / len(tool_call_messages) if tool_call_messages else 0.0
)

分母はツール呼び出しを含むメッセージだけで、テキストだけの応答は数えません。たとえばツール呼び出しを含むメッセージが10件、tool_useブロックの合計が14個なら、値は1.4です。全件が1メッセージ1呼び出しなら、合計も10個になり値はちょうど1.0です。

この値が1.0を超えていれば、少なくとも一部のメッセージで並列呼び出しが起きています。変更の前後で比べれば、プロンプトを強めた効果が数字で見えます。1.0付近に張りついたままなら、プロンプト以外の要因を疑う段階です。

本番ログを集計する前に、単発のリクエストで並列呼び出しが起きるかを見ておくと手戻りが減ります。「サンフランシスコとニューヨークの天気と時刻を教えて」のように、独立した情報を同時に求める1文を送ります。返ってきた応答のtool_useブロックが2件以上あれば、並列化は機能しています。見るのはstop_reasonがtool_useであること、tool_useブロックが2個以上あること、そして続きのリクエストで全tool_resultを1つのuserメッセージに入れて返せていることの3点です。3つ目まで通して初めて、会話が続いても並列化が保たれるかが分かります。

並列化が増えないときに疑う順序

プロンプトを強めても効果が出ないときは、プロンプト以外を先に疑います。公式のトラブルシューティングでも、最初に挙がるのはtool_resultの返し方です。以下はそれを先頭に置いた疑う順序の目安です。

手順

疑う順序の目安

  1. 1

    tool_resultの返し方

    並列で呼ばれた複数ツールの結果を、1件ずつ別のユーザーメッセージに分けて返していないか見ます。分割して送ると、会話履歴が「逐次実行が普通」というパターンをClaudeに教えてしまいます。

  2. 2

    プロンプトの強度

    既定のままで並列化率が変わらないなら、標準の一文、続いて<use_parallel_tool_calls>タグの順で強めます。

  3. 3

    Fable 5.1の長いエージェントループ

    次に読むものが暗黙的にしか示されないループで並列数が減るなら、次の節の追加指示を足します。

返し方の形は次のとおりです。assistantのターンにtool_useが2つ並んでいるとき、結果は1つのuserメッセージに入れます。

くらべる

tool_resultの返し方

並列が減る

分割して返す

userメッセージをtool_result_1用とtool_result_2用に2回送る形です。

並列が保たれる

1つにまとめて返す

1つのuserメッセージのcontentにtool_result_1とtool_result_2を並べる形です。

まとめて返すときは、すべてのtool_resultブロックをテキストより前に置き、各結果をtool_use_idで呼び出しに対応させます。一部の呼び出しをあえて実行しなかった場合も、その呼び出しにはis_error: trueと短い説明を付けたtool_resultが必要です。実行しなかった呼び出しの書き方は、並列ツール呼び出しで一部だけ失敗したときの返し方で扱っています。

Fable 5.1の長いエージェントループで並列数が減るとき

Claude Fable 5.1は、依頼文が複数の対象を名指ししているときは、基本的に並列で呼び出します。減るのは、次に読むものがタスクから暗黙に決まるだけのループです。自作のコーディングエージェント、bashとtext editorを使うハーネス、computer useが当たります。回答の質には影響しませんが、余計なターンごとにトークンと往復、待ち時間がかかります。

対処は1文の追加指示です。Fable 5.1向けの手引きでは、次の文を現在の依頼の末尾に添える形が示されています。

First privately list what you need next; then request every item that doesn't depend on another's result in this one response.

置き場所は、ツール結果を返すたびに、そのuserメッセージの後ろです。

手順

追加指示を置く場所

  1. 1

    ターン限定のシステムメッセージにする(beta)

    messagesにrole: "system"のエントリを入れ、clear_at: "next_user_message"を付けます。次のuserメッセージが来ると古いコピーはAPI側で消え、モデルには最新の1件だけが見えます。betaヘッダーmid-conversation-system-clear-at-2026-08-21が必要です。

  2. 2

    betaを使わないときは同じuserメッセージに入れる

    tool_resultブロックの後ろにテキストブロックとして同じ一文を置きます。

  3. 3

    古いコピーは書き換えない

    毎ターン新しいコピーを末尾に足し、以前のコピーはバイト単位でそのまま残します。削除や書き換えは過去のターンの編集にあたり、プロンプトキャッシュがその位置から作り直されます。

この指示は、通常の関数呼び出し用の指示とは別の箇所に切り出しておくと、この一文を外した状態と比べて並列数の変化を確かめられます。

Claude Codeのサブエージェントを使った並列実行パターンは、Messages APIのtool_useレベルの並列化とは別のレイヤーの話です。混同しないよう区別しておくと設計がぶれません。

システムプロンプトとユーザーメッセージのどちらから直すか

アプリケーション全体のシステムプロンプトを自分で管理できるかが、最初の分岐点です。管理できるなら、標準の一文を足すのが影響範囲も広く、修正箇所も1か所で済みます。マルチテナントのプロダクトのように機能ごとに管理主体が違う場合は、機能ごとのユーザーメッセージのテンプレートを見直すほうが現実的です。どちらの経路でも、tool_resultの返し方が崩れていれば効果は相殺されます。プロンプトの文面を直す前に、履歴を作るコードを一度読み返してください。

まとめ

逆に並列呼び出しそのものを止めたい場面では、プロンプトではなくtool_choiceのdisable_parallel_tool_useで制御します。この指定は、tool_useを返させるリクエスト自体に付ける必要があり、後続のリクエストに付けても前の呼び出しには効きません。プロンプト変更の効果を体系的に検証したい場合は、プロンプト評価をAnthropicの公式コースで学ぶ実践ガイドが参考になります。

この記事を共有:XはてブLinkedIn