🐋

Anthropicに学ぶ:AIエージェント向けツール設計の5原則

に公開

これはなに?

Xでも絶賛しましたが、Anthropicが発表した "Writing effective tools for agents — with agents" という記事が素晴らしい内容でした。

その中でも特に「Principles for writing effective tools (効果的なツールを作成するための原則)」のセクションが実践的な内容だったので、AIエージェント向けツールを実装するときの社内ガイドラインとしても使えるように抜き出して簡潔にまとめてみました。

以下の5つの原則としてまとめてあります。

  1. AIの特性に合わせたツール設計をしよう
  2. ツール名を体系的に整理しよう
  3. AIが理解しやすい情報を返そう
  4. トークン効率を最適化しよう
  5. ツール説明文のPromptingをしよう

かなり意訳しているので間違いと思われるところがあれば教えてください🙏

https://www.anthropic.com/engineering/writing-tools-for-agents

(元記事も読んでいただきたいのですが、日本語訳として出ている記事は機械翻訳でひどい出来のものもあるので、元の記事をPLaMo翻訳とかで翻訳して読むのをお勧めします。)

1. AIの特性に合わせたツール設計をしよう

設計の指針

  • AIエージェントはツールの名前・説明・返り値から使い方を推測して行動を選ぶことができるため、従来の決定論的プログラムとは設計を変えよう。
  • コンテキスト制限を考慮し、複数の操作を統合したタスク単位でツールを設計しよう。(人間が「会議を設定する」を1つのタスクと認識するように)
  • 既存APIの単純なラップは避け、AIがタスクを効率的に完遂できる粒度でツールを提供しよう。
  • ツールは多ければ良いわけではなく、少数の高品質なツールセットを慎重に選定しよう。
  • 人間が住所録で探すときに目次を使うように、AIも必要な情報に効率よくアクセスできる設計にしよう。

よくない例

  • list_contacts: AIが全件を読む必要がありトークンを無駄に消費する
  • list_users, list_events, create_event: 別々のツールに順番にアクセスするのは中間ステップでのトークン消費に無駄が多い
  • get_customer_by_id, list_transactions, list_notes: 情報取得が断片的で複数回のツールアクセスが必要
  • read_logs: list_contactsと同様にログファイル全体を読み込むのは無駄が多い

良い例

  • search_contacts または message_contact: 必要な連絡先だけを検索して返す、または直接メッセージを送る
  • schedule_event: 参加者の空き時間確認から予約まで1つのツールで完結
  • get_customer_context: 顧客の最近の取引、メモ、関連情報を一括取得
  • search_logs: 関連するログ行と周辺コンテキストのみを返す

詳細な説明

LLMはコンテキストに厳しい制限があり、動作原理も従来のソフトウェアとは根本的に異なります。ツールの説明文自体もコンテキストを消費するため、重複や曖昧なツールが多いとコンテキストを圧迫し精度劣化の原因となってしまいます。例えば人間が住所録を検索する時に全ページを読まず目次を使って該当ページに飛ぶように、AIも効率的なアクセス方法が必要です。

複数ツールを利用して目的を達成しようとすると中間出力がコンテキストに蓄積されてしまい、本来のタスクに使える容量が減少します。ツールが多すぎたり機能が重複していると、AIは効率的な戦略を見つけづらく、適切な選択ができなくなります。

「会議を設定する」というタスクは、人間にとっては1つのまとまった作業です。これを「参加者リストの取得」「イベント一覧の取得」「イベント作成」という3つの別々のツールに分けるのではなく、1つのschedule_eventツールとして提供することで、AIも人間と同じ粒度でタスクを処理でき、かつ中間出力によるコンテキスト消費を避けられます。各ツールは明瞭かつ独自の目的を持つべきで、慎重に選定されたツールセットが大きな成果につながります。

2. ツール名を体系的に整理しよう

設計の指針

  • AIが将来的に数百のツール (他の開発者が作ったものを含む) にアクセスすることを前提に命名しよう
  • Namespacing (共通接頭辞でのグループ化) により、多数のツール間の境界を明確にしよう。
  • 機能の重複や目的の曖昧さを避け、AIが適切なツールを確実に選択できるようにしよう。
  • サービス・リソース・操作の階層を反映した体系的な命名規則を採用しよう
  • 同義語の乱立を避け、チーム全体で一貫した語彙を使用しよう

よくない例

  • search, get_data, process: 汎用的すぎて何をするツールかわからない
  • find_user, search_person, get_employee: 同義語が乱立していて違いが分かりづらい
  • 体系化されていないランダムな命名: 論外

良い例

  • サービス別に分ける: asana_search, jira_search, slack_search
  • リソース別で分ける: asana_projects_searchasana_users_searchasana_tasks_search
  • 階層的な命名にする: gmail_messages_searchgmail_messages_sendgmail_messages_delete

詳細な説明

適切な命名規則を採用することで、AIは関連ツールをグループとして認識し、タスクに応じて適切なグループから選択できます。他の開発者が作ったツールとの名前の重複をできるだけ防ぐことで、無駄な混乱を避けられます。

ツールの名前を見ただけで違いがわかりやすいと、間違ったツールを呼ぶ、間違ったパラメータで呼ぶ、必要なツールを呼ばない、ツール応答を誤って処理するなどのミスが減少します。また、タスクの自然な分割を反映した名前により、ツール数とツール説明文の量を削減でき、AIのコンテキストへの負荷を軽減できます。

接頭辞型 (asana_search_projects) と接尾辞型(search_asana_projects) のどちらがいいかはモデルの種類によって異なります。チームで評価した上で最適な命名規則を選びましょう。MCPが自動的に命名規則を決定する場合もあるので、その仕様も確認しておきましょう。

3. AIが理解しやすい情報を返そう

設計の指針

  • AIには本質的で有用な情報のみを返し、意味の分かりづらい情報 (uuid, 256px_image_url, mime_type など) はなるべく使わないようにしよう。
  • 自然言語の名前や用語を優先し、AIが正確に処理できるようにしよう。
  • response_format パラメータを実装し、用途に応じてレスポンスの詳細度を切り替え可能にしよう。
  • フィールド名はname, image_url, file_typeのように意味が明確な形にしよう
  • 後続処理で必要なIDと人間が理解しやすい情報を両立させよう

よくない例 (conciseモードでは避ける)

  • 技術的な内部フィールド名: uuid, 256px_image_url, mime_type, timestamp_ms
  • ランダムな英数字: a7b9c2d4-e5f6-4321-8765-432156789abc
  • 診断情報を常に表示: status_code: 200, cache_hit: true

良い例

二段構えの設計 (response_format enumパラメータの実装)

enum ResponseFormat {
  DETAILED = "detailed",
  CONCISE = "concise"
}
  • conciseモード: 人間が読みやすいような情報のみを提供
    • スレッド内容、作成者名、作成日時など
    • 機械的なIDや診断情報は省略
  • detailedモード: 次のツールコールなども考慮して機械的なIDや診断情報も提供
    • thread_ts, channel_id, user_id など後続処理に必要なID
    • 人間が読みやすいラベルと機械的なIDのペアを両方返す

詳細な説明

LLMは自然言語での学習をベースとしているため、人間が読みやすい形式の方が正確に処理できます。Anthropicの実例では、恣意的な英数字のUUIDを意味のある言語 (または0始まりのID) に変更するだけで、Claudeの検索タスクの精度が大幅に向上し、ハルシネーションが減少したとのことです。また、内部的に行ったSlack連携ツールの実装例では、conciseモードとdetailedモードの使い分けでトークン使用量を約65%削減 (206→72トークン) できたと報告されています。

ただし、後続のツール呼び出しに内部IDが必要な場合があるため (例: search_user(name='jane')send_message(id=12345)) 、バランスを考慮して適切なレスポンスを構築しましょう。汎用性を追求するのではなく、タスクに直接関連する情報を優先し、エージェントの次のアクションや応答に直接役立つフィールド (name, image_url, file_typeなど) を選びましょう。

ツール応答の形式(XML、JSON、Markdownなど)も性能に影響します。LLMは学習データと同じ形式でより良い性能を発揮するため、最適な形式はタスクとエージェントによって大きく異なります。チームで評価を行った上で最適な形式を選びましょう。

4. トークン効率を最適化しよう

設計の指針

  • コンテキストの質と量の両方を最適化し、大量のコンテキストを消費する可能性のあるツールには必ず制限機能を実装しよう。
  • ページネーション、範囲選択、フィルタリング、切り詰め (truncate) を適切なデフォルト値と共に実装しよう。
  • 切り詰めた場合は必ず明示し、残りの部分の取得方法が簡単にわかるようにしよう。
  • エラーメッセージを詳細に書くことでAIが正しい使い方を簡単に理解できるようにしよう
  • デフォルト値は保守的に設定し、エージェントが必要に応じて追加取得するよう誘導しよう。

よくない例

  • 全データを一度に返す: 検索結果1000件をすべて返す
  • 分かりづらいエラーメッセージ: Error: Invalid format, Error code: 400, 単なるStack Trace
  • 切り詰めの説明なし: データを途中で切ったことを通知しない

良い例

ページングAPIの実装

{
  "items": [...],
  "has_more": true,
  "next_cursor": "eyJwYWdlIjozfQ==",
  "approx_total": 150,
  "truncated": false
}

具体的な修正方法を示すエラーメッセージ (structuredContentとisErrorの併用)

{
  "isError": true,
  "content": [
    {"type": "text", "text": "日付の形式が不正です。YYYY-MM-DD を使用してください。"}
  ],
  "structuredContent": {
    "error_code": "INVALID_DATE",
    "hint": "start_date/end_date を修正してください",
    "example": {"start_date": "2025-09-01", "end_date": "2025-09-13"}
  }
}

切り詰めの明示

表示件数が上限の100件に達しました。より具体的なフィルタを使用するか、
pageパラメータ (現在: page=1) を使用して続きを取得してください。
例: {"page": 2, "filter": "status:active"}

詳細な説明

LLMにとってコンテキストは貴重なリソースなため、無駄遣いするとタスク完了に必要な情報を処理できなくなります。適切な制限を加えることで、AIは自動的に効率的な方法を学習します。(例:あいまいな検索ではなく小さくスコープを絞った複数の検索を行う、など)

Claude Codeではツール応答をデフォルトで25,000トークンに制限することで実用的なバランスを実現しているとのことです。デフォルト値は保守的に設定しましょう。具体的な件数は用途によりますが、エージェントが必要に応じて追加取得するようにするのが良いでしょう。

エラーメッセージを工夫することで、AIが正しい使い方を簡単に理解できるようにしましょう。レスポンスを切り詰めた時は、次のコンテンツを取得する方法を明示しておくとAIエージェントがより良い行動をとることができます。エラーの場合は、正しくフォーマットされたツール入力の例を提供したり、フィルタやページネーションを使用するようAIエージェントを誘導したりして、より効率的なツールの使用を促しましょう。​​​​​​​​​​​​​​​​

5. ツール説明文のPromptingをしよう

(Prompting = Prompt Engineering)

設計の指針

  • ツール説明文はAIがツールを理解する唯一の手がかりなので、ツール説明文のPromptingは、ツールを改善する最も効果的な方法の1つである。
  • 新たにチームに加わったメンバーに説明するように、暗黙的に前提としている文脈 (特殊なクエリ形式、ニッチな専門用語の定義、基盤となるリソース間の関係性など) を記述しよう。
  • パラメータ名に型情報を含め (user_id、user_emailなど)、曖昧さを徹底的に排除しよう。
  • 日付形式、単位、取り得る値の範囲を例示付きで明記し、関連ツールとの連携方法も含めよう。
  • 破壊的操作には明確な警告を含め、dry_runrequires_confirmationパラメータの実装を検討しよう。

よくない例と良い例の対比

パラメータ名

  • 悪い例: user (IDなのか名前なのかメールアドレスなのか不明)
  • 良い例: user_id (整数), user_email (文字列)

説明文

  • 悪い例: 「データを取得します」
  • 良い例: 「指定された顧客IDの過去30日間の購入履歴を取得します。結果は新しい順にソートされます」

日付形式

  • 悪い例: 説明なし
  • 良い例: date_format: 'YYYY-MM-DD' (例: 2024-01-15)

関連性の説明

  • 良い例: 「project_idasana_projects_searchで取得できます」

詳細な説明

ツール説明文のPromptingは、ツールを改善する最も効果的な方法の1つです。新たにチームに加わったメンバーに説明するように、説明文を丁寧かつ詳細に書きましょう。

曖昧な説明文はエラーの原因となり、タスク完了率を大きく低下させます。一方で、わずかな改善でも劇的な性能向上につながることがあるため、決して軽視してはいけません。Claude Sonnet 3.5がSWE-bench Verifiedで最高性能を達成できたのは、ツール説明文の改善によってエラー率が劇的に削減できたことが一つの要因だったとのことです。

実際の運用で発見された問題を説明文の改善で解決した例もあるようです。Web検索ツールでは、Claudeが検索クエリに不要な「2025」を追加してしまい、検索結果にバイアスがかかって性能が低下する問題があったとのこと。この問題は、ツール説明文を適切に修正することでClaudeの挙動を正しい方向に導き、解決することができたそうです。

破壊的操作 (削除・送信・決済など) を行うツールには、MCPの仕様に従って明確な警告を含め、dry_runrequires_confirmationといったパラメータの実装を検討しましょう。

まとめ

AIエージェント向けの効果的なツールを構築するには、従来の「決まった手順を確実に実行する」ソフトウェア開発から、「文脈から推測して柔軟に動作する」エージェントの特性を考慮した開発へと、考え方を転換する必要があります。

効果的なツールは、意図的かつ明確に定義され、AIエージェントのコンテキストを慎重に使用し、多様なワークフローで組み合わせることができ、AIエージェントが直感的に実世界のタスクを解決できるようにすると良いでしょう。

今後もAIエージェントの能力は向上し続け、より複雑なタスクをこなせるようになっていくはずです。MCPプロトコルのアップデートやLLM自体の性能向上など、技術は常に進化していきます。だからこそ、実際にツールを使ってみて問題点を見つけ、継続的に改善していくことが重要です。このようなアプローチを続けることで、AIエージェントの進化に合わせてツールも一緒に成長させることができるでしょう。

Discussion