
【CCA Foundations対策 / Claude API編 #8】Tool Useの基礎①——ツール定義とスキーマ設計
Anthropic Academyの「Claude APIを使用した構築」コースをもとに解説しています。
Claude APIはデフォルトでは訓練データの範囲内の知識しか持たない。「今日の株価は?」「最新の在庫数は?」のような質問に対して、正直に「リアルタイムの情報は持っていない」と返すしかない。Tool Useはこの限界を突破する仕組みで、Claudeが外部APIやデータベースに問い合わせを要求し、その結果を使って回答できるようになる。
この記事(#8)では「ツールをどう定義するか」に絞って解説する。Claudeにツールを渡してレスポンスを読む実装は次回(#9)で扱う。
この記事でわかること:
- Tool Useの全体フローと「なぜClaudeはリアルタイム情報を取得できないのか」
- ツール関数の書き方——命名・バリデーション・エラーメッセージの3原則
- JSON schemaの構造と、descriptionが選択信頼性を左右する理由
ToolParam型を使った型安全な実装
Tool Useの全体像
Tool Useのフローは「Claude ↔ アプリ」の往復で構成される:
① ユーザーの質問 + ツール定義 → Claudeに送信
② Claude が「このツールを実行してほしい」とリクエストを返す
③ アプリがツール(Python関数)を実行して結果を取得
④ 結果を Claudeに返す
⑤ Claude が結果を使って最終回答を生成
ポイントは、Claudeは直接外部APIを呼べないことにある。Claudeができるのは「どのツールを・どんな引数で呼び出してほしいか」を伝えることだけで、実際にAPIを叩くのはアプリ側のコードになる。
この往復の制御は stop_reason というフィールドで判断する(詳細は #9 で解説する)。
ツール関数を定義する
Tool Useに必要な準備の第一歩は、Python関数としてツールを実装すること。Claudeが「この情報が必要だ」と判断したとき、アプリ側でこの関数を呼び出し、結果をClaudeに返す。
ここでは「株価取得ツール」を例に使う。
3つの設計原則
① 関数名と引数名は説明的に
関数名と引数名はClaudeが読む情報でもある。f(x) より get_stock_price(ticker_symbol) の方が、Claudeが「何を渡せばいいか」を理解しやすい。
② 入力を必ずバリデーションする
不正な入力に対してはすぐにエラーを発生させる。Claude側でエラーメッセージが確認でき、引数を修正して再試行できる。
③ エラーメッセージは具体的に
「エラーが発生しました」ではなく「ticker_symbol には英字のみ使用できます(例: AAPL, GOOG)」のように書く。Claudeがエラーの内容を読んで次のアクションを判断するため、具体的なメッセージが修正を早める。
実装例
import re
def get_stock_price(ticker_symbol: str, currency: str = "USD") -> dict:
"""指定した銘柄の株価を取得する"""
if not ticker_symbol:
raise ValueError("ticker_symbol は必須です(例: AAPL)")
if not re.match(r'^[A-Z]{1,5}$', ticker_symbol.upper()):
raise ValueError(
f"ticker_symbol には大文字英字のみ使用できます(例: AAPL, GOOG)。"
f"受け取った値: {ticker_symbol!r}"
)
if currency not in ("USD", "JPY", "EUR"):
raise ValueError(
f"currency は USD / JPY / EUR のいずれかを指定してください。"
f"受け取った値: {currency!r}"
)
# 実際の実装では外部APIを呼び出す
# ここではデモ用にモックデータを返す
mock_prices = {
"AAPL": 189.50,
"GOOG": 175.30,
"MSFT": 420.10,
}
price = mock_prices.get(ticker_symbol.upper(), 100.00)
return {
"ticker": ticker_symbol.upper(),
"price": price,
"currency": currency,
}
# 正常系
get_stock_price("AAPL")
# → {"ticker": "AAPL", "price": 189.5, "currency": "USD"}
# バリデーションエラー
get_stock_price("aapl123")
# → ValueError: ticker_symbol には大文字英字のみ使用できます(例: AAPL, GOOG)。受け取った値: 'aapl123'
エラーメッセージをClaudeが読む、という前提で書くのがポイント。「受け取った値」を含めることで、Claudeが何を誤ったのかを認識して正しい引数で再試行しやすくなる。
ツールスキーマを書く
ツール関数を定義したら、次はClaudeに「このツールの使い方」を伝えるためのJSON schemaを書く。これがClaudeとアプリの「契約書」に相当する。
スキーマの構造
from anthropic.types import ToolParam
get_stock_price_schema = ToolParam({
"name": "get_stock_price",
"description": (
"指定した株式銘柄(ティッカーシンボル)の現在の株価を取得する。"
"リアルタイムの株価情報が必要なときに使用する。"
"ticker_symbol には AAPL(Apple)や GOOG(Google)のような英字のティッカーシンボルを渡す。"
"戻り値は ticker(銘柄)・price(株価)・currency(通貨)を含む辞書。"
),
"input_schema": {
"type": "object",
"properties": {
"ticker_symbol": {
"type": "string",
"description": (
"株式銘柄のティッカーシンボル(大文字英字、1〜5文字)。"
"例: AAPL(Apple)、GOOG(Google)、MSFT(Microsoft)"
),
},
"currency": {
"type": "string",
"description": "株価を表示する通貨。USD(米ドル)/ JPY(日本円)/ EUR(ユーロ)のいずれか。省略時はUSD。",
"enum": ["USD", "JPY", "EUR"],
"default": "USD",
},
},
"required": ["ticker_symbol"],
},
})
3つのフィールドの役割
| フィールド | 役割 |
|---|---|
name |
Claude がツールを参照するときの識別子。関数名と合わせる |
description |
Claudeがツールを選択する際の主要判断材料。詳しくは後述 |
input_schema |
引数の型・必須かどうか・説明を定義する JSON schema |
descriptionの重要性
description はClaudeがツールを選ぶときに読む説明文で、選択信頼性を最も左右するフィールド。
たとえば似た名前のツールが2つある場合を考える:
# ❌ 説明が曖昧なスキーマ
{
"name": "get_stock_price",
"description": "株式情報を取得する",
...
}
{
"name": "get_company_info",
"description": "企業情報を取得する",
...
}
この2つは説明が短すぎて、Claudeが「ユーザーが株価を知りたいとき」にどちらを呼ぶべきか判断しにくい。「株式情報」と「企業情報」の境界が曖昧で、誤選択が起きやすい。
# ✅ 境界を明確にした説明
{
"name": "get_stock_price",
"description": (
"指定した銘柄の現在の株価(数値)を取得する。"
"ユーザーが「〇〇の株価は?」「今の値段は?」と聞いたときに使用。"
"会社概要・事業内容・財務情報は返さない。価格のみ。"
),
...
}
{
"name": "get_company_info",
"description": (
"指定した銘柄の企業概要を取得する。"
"事業内容・設立年・従業員数・CEOなどのプロフィール情報を返す。"
"株価・時価総額などの市場データは含まない。"
),
...
}
改善のポイントは2つ:
- いつ使うか(「〜と聞かれたとき」)を書く
- 何を返さないか(境界条件)を書く
「何を返さないか」を書くことで、Claudeが誤って別のツールを選ぶリスクを減らせる。
📋 試験ガイドより
公式試験ガイドのDomain 2(Tool Design & MCP Integration)Task 2.1では、ツール記述(description)がLLMのツール選択における主要な判断材料であり、説明が短いと類似ツール間で誤選択が起きると明記されている。「いつ使うか・何を返さないか」を含む3〜4文の説明が、選択信頼性を高める設計として取り上げられている。
ToolParam 型を使う理由
スキーマを素の辞書として定義してもAPIは動作するが、ToolParam 型でラップしておくと型チェックが効いてIDEで補完が使えるようになる。実装上のメリットが大きいため、プロダクションコードでは積極的に使う。
from anthropic.types import ToolParam
# ToolParam でラップする(スキーマの内容は同じ)
schema = ToolParam({
"name": "...",
"description": "...",
"input_schema": { ... },
})
よくある誤解まとめ
| 誤解 | 実際 |
|---|---|
| Claude が直接外部APIを呼び出している | Claudeは「どのツールを呼んでほしいか」を伝えるだけ。実際にAPIを呼ぶのはアプリ側のコード |
| ツール関数のエラーメッセージは開発者だけが読む | Claudeがエラーメッセージを読んで引数を修正して再試行する。具体的なメッセージが重要 |
description は短くてよい |
descriptionはClaudeがツールを選択する主要判断材料。短すぎると類似ツールとの誤選択が起きる |
required に含めれば引数は必ず渡される |
required はClaudeへの指示だが、バリデーションはコード側でも行う必要がある |
input_schema の type だけ書けば十分 |
description フィールドを各引数に書くと、Claudeが何を渡すべきかをより正確に理解できる |
設計の判断基準
| 場面 | やりがちな選択 | 正しい選択 | 判断の根拠 |
|---|---|---|---|
| Claudeが正しいツールを選ばない | プロンプトに「〇〇ツールを使え」と追記する | descriptionを「いつ使うか・何を返さないか」を含む説明に書き直す | ツール選択はdescriptionが主判断材料。プロンプト指示より確実で根本的 |
| ツールのエラーが繰り返し発生する | デフォルト値を返して処理を続行する | 「受け取った値・正しい形式の例」を含む具体的なエラーメッセージをtool_resultで返す | Claudeがエラーを読んで引数を修正して再試行する。具体的な情報がないと同じ誤りを繰り返す |
| 似た機能のツールが2つある | 1つの大きなツールに統合する | 用途の境界を明確にしてdescriptionで「何を返さないか」を明示する | 統合するとClaudeが引数で使い分けられなくなる。分割+境界説明が正しい設計 |
まとめ
- Tool Useは「Claudeがツール呼び出しを要求 → アプリが実行 → 結果をClaudeに返す」という往復の仕組み
- ツール関数は命名・バリデーション・具体的なエラーメッセージの3原則で設計する
- JSON schemaの
descriptionはClaudeがツールを選択する主要判断材料。何を返さないか(境界条件)も書く - 似た名前・機能のツールが複数あるときほど、descriptionの差別化が選択信頼性に直結する
- 次回(#9)ではスキーマをAPIに渡してClaudeのレスポンスを読み、ツールを実行してループを回す実装を扱う