第2章: ツール呼び出しの正体 ─ テキスト機械が関数を呼ぶ仕掛け
Claude Code を起動して「fibonacci.py を読んで」とだけ打つと、画面にこう出る。
● Read(fibonacci.py)
⎿ 読み込み完了 (23 行)LLM は「続きを書くだけの機械」のはずだった (第1章)。
なのにここでは、まるで関数を呼んだかのように Read(fibonacci.py) という構造化された命令が現れている。
テキストを吐くだけのモデルが、どうやって “関数呼び出し” を返しているのか。 答えは、思ったよりずっと単純で、しかし設計としてはきわめて巧い。
2.1 そもそも、なぜ “テキストのまま” ではダメなのか
ハーネス (第1章) は、LLM の出力を受け取って 本当にファイルを読みに行く プログラムだ。
LLM が「fibonacci.py を読んでください」という自然言語を返してきたら、これを解釈してファイル操作を実行する必要がある。
素直に思いつくのは「自然言語のままパースする」やり方だ。
「ファイル
fibonacci.pyを読んでください」 → 動詞 “読む” を検出 → 目的語 “fibonacci.py” を抽出 →read_file("fibonacci.py")を実行
ところがこれは、すぐに破綻する。
- 言い回しが無限: 「読んで」「開いて」「見てほしい」「中身を確認」「内容を取得」…
- 引数の曖昧さ: 「設定ファイルを読んで」と言われたら、どのファイル?
- 複数同時の指示: 「
a.pyとb.pyを読んで、ついでにtests/をgrepして」 - 誤検出: 「
Read me firstという README があります」のような文中の “Read” まで拾ってしまう
自然言語パースは、揺らぎに弱すぎる。 ハーネスが LLM の意図を確実に受け取るには、形が決まった “命令の入れ物” が要る。
ここで業界が選んだ答えが、本章のテーマ ── ツール呼び出し (tool call / function calling) だ。
2.2 ツールを “カタログ” として LLM に渡す
仕組みを最小単位から組み立てよう。 まず、ハーネスは LLM に 「あなたが使えるツール一覧」 を最初に渡す。
具体的にはこんなイメージ。
{
"name": "Read",
"description": "ファイルの中身を読み取って返す",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "読みたいファイルの絶対パス" }
},
"required": ["path"]
}
}
3 つの部品だけだ。
| 部品 | 意味 |
|---|---|
| name | このツールの呼び名 (Read, Write, Bash など) |
| description | このツールが何をするかの自然言語説明。LLM が「いつ使うか」を判断する材料 |
| input_schema | 引数の形を JSON Schema で厳密に定義 |
JSON SchemaJSON の値が満たすべき形を、別の JSON で記述するための仕様。「この値はオブジェクトで、
pathという文字列フィールドを必ず持つ」のような構造制約を機械可読に書ける。Web API の業界で長く使われてきた、枯れた標準。
このカタログを システムプロンプト相当の位置 (第5章で詳述) に流し込んでおくと、LLM はそれを「自分が使える道具のリスト」として認識する。
ここがコーディングエージェント設計の急所のひとつだ。
ツールの description は、LLM への一種の “求人票” になっている。
「いつ私を呼んでください」を自然言語で説得力のある形で書けるかどうかで、エージェントの賢さが大きく変わる。
description の磨き込みは、第6章 (ツール設計) でまた戻ってくる。Skills でも description という似た仕掛け (役割は違うが) が出てくる。
2.3 LLM の出力に “tool_use ブロック” が混ざる
カタログを与えられた LLM は、頼みごとを受けてこう考える。
「
fibonacci.py を読んでと言われた。カタログを見るとReadという道具がある。description にも合致する。これを呼べばよさそうだ」
そして LLM は、普通の文章ではなく、カタログのスキーマに沿った構造化データ を出力する。 Anthropic の API ではこれが tool_use ブロック という形で返ってくる。
{
"type": "tool_use",
"id": "toolu_01ABC",
"name": "Read",
"input": { "path": "/repo/fibonacci.py" }
}
ポイントは 3 つ。
type: "tool_use"という型タグで、「これは喋りではなく道具呼び出しだ」とハーネスに伝えるnameがカタログのnameと一致するinputがinput_schemaの制約を満たしている (path が文字列で、必須項目が揃っている)
ハーネスは型タグを見て即座に分岐する。 普通のテキストなら画面に表示し、tool_use ブロックなら対応する関数を呼ぶ。
LLM は内部では「続きを書く機械」のままだ。 ただ、出力する “続き” の中に 構造化された JSON ブロック を混ぜることを学習している。
つまり LLM がしているのは 「文章を書く」と「JSON を書く」の出し分け であり、それを ハーネスが型で見分けて分岐している だけ。 魔法ではなく、出力フォーマットの約束事の話だ。
2.4 なぜ “テキスト” ではなく “構造” なのか
ここで改めて、設計判断の意味を噛みしめておく。 LLM 側もハーネス側も、わざわざ JSON という構造化フォーマットを介する。なぜか。
理由 1: パースの安定性
JSON は文法が厳密だ。{} で囲まれ、キーと値がコロンで結ばれる。パーサーはこの形をきっちり検証できる。
「Read me first というドキュメントを開いた」のような自然文と違って、誤検出の余地がほぼない。
理由 2: 引数の型保証
input_schema が「path は文字列」と宣言している。
LLM が万一おかしな値を入れようとすれば、ハーネス側でスキーマ違反として弾ける。
これは Web API のリクエストバリデーションと全く同じ思想だ。
理由 3: 複数ツールの同時呼び出し
1 回の応答で 複数の tool_use ブロックを並べて返せる (詳しくは第3章)。
[
{ "type": "tool_use", "name": "Read", "input": { "path": "a.py" } },
{ "type": "tool_use", "name": "Read", "input": { "path": "b.py" } }
]
自然言語だと「a.py と b.py を読んで」を 2 つの操作に分解するのにまた解析が要るが、構造化なら 配列の要素を順に処理するだけ。
ツール呼び出しの本質は、自然言語の “意図” と機械の “実行” の間に、揺らがない契約 (JSON Schema) を一枚挟むこと。 これにより、LLM 側は「呼びたい道具と引数」を考えることだけに集中でき、ハーネス側は「契約に沿って実行する」ことだけに集中できる。
2.5 業界横断: 名前は違うが構造は同じ
この仕組みは Anthropic 固有のものではない。主要 LLM プロバイダはほぼ全員、同じ構造を提供している。呼び名だけ違う。
| プロバイダ | 呼び名 | スキーマ |
|---|---|---|
| Anthropic (Claude) | tool use | JSON Schema |
| OpenAI (GPT) | function calling | JSON Schema |
| Google (Gemini) | function calling | OpenAPI スキーマ (実体は JSON Schema 系) |
最初に “function calling” という名前を市場に広めたのは 2023 年の OpenAI で、業界はその語感のまま定着した。 本書では Claude Code の文脈に合わせて主に tool use / ツール呼び出し と呼ぶが、他社ドキュメントの “function calling” を見たら同じものだと思ってよい。
実務メモモデルによって “ツールを呼ぶのが上手い・下手” の差がある。description が曖昧でも適切に呼んでくれるモデルもあれば、明示的に「次は必ず Read を使え」と書かないと自然言語で済ませてしまうモデルもある。Claude Code が Claude 系モデルに最適化されているのは、この呼び出し能力の高さに依るところが大きい。
2.6 一往復の全体像 ── 図で押さえる
ここまでの流れを一枚の図にまとめる。 LLM とハーネスの間で 1 回の道具呼び出し が完結するまでに、何が往復しているのか。
ステップに分解するとこうなる。
- 準備: ハーネスがツールのカタログ (name + description + JSON Schema) を LLM に渡しておく
- 判断: LLM がユーザーの依頼を読み、
tool_useブロックを返す - 分岐: ハーネスが
typeとnameを見て、対応する実関数を呼ぶ - 観測: 実行結果を 続きの入力 として LLM に戻す (下記 tool_result)
tool_result
tool_useと対になるブロック。ハーネスがツールを実行した結果 (ファイルの中身、コマンド出力、エラーメッセージなど) を LLM に戻すための型タグ付き入れ物。tool_useのidと紐付けて返すことで、LLM は「どの呼び出しに対する結果か」を識別できる。
ここで返した結果を見て LLM が次の手を考える ── つまり このサイクルが繰り返される ことで、第1章で見た “動ける” 状態が生まれる。 そのサイクル自体は、次の第3章 (think → act → observe) で正面から扱う。
2.7 章を貫く問いへの答え
冒頭の問いに戻ろう。
テキストを吐くだけのモデルが、どうやって “関数呼び出し” を返しているのか。
答えはこうだ。
- LLM は依然として テキスト (トークン列) を生成しているだけ
- ただし、その出力の一部に JSON Schema に従った構造化ブロック を埋め込めるよう学習されている
- ハーネスは型タグでブロックを見分け、name で実関数に分岐し、結果を 次の入力 として戻す
- JSON Schema による契約 が、自然言語の揺らぎから機械の実行を守っている
つまり「関数呼び出し」と見えていたものは、LLM が JSON を書き、ハーネスが JSON をパースして実行する という、地に足のついた約束事の積み重ねだった。
ここまでくると、「LLM がツールを使いこなす」という言い方が、少し違って聞こえてくるはずだ。 LLM 側は 「JSON を上手に書ける」 だけ。ツールを実際に動かしているのはハーネスのほう。
この役割分担を頭に置いておくと、第6章で「ツールの良し悪し」を語るとき、なぜ description の言葉選びがそんなに大事なのかが自然に腑に落ちる。
この章の振り返り
- 自然言語パースは揺らぎに弱すぎる。だから LLM とハーネスの間に 構造化された “契約” を挟む
- ツールは name + description + JSON Schema の 3 部品で定義する
- LLM はカタログを見て、出力の中に
tool_useブロック という構造化データを混ぜて返す - ハーネスは型タグでブロックを見分け、対応する関数を呼び、結果を続きとして戻す
- JSON を介す理由は パース安定性・型保証・複数同時呼び出し の 3 点
- Anthropic は tool use、OpenAI/Google は function calling ── 呼び名が違うだけで本質は同じ
この章で読めるようになるツイート / ブログ
- 「Claude のツール description を 1 行書き換えたら、呼び出し率が劇的に上がった」 → description は LLM への “求人票”。LLM はそれを読んで使うかを判断していると分かる
- 「GPT-4 の function calling と Claude の tool use って何が違うの? 結局同じ?」 → 名前と細部のフォーマットが違うだけで、JSON Schema を契約にする構造は同じ、と読める
- 「並列ツール呼び出しに対応したらエージェントが2倍速くなった」 → 1 応答に複数の tool_use ブロックを並べる仕組みのこと。第3章で詳述される話に直結する
次章では、ここで見た 一往復 を 何往復も繰り返すループ に拡張する。 LLM が考え、ツールを呼び、結果を観測し、また考える ── エージェント本体の心臓部、think → act → observe サイクルの解剖に進もう。