AIエージェントに tool を持たせると、急に仕事が進むように見えます。けれど、実務で事故るのは「tool が足りない」場面より、tool の名前、説明、引数、戻り値が曖昧なまま増えた場面です。update_ticket が本当に外部 ticket を更新するのか、draft を作るだけなのか。query に何を入れてよいのか。失敗時に retry してよいのか。ここが曖昧だと、承認フローや監査ログを後から足しても苦しくなります。
この記事では、AIエージェントの tool schema 設計 を扱います。tool の危険度分類は AIエージェント tool risk tier 設計ガイド、成果物の返却形式は AIエージェント出力契約設計ガイド、本番前の検証は AIエージェント評価設計ガイド、MCP server 単位の審査は MCP サーバーカタログ設計ガイド に分けています。
今回の論点は、モデルに見せる tool の契約を、実行前にレビューできる形へ固定することです。
先に結論: schema は型定義ではなく操作境界である
tool schema を単なる JSON Schema として扱うと、足りません。
もちろん型は重要です。OpenAI の function calling は function tool を JSON Schema で定義します。MCP Tools も inputSchema と任意の outputSchema を持ち、structured result を検証できます。Claude の tool use でも、モデルは tool description を見て呼び出すかどうかを判断し、strict tool use では schema 一致を強められます。
ただ、AIエージェント運用で必要なのは「形が合っている」だけではありません。
私なら、tool schema を次の5つの境界として設計します。
| 境界 | 決めるもの | 曖昧だと起きること |
|---|---|---|
| intent | 何をする tool か、何をしないか | 似た tool を誤選択する |
| target | どの resource / system / scope を触るか | 依頼外の外部状態へ広がる |
| input | 必須引数、enum、制約、禁止値 | 自然文を丸投げする |
| output | 人間と後段 agent が使う戻り値 | tool result を読み違える |
| failure | 失敗、部分成功、retry 可否 | 二重実行や silent failure が起きる |
型定義はこの一部です。実務では、schema を「モデルが呼べる API」ではなく「人間が許可できる操作単位」として見た方が安定します。
tool description は短いほどよい、ではない
最初に壊れるのは tool description です。
悪い例はこうです。
{
"name": "update_ticket",
"description": "Update a ticket",
"parameters": {
"type": "object",
"properties": {
"id": { "type": "string" },
"body": { "type": "string" }
},
"required": ["id", "body"]
}
}
これは短いですが、運用には弱いです。どの ticket system か、comment なのか status 変更なのか、customer visible なのか、承認が必要なのかが分かりません。
私なら、tool description には最低限次を入れます。
{
"name": "ticket.add_internal_note",
"description": "Add an internal-only note to an existing support ticket. Does not notify the customer, change status, assign owner, or close the ticket. Use only after the ticket id is known.",
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"ticket_id": {
"type": "string",
"description": "Existing support ticket id, for example TCK-12345"
},
"note": {
"type": "string",
"minLength": 20,
"maxLength": 2000,
"description": "Internal note. Do not include secrets, raw logs, or customer personal data."
}
},
"required": ["ticket_id", "note"]
}
}
ポイントは、できることだけでなく しないこと を書くことです。add_internal_note と send_customer_reply を分ける。draft_pull_request_comment と post_pull_request_comment を分ける。read と write を同じ tool にしない。これだけで、承認フローと監査ログがかなり扱いやすくなります。
input schema は「自由記述」を減らすために使う
AIエージェント向け tool では、string だけの引数が増えがちです。
{
"query": { "type": "string" }
}
検索 tool ならこれで足りることもあります。しかし、write 系や外部送信系で自由記述が多いと、モデルの判断が tool 内部へ流れ込みます。どの対象に、どの目的で、どの権限で、どの形式の payload を送るのかが見えません。
私なら、input schema は次の順で狭めます。
| 設計項目 | 例 | 目的 |
|---|---|---|
| enum | `action: “draft" | "submit_for_review”` |
| pattern | ticket_id: "^TCK-[0-9]+$" | 対象 ID の取り違えを減らす |
| maxLength | note: maxLength 2000 | raw log 丸投げを避ける |
| additionalProperties | false | 想定外の payload を落とす |
| explicit reason | business_reason | 承認・監査とつなぐ |
OpenAI Agents SDK では Pydantic の Field で引数の制約や説明を schema と validation に反映できます。MCP Tools でも inputSchema は tool metadata の中心です。つまり、schema はドキュメントではなく、実行前の validation point として使えます。
output schema は「次に誰が読むか」で決める
tool の戻り値は、モデルが読むだけではありません。人間、別 agent、監査ログ、eval、監視基盤も読みます。
ここで全部を1つの巨大な output に入れると壊れます。
{
"success": true,
"message": "done"
}
これは小さすぎます。一方で、raw API response をそのまま返すのも危険です。不要な個人情報、secret に近い値、内部 URL、長すぎるログが context に流れます。
私なら、write 系 tool の output schema はこの程度から始めます。
{
"type": "object",
"additionalProperties": false,
"properties": {
"status": {
"type": "string",
"enum": ["drafted", "submitted", "blocked", "failed"]
},
"external_id": {
"type": "string",
"description": "Id of the created or updated external object"
},
"summary": {
"type": "string",
"maxLength": 500
},
"requires_human_review": {
"type": "boolean"
},
"retry_safe": {
"type": "boolean"
}
},
"required": ["status", "summary", "requires_human_review", "retry_safe"]
}
retry_safe は地味ですが重要です。外部 write が絡む tool は、失敗時に同じ call をもう一度投げてよいとは限りません。AIエージェントに自己修正させたいなら、失敗メッセージだけでなく、再実行してよいかを明示します。
error は例外ではなく返却契約に入れる
tool 失敗を全部 exception にすると、agent は「失敗した」ことしか見えません。
MCP の Tools 仕様では、tool execution error はモデルが自己修正に使える actionable feedback として扱えます。これは実務でも重要です。入力 validation で落ちたのか、権限不足なのか、外部 system が落ちたのか、承認待ちなのかで、次の行動は違います。
私なら、失敗を次のように分類します。
| error kind | 例 | agent に許す次アクション |
|---|---|---|
validation_error | ticket_id の形式不正 | 引数を修正して再試行 |
permission_required | write scope 不足 | 承認フローへ戻す |
not_found | 対象 ticket が存在しない | source of truth を確認 |
conflict | 既に更新済み | state reconciliation へ回す |
external_failure | API timeout | retry policy を確認 |
policy_blocked | secret を含む payload | 停止し、人間へ返す |
ここを決めずに retry だけ強くすると、外部 comment を二重投稿したり、承認されていない write を別 tool で迂回したりします。error schema は安全運用の一部です。
version と owner を schema に持たせる
tool schema は変わります。引数が増える、enum が変わる、外部 API の仕様が変わる、MCP server が新しい tool を追加する。だから、schema には version と owner を持たせた方がよいです。
tool:
name: ticket.add_internal_note
schema_version: ticket-tools@2026-07-06
owner: platform-support
risk_tier: T3-write-internal
approval:
required_when:
- note contains customer_data
- ticket is security_incident
audit:
event_name: ticket_internal_note_added
correlation_id_required: true
これは厳密な標準形式ではありません。実務用の管理メタデータです。大事なのは、tool schema を code の近くに置くだけで終わらせず、MCP catalog、承認フロー、eval、監視に同じ version を渡すことです。
AGENTS.md に置くなら短くする
AGENTS.md に巨大な schema を貼る必要はありません。人間と agent が読む場所には、原則だけ置きます。
## Tool schema rules
- read / draft / write / external action を同じ tool に混ぜない
- write tool は `reason`, `target_id`, `retry_safe` を必須にする
- customer visible な操作は draft tool と submit tool を分ける
- tool result に raw log, secret, PII を返さない
- schema を変えたら eval と approval rule も同じ PR で更新する
詳細 schema は tool 定義、MCP server、SDK 側の型、または catalog に寄せる。AGENTS.md には「どういう設計なら受け入れるか」を置く。この分担の方が運用しやすいです。
よくある失敗
1. tool 名が動詞だけ
update, send, create のような名前は危険です。対象 system と副作用を名前に入れます。ticket.add_internal_note、github.draft_pr_comment、billing.create_refund_request のように、少し長くても誤選択しにくい名前にします。
2. read と write を mode で分ける
mode: "read" | "write" のように1つの tool へ押し込むと、承認や監査が難しくなります。read tool と write tool は別にします。危険度が違うものは、schema ではなく tool を分ける方が安全です。
3. output に raw response を返す
raw response は便利ですが、agent context へ流すには重すぎます。必要な ID、summary、status、next action だけ返し、raw payload は隔離ログや監査ログへ逃がします。
4. schema 更新を eval から外す
prompt を変えた時だけ eval するのは不十分です。tool description、input schema、output schema、error 分類を変えたら、agent の行動も変わります。schema 更新は eval 対象です。
導入順序
最初から全 tool を再設計しようとすると止まります。私なら、次の順で始めます。
- write / external action tool だけ棚卸しする
- tool 名を
system.action_targetへ寄せる - description に「しないこと」を入れる
- input schema に enum、制約、
additionalProperties: falseを入れる - output schema に
status,external_id,retry_safe,requires_human_reviewを入れる - error kind を6種類程度に分ける
- schema version を eval と approval rule に渡す
これだけでも、AIエージェントの誤実行はかなり減ります。特に、MCP server や社内 API をつなぐ前にこの棚卸しをやると、便利な tool を増やしてもレビューしやすい状態を保てます。
まとめ
AIエージェントの tool schema は、単なる型定義ではありません。モデルが何を呼べるか、人間が何を承認できるか、監査ログが何を説明できるか、eval が何を落とせるかをつなぐ操作契約です。
tool risk tier は危険度を分類する表です。output contract は作業後の返却形式です。eval は期待通りの操作列かを見る gate です。tool schema はその手前で、tool そのものを誤解されにくい形へ固定します。
まずは write 系 tool だけで十分です。名前、description、input、output、error、version をそろえる。AIエージェントに tool を足す前に、この契約をレビューできる状態にしておく方が、後から監査や承認を足すよりずっと安く済みます。