0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントの「ツール設計」パターン5選 — LLMが"使える"ツールと"使えない"ツールの違い

0
Posted at

はじめに — 30個のツールを渡して、何個使われたか

AIエージェントにツールを30個渡しました。

で、LLMがちゃんと使ってくれたの、何個だと思います?

...正直に言うと、 半分くらい でした。

残りの半分は「存在を無視される」か「間違ったタイミングで呼ばれる」か「引数が全然違う」か。とにかく、思ったように動かない。

最初は「LLMの性能の問題かな」と思ってた。でも、ツール定義の 書き方 を変えただけで、劇的に変わったんです。

この記事では、僕がAIエージェントを半年以上運用する中で見つけた ツール設計の5つのパターン を共有します。どれも「今日から1つ書き直すだけ」で効果が出るものばかりです。

前提: Function Callingとは

LLM(ChatGPT、Claude、Gemini等)に「こういうツールが使えますよ」と定義を渡すと、必要な場面でLLMがツールを選んで引数を生成してくれる仕組みです。ツール定義は基本的にJSON Schema形式で書きます。この記事では特定のSDKやフレームワークに依存しない、 JSON Schema形式 で統一して解説します。


パターン1: Naming — 動詞_名詞で意図を伝える

これ、一番簡単なのに一番効果がある。

ツール名って、LLMにとっては 名刺 みたいなものなんです。初対面の人が名刺を見て「この人は何をしてくれる人か」を判断するのと同じで、LLMはツール名を見て「このツールは何をしてくれるか」を判断する。

Before(ダメな例)

{
  "name": "process_data",
  "description": "データを処理する"
}

process_data って...何を処理するんですかね。人間でもわからない。LLMにわかるわけがない。

After(改善例)

{
  "name": "extract_csv_rows",
  "description": "CSVファイルから条件に合う行を抽出する"
}

動詞_名詞 のフォーマットにするだけで、ツールの目的が一目瞭然になる。

命名のルール

❌ 曖昧な名前 ✅ 明確な名前
handle_data parse_json_response
do_task create_github_issue
process resize_image_to_thumbnail
run execute_sql_query
get_info fetch_user_profile_by_id

ポイントは3つ。

  1. 動詞で始めるsearch_, create_, delete_, list_, update_
  2. 対象を明記する — 何を操作するのかを名前に含める
  3. 曖昧語を避けるprocess, handle, manage, do は禁止

僕の環境では、ツール名を具体的にしただけで、 ツール選択の正確性が体感で倍近く 上がりました。たった名前を変えるだけなのに。


パターン2: Parameter — 5個以下ルールと型の力

パラメータが10個あるツール、想像してみてください。人間でも混乱しますよね。LLMも同じです。

Before(パラメータ過多)

{
  "name": "search_articles",
  "parameters": {
    "type": "object",
    "properties": {
      "query": { "type": "string" },
      "category": { "type": "string" },
      "author": { "type": "string" },
      "date_from": { "type": "string" },
      "date_to": { "type": "string" },
      "sort_by": { "type": "string" },
      "sort_order": { "type": "string" },
      "limit": { "type": "integer" },
      "offset": { "type": "integer" },
      "include_drafts": { "type": "boolean" }
    },
    "required": ["query", "category", "author", "date_from", "date_to",
                  "sort_by", "sort_order", "limit", "offset", "include_drafts"]
  }
}

10個のパラメータが 全部required 。これ、LLMは高確率でどれかを間違えます。

After(最小限のパラメータ)

{
  "name": "search_articles",
  "parameters": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "検索キーワード"
      },
      "category": {
        "type": "string",
        "enum": ["tech", "business", "lifestyle"],
        "description": "記事カテゴリ(省略時: 全カテゴリ)"
      },
      "limit": {
        "type": "integer",
        "description": "取得件数(省略時: 10件)",
        "default": 10
      }
    },
    "required": ["query"]
  }
}

必須は1個だけ 。残りはOptionalでデフォルト値を持たせる。

なぜ5個以下なのか

人間のワーキングメモリは「7±2」と言われますが、LLMのパラメータ生成精度も似たような傾向がある。僕の運用では、 パラメータが5個を超えると誤りが急増 しました。特にrequiredが多いほど顕著です。

もう一つ大事なのが enum型 の活用。

"format": {
  "type": "string",
  "enum": ["json", "csv", "text"],
  "description": "出力形式"
}

自由入力の string より、 enum で選択肢を制約した方がLLMは 100%正しく指定 してくれます。これは選択肢が明確だからですね。「何でも入れていいよ」より「この3つから選んで」の方が迷わない。人間と同じです。

パラメータ設計のルール

  1. requiredは最小限に — 本当に必須なものだけ
  2. Optionalにはdefault値を設定 — 省略時の挙動を明確に
  3. 自由入力よりenumを使う — 選択肢が有限なら制約する
  4. 各パラメータにdescriptionを書く — 名前だけでは不十分
  5. 5個を目安に — 超える場合はツール分割を検討

パターン3: Description — LLMが"読む"仕様書を書く

ここが一番見落とされがちで、一番効果が大きいパターンかもしれない。

ツールの description って、多くの人が1行で済ませてる。「ファイルを読む」「データを取得する」とか。でも、LLMにとってdescriptionは ツールを選ぶかどうかの最終判断材料 なんです。

Before(雑なDescription)

{
  "name": "read_file",
  "description": "ファイルを読む"
}

「ファイルを読む」。...で? どんなファイル? いつ使う? 何が返ってくる?

After(構造化されたDescription)

{
  "name": "read_file",
  "description": "指定パスのテキストファイルの内容を読み取って返す。UTF-8エンコーディングのテキストファイル(.txt, .md, .json, .csv等)に対応。バイナリファイル(画像、動画等)には使用しない。Use when: ユーザーがファイルの中身を確認したい時、設定ファイルの内容を取得したい時。Don't use when: 画像ファイルの場合(→ analyze_image を使う)、ディレクトリ一覧が欲しい場合(→ list_files を使う)。"
}

長い? そう、長くていいんです。

descriptionは人間が読むドキュメントじゃなくて、 LLMが読む仕様書 。必要な情報は全部書く。

Description テンプレート

僕が実際に使っているフォーマットはこう。

{目的を1文で}。{対応する入力の種類}。{出力の形式}。
Use when: {使うべき場面を具体的に}。
Don't use when: {使うべきでない場面と、代替ツールへの誘導}。

この Use when / Don't use when のフォーマットが本当に効きます。特にツールが10個以上ある環境では、LLMが「このツールとあのツール、どっちを使うべき?」と迷う場面が多い。そこで「この場面では使わないでください。代わりに○○を使ってください」と明示してあげると、ツール選択の精度が格段に上がる。

実例: 類似ツールの使い分け

[
  {
    "name": "web_search",
    "description": "Webを検索してAI合成された回答と引用元URLを返す。最新ニュースやリアルタイム情報の取得に使用。Use when: 最新情報が必要な時、事実確認をしたい時。Don't use when: 特定URLの中身を読みたい場合(→ web_fetch を使う)。"
  },
  {
    "name": "web_fetch",
    "description": "指定URLのWebページを取得してMarkdown形式で返す。特定ページの内容を詳しく読みたい時に使用。Use when: URLが既知で、そのページの内容を読みたい時。Don't use when: 検索クエリから情報を探したい場合(→ web_search を使う)。"
  }
]

web_searchweb_fetch 。名前だけだと似てるけど、descriptionでの 相互参照 を入れることで、LLMは迷わずに正しい方を選べるようになる。


パターン4: Error Response — 次のアクションを導くエラー設計

エラーが起きた時、LLMに何を返していますか?

Before(情報ゼロのエラー)

{
  "error": "failed"
}

これだと、LLMは「失敗した」ことしかわからない。で、同じツールを同じ引数でもう一度呼ぶ。当然また失敗する。無限ループの始まりです。

After(次のアクションを導くエラー)

{
  "error": "file_not_found",
  "message": "パス '/data/report.csv' にファイルが見つかりません",
  "suggestion": "list_files ツールで '/data/' ディレクトリの中身を確認してください",
  "available_actions": ["list_files", "create_file"]
}

ポイントは3つ。

  1. エラーの種類を明示file_not_found, permission_denied, invalid_format
  2. 何が起きたかを人間語で説明 — LLMはこの文を読んで状況を理解する
  3. 次に何をすべきかを提案suggestion フィールドで具体的なアクションを示す

特に suggestion フィールドの威力がすごい。LLMはエラーメッセージを読んで次のアクションを決めるので、 「次はこのツールを使ってください」と教えてあげる だけで、自律的にリカバリできるようになる。

エラーレスポンスのテンプレート

type ToolErrorResponse = {
  error: string;          // エラーコード(snake_case)
  message: string;        // 人間語での説明
  suggestion?: string;    // 次にすべきアクション
  available_actions?: string[];  // 使えるツール名の候補
  context?: Record<string, unknown>;  // デバッグ用の追加情報
};

これを統一するだけで、 LLMのエラー時の自律復旧率 が大きく変わります。僕の環境では、エラー後に正しいリカバリアクションを取れる確率が体感で5割から8割くらいに上がりました。

よくあるエラーパターンと suggestion の例

エラー suggestion
file_not_found list_files ツールでディレクトリの中身を確認してください
permission_denied このファイルは読み取り専用です。別のパスに書き出してください
rate_limit_exceeded 30秒後に再試行してください
invalid_parameter format は "json", "csv", "text" のいずれかを指定してください
empty_result 検索条件を変えて再検索してください。キーワードを短くすると見つかりやすいです

エラーメッセージは「怒る」のではなく、 「導く」 もの。「ダメです」ではなく「こうしてみてください」。LLMに限らず、良いAPIはそういう設計になっている。


パターン5: Negative Examples — 使わない場面を明記する

最後のパターンは、地味だけど本当に効くやつ。

ツールが5個くらいなら問題ないんですが、10個、20個、30個と増えていくと、 似たようなツール同士の誤選択 が増えてくる。

例えば、ファイル操作に関するツールが3つあるとする。

  • read_file — ファイルを読む
  • write_file — ファイルに書く
  • edit_file — ファイルの一部を編集する

「ファイルの内容を変えたい」時に、LLMが write_file(全上書き)を選ぶか edit_file(部分編集)を選ぶか。これ、descriptionだけでは判断しきれないことがある。

Negative Examples のフォーマット

{
  "name": "write_file",
  "description": "指定パスにファイルを新規作成または上書きする。ファイルが存在しない場合は新規作成、存在する場合は全内容を上書き。Use when: 新しいファイルを作成する時、ファイルの全内容を置き換えたい時。Don't use when: 既存ファイルの一部だけを修正したい場合(→ edit_file を使う。上書きするとdiffが見えなくなる)。"
}
{
  "name": "edit_file",
  "description": "既存ファイルの特定箇所を検索して置換する。oldTextに完全一致する箇所をnewTextで置き換える。Use when: ファイルの一部を修正したい時、関数名を変更したい時、バグを1行修正したい時。Don't use when: ファイルを新規作成したい場合(→ write_file を使う)。ファイルの大部分を書き換えたい場合(→ write_file で全体を書き直す方が効率的)。"
}

Don't use when で「なぜ使わないか」の理由まで書く。「上書きするとdiffが見えなくなる」「全体を書き直す方が効率的」といった理由があると、LLMは文脈に応じて判断できる。

実運用での効果

僕がAIエージェントのスキルシステムを設計した時、各スキルに Negative Examples を導入しました。

description: Remotionで動画を作成する
Use when: PR動画、プロモーション動画、一般的な解説動画
Don't use when: プログラミング・技術コード解説動画
  → tech-guide-remotion を使う
Negative examples:
  - ❌「Pythonの解説動画を作って」→ tech-guide-remotion
  - ❌「git コマンドの使い方動画」→ tech-guide-remotion

これを入れる前は、技術解説動画の依頼が一般動画スキルに誤ルーティングされることが頻繁にありました。Negative Examplesを追加してからは、 誤発火がほぼゼロ になった。

重要なのは、「使わない場面」だけじゃなくて 「代わりに何を使うか」 まで書くこと。LLMは「これは違う」と言われたら「じゃあ何を使えばいいの?」と探し始める。その時に答えが書いてあれば、迷わない。


まとめ — ツール設計は「LLMへの思いやり」

5つのパターンをまとめます。

# パターン 一言で 即効性
1 Naming 動詞_名詞で名前をつける ⭐⭐⭐ 5分で改善可
2 Parameter 必須は最小限、enumで制約 ⭐⭐⭐ 5分で改善可
3 Description Use when / Don't use when を書く ⭐⭐⭐ 10分で改善可
4 Error Response suggestion で次のアクションを導く ⭐⭐ 実装変更あり
5 Negative Examples 使わない場面と代替を明記 ⭐⭐⭐ 10分で改善可

全部やる必要はない。 今日、1つだけツール定義を書き直してみてください

結局のところ、ツール設計って LLMへの思いやり なんだと思う。

「この名前でわかるかな」「このパラメータ、多すぎないかな」「エラーの時、次に何すればいいか伝わるかな」

...これ、人間同士のコミュニケーションと同じですよね。相手の立場に立って、わかりやすく情報を伝える。API設計の基本であり、ツール設計の本質でもある。

ツール定義を1つ書き直す。たったそれだけで、エージェント全体の振る舞いが変わる。そしてエージェントの精度が上がると、開発がどんどん楽しくなる。

...なんかそういうポジティブなループに入れたら、最高だなと思います。

補足: トークン消費について

ツールの description を充実させるとトークン消費が増えるのでは?という懸念はもっともです。確かに増えます。ただ、description を雑にして ツール選択ミス → リトライ → また失敗 のループに入る方がトータルのトークン消費は大きい。「一発で正しいツールを選ぶ」ための投資と考えると、description の充実はコスト削減にもなります。

ツール数が非常に多い場合(50個以上等)は、全ツールを毎回渡すのではなく、文脈に応じて 必要なツールだけを動的に選択して渡す 設計(Lazy Loading)も検討する価値があります。この辺りは別の記事で詳しく書こうかなと思っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?