
エージェントツールコール ベストプラクティス:なぜエージェントは間違ったツールを選ぶのか
エージェントツールコール ベストプラクティスは、動くデモと、本番環境でこっそり間違ったツールを呼び続けるエージェントを分ける境界線です。Anthropicのエンジニアリングチームは、説明を1つ書き直しただけで、あるツールの結果が206トークンから72トークンへ減ったことを測定しました。そしてClaude Codeは現在、すべてのツールレスポンスを25,000トークンで強制的に打ち切っています。浪費が実在するからです。エージェントの失敗は4パターンあり、間違ったツール、間違った引数、暴走ループ、トークンの浪費のそれぞれに、今週中にリリースできる対策があります。
要点:
- エージェントツールコールの失敗は正確に4種類です。間違ったツール、間違った引数、暴走ループ、トークンの浪費。
- ツールの説明は、選択時にモデルが参照する唯一の指示です。そのため、ツールの誤選択の大半は説明で直ります。
- フラットでタスクに合ったスキーマと、検証された入力が、引数エラーの大半をなくします。
- 簡潔なツールレスポンスと、変更ごとの評価ループが、トークンコストとリグレッションを測定可能な状態に保ちます。
なぜエージェントツールコールは本番環境で失敗するのか?
エージェントツールコールは4つの形で失敗します。モデルが間違ったツールを選ぶ、間違った引数を書く、暴走ループに陥る、あるいは肥大化したレスポンスでトークンを浪費する、の4つです。各失敗は呼び出しループの異なるステップで発生するため、修正する順序が重要になります。まず選択から着手してください。ツールの選択ミスは、その後のすべてのステップを汚染するからです。
| 失敗モード | ループ内の発生箇所 | 解決するプラクティス | 工数 |
|---|---|---|---|
| 間違ったツール | モデルがツール一覧から選択する場面 | 1(説明)+ 4(名前空間・フィルタリング) | 低 |
| 間違った引数 | モデルが tool_call の JSON を記述する場面 | 2(フラットなスキーマ)+ 6(バリデーション) | 低〜中 |
| 暴走ループ | tool_result がモデルへ循環して戻る場面 | 3(アトミックなツール)+ 7(人間による承認ゲート) | 中 |
| トークンの浪費 | tool_result がコンテキストウィンドウへ返る場面 | 5(簡潔な結果)+ 8(評価ループ) | 低〜中 |
処方箋の全体像を一覧にすると、次のようになります。
| プラクティス | 解決する失敗 | 工数 |
|---|---|---|
| 1. モデルが行動できる説明を書く | 間違ったツール | 低 |
| 2. スキーマはフラットに、タスクに合った形にする | 間違った引数 | 低 |
| 3. 複数ステップの手順をアトミックなツールにまとめる | 暴走ループ | 中 |
| 4. 名前空間・整理・動的フィルタリングを行う | 間違ったツール | 中 |
| 5. 簡潔でシグナル密度の高い結果を返す | トークンの浪費 | 低 |
| 6. すべての呼び出しを検証し、エラーで学ばせる | 間違った引数 | 中 |
| 7. 破壊的な操作は人間の承認ゲートに通す | 暴走ループ・安全性 | 中 |
| 8. ツール変更のたびに評価ループを回す | 4つすべて(リグレッションとして) | 中 |
この順序で取り組んでください。プラクティス1と2なら半日で済み、今見えているツールの誤選択と引数エラーの大半を取り除けます。ツールの説明はドキュメントではありません。選択時にモデルが受け取る、唯一の指示です。
Phase 1:モデルが実際に使えるツールを設計する
エージェントツールコールにおいて、最も安く済む信頼性の改善は、プロンプトでもモデル選びでもなく、ツールの定義にあります。モデルはAPIドキュメントもREADMEも読みません。モデルが見るのは名前と説明の文字列、そしてJSONスキーマだけで、その3つだけから判断を下します。この3つを正しく整えれば、他に手を加える前に選択精度が動き始めます。
プラクティス1:モデルが行動できる説明を書く
ツールの説明は、APIドキュメントとしてではなく、モデルへの指示として書いてください。人間の開発者を満足させる説明(「usersエンドポイントのRESTラッパー」など)は、モデルに判断材料を何も与えません。Anthropicのツール作成に関するエンジニアリングガイドと、そのツール定義のベストプラクティスは、どちらも同じパターンを推奨しています。いつそのツールを使うのか、何を返すのか、そしていつ使ってはいけないのかを書く、というパターンです。
{
"name": "get_user",
"description": "Fetches a user profile. Use ONLY when you already have a user_id. Do NOT use to search or list users; call search_users instead. Returns name, email, plan. Errors if user_id is not a valid UUID."
}一方、多くのチームがリリースしてしまうのは次のようなバージョンです。
{
"name": "get_user",
"description": "Gets a user."
}ここで効いてくるルールは2つです。1つ目は、意味が曖昧にならないようパラメータに名前を付けることです。user や id ではなく user_id にしてください。user だと、UUIDが必要な場所に名前やメールアドレスを渡してよいとモデルが解釈してしまいます。2つ目は、除外条件を明示することです。「ユーザー検索には使わない」という一文は、どれほど多くの肯定的な説明よりもツールの誤選択を防ぎます。モデルは、重複するツールの見分けがつかないことのほうが、境界が明確な単独ツールを誤解することよりはるかに多いからです。これらの定義がOpenAI、Anthropic、Googleの各APIへどのように届くかというプロバイダーレベルの仕組みは、マルチプロバイダー対応のfunction callingガイドを参照してください。
プラクティス2:スキーマはフラットに、タスクに合った形にする
入力スキーマはフラットに保ち、タスクに必要なフィールドだけを置き、不要なフィールドは置かないでください。オプション分岐を持つネストされたオブジェクトは、引数エラーの温床です。モデルは、例を見たことのない構造を推測しなければなりません。OpenAIのfunction callingガイドは任意のJSON Schemaを受け付けますが、寛容であることと信頼できることは同じではありません。
{
"name": "create_ticket",
"parameters": {
"type": "object",
"properties": {
"ticket": {
"type": "object",
"properties": {
"details": {
"type": "object",
"properties": {
"title": { "type": "string" },
"meta": { "type": "object" }
}
}
}
}
}
}
}これをタスクに合わせてフラットにします。
{
"name": "create_ticket",
"parameters": {
"type": "object",
"properties": {
"title": { "type": "string" },
"priority": { "type": "string", "enum": ["low", "medium", "high"] },
"assignee_id": { "type": "string" }
},
"required": ["title", "priority"]
}
}値の集合が限られているフィールドには、自由記述よりenumが向いています。何でもオプションにするより、requiredの配列で指定するほうが優れています。モデルがほぼ常に必要とするフィールドは、API側がオプションと呼んでいても、ツールスキーマではrequiredにしてください。あなたはAPIを鏡写しにしているのではありません。特定の1つのモデルが正しく入力できるインターフェースを設計しているのです。
Phase 2:個別のツールだけでなくツールセット全体を管理する
エージェントが片手で数えられる以上のツールを持つようになると、個々のツールの品質だけでは足りなくなります。モデルが読む一覧が大きくなるほど、選択エラーも増えるからです。
プラクティス3:複数ステップのAPI呼び出しをアトミックなツールにまとめる
固定されたAPI呼び出しの連続は、1つのアトミックなツールに畳んでください。Anthropicのエンジニアリング記事は、schedule_event と get_customer_context をお手本として挙げています。全体の仕事をする1回の呼び出しは、毎回正しく連鎖させなければならない3回の呼び出しに勝ります。連鎖の輪が1つ増えるたびに、モデルが停止したり、間違ってリトライしたり、ループしたりするターンが1つ増えます。
# What the agent does WITHOUT an atomic tool: 3 calls, 3 chances to fail
calendar = call_tool("list_calendars", {})
free = call_tool("find_free_slot", {"calendar_id": calendar["items"][0]["id"], "duration": 30})
call_tool("create_event", {"calendar_id": calendar["items"][0]["id"], "start": free["start"]})
# One atomic tool: the sequence lives in your code, not the model's head
call_tool("schedule_event", {"duration": 30, "attendees": ["[email protected]"]})経験則はシンプルです。モデルが常にAの次にBを呼ばなければならないなら、AとBは2つの衣装を着た1つのツールです。
プラクティス4:名前空間・整理・動的フィルタリングを行う
すべてのツール名に名前空間を付け、各エージェントには現在のタスクに必要なサブセットだけを表示してください。汎用的な名前は、2つの連携を接続した瞬間に衝突します。2つのMCPサーバーに接続されたエージェントを想像してください。どちらも search というツールを公開しています。同じ動詞が2つ並び、見分ける方法がありません。Anthropicは、プレフィックスによる名前空間付けで評価が測定可能に向上したことを記録しています。
| 変更前 | 変更後(プレフィックス) | 変更後(サフィックス) |
|---|---|---|
search | asana_projects_search | search_asana_projects |
create | asana_tasks_create | create_asana_tasks |
search(2つ目のサーバー) | github_repos_search | search_github_repos |
整理(プルーニング)も命名と同じくらい重要です。パスワードの質問に答えているサポートエージェントに、課金ツールを読み込ませておく必要はありません。標準的な対策はプランナー・ワーカーパターンです。プランナーがタスクをワーカーに振り分け、ワーカーは関連するツールだけを読み込む構成です。LangGraphの動的ツール読み込みハウツーが実装を順に説明しています。ツールは何個から多すぎになるのでしょうか。エージェントあたり5〜10個を、法則ではなく実務上の目安として扱ってください。一覧が伸びるほど精度は落ち、その治療薬はフィルタリングであって、より大きなモデルではありません。ルーティングとフィルタリングの層そのものを選定中なら、おすすめのfunction callingライブラリ比較で選択肢を比べてみてください。
Phase 3:戻り値と送信内容を制御する
ループは双方向に動きますが、多くのチームは出ていく半分しか設計していません。ツールが何を返すかで、次のターンまでコンテキストウィンドウがどれだけ生き残るかが決まり、バリデーションが何を拒否するかで、モデルがミスから学ぶか、同じミスを繰り返すかが決まります。
プラクティス5:簡潔でシグナル密度の高い結果を返す
モデルが行動できる最小の結果を、生のIDではなく人間に読める識別子で返してください。Anthropicのエンジニアリング記事は、デフォルトの結果が206トークンだったツールを記録しています。簡潔な response_format 設定を1つ加えたところ、同じ結果が72トークンに縮みました。おおよそ3分の1です。タスクあたり数十回の呼び出しにこれを掛けると、エージェントが最後まで完了できるかどうかがこれで決まります。
// Before: 206 tokens (shape per Anthropic's documented example)
{
"status": "success",
"data": {
"id": "8f14e45f-ceea-3f9c-a2f3-90c1b5e0a7d2",
"object": "task", "created_at": "2026-07-02T09:14:00Z",
"updated_at": "2026-07-11T16:40:12Z", "completed_at": null,
"assignee": {"id": "c9a1...f2", "object": "user"},
"projects": [{"id": "b7d3...91", "object": "project"}],
"permalink": "https://app.asana.com/0/.../f"
}
}
// After: 72 tokens
{ "task": "Fix login redirect", "assignee": "Dana Kim", "project": "Web App", "due": "2026-07-20" }同じ出典から、あと2つ補足します。Anthropicはツール定義に response_format のenum(detailed と concise)をサポートしているため、放水ホースを解析する代わりに、欲しい形を宣言できます。また、Claude Codeはツールレスポンスを25,000トークンで上限打ち切りしており、肥大化した結果はどうせ切り詰められます。Anthropicはさらに、自社知見として、UUIDを意味のある名前へ解決すると検索ハルシネーションが測定可能に減ったと報告しています。上の「変更後」ペイロードが c9a1...f2 ではなく「Dana Kim」と書くのはこのためです。肥大化したレスポンスはコストの問題でもあります。全体像はLLM APIコスト削減ガイドを参照してください。
プラクティス6:すべての呼び出しを検証し、エラーでモデルに学ばせる
すべてのツール呼び出しをサーバー側で検証し、直し方を含んだエラーを返してください。Martin Fowlerのfunction callingに関する記事は率直に言い切っています。モデルの出力を決して信頼するな、と。モデルはenumが必要な場所に文字列を渡し、存在しないIDを捏造します。
def create_ticket(args):
if args.get("priority") not in {"low", "medium", "high"}:
return {"error": f"priority must be one of: low, medium, high. Got '{args.get('priority')}'. Pass priority='medium' for normal issues."}
if not is_valid_uuid(args.get("assignee_id")):
return {"error": "assignee_id must be a UUID. Call list_team_members to get valid IDs, then retry."}
return db.create_ticket(**args)エラー文字列が勝負のすべてです。比べてみてください。
# Unhelpful: the model retries the same bad call
{"error": "invalid input"}
# Helpful: the model knows exactly what to change
{"error": "priority must be one of: low, medium, high. Got 'urgent'. Use 'high'."}ツールが返すすべてのバリデーションエラーは、モデルの次の試行に向けてあなたが書いているプロンプトです。制約の名前を挙げ、修正に使うツールを指し示すエラーは、リトライループを一発回復に変えます。これはセキュリティの最初の防衛線でもあります。LLMガードレールガイドで詳しく扱っています。
Phase 4:安全性を確保し、そのうえで測定可能にするには?
安全性と測定は同じフェーズです。承認ゲートのない破壊的操作も、測定していないリグレッションも、どちらも「予見できなかったインシデント」という同じ姿で表面化するからです。元に戻せない操作にゲートを設け、そのうえで全てを計測してください。そうすれば次のツール変更は、根拠のある判断になり、願望ではなくなります。
プラクティス7:破壊的な操作は人間の承認ゲートに通す
読み取りツールと書き込みツールを分け、破壊的な操作にはすべて人間による確認ゲートを設けてください。MCP仕様のツールアノテーションはまさにこのために存在します。destructiveHint は破壊的な更新を行うツールを示し、openWorldHint は外部システムに触れるツールを示すので、クライアントは実行前に確認を求められます。使ってください。
この失敗モードは仮定の話ではありません。Laurent Kubaskiは、元のレポートへのリンク付きで公開した2025年7月のツールコール記事で、ある事例を記録しています。ユーザーがExcelのCopilotに4行目への操作を指示したところ、エージェントが代わりに8行目へ操作を実行したという事例です。間違った行と書き込みの間には、確認ゲートが存在しませんでした。対策はAWSがBedrock Agents向けに文書化しているパターンです。エージェントが操作を準備し、承認のために返し、人間が確認したあとでのみ実行します。Cursorもファイル編集で同じことをしています。読み取りだけで済むタスクでは認証情報をリードオンリーに絞り、確認ゲートはインジェクション攻撃の対象面の一部として扱ってください。このテーマはプロンプトインジェクション対策ガイドで扱っています。
プラクティス8:ツール変更のたびに評価ループを回す
ツールを変更するたびに、小規模な評価スイートを変更前と変更後で実行し、メトリクスは固定された順序で読んでください。Paragonの最適化ガイドは、採用する価値のある4指標の枠組みを提案しています。
| メトリクス(Paragon準拠) | 検知できる問題 | 測定方法 |
|---|---|---|
| ツールの正確性 | 間違ったツールの呼び出し | タスクに対して正しいツールを呼び出したか? |
| 入力の正確性 | 間違った引数 | 引数は有効で完全だったか? |
| タスク完了 | エンドツーエンドの失敗 | ユーザーの目標は達成されたか? |
| タスク効率 | トークンの浪費・ループ | 呼び出し回数とトークン数は? |
Anthropicのツール評価クックブックは、実際のSlackとAsanaのMCP評価をもとに、良い評価タスクと悪い評価タスクがどんなものかを示しています。
# Weak: vague, many valid paths, impossible to score
"Use the Asana tools to organize some work."
# Strong: one correct tool, checkable arguments, binary outcome
"Create a task titled 'Renew TLS cert' in project 'Infra' assigned to [email protected], due 2026-08-15. Expect exactly one create_task call with those four fields."私たちの解釈として、そう明記したうえで述べます。公開されている数値は、取り組むべき順序を教えてくれます。まずツールの正確性を確認してください。Anthropic自身の測定が、説明と命名の変更がこの指標を直接動かすことを示しているためです(206トークンから72トークンへの書き直し、UUIDから名前への解決がハルシネーションを減らしたという知見)。タスク効率は最後に回してください。この指標は主に、最初の3指標がすでに捉えた失敗を反映するだけだからです。スターター用のスイートとしては、15〜30個のタスクを設計し、ツールごとに2〜3個、それぞれに単一の期待呼び出しと二値の合格条件を置いてください。この規模なら、説明の書き直しによるリグレッションを、1週間もラベリングに費やさずに検知できます。クックブックのSlackとAsanaの構成は、この規模のスイートこそが意図された出発点であり、近道ではないことの証拠だと私たちは読んでいます。さらに深い仕組みは本番環境のAIエージェント評価ガイドにあります。もし評価結果が「ツール自体は問題ないが、オーケストレーションが問題だ」と示しているなら、そのときがフレームワーク選びをおすすめのAIエージェントフレームワークと照らし合わせて見直すタイミングです。
エージェントツールコールとMCP:何が違うのか?
MCPはトランスポートとレジストリの標準であって、信頼性の層ではありません。そのため、ツールがMCP経由で届こうと、インラインで定義されていようと、同じ8つのプラクティスが当てはまります。ネイティブのツールコールはモデルプロバイダーとの契約です。モデルが tool_call をどう出力し、tool_result をどう読むかを定めます。MCPはツールがモデルに届くまでの経路を標準化しますが、モデルが正しいものを選ぶかどうかには何もしません。
| ネイティブのツールコールが担うもの | MCPが加えるもの | どちらも担わないもの |
|---|---|---|
| tool_call / tool_result のメッセージ形式 | 任意のクライアントから任意のサーバーへ届く共通プロトコル | 説明の品質 |
| プロバイダー固有のスキーマ | ツールの検出とレジストリ | スキーマ設計・バリデーション |
| 並列呼び出しのネゴシエーション | destructiveHint などのアノテーション | 人間の承認ゲート・評価・レスポンスの最適化 |
search という名前に「何かを検索する」という説明を付けたツールを公開するMCPサーバーは、同じ定義のインライン関数とまったく同じように失敗します。まず定義を直し、トランスポートの心配はその後でしてください。プロトコル側はModel Context Protocolガイドで端到端を網羅しています。
Techsyがこの8つのプラクティスをどう適用しているか
クライアントのエージェント構築では、他の何を出す前に、必ず3つを徹底しています。指示として書かれた説明(プラクティス1)、すべての書き込みツールへのバリデーションゲート(プラクティス6)、そしてインシデントの後ではなくデプロイ前に回る評価スイート(プラクティス8)です。この3つで、ツールの誤選択、引数エラー、そしてその両方を再発させるリグレッションをカバーできます。私たちがデバッグした本番エージェントのインシデントは、すべてここから始まっていました。残りの5つのプラクティスは、エージェントの成長に合わせて導入していきます。エージェントがデモ段階を超えて、間違ったツールを選ぶようになっているなら、無料相談をご利用ください。8つのうちどれを最初に直すべきか、私たちが診断します。
著者について
Mert BaturはTechsy.ioのCo-Founderです。TechsyのチームはB2Bクライアント向けにAIエージェント、自動化システム、音声/SDRパイプラインを提供しています。Techsyチームが実際に本番環境で使っているLLMツールスタックについて執筆しています。LinkedInでつながってください。
よくある質問
エージェントツールコールとは何ですか?
エージェントツールコールは、LLMが外部関数の呼び出しを判断し、構造化された tool_call を出力して、あなたのコードが推論に使える tool_result を返すのを待つ仕組みです。チャットモデルを、データベースへの問い合わせ、API呼び出し、行動の実行ができるエージェントに変えるのがこの仕組みです。モデルがツールと引数を選び、あなたのエグゼキューターがそれを実行します。
エージェントツールコールのループはどう動きますか?
ループは5ステップです。ユーザーのリクエストがモデルに届き、モデルがツールを選んで tool_call を書き、あなたのエグゼキューターがそれを実行し、tool_result がモデルに戻り、モデルが回答するか、別の呼び出しを出すかのどちらかを選びます。このサイクルがタスク完了まで繰り返されます。本ガイドの4つの失敗モードは、それぞれこのループの特定のステップに存在します。
なぜエージェントが間違ったツールを選ぶのですか?
たいていは、2つのツールが重複していて、その説明がどちらがどちらかを書いていないためです。モデルは名前と説明だけで選ぶので、「ユーザーを取得する」と「ユーザーを探す」は交換可能に見えます。除外条件の行(「検索には使わない」)、名前空間付きの名前、コンテキスト内のツール数削減で直してください。Kubaskiの4モデルテストでは、強力なモデルでさえ曖昧な一覧では誤ったルーティングを行うことが示されています。
ツールコールエージェントの出力を構造化させるには?
制約するのはプロンプトではなくスキーマです。値が限定されるフィールドにはenumを、タスクに必要なものにはrequired配列を、ネストよりフラットなオブジェクトを使ってください。ツール呼び出しではなく最終回答については、OpenAIのstructured outputsやAnthropicのtool-choiceモードといったプロバイダー機能が特定の形を強制します。構造化出力ガイドがコード付きで両方の経路を扱っています。
エージェントツールコールとMCP:何が違うのですか?
ネイティブのツールコールは、あなたのコードと1つのモデルプロバイダーの間の契約です。tool_call と tool_result のメッセージ形式を定めます。MCPは、ツールの検出方法と、互換クライアントへの配信方法を標準化するプロトコル層です。MCPは配管を変えますが、信頼性は変えません。説明がまずいツールは、どちらの経路でも同じように失敗します。Model Context Protocolガイドで解説しています。
LLMエージェントにとってツールは何個から多すぎですか?
エージェントあたり5〜10個を、法則ではなく実務上の目安として扱ってください。表示される一覧が伸びるほど選択精度は落ちます。特に名前や説明が重複している場合に響きます。対策はより大きなモデルではなく、フィルタリングです。プランナー・ワーカーの分離を使い、現在のタスクに必要なサブセットだけを読み込んでください。すべてに名前空間を付け、2つの連携が素の search を両方公開するような状態をなくしてください。
ツールコールに最適なモデルはどれですか?
唯一の答えはなく、この分野では公開ベンチマークの鮮度が落ちやすいのが実情です。OpenAI、Anthropic、Googleのフロンティアモデルはどれも基本的なツール利用タスクをこなします。一方、うまく設計したツールと組んだ小型モデルも、トークンコストのごく一部で、ほぼ同じ頻度でタスクを完了することが珍しくありません。プラクティス8の15〜30個のタスク評価スイートを構築し、自分のツールに対して候補モデルをテストしてください。
ツールコールによるトークンコストをどう減らせますか?
戻ってくるものを削ってください。生のAPIペイロードではなく、簡潔でシグナル密度の高い結果を返します。Anthropicは response_format の変更1つで206トークンから72トークンへの削減を記録しました。UUIDを名前に解決し、モデルが一切使わないフィールドを落とし、すべてのツール結果が後続のすべてのターンでコンテキストウィンドウに入り直すことを忘れないでください。アトミックなツールで呼び出し回数を減らせば、結果そのものが請求から消えます。
ツールコールの品質をどう評価しますか?
4つの指標を順番に採点してください。ツールの正確性(正しいツールか?)、入力の正確性(有効な引数か?)、タスク完了(目標達成か?)、タスク効率(トークンと呼び出しの回数)です。15〜30個のタスクを書き、それぞれに特定の1つの呼び出しと、検証可能な引数、二値の合格条件を期待させます。ツール変更のたびにスイートを変更前後で実行し、説明の書き直しが未測定のままリリースされないようにしてください。
まとめ
最適化の前に診断をしてください。エージェントが間違ったツールを選ぶ理由は4つのうちいずれか1つです。そして上記8つのプラクティスのうち3つ、説明、フラットなスキーマ、フィルタリングが、本番インシデントの大半を引き起こす選択の失敗を直します。半日で済み、この問題がそもそも修正可能である理由そのものなので、そこから始めてください。バリデーションエラーは情報量を保ち、破壊的な操作はすべて人間の承認ゲートに通し、変更のたびに評価ループを回してください。そうすれば、モデルを乗り換える前に測定ができます。ツールの誤選択はモデルの問題ではありません。ツール設計の問題です。そして設計の主はあなたです。