これまでのレッスンでは、Claudeは質問に答えるだけでした。ここからは「何かを調べさせる」「何かを実行させる」ところまで進みます。この仕組みをツール使用と呼びます。
ツールは契約であって実行ではない
最初に押さえておきます。Claude自身は、ツールを実行しません。Claudeがするのは「この引数で呼びたい」という要求を返すところまでです。実際にコードを動かすのは、常にあなたのアプリ側です。
通常の関数呼び出しに近い形だと考えると分かりやすくなります。関数のシグネチャを渡しておくと、Claudeは呼ぶべきだと判断したときに、引数を添えたリクエストを返します。あなたのコードがそれを受けて実行し、結果を送り返します。
ツールの定義: 3要素
ツールは次の3要素を持つJSON Schemaとして定義します。
ツールの識別子
何をするか・いつ使うか・何を返すかの説明文
引数の形。JSON Schemaで型と必須項目を指定
descriptionの書き方が、ツールが正しく呼ばれるかを左右します。あいまいだと、似た場面で違うツールを選んだり、呼ぶべき場面で呼ばなかったりします。
会議室予約システムを調べるツールを例にします。
tools = [{
"name": "check_room_availability",
"description": "会議室の予約可否を調べる。空きの真偽値と、埋まっていれば次に空く時刻を返す。",
"input_schema": {
"type": "object",
"properties": {
"room": {"type": "string", "description": "会議室名。例: 3階A会議室"},
"date": {"type": "string", "description": "YYYY-MM-DD形式"},
"start_hour": {"type": "integer"},
},
"required": ["room", "date", "start_hour"],
},
}]
往復の流れ: tool_useとtool_result
ツールを渡したリクエストにClaudeが応じるとき、stop_reason が "tool_use" になり、呼びたいツールの名前と引数を持つ tool_use ブロックが返ります。
リクエスト送信
toolsとuserメッセージを送る
tool_useで応答
Claudeがツール名と引数を返す。stop_reasonはtool_use
自分のコードで実行
実際に関数を呼び、結果を得る
tool_resultを送り返す
結果をtool_use_idと紐づけて次のリクエストへ
最終応答
Claudeが結果をもとに答える。stop_reasonはend_turn
def check_room_availability(room, date, start_hour):
return {"available": False, "next_free_hour": 15} # 本来は予約システムに問い合わせる
def run_tool(name, tool_input):
if name == "check_room_availability":
return check_room_availability(**tool_input)
raise ValueError(name)
def call(messages):
return client.messages.create(
model="claude-sonnet-5", max_tokens=1024, tools=tools, messages=messages)
messages = [{"role": "user", "content": "明日の午前10時、3階A会議室は空いてる?"}]
response = call(messages)
while response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
results = []
for b in response.content:
if b.type != "tool_use":
continue
try:
r = {"type": "tool_result", "tool_use_id": b.id, "content": str(run_tool(b.name, b.input))}
except Exception as e:
r = {"type": "tool_result", "tool_use_id": b.id, "content": f"エラー: {e}", "is_error": True}
results.append(r)
messages.append({"role": "user", "content": results})
response = call(messages)
print(next(b.text for b in response.content if b.type == "text"))
このループはstop_reasonが"tool_use"でなくなるまで続きます。「空いていなければ別の会議室も調べて」のような依頼では、呼び出しが何度か連続します。
注意
ツールの失敗はis_errorで伝える
失敗しても、結果を黙って無視してはいけません。is_error: trueを付けたtool_resultで返すと、Claudeは引数を直した再試行や利用者への説明を判断できます。握りつぶすと、見当違いの答えを出すことがあります。
複数のツールを足すとき
新しいツールを足す手順は、いつも同じです。
ツールを1つ増やす4手順
関数を実装
Pythonの普通の関数として書く
スキーマを書く
name・description・input_schemaを定義
toolsリストに追加
リクエストに渡すtoolsの配列に足す
ルーターに追加
run_toolのif/elifにツール名の分岐を足す
やってみよう
演習1: ツールを1つ定義してみる
自分の仕事でClaudeに「調べさせたい」ことを1つ選び、name・description・input_schemaの形に落とし込んでください。
演習2: 失敗したときの伝え方
そのツールが失敗する場面を1つ想定し、is_errorの中身としてClaudeに何を伝えれば、次の判断に活かせるか考えてください。
今日のまとめ
3行で振り返ります。
- ツールを実行するのはClaudeではなく、常にあなたのコード。Claudeが返すのは「呼びたい」という構造化された要求だけ
- 往復は
stop_reason == "tool_use"のあいだ繰り返すループとして実装する。結果はtool_use_idと紐づけて返す - ツールの失敗は
is_error: trueで伝える。黙って無視すると、Claudeは何が起きたか分からないまま答えてしまう
次のレッスンでは、社内文書や長い資料をもとにClaudeに答えさせる方法を扱います。
セルフチェック
1. ツール使用について正しい説明はどれですか。
2. ツール呼び出しのループを終える条件はどれですか。
3. ツールの実行が失敗したときの正しい対処はどれですか。