mcpField
A
ai-patterns-tool-use-patterns

ai-patterns-tool-use-patterns

github.com/agents-incエージェントワークフロー2645 回インストール2 日前に更新
リポジトリ

ai-patterns-tool-use-patterns について

LLMのツール呼び出しやエージェントループを実装する開発者向けのスキルです。スキーマ設計、実行回数の制限、入力検証、エラー処理、ツール選択を扱います。

主な機能

  1. 01JSON Schemaによるツール定義
  2. 02上限付きツール呼び出しループ
  3. 03入力検証と構造化エラー
  4. 04ツール選択の制御
  5. 05並列呼び出しと承認ゲート
  6. 06TypeScriptの型安全な実装パターン

活用シーン

  1. 01プロバイダー横断のツール呼び出し実装
  2. 02複数段階のエージェントワークフロー構築
  3. 03並列ツール呼び出しの処理
  4. 04リスクの高い操作への承認追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agents-inc/skills --skill ai-patterns-tool-use-patterns

インストール後、Claude Code で/ai-patterns-tool-use-patternsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

metadata.yaml
reference.md
examples/
advanced.md
core.md

詳細

ツール使用パターン

クイックガイド: ツール使用(関数呼び出し)では、LLM が外部関数を呼び出せます。基本的なパターンは、ツールのスキーマを定義する(パラメーターには JSON Schema を使用)→ ツールとメッセージを LLM に送信する → 応答内の tool_use を検出する → ローカルで実行する → 結果を LLM に返す → モデルがテキストで応答するまで繰り返す、という流れです。無限ループや API 呼び出しの暴走によるコスト増大を防ぐため、すべてのループに最大ステップ数を設定し、実行前にすべてのツール入力を検証し、モデルが復旧できるよう構造化されたエラーを返してください。ツール選択制御(auto、required、none、特定のツール)を使って、モデルの動作を誘導します。


<critical_requirements>

重要:このスキルを使用する前に

すべてのコードは、CLAUDE.md のプロジェクト規約(kebab-case、名前付きエクスポート、インポート順序、import type、名前付き定数)に従う必要があります。

(すべてのツールループには最大ステップ数を必ず設定してください。無制限のループは API 呼び出しの無限化やコストの暴走につながります。)

(実行前に、すべてのツール入力引数を必ず検証してください。LLM が生成した引数は信頼できない入力です。)

(ツールの実行に失敗した場合は、必ず構造化されたエラーメッセージをモデルに返してください。エラーを黙って無視したり、空の結果を返したりしてはいけません。)

(ツールのパラメーター定義には必ず JSON Schema を使用してください。主要なプロバイダーはすべてこの形式を必要とします。)

(ツール定義はトークンコストとして扱ってください。すべてのツールスキーマは API 呼び出しのたびに送信されるため、説明は簡潔かつ正確にしてください。)

</critical_requirements>


自動検出: ツール使用、関数呼び出し、tool_calls、tool_use、ツール呼び出しループ、エージェントループ、ツール定義、ツールスキーマ、toolChoice、tool_choice、並列ツール呼び出し、人間参加型、ツール承認、エージェント型ワークフロー、マルチステップエージェント、ツール結果、ツールエラー

使用する場面:

  • あらゆるプロバイダーで LLM のツール呼び出し/関数呼び出しを実装する場合
  • タスクが完了するまでツールを反復的に呼び出すエージェントループを構築する場合
  • 並列ツール呼び出し(1 回の応答内に複数のツール呼び出しがある場合)を処理する場合
  • モデルが復旧できるよう、ツールエラーをモデルに報告する場合
  • ツール選択(自動、必須、なし、特定のツールを強制)を制御する場合
  • 危険なツールの実行前に、人による承認ゲートを追加する場合
  • ツール呼び出しを含む応答をストリーミングする場合

対象となる主なパターン:

  • ツール定義スキーマ(パラメーターと説明の JSON Schema)
  • ツール呼び出しの基本ループ(送信 → 検出 → 実行 → 返却 → 再送信)
  • 並列ツール呼び出し(1 回の応答内に複数の呼び出しがある場合の処理)
  • エラー処理(ツールの実行失敗をモデルに報告)
  • ツール選択制御(自動、必須、なし、特定のツール)
  • 会話状態を用いたマルチステップのエージェントワークフロー
  • 人間参加型の承認パターン
  • TypeScript での型安全なツール定義
  • セキュリティ(入力検証、サンドボックス化、最小権限)
  • ツール呼び出しを伴うストリーミング

使用しない場面:

  • ツール呼び出しを伴わない単純なテキスト生成 — ツールは必要ありません
  • 構造化出力/JSON 抽出 — 代わりに、使用するプロバイダーの構造化出力機能を使用してください
  • プロバイダー固有の SDK パターン — SDK 固有の API には、使用するプロバイダーの SDK スキルを使用してください

詳細リソース:

  • examples/core.md -- ツール定義、ツール呼び出しループ、エラー処理、型安全なツール
  • examples/advanced.md -- 並列ツール呼び出し、マルチステップエージェント、人間参加型、ストリーミング、セキュリティ
  • reference.md -- 判断フレームワーク、プロバイダー比較、アンチパターンのチェックリスト

## 哲学

ツールの使用は、LLM をテキスト生成器からエージェントへと変える仕組みです。モデルはコードを実行したり、データベースを照会したり、API を呼び出したりすることはできません。構造化されたツール呼び出しを出力して、コードに実行を 要求 することしかできません。実行するのはコードであり、計画するのはモデルです。

基本原則:

  1. モデルが計画し、コードが実行する -- LLM は、構造化された引数を含むツール呼び出しのリクエストを出力します。コードはそれを検証、実行し、結果を返します。モデルに任意のコードを直接実行させてはいけません。
  2. エージェントはループで動作する -- シンプルな天気予報ボットから複雑なコーディングアシスタントまで、すべてのエージェントは同じループに従います。LLM が判断する -> システムが実行する -> 結果をフィードバックする -> 繰り返す。複雑さを生み出すのはツールと状態であり、ループ自体ではありません。
  3. ツールはスキーマである -- ツール定義は JSON Schema であり、どの関数が存在するか、どのようなパラメーターを受け取るか、いつ使用するかをモデルに伝えます。説明が優れているほど、ツールの選択と引数の品質が向上します。
  4. エラーは情報である -- ツールが失敗したときは、構造化されたエラーメッセージをモデルに返します。モデルは多くの場合、異なる引数で再試行したり、別のツールを選んだり、ユーザーに失敗を説明したりして復旧できます。
  5. 多層防御 -- LLM が生成した引数は信頼できない入力です。スキーマを検証し、型を強制し、引数の範囲を制限し、実行をサンドボックス化し、危険な操作には承認を必須にしてください。

ツール呼び出しを使用する場面:

  • モデルが持っていない現実世界のデータ(天気、データベース、API)が必要なタスク
  • 副作用(メールの送信、レコードの作成、ファイル操作)が必要なタスク
  • 中間データの検索を伴う複数段階の推論が必要なタスク
  • モデルが確実には実行できない計算(数学、コードの実行)が必要なタスク

ツール呼び出しを使用しない場面:

  • モデルが学習データだけで回答できる場合
  • 構造化された JSON 出力だけが必要な場合(代わりに構造化出力機能を使用してください)
  • 「ツール」が関数を装った単なるプロンプトエンジニアリングである場合
  • 決定論的な動作が必要な場合(ツール呼び出しではモデルの判断による非決定性が加わります)

基本パターン

パターン 1:ツール定義スキーマ

すべてのツール定義には、名前、説明、パラメータースキーマの 3 つの要素があります。説明が最も重要です。ツールを呼び出すかどうか、また引数をどのように構成するかというモデルの判断を導きます。

// Provider-agnostic tool definition shape
interface ToolDefinition {
  name: string; // ^[a-zA-Z0-9_-]{1,64}$
  description: string; // When and why to use this tool
  parameters: JsonSchema; // JSON Schema for input arguments
}
良いツール定義
// Good: precise description, constrained parameters, .describe() on each property
const VALID_UNITS = ["celsius", "fahrenheit"] as const;

const getWeatherTool: ToolDefinition = {
  name: "get_current_weather",
  description:
    "Get the current weather conditions for a specific city. " +
    "Returns temperature, humidity, and conditions. " +
    "Use this when the user asks about current weather, NOT forecasts.",
  parameters: {
    type: "object",
    properties: {
      location: {
        type: "string",
        description: "City name and optional country code, e.g. 'London, UK'",
      },
      unit: {
        type: "string",
        enum: VALID_UNITS,
        description: "Temperature unit. Defaults to celsius if not specified.",
      },
    },
    required: ["location"],
  },
};

良い理由: 説明では、ツールが何を返すか、いつ使用するか、いつ使用しないかを説明しています。パラメーターには説明と制約(列挙型)があります。有効な値には名前付き定数を使用しています。

悪いツール定義
// Bad: vague description, no parameter descriptions, no constraints
const weatherTool: ToolDefinition = {
  name: "weather",
  description: "Gets weather", // BAD: too vague
  parameters: {
    type: "object",
    properties: {
      loc: { type: "string" }, // BAD: no description, cryptic name
      u: { type: "string" }, // BAD: no enum constraint
    },
  },
};

悪い理由: 曖昧な説明によりツールの選択を誤りやすくなり、パラメーターの説明がないと不正な引数が生成されやすくなります。また、列挙型による制約がないと無効な値が使われやすくなり、分かりにくいパラメーター名はモデルを混乱させます。


パターン 2:基本的なツール呼び出しループ

ツールを使用するための基本パターンです。ツール定義を含むメッセージを送信し、応答にツール呼び出しが含まれているか確認して実行し、結果を返して、モデルに最終応答を生成させます。完全な実装については、examples/core.md を参照してください。

const MAX_TOOL_STEPS = 10;

for (let step = 0; step < MAX_TOOL_STEPS; step++) {
  const response = await callLLM({ messages, tools });
  if (!response.toolCalls?.length) return response.text;
  messages.push({ role: "assistant", toolCalls: response.toolCalls });
  for (const tc of response.toolCalls) {
    messages.push({
      role: "tool",
      toolCallId: tc.id,
      content: JSON.stringify(await executeTool(tc)),
    });
  }
}

重要なポイント: 名前付き定数(MAX_TOOL_STEPS)を使用した上限付きの for ループを必ず使い、while (true) は決して使わないでください。ツールの結果より前に、必ずアシスタントメッセージ(ツール呼び出しを含む)を追加してください。ステップ数の上限に達した場合は、適切に終了する処理を含めてください。

### パターン 3:エラー処理を伴うツールの実行

ツールが失敗した場合、クラッシュさせるのではなく、構造化されたエラーをモデルに返します。モデルは、再試行したり、別の方法を選択したり、失敗について説明したりすることで、復旧できることがよくあります。完全な検証パイプラインについては、examples/core.md を参照してください。

// Four-step validation: tool exists -> JSON parses -> schema validates -> execution succeeds
async function executeTool(toolCall: ToolCall): Promise<ToolResult> {
  const handler = toolRegistry[toolCall.name];
  if (!handler)
    return { success: false, error: `Unknown tool: "${toolCall.name}"` };
  const validated = handler.schema.safeParse(toolCall.arguments);
  if (!validated.success)
    return { success: false, error: `Invalid args: ${validated.error}` };
  try {
    return { success: true, data: await handler.execute(validated.data) };
  } catch (error) {
    return {
      success: false,
      error: `Tool failed: ${error instanceof Error ? error.message : "Unknown"}`,
    };
  }
}

重要なポイント: ツールの存在を必ず確認し、JSON 引数を解析してスキーマに照らして検証し、実行時のエラーを捕捉します。モデルが復旧できるよう、構造化された { success, data?, error? } の結果を返します。ツールのエラーでループを決してクラッシュさせないでください。


パターン 4:ツール選択の制御

モデルがツールを使用するかどうか、またどのように使用するかを制御します。主要なプロバイダーはすべて、4 つのモードに対応しています。

type ToolChoice =
  | "auto" // Model decides whether to call tools (default)
  | "required" // Model MUST call at least one tool
  | "none" // Model MUST NOT call any tools
  | { name: string }; // Model MUST call this specific tool
各モードを使用するタイミング
// auto (default) -- let the model decide
const response = await callLLM({
  messages,
  tools,
  toolChoice: "auto",
});

// required -- force tool use (e.g., first step of an agent must act)
const response = await callLLM({
  messages,
  tools,
  toolChoice: "required",
});

// none -- disable tools (e.g., final response must be text only)
const response = await callLLM({
  messages,
  tools,
  toolChoice: "none",
});

// specific tool -- force a particular tool (e.g., classification step)
const response = await callLLM({
  messages,
  tools,
  toolChoice: { name: "classify_intent" },
});

判断の目安:

モード使用する場面
auto汎用エージェント――モデルがコンテキストに基づいて判断する
requiredエージェントの最初のステップで必ずツールを呼び出す必要がある場合(例:データ検索)
none最終応答の生成時――以降のツール呼び出しを許可しない場合
{ name }特定のツールを使う必要があるパイプラインのステップ(例:抽出、分類)

使用するタイミング: ワークフローの特定の段階で、ツールの動作を制限または保証する必要がある場合


パターン 5:TypeScript での型安全なツール定義

Zod スキーマを使った型付きレジストリパターンにより、ツール定義のコンパイル時の安全性と、実行時の検証を実現します。ToolRegistry クラスの完全な実装については、examples/core.md を参照してください。

import { z } from "zod";

// Define tool with schema + execute function -- TypeScript infers argument types
function defineTool<T extends z.ZodType>(config: {
  name: string;
  description: string;
  schema: T;
  execute: (args: z.infer<T>) => Promise<unknown>;
}) {
  return config;
}

使用するタイミング: ツール呼び出しを実装するあらゆる TypeScript プロジェクト――Zod は実行時に検証を行い、TypeScript はスキーマとハンドラーの不一致をコンパイル時に検出します


<decision_framework>

判断フレームワーク

ツール呼び出しは必要ですか?
Does the task require information the model doesn't have?
+-- YES -> Tool calling (fetch data from APIs, databases, files)
+-- NO -> Does the task require side effects?
    +-- YES -> Tool calling (send email, create record, execute code)
    +-- NO -> Do you need structured JSON output?
        +-- YES -> Use structured output features (NOT tool calling)
        +-- NO -> Plain text generation, no tools needed
どのループパターンを使いますか?
How many tools might the model call?
+-- Single tool call per request
|   +-- Simple request-response with one tool execution
|   +-- No loop needed, just one round-trip
+-- Multiple sequential tool calls
|   +-- Use the bounded tool call loop (Pattern 2)
|   +-- Set MAX_TOOL_STEPS based on task complexity
+-- Multiple parallel tool calls in one response
|   +-- Execute all tool calls concurrently (Promise.all)
|   +-- Then return all results and loop
+-- Complex multi-step agent
    +-- Use the bounded loop with conversation state
    +-- Add human-in-the-loop for dangerous operations
    +-- Consider per-step tool filtering
ツールのエラーをどのように処理しますか?
Tool execution failed. What to do?
+-- Return structured error to the model
|   +-- Include error message and context
|   +-- Model can retry, choose alternative, or explain failure
+-- NEVER: silently return empty result
+-- NEVER: crash the loop
+-- NEVER: retry automatically without telling the model
    (the model should decide whether to retry)

</decision_framework>


<red_flags>

危険信号

優先度の高い問題:

  • ステップカウンターのない無制限のツールループ(while (true))――無限に API を呼び出し続け、コストが際限なく増大するリスクがあります
  • 検証せずにツール引数を実行すること――LLM が生成した引数は信頼できない入力です。ユーザー入力と同様に扱ってください
  • ツールのエラーを黙って握りつぶすこと(null や {} を返すなど)――モデルは、知らされていないエラーから復旧できません
  • サンドボックス化せずに、ツール引数から任意のコードを実行できるようにすること――プロンプトインジェクションによってコード実行にエスカレートするおそれがあります
  • 「データを取得します」のようなツールの説明――説明が曖昧だと、誤ったツール選択や不正な引数につながります

優先度が中程度の問題:

  • 関連するツールが一部だけなのに、すべての API 呼び出しで全ツールを送信すること――トークンを浪費し、モデルを混乱させます
  • ツールの結果より前に、ツール呼び出しを含むアシスタントメッセージを会話履歴に含めないこと――メッセージの順序が崩れます
  • ツールを使うのが適切でない場合の代替策なしに tool_choice: "required" を使用すること――意味のないツール呼び出しを強制します
  • 生のデータベース行や API の完全な応答をツールの結果として返すこと――無関係なデータでコンテキストを圧迫します。要約するか、切り詰めてください
  • ツールの呼び出しと結果を記録しないこと――本番環境でエージェントの動作をデバッグできなくなります

よくある間違い:

  • ツール呼び出しの引数は、解析済みのオブジェクトではなく JSON 文字列として届くことを忘れる――使用前に必ず JSON.parse() してください
  • ツールを利用できる状態なら、モデルが必ずツールを呼び出すと思い込む――auto モードでは、テキストで直接応答することがあります
  • ツール呼び出しを構造化出力として扱う――両者は異なる問題を解決します(アクションとデータ抽出)
  • ツールの実装ではなく、ツールの説明にビジネスロジックを記述すること――説明は選択を導くものであり、実行を担うものではありません

注意点とエッジケース:

  • ツール定義は API 呼び出しのたびにトークンを消費します――詳細なスキーマを持つツールが 10 個あると、リクエストごとに 1000 以上のトークンを使うことがあります
  • 並列のツール呼び出しは任意の順序で届く可能性があります――実行順序が定義順と一致すると決めつけないでください
  • 定義にないツール名や引数を幻覚するモデルもあります――ツール名がレジストリに存在することを必ず検証してください
  • ツール呼び出しを含むストリーミング応答では、解析する前に部分的な JSON チャンクを蓄積する必要があります――引数は一度に届くのではなく、段階的に届きます
  • 非常に大きなツール結果(4000 トークン超)を返すと、会話がコンテキスト上限を超える可能性があります――大きな結果は切り詰めるか、要約してください
  • モデルは、すべてのツール呼び出しと結果を含む会話の全コンテキストを保持します――エージェントの実行時間が長くなると、トークン使用量が大幅に増加します
  • プロバイダーによって、ツールの結果に異なるメッセージ形式(role: "tool" とコンテンツブロックなど)が使われます――これを callLLM ラッパーで抽象化してください

</red_flags>


<critical_reminders>

重要な注意事項

すべてのコードは CLAUDE.md のプロジェクト規約(ケバブケース、名前付きエクスポート、インポート順序、import type、名前付き定数)に従う必要があります

(すべてのツールループに最大ステップ数の上限を必ず設けてください――無制限のループは API の無限呼び出しやコストの際限ない増大につながります)

(実行前にすべてのツール入力引数を必ず検証してください――LLM が生成した引数は信頼できない入力です)

(ツールの実行に失敗した場合は、構造化されたエラーメッセージを必ずモデルに返してください――エラーを黙って握りつぶしたり、空の結果を返したりしてはいけません)

(ツールパラメーターの定義には必ず JSON Schema を使用してください――主要なプロバイダーはすべてこの形式を要求します)

(ツール定義はトークンコストとして必ず考慮してください――すべての API 呼び出しで各ツールのスキーマが送信されるため、説明は簡潔かつ正確にしてください)

これらのルールに従わないと、無限ループで API コストが増大したり、検証されていない入力を実行したり、モデルが復旧できないまま黙って失敗したりするエージェントが生まれます。

</critical_reminders>

コメント