第5章: function calling の正体 ── JSON Schema が支える契約
Claude Code、Cursor、Codex CLI、Aider ── どのプロダクトでも、起動して「fibonacci.py を読んで」と頼むと、画面にはほぼ同じものが現れる。
● Read(fibonacci.py)
⎿ 23 行を読み込みましたLLM は「続きを書くだけの機械」だった (第1章)。 「考える・動く・観る」のうち、動く の中身が tool 呼び出しだった (第3章)。
しかしここに、一段下の謎が残っている。
テキストを吐くだけのモデルが、なぜ Read(fibonacci.py) のような構造化された命令を返せるのか。
答えは ── どのプロダクトの中を覗いても ── 同じひとつの仕掛けに行き着く。 JSON Schema による「契約」 だ。
5.1 そもそも、なぜ “テキストのまま” ではダメなのか
ハーネス (第2章) は、LLM の出力を受け取って 本当にファイルを読みに行く プログラムだ。
LLM が「fibonacci.py を読んでください」という日本語を返してきたら、ハーネスはこれを解釈して実際の関数を呼ばないといけない。
素直に思いつくのは「自然言語のままパースする」やり方だろう。
「ファイル
fibonacci.pyを読んでください」 → 動詞「読む」を検出 → 目的語「fibonacci.py」を抽出 →read_file("fibonacci.py")を実行
ところがこのやり方は、すぐに破綻する。
- 言い回しが無限: 「読んで」「開いて」「見てほしい」「中身を確認」「内容を取得」…
- 引数の曖昧さ: 「設定ファイルを読んで」と言われたら、どのファイル?
- 複数同時の指示: 「
a.pyとb.pyを読んで、ついでにtests/をgrepして」 - 誤検出: 「
Read me firstという README があります」のような文中の “Read” まで拾ってしまう
自然言語のパースは、揺らぎに弱すぎる。 LLM の意図を確実にハーネスへ届けるには、形が決まった「命令の入れ物」 が要る。
ここで業界が選んだ答えが ── 名前はプロダクトによって違うが ── 本章のテーマ function calling / tool use だ。
5.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 の業界で長く使われてきた、枯れた標準。
このカタログを システムプロンプト相当の位置 (第7章で詳述) に流し込んでおくと、LLM はそれを「自分が使える道具のリスト」として認識する。
ここがエージェント設計の急所のひとつだ。 description は LLM への「求人票」 になっている。 「いつ私を呼んでください」を説得力のある言葉で書けるかどうかで、ツールの呼ばれ方が大きく変わる。 description の磨き込みは、次の第6章 (道具設計) で正面から扱う。
5.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 を書く」の出し分け であり、それを ハーネスが型で見分けて分岐している だけ。 魔法ではなく、出力フォーマットの約束事の話だ。
5.4 なぜ “テキスト” ではなく “構造” なのか
ここで改めて、JSON という構造を介する理由を噛みしめておく。 LLM 側もハーネス側もコストを払って、わざわざ JSON という構造化フォーマットに合わせる。なぜか。
理由 1: パースの安定性
JSON は文法が厳密だ。{} で囲まれ、キーと値がコロンで結ばれる。パーサーはこの形をきっちり検証できる。
「Read me first というドキュメントを開いた」のような自然文と違って、誤検出の余地がほぼない。
理由 2: 引数の型保証
input_schema が「path は文字列」と宣言している。
LLM が万一おかしな値を入れようとすれば、ハーネス側でスキーマ違反として弾ける。
これは Web API のリクエストバリデーションと全く同じ思想だ。
理由 3: 複数ツールの同時呼び出し
1 回の応答で 複数の tool_use ブロックを並べて返せる。
[
{ "type": "tool_use", "name": "Read", "input": { "path": "a.py" } },
{ "type": "tool_use", "name": "Read", "input": { "path": "b.py" } }
]
自然言語だと「a.py と b.py を読んで」を 2 つの操作に分解するのにまた解析が要るが、構造化なら 配列の要素を順に処理するだけ。並列呼び出しは次章 (第6章) のもうひとつの主題でもある。
ツール呼び出しの本質は、自然言語の「意図」と機械の「実行」のあいだに、揺らがない契約 (JSON Schema) を一枚挟むこと。 LLM 側は「呼びたい道具と引数を考えること」に集中でき、ハーネス側は「契約に沿って実行すること」に集中できる。
5.5 業界横断 ── 名前は違うが構造は同じ
この仕組みは Anthropic 固有のものではない。主要 LLM プロバイダはほぼ全員、同じ構造を提供している。呼び名と細部のフォーマットだけ違う。
| プロバイダ | 呼び名 | スキーマ |
|---|---|---|
| Anthropic (Claude) | tool use | JSON Schema |
| OpenAI (GPT) | function calling | JSON Schema |
| Google (Gemini) | function declarations | OpenAPI スキーマ (実体は JSON Schema 系) |
最初に “function calling” という名前を市場に広めたのは 2023 年中盤の OpenAI で、業界はその語感のまま定着した。 Anthropic はあとから “tool use” という呼び方を採用し、Google は “function declarations” と名乗っている。しかし三者を並べてみると、name + description + JSON Schema、そして応答に構造化ブロックを混ぜる ── という骨格は完全に同じ だ。
プロダクトに関係なく、LLM とハーネスを繋ぐ「契約面」はほぼ単一の形式に収束した。 だから Claude Code・Cursor・Codex CLI・Aider・Devin のような別チームのエージェントが、それぞれの内部で同じ概念を扱っている。 ツール定義を一度書けば、複数プロバイダのモデルで使い回せる ── という小さな移植性も、この収束のおかげで手に入っている。
実務メモモデルによって「ツールを呼ぶのが上手い・下手」の差は実在する。description が曖昧でも適切に呼んでくれるモデルもあれば、明示的に「次は必ず Read を使え」と書かないと自然言語で済ませてしまうモデルもある。同じ JSON Schema を渡しても、Claude / GPT / Gemini で呼び方の癖は違う ── これは本章の契約構造とは別レイヤの、モデル個性の話だ。
5.6 tool_use と tool_result ── 対になるブロック
ハーネスがツールを実行したあとは、その結果を LLM に戻す必要がある (第3章の observe 相当)。 ここでも同じ思想 ── 構造化ブロック ── が貫かれる。 Anthropic では tool_result という型タグ付きの入れ物を使う。
{
"type": "tool_result",
"tool_use_id": "toolu_01ABC",
"content": "1\tdef fibonacci(n):\n2\t if n < 2:\n..."
}
ポイントはひとつ。
tool_use_idによって、「どの呼び出しに対する結果か」を LLM に明示する
なぜ id で紐付けるのか。複数の tool_use を一度に並べたとき (5.4 の理由 3)、結果も並列に返ってくる。順序ではなく id で対応させる ことで、ハーネス側も LLM 側も、どの結果がどの呼び出しに対応するか取り違えない。
ステップに分解するとこうなる。
- 準備: ハーネスがツールカタログを LLM に渡しておく
- 判断: LLM がユーザーの依頼を読み、
tool_useブロックを返す (id 付き) - 分岐: ハーネスが
typeとnameを見て、対応する実関数を呼ぶ - 観測: 実行結果を
tool_resultブロック (同じ id) に詰めて LLM に返す
ここで戻した結果を見て LLM が次の手を考える。 このサイクルそのものは第3章で見た think → act → observe の act と observe の中身であり、繰り返すことで第1章の「動ける」状態が生まれる。
5.7 章を貫く問いへの答え
冒頭の問いに戻ろう。
テキストを吐くだけのモデルが、どうやって関数呼び出しを返しているのか。
答えはこうだ。
- LLM は依然として テキスト (トークン列) を生成しているだけ
- ただし、その出力の一部に JSON Schema に従った構造化ブロック を埋め込めるよう学習されている
- ハーネスは型タグでブロックを見分け、name で実関数に分岐し、結果を同じ id の tool_result で戻す
- JSON Schema による契約 が、自然言語の揺らぎから機械の実行を守っている
つまり「関数呼び出し」と見えていたものは、LLM が JSON を書き、ハーネスが JSON をパースして実行する という、地に足のついた約束事の積み重ねだった。
ここまで腑に落ちると、「LLM がツールを使いこなす」という言い方が、少し違って聞こえてくるはずだ。 LLM 側は JSON を上手に書ける だけ。ツールを実際に動かしているのはハーネスのほう。
この役割分担を頭に置いておくと、次章で「良い道具・悪い道具」を語るとき、なぜ description の言葉選びと粒度の決め方が そんなに 大事なのかが自然に腑に落ちる。
この章の振り返り
- 自然言語パースは揺らぎに弱すぎる。だから LLM とハーネスのあいだに 構造化された「契約」 を挟む
- ツールは name + description + JSON Schema の 3 部品で定義する
- LLM はカタログを見て、出力の中に
tool_useブロック という構造化データを混ぜて返す - ハーネスは型タグで見分け、対応する関数を呼び、結果を
tool_resultブロックで戻す。両者は id で紐付く - JSON を介す理由は パース安定性・型保証・複数同時呼び出し の 3 点
- Anthropic は tool use、OpenAI は function calling、Google は function declarations ── 呼び名が違うだけで骨格は同じ
- ツールを呼んでいるのは LLM ではなくハーネス。LLM がしているのは JSON を上手に書くこと だけ
この章で読めるようになるツイート / ブログ
- 「OpenAI の function calling と Claude の tool use って何が違うの? 結局同じ?」 → 名前と細部のフォーマットが違うだけで、JSON Schema を契約とする骨格は同じ、と読める
- 「ツールの description を一行書き換えたら呼び出し率が劇的に上がった」 → description は LLM への「求人票」。LLM はそれを読んで使うかを判断している
- 「Cursor も Codex CLI も Aider も、結局やってることは tool_use の往復だよね」 → そう。プロダクトの差は契約面ではなく、その上の「道具セットの切り方」と「ループの回し方」にある (第6章以降)
次章は、ここで握った契約の上に立つ ── 「良い道具・悪い道具」 の話だ。 description はどう書くか、道具の粒度はどう切るか、権限・副作用はどう線引きするか、複数の tool_use を 並列に並べる とは何が起きているのか。 契約構造を踏まえた、道具設計の急所を見にいく。