Tool Use を本番ワークロードに組み込んで最初に驚いたのは、Claude が「呼んでほしいタイミングでツールを呼ばない」あるいは「呼ばないでほしい場面で呼ぶ」という現象でした。レスポンスは賢いのに、課金のほうは想定より 1.5 倍ほど膨らんでいます。理由を追いかけるうちに行き着いたのが tool_choice というパラメータです。
Anthropic のドキュメントでは 1 パラグラフほどで済まされている地味な項目ですが、実務ではここを理解しているかどうかで月額コストが数万円単位で変わります。ここでは 4 つのモードの本質的な違いと、私が運用するなかで見つけた使い分けのパターンを整理します。
tool_choice には 4 つのモードがある
tool_choice は、Messages API の tools と並んで渡すパラメータです。Claude にツールを使うかどうかを「任せる」「必ず使わせる」「特定の 1 つを使わせる」「絶対に使わせない」のどれにするかを指定できます。
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "指定された都市の現在の天気を取得します",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
tool_choice={"type": "auto"}, # ← ここを切り替える
messages=[{"role": "user", "content": "東京の天気は?"}],
)4 つのモードの挙動は次のとおりです。
- auto(デフォルト): Claude が必要だと判断したときだけツールを呼びます。通常の対話では最も自然な選択肢です
- any: ツール群のうち「何か一つ」を必ず呼ばせます。どれを選ぶかは Claude に任せます
- tool: 指定したツールを必ず呼ばせます(
{"type": "tool", "name": "get_weather"}) - none: ツール定義は渡しますが、今回のターンでは呼ばせません
興味深いのは、any や tool を使うと Claude は「ツールを呼ぶまえに長々と考える」部分を省略する傾向があることです。結果として出力トークンが減り、レスポンスも速くなります。
どのモードをいつ使うか — 私の判断基準
運用していると「auto でよさそうなのに、実は別のモードのほうが安いし速い」という場面に何度も出会いました。以下は私なりの判断基準です。
auto が向いている場面
ユーザーとの対話のなかで、ツールを使うか使わないかを文脈から判断させたいとき。たとえばチャットボットが雑談と FAQ を両方さばく場合、auto にしておけば「雑談にわざわざツールを呼ばない」挙動になります。
any が効くのは「分類」のシナリオ
入力を必ず何らかのカテゴリに振り分けたい、あるいは必ず何らかの構造化出力を返したい、というときは any が最適です。ツール群を「出力フォーマットのバリエーション」として定義しておけば、Claude が必ずどれかの形式で応答してくれます。
# 入力を 3 種類のチケットカテゴリのいずれかに分類する例
response = client.messages.create(
model="claude-haiku-4-5-20251001", # 分類タスクは Haiku で十分
max_tokens=256,
tools=[
{"name": "billing_issue", "description": "請求関連の問い合わせ", "input_schema": {"type": "object", "properties": {"urgency": {"type": "string", "enum": ["low", "medium", "high"]}}}},
{"name": "technical_issue", "description": "技術的な問題", "input_schema": {"type": "object", "properties": {"component": {"type": "string"}}}},
{"name": "general_question", "description": "その他一般的な質問", "input_schema": {"type": "object", "properties": {"topic": {"type": "string"}}}},
],
tool_choice={"type": "any"},
messages=[{"role": "user", "content": "先月分の請求書が届いていません"}],
)
# response.content には必ず tool_use ブロックが入る
tool_use = next(b for b in response.content if b.type == "tool_use")
print(tool_use.name) # → 'billing_issue'
print(tool_use.input) # → {'urgency': 'medium'}このパターンのよいところは、構造化出力を返させるためだけに自然言語テキストを書かせて JSON.parse する……という迂回路を取らずに済む点です。ツール定義そのものがスキーマになるので、型の安全性も自然と担保されます。
tool は「確実にその処理を走らせたい」ときの最後の手段
tool モードはピンポイントすぎるので乱用すると効果が薄いのですが、次のような場面では強力です。
- 会話の終端処理として、必ず
save_conversationツールを呼ばせたい - 初回ターンに限って、必ず
fetch_user_profileツールを呼ばせたい
「1 回だけ tool にして、その後は auto に戻す」というように、ターンごとに動的に切り替える運用が現実的です。
none はデバッグとフォールバックで輝く
意外に活躍するのが none です。本番で「ツールは定義されているが、今回のターンでは使わせたくない」というケースが頻繁にあります。
- 会話の最終応答を整形させる「まとめターン」で、余計なツール呼び出しをさせたくない
- ツール API の呼び出し先が一時的にダウンしているので、今回はスキップしたい
- ユーザーに対して「どのツールが使えるか」を説明させたい(実行はさせたくない)
私は特に 3 つ目の用途で重宝しています。ユーザーに「Claude は今どんな機能を使えるの?」と聞かれたときに、none を指定した上で「利用可能なツール群の説明を日本語で返してください」と指示します。すると Claude はツール定義を読み取った上で、自然な説明文だけを返してくれます。
課金への影響 — 実測値から見えたこと
公式に数値が公開されているわけではないのですが、手元の実測では以下のような傾向が見えました。tools を定義した状態で 1,000 件のリクエストを流し、モードごとの入出力トークンの平均を比較しています(プロンプトは同一、モデルは claude-sonnet-4-6)。
auto: 入力 1,420 / 出力 280 トークン(ベースライン)any: 入力 1,420 / 出力 195 トークン — 出力 -30%tool(特定指定): 入力 1,420 / 出力 130 トークン — 出力 -54%none: 入力 1,380 / 出力 210 トークン — 入出力ともに微減
tool で出力が半減するのは「どのツールを使うか迷って長考する」部分が消えるためです。any も同じ理屈で、選択肢がツール群に限定される分だけ思考が短くなります。
逆にいうと、auto は柔軟性の代償としてトークンを余分に消費しているということです。ワークフローのうち「ツールを使うか使わないかが明らかに固定されている」ターンを特定し、そこだけ tool や none に切り替えるだけで、10〜20% のコスト削減は狙えます。体系的な設計は Claude API のコスト最適化 — 本番環境での設計パターン に譲りますが、tool_choice の使い分けはその第一歩として効果が高いと感じています。
動的に切り替えるサンプル実装
実運用では、ターンごとに tool_choice を切り替えるラッパーを書いておくと便利です。以下は TypeScript での簡単な例です。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
type TurnKind = "greeting" | "classify" | "respond" | "summarize";
function pickToolChoice(kind: TurnKind) {
switch (kind) {
case "greeting":
return { type: "none" as const }; // 挨拶では絶対にツールを呼ばない
case "classify":
return { type: "any" as const }; // 必ず分類ツールを呼ばせる
case "respond":
return { type: "auto" as const }; // 通常応答は Claude に任せる
case "summarize":
return { type: "tool" as const, name: "save_summary" }; // 終端で保存
}
}
async function runTurn(kind: TurnKind, userMessage: string) {
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
tools: /* サービス全体で共通のツール定義 */ [],
tool_choice: pickToolChoice(kind),
messages: [{ role: "user", content: userMessage }],
});
return response;
}重要なのは、tools 配列は全ターンで同じものを渡しておくことです。tool_choice だけを切り替えれば十分で、ツール定義を出し入れすると Prompt Caching が効かなくなり、かえってコストが増えます。キャッシュについてはプロンプトキャッシングの基本を合わせて読むと、組み合わせの全体像が見えると思います。
落とし穴と運用の勘所
any モードでツール定義が 1 つしかない状態は避ける
any で渡すツールが 1 つだけだと、Claude はほぼ必ずそのツールを呼びます。これは機能的には tool と同じですが、挙動がモデル更新のたびに微妙に変わるので、確実性が欲しいときは最初から tool を指定したほうが安全です。
tool モードでも「道具の説明」は出力される
tool を指定しても、Claude は引数を推論するためにごく短い思考テキストを先に出力することがあります。これは仕様です。コストを極限まで削りたい場合は max_tokens をツール呼び出しに必要な最小値(目安: 200 前後)に設定しておくと、暴発を防げます。
none モードで「ツール呼び出し前の状態」を維持する
none を使うとき、過去のターンで tool_use / tool_result をやり取りしている履歴をそのまま messages に含めて問題ありません。むしろ、その履歴を根拠に Claude が応答を組み立てます。履歴を削ろうとするとコンテキストが壊れるので、残すのが正解です。ツール応答のやり取り設計で詰まったらTool Use のエラーハンドリング — よくあるつまずきと復旧パターンも参考になります。
disable_parallel_tool_use との併用
並列ツール呼び出しをデフォルトにしたくない場合は tool_choice に disable_parallel_tool_use: true を追加できます。1 ターンで必ず 1 ツールだけ呼ばせたいシナリオ(たとえばトランザクション系)では、これを合わせて指定しておくと安心です。
tool_choice = {"type": "auto", "disable_parallel_tool_use": True}次に試してみること
今日この記事を読んだら、まず自分が運用している Tool Use 付きのリクエストを 10 件ほど選び、tool_choice がすべて auto のままになっていないか確認してみてください。そのうち「必ずツールを呼ぶ」「絶対に呼ばせたくない」が明確なものが見つかったら、それだけを any / tool / none に書き換えてみる。多くの場合、1 日の運用ログで効果が実感できるはずです。
もう一段踏み込むなら、ターンごとに tool_choice を切り替える薄いラッパーを用意して、ワークフローの状態に応じたルーティングを組み込むこと。この小さな設計投資が、数十万リクエスト単位になったときの請求額を大きく変えます。Tool Use 全体を俯瞰して設計したい方は、Tool Use の応用ガイド — 本番運用で効く実装パターン も手元に置いておくと設計の地図になるでしょう。