9.3.4 ツール呼び出し戦略

前の節で、ツールを安全にモデルの後ろへつなぐ方法はすでに学びました。
この節では、さらに一歩先へ進みます。
ツールはつなげばよいわけではなく、大事なのはどう使うかです。
システムの品質差を大きく左右するのは、「ツールがあるか」よりも「いつ呼ぶか、最初にどれを呼ぶか、失敗したらどうするか」です。
学習目標
- ツール呼び出し戦略が、なぜ Agent 成功の鍵のひとつなのかを理解する
- 「呼ばない / 1回だけ呼ぶ / 複数ステップで呼ぶ / フォールバックする」の代表的なパターンを区別する
- 基本的なルーティング、リトライ、検証ロジックの設計方法を学ぶ
- より完全なツール戦略の例を読めるようになる
なぜ「ツールがある」ことと「ツールを使える」ことは同じではないのか?
よくある誤解
Agent を作るとき、最初に多くの人がやることは:
- 検索ツールをつなぐ
- 計算ツールをつなぐ
- データベースをつなぐ
そして、これでシステムが強くなると思いがちです。
でも実際には、よく次のような問題が起こります。
- ツールを呼ばなくてよい場面で、むやみに呼んでしまう
- 呼ぶべき場面で、逆に呼ばない
- 違うツールを呼んでしまう
- ひとつの処理で 5 回も連続で呼び、止まらなくなる
つまり、本当の問題は:
システムにツールがあるか
ではなく、
システムに「ツールを使う戦略」があるか
です。
生活のたとえ
家のキッチンに包丁、鍋、オーブン、電子レンジがあっても、それだけで料理ができるわけではありません。
大事なのは:
- いつ切るか
- いつ煮るか
- いつ焼くか
- どこで失敗したらどう直すか
Agent のツール呼び出し戦略も同じです。
まずは代表的な戦略を整理する
ツールを呼ばない
次のような場合に向いています。
- 一般的な説明
- 簡単な言い換え
- 文体の変換
たとえば:
「この文章をもっとフォーマルにして」
このようなタスクは、通常は外部ツールを必要としません。
1 回だけ呼ぶ
次のような場合に向いています。
- 天気を調べる
- 数式を計算する
- 1 件の知識ベース記録を調べる
これは最もシンプルで、最も安定しやすい呼び出し方法です。
複数ステップで呼ぶ
次のような場合に向いています。
- まず注文を調べて、次に返金ルールを調べて、最後に結論を出す
- まず資料を検索し、次に要約し、最後に出力を生成する
このときの戦略は、単に「どのツールを呼ぶか」ではなく、「次のステップでもまだ呼ぶ必要があるか」です。
フォールバックと代替処理
もし:
- メインのツールが失敗した
- 結果が信頼できない
- パラメータ検証に通らない
場合は、システムは次のどれをするか決める必要があります。
- リトライする
- 別のツールに切り替える
- ユーザーに追加情報を求める
- その場で「できない」と伝える
これもツール戦略の大事な部分です。
ツールを呼ぶ前に何を判断するべきか?
本当にツールが必要か?
すべての問題がツールチェーンを通るべきとは限りません。
ツールを呼ぶたびに、次のものが増えます。
- 遅延
- コスト
- 失敗経路
そのため、最初のステップはしばしば:
まずツールが必要かどうかを判断すること。
必要なら、どのツールを選ぶか?
たとえばユーザーがこう聞いたとします。
「この注文はまだ返金できますか?」
必要になる可能性があるのは:
- 注文状態を調べる
- 返金ポリシーを調べる
つまり、ツール選択は常に「1つ選べば終わり」ではなく、順番のある組み合わせ問題になることがあります。
パラメータは十分か?
どのツールを使うべきか分かっていても、パラメータが足りないことがあります。
たとえば:
「天気を調べて」
これでは都市名がありません。
この場合、最も自然な戦略は、適当に推測することではなく:
先にユーザーへ確認すること
です。
ツールを呼んだ後に何を判断するべきか?
結果は信頼できるか?
ツールから返答があっても、そのまま使ってよいとは限りません。
たとえば:
- API タイムアウト後に空の値が返る
- 検索結果の関連性が低い
- データベースに記録が見つからない
次のステップが必要か?
タスクによっては、1 回の呼び出しでは最終答えまで行けません。
たとえば:
- まず知識ベースを調べる
- 次に計算する
- 最後にユーザーが読みやすい形でまとめる
つまり、ツール戦略は本質的にはよく次の流れになります。
呼び出す -> 観察する -> 次の一手を決める
最小限だが学習価値のある戦略例
次の例では、3 つのケースを分けます。
- ツールを呼ばない
- 1 つのツールを呼ぶ
- パラメータが足りないときは先に確認する
def route_query(query):
if "要約" in query or "書き換え" in query:
return {"action": "no_tool", "reason": "純テキストタスク"}
if "天気" in query:
if "北京" in query:
return {"action": "tool", "tool": "weather", "arguments": {"city": "北京"}}
return {"action": "ask_user", "question": "どの都市の天気を調べたいですか?"}
if "計算" in query:
expression = query.replace("計算", "").strip()
return {"action": "tool", "tool": "calculator", "arguments": {"expression": expression}}
return {"action": "fallback", "reason": "現在、適切な戦略がありません"}
queries = [
"この文章を要約して",
"北京の天気はどうですか",
"天気を調べて",
"計算 12 * 7"
]
for q in queries:
print(q, "->", route_query(q))
期待される出力:
この文章を要約して -> {'action': 'no_tool', 'reason': '純テキストタスク'}
北京の天気はどうですか -> {'action': 'tool', 'tool': 'weather', 'arguments': {'city': '北京'}}
天気を調べて -> {'action': 'ask_user', 'question': 'どの都市の天気を調べたいですか?'}
計算 12 * 7 -> {'action': 'tool', 'tool': 'calculator', 'arguments': {'expression': '12 * 7'}}
この例はシンプルですが、「戦略」という層をよく表しています。
- すべての入力をツールに渡すわけではない
- パラメータが足りないからといって、無理に推測しない
- どうしても分からないときは fallback がある
より完全な戦略の閉ループ
いくつかのツールを定義する
import ast
import operator
OPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
}
def safe_calculate(expression):
def visit(node):
if isinstance(node, ast.Expression):
return visit(node.body)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp) and type(node.op) in OPS:
return OPS[type(node.op)](visit(node.left), visit(node.right))
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
return -visit(node.operand)
raise ValueError("unsupported_expression")
return visit(ast.parse(expression, mode="eval"))
def get_weather(city):
return {"city": city, "temperature": 22, "condition": "sunny"}
def calculate(expression):
return {"result": safe_calculate(expression)}
振り分け + 検証 + 実行
def execute_strategy(query):
decision = route_query(query)
if decision["action"] == "no_tool":
return {"type": "answer", "content": "この種のタスクは、モデルが直接テキストを生成するほうが向いています。"}
if decision["action"] == "ask_user":
return {"type": "question", "content": decision["question"]}
if decision["action"] == "tool":
if decision["tool"] == "weather":
result = get_weather(**decision["arguments"])
return {"type": "tool_result", "content": result}
if decision["tool"] == "calculator":
result = calculate(**decision["arguments"])
return {"type": "tool_result", "content": result}
return {"type": "fallback", "content": "このリクエストを安定して処理できません。"}
for q in ["北京の天気はどうですか", "天気を調べて", "計算 9 + 8"]:
print(q, "->", execute_strategy(q))
期待される出力:
北京の天気はどうですか -> {'type': 'tool_result', 'content': {'city': '北京', 'temperature': 22, 'condition': 'sunny'}}
天気を調べて -> {'type': 'question', 'content': 'どの都市の天気を調べたいですか?'}
計算 9 + 8 -> {'type': 'tool_result', 'content': {'result': 17}}

このコードが本当に教えているのは次のことです。
ツール呼び出し戦略は、1 行の
ifではなく、「判断 + 分岐 + 実行 + フォールバック」の連なりとして設計するものです。
実際のシステムでよく使う戦略パターン
Router パターン
まず、問題がどのツール、またはどのサブシステムに属するかを判断します。
向いている場面:
- ツールが多い
- タスクの境界がはっきりしている
Verify パターン
ツールを呼んだ後、すぐに結果を信じず、もう一度確認します。
向いている場面:
- 外部データが不安定
- ツールの失敗率が高い
Retry / Fallback パターン
まずリトライし、それでもだめなら段階的に性能を落とし、最後に兜底します。
向いている場面:
- 外部 API が不安定
- オンラインサービスが揺れやすい
Plan-then-tool パターン
先に計画を立て、それからツールの順番を決めます。
向いている場面:
- 複数ステップのタスク
- 複数ツールの依存関係がある場合
いつ「ツールを少なく使う」べきか?
これも、とても重要な戦略能力です。
ツールを少なくしてよい典型例
- ただ要約する
- ただ言い換える
- 文体を変える
- すでに十分な文脈がある
なぜ少ないほうがよいことがあるのか?
ツールを 1 回増やすたびに、次のコストも増えます。
- レイテンシ
- 失敗の可能性
- 状態管理のコスト
だから成熟したシステムは、「呼べるなら何でも呼ぶ」のではなく、
省けるところは省く
という考え方を持っています。
初心者がよくはまる落とし穴
ツール呼び出し戦略を「ルーティングルール」だと考える
ルーティングはその一部にすぎません。
本当の戦略には、次の判断も含まれます。
- 呼ぶかどうか
- 追い質問するかどうか
- 次のステップに進むかどうか
- フォールバックするかどうか
呼び出し失敗後の次の手がない
リトライもなく、fallback もなく、追加確認もない。こうしたシステムは本番ではとても脆いです。
毎回モデルに全部決めさせる
実際の開発では、多くの戦略はプログラム側のフレームワークで明確に制御すべきです。モデルに完全に任せきりにするべきではありません。
まとめ
この節で最も大事なのは、「どのツールを使えるか」を知ることではなく、次を理解することです。
ツール呼び出し戦略は、Agent が正しいタイミングで、正しい順序で、正しい方法でツールを呼べるかどうかを決めます。
これはしばしば、「ツールをたくさんつなげる」こと以上に、システム品質へ大きく影響します。
練習
- この節の例に
search_docs(keyword)ツールを追加し、ルーティングロジックを拡張してください。 - 「ツール実行でエラーが出たら、人間の確認に fallback する」分岐を追加してください。
- 考えてみましょう。ユーザーが「天気を調べて、着る服の指標も計算して」と聞いたら、戦略層ではこの処理をどう分割すべきでしょうか?
- 自分の言葉で説明してみましょう。なぜツール呼び出し戦略は、Agent の品質を分ける分岐点のひとつだと言えるのでしょうか?