ツールランナー(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呼び出しの前にランナーの状態を変更できます。各イテレーションは次のライフサイクルに従います。
- ランナーは現在の状態でMessages APIにリクエストを送信します。
- ランナーはレスポンスメッセージをループ本体にyieldします。
- ループ本体が実行されます。メッセージを読み取り、必要に応じてランナーの状態を変更できます。
- ループ本体が戻ると、ランナーはメッセージ履歴が変更されたかどうかを確認します。
- メッセージ履歴を変更しなかった場合: メッセージにツール呼び出しが含まれている場合、ランナーはアシスタントメッセージとツール結果を追加して続行します。ツール呼び出しがない場合、ループは終了します。
- メッセージ履歴を変更した場合: ランナーは自動追加をスキップし、あなたの状態をそのまま使用します。メッセージ履歴の引き継ぎを参照してください。
メッセージ履歴の引き継ぎ
デフォルトでは、ランナーが会話の状態を管理します。各ツール呼び出しターンの後、アシスタントメッセージとツール結果を自身のメッセージ履歴に追加します。ターンをリトライしたい場合(レスポンスを破棄して再送信する)、フォローアップメッセージを挿入したい場合、またはツール結果を自分で構築したい場合に、メッセージ履歴を引き継ぎます。
ループ本体の内部からランナーのメッセージを変更することで引き継ぎます。正確な方法は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=debugGo、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?