Claude Platform Docs
Messagesツール

ツールランナー(SDK)

SDKのツールランナーを使用して、エージェントループ、エラーのラップ、型安全性を自動的に処理します。

ツールランナーは、エージェントループ、エラーのラップ、型安全性を処理するため、自分で対応する必要がありません。人間による承認(human-in-the-loop)、カスタムロギング、条件付き実行が必要な場合は、代わりに手動ループを使用してください。

ツール呼び出し、ツール結果、会話管理を手動で処理する代わりに、ツールランナーは自動的に以下を行います。

  • Claudeがツールを呼び出したときにツールを実行する
  • リクエスト/レスポンスのサイクルを処理する
  • 会話の状態を管理する
  • 型安全性とバリデーションを提供する

基本的な使い方

SDKのヘルパーを使用してツールを定義し、ツールランナーを使用してそれらを実行します。

SDKのツールシグネチャに応じて、ツールは結果を文字列またはコンテンツブロック(テキスト、画像、またはドキュメントブロック)として返すため、ツールはマルチモーダルな結果を返すことができます。返された文字列は単一のテキストコンテンツブロックになります。JSONオブジェクトや数値などの構造化データを返すには、まず文字列としてエンコードしてください。

@beta_toolデコレーターを使用して、型ヒントとdocstringでツールを定義します。

import json
from anthropic import Anthropic, beta_tool

client = Anthropic()


@beta_tool
def get_weather(location: str, unit: str = "fahrenheit") -> str:
    """Get the current weather in a given location.

    Args:
        location: The city and state, e.g. San Francisco, CA
        unit: Temperature unit, either 'celsius' or 'fahrenheit'
    """
    return json.dumps({"temperature": "20°C", "condition": "Sunny"})


@beta_tool
def calculate_sum(a: int, b: int) -> str:
    """Add two numbers together.

    Args:
        a: First number
        b: Second number
    """
    return str(a + b)


runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[get_weather, calculate_sum],
    messages=[
        {
            "role": "user",
            "content": "What's the weather like in Paris? Also, what's 15 + 27?",
        }
    ],
)
for message in runner:
    print(message)

@beta_toolデコレーターは、関数の引数とdocstringを検査してJSONスキーマを導出します。

ツールランナーのイテレーション

ツールランナーは、Claudeからのメッセージをyieldするイテラブルです。各イテレーションで、ランナーはClaudeがツール使用をリクエストしたかどうかを確認します。リクエストした場合、ツールを実行して結果を自動的にClaudeに送り返し、その後Claudeからの次のメッセージをyieldしてループを継続します。

break文を使用して、任意のイテレーションでループを終了できます。ランナーは、Claudeがツール使用を含まないメッセージを返すまで、または設定している場合はmax_iterationsに達するまでループします。

中間メッセージが不要な場合は、最終メッセージを直接取得できます。

runner.until_done()を使用して最終メッセージを取得します。

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[get_weather, calculate_sum],
    messages=[
        {
            "role": "user",
            "content": "What's the weather like in Paris? Also, what's 15 + 27?",
        }
    ],
)
final_message = runner.until_done()
for block in final_message.content:
    if block.type == "text":
        print(block.text)

高度な使い方

ループ内で、各レスポンスメッセージを読み取り、次のAPI呼び出しの前にランナーの状態を変更できます。各イテレーションは次のライフサイクルに従います。

  1. ランナーは現在の状態でMessages APIにリクエストを送信します。
  2. ランナーはレスポンスメッセージをループ本体にyieldします。
  3. ループ本体が実行されます。メッセージを読み取り、必要に応じてランナーの状態を変更できます。
  4. ループ本体が戻ると、ランナーはメッセージ履歴が変更されたかどうかを確認します。
    • メッセージ履歴を変更しなかった場合: メッセージにツール呼び出しが含まれている場合、ランナーはアシスタントメッセージとツール結果を追加して続行します。ツール呼び出しがない場合、ループは終了します。
    • メッセージ履歴を変更した場合: ランナーは自動追加をスキップし、あなたの状態をそのまま使用します。メッセージ履歴の引き継ぎを参照してください。

メッセージ履歴の引き継ぎ

デフォルトでは、ランナーが会話の状態を管理します。各ツール呼び出しターンの後、アシスタントメッセージとツール結果を自身のメッセージ履歴に追加します。ターンをリトライしたい場合(レスポンスを破棄して再送信する)、フォローアップメッセージを挿入したい場合、またはツール結果を自分で構築したい場合に、メッセージ履歴を引き継ぎます。

ループ本体の内部からランナーのメッセージを変更することで引き継ぎます。正確な方法はSDKによって異なります。以下の言語別タブを参照してください。

あるイテレーションで管理を引き継ぐと、ランナーはそのターンのアシスタントメッセージやツール結果を追加しません。会話を有効な状態に保つ責任はあなたにあります。(そのターンをカウントしたい場合は)アシスタントメッセージとツール結果を自分で追加し、ツール呼び出しがない場合にもループが終了できるよう状態を条件付きで変更し、ループを制限するためにmax_iterationsを渡してください。7つのSDKすべてがmax_iterationsをサポートしています。

generate_tool_call_response()を使用してツール結果を検査または計算します。ループ内でappend_messages()を呼び出すと、履歴を自分で管理していることがランナーに伝わるため、追加する内容にアシスタントメッセージとツール結果を含めてください。

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    max_iterations=10,
    tools=[get_weather],
    messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()
    if tool_response is not None:
        # append_messages() は状態を変更済みとしてフラグを立てるため、ランナーはこの
        # イテレーションでの自動追加をスキップします。アシスタントメッセージと
        # tool result、および任意のフォローアップを自分で追加してください。
        runner.append_messages(
            message,
            tool_response,
            {"role": "user", "content": "Please be concise."},
        )
    # ツール呼び出しがない場合は、ループが終了するように状態をそのままにします。

メッセージ履歴を引き継がずにmax_tokensなどのリクエストパラメーターを変更するには、set_messages_params()を使用します。ランナーは引き続きアシスタントメッセージとツール結果を自動的に追加します。

for message in runner:
    runner.set_messages_params(lambda params: {**params, "max_tokens": 2048})

自動コンテキスト管理

長時間実行されるエージェントタスク向けに、TypeScriptとRubyのツールランナーは自動「compaction」(コンパクション)をサポートしています。これはトークン使用量がしきい値を超えたときに要約を生成し、会話が「context window」(コンテキストウィンドウ)の制限を超えて継続できるようにするものです。両SDKとも、このクライアントサイドのオプションを非推奨とし、サーバーサイドのコンパクションを推奨しています。サーバーサイドのコンパクションは、context_managementリクエストパラメーターを通じてすべてのSDKのツールランナーで動作します。Python SDK(v1.0以降)およびGo、Java、C#、PHPのツールランナーには、クライアントサイドのコンパクションは含まれていません。Python、TypeScript、C#、Go、Java、PHP、Rubyのツールランナーには、オンデマンドのコンパクション用にcompact_before_next_turn()ヘルパーがあります。ループでコンパクションするを参照してください。ランナーでは、これかcontext_managementのコンパクション編集のいずれかを使用し、両方を使用しないでください。

ツール実行のデバッグ

ツールが例外をスローすると、ツールランナーはそれをキャッチし、is_error: trueを持つツール結果としてエラーをClaudeに返します。ツール結果には完全なスタックトレースではなく、例外のメッセージ(Pythonでは型とメッセージ)が含まれます。

SDKが何をログに記録するかは言語によって異なります。Python SDKは、ツールが未処理の例外を発生させるたびに、標準のloggingモジュールを通じてスタックトレースを含む完全な例外をログに記録します。Python、TypeScript、Java SDKはANTHROPIC_LOG環境変数を読み取ってSDKのロギングを有効にします。これにはリクエストとレスポンスの詳細が含まれます。

# infoレベルでログを出力
export ANTHROPIC_LOG=info

# より詳細な出力のためにdebugレベルでログを出力
export ANTHROPIC_LOG=debug

Go、Ruby、C#、PHP SDKはANTHROPIC_LOGを読み取りません。Python以外では、失敗したツールをログに記録するSDKはありません。ツールが失敗した理由を確認するには、返すまたは再スローする前に、ツール関数内で例外をキャッチしてログに記録してください。

ツールエラーのインターセプト

デフォルトでは、ツールエラーはClaudeに返され、Claudeは適切に応答できます。ただし、エラーを検出して別の方法で処理したい場合もあります。たとえば、実行を早期に停止したり、カスタムエラー処理を実装したりする場合です。

PythonおよびTypeScript SDKでは、ツールレスポンスメソッド(Pythonではgenerate_tool_call_response()、TypeScriptではgenerateToolResponse())を使用して、Claudeに送信される前にツール結果をインターセプトしてエラーを確認します。他のSDKはそのフックを公開していません。それらのタブでは最も近い代替手段を説明しています。

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[my_tool],
    messages=[{"role": "user", "content": "Run my_tool with the query 'hello'."}],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()

    if tool_response is not None:
        # tool_response は dict です: {"role": "user", "content": [...]}
        # いずれかのツール結果にエラーがあるか確認します
        for block in tool_response["content"]:
            if block.get("is_error"):
                # オプション 1: 例外を発生させてループを停止します
                raise RuntimeError(f"Tool failed: {json.dumps(block['content'])}")

                # オプション 2: ログに記録して続行します(Claude に処理を任せます)
                # logger.error(f"Tool error: {json.dumps(block['content'])}")

    # メッセージを通常どおり処理します
    print(message.content)

ツール結果の変更

Claudeに送り返される前にツール結果を変更できます。これは、ツール結果で「prompt caching」(プロンプトキャッシング)を有効にするためにcache_controlなどのメタデータを追加したり、ツール出力を変換したりするのに便利です。詳細はプロンプトキャッシングを参照してください。

PythonおよびTypeScript SDKでは、ツールレスポンスメソッドを使用してツール結果を取得し、ランナーが続行する前にそれを変更します。変更した結果を明示的に追加するか、その場で変更するかはSDKによって異なります。各タブのコードコメントを参照してください。

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[search_documents],
    messages=[
        {
            "role": "user",
            "content": "Search for information about the climate of San Francisco",
        }
    ],
)

for message in runner:
    tool_response = runner.generate_tool_call_response()

    if tool_response is not None:
        # tool_response は dict です: {"role": "user", "content": [...]}
        # ツール結果を変更してキャッシュ制御を追加します
        for block in tool_response["content"]:
            if block["type"] == "tool_result":
                # このツール結果をキャッシュするために cache_control を追加します
                block["cache_control"] = {"type": "ephemeral"}

        # 変更したレスポンスを追加します(これにより元のレスポンスの自動追加が防止されます)
        runner.append_messages(message, tool_response)

    print(message.content)

ストリーミング

ストリーミングを有効にすると、各ターンのレスポンスを段階的に処理できます。各イテレーションは、イベントをイテレーションできるストリームオブジェクトをyieldします。

stream=Trueを設定し、get_final_message()を使用して蓄積されたメッセージを取得します。

client = anthropic.Anthropic()

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[calculate_sum],
    messages=[{"role": "user", "content": "What is 15 + 27?"}],
    stream=True,
)

# ストリーミング時、runner は BetaMessageStream を返します
for message_stream in runner:
    for event in message_stream:
        print("event:", event)
    print("message:", message_stream.get_final_message())

print(runner.until_done())

次のステップ

文法制約付きサンプリングにより、ClaudeのツールインプットにJSON Schemaへの準拠を強制します。

tool_useブロックをパースし、tool_resultレスポンスをフォーマットし、is_errorでエラーを処理します。

並列ツール呼び出しの有効化、フォーマット、無効化について、メッセージ履歴のガイダンスとトラブルシューティングとともに説明します。

ツールスキーマを指定し、効果的な説明を記述し、Claudeがツールを呼び出すタイミングを制御します。

Was this page helpful?