AI に「JSON で返して」と頼んだのに、前後に説明文が付いてきてパースに失敗した。
そんな経験を持つ人は少なくないはずです。
プロンプトでいくら形式を指定しても、モデルは確率的に次の単語を選ぶので、たまに余計な一言を足してしまう。
その「たまに」がアプリケーションでは致命傷になります。
構造化出力(Structured Outputs)は、この「たまに」をモデル側の仕組みで潰すための機能。
お願いではなく、生成そのものを制約するという発想の転換がここにあります。
構造化出力は「JSONで返して」とお願いすることではない
まず押さえたいのは、プロンプトでの依頼と構造化出力は別物だという点です。
プロンプトに「必ず JSON だけを返してください」と書くのは、あくまで指示。
モデルは指示を尊重しますが、守る保証はどこにもありません。
一方の構造化出力で渡すのは、API の呼び出しに載せる JSON スキーマそのもの。
OpenAI の公式ドキュメントは、この機能を「モデルが常に、渡された JSON スキーマに従った応答を生成することを保証するもの」と説明しています。
Structured Outputs – OpenAI API
Anthropic 側の説明も同じ方向を向いていて、スキーマに沿った検証済みの JSON を返すことを保証する、という書き方になっています。
Structured outputs – Claude Docs
つまり「お願い」から「契約」への格上げ。
呼び出し側は JSON.parse() が失敗する前提のリトライ処理を、まるごと捨てられます。
制約付きデコーディングが「必ず通る」を作る
では、なぜ保証できるのでしょうか。
鍵になるのが制約付きデコーディング(constrained decoding)という手法です。
言語モデルは、次に出す 1 トークンを候補の確率分布から選び続けることで文章を作ります。
このとき「今の位置でスキーマ的にありえないトークン」を候補から外してしまえば、出力は文法的に外れようがない。
Anthropic のドキュメントは、この生成アルゴリズムによってスキーマ準拠をリアルタイムに担保していると説明しており、スキーマから作った文法(grammar)をコンパイルしてキャッシュする実装だと明記しています。
トークン単位で選択肢を削るという発想は、文章の意味ではなく形だけを縛るところが肝。
だからこそ「中身が正しいか」は別問題として残るのです。
ファンクションコーリングとの違いは「誰に渡すか」
構造化出力を調べていると、必ずファンクションコーリングとの違いでつまずきます。
どちらも JSON スキーマを書く点が同じなので、混乱するのも当然でしょう。
OpenAI のドキュメントは、この 2 つを用途で切り分けています。
外部のツールやデータベース、UI の機能へモデルを接続したいときはファンクションコーリング。
ユーザーへ返す応答そのものの形を指定したいときは構造化出力。
Anthropic 側はさらに整理していて、応答の形を決めるのが JSON Outputs、ツールに渡す引数を検証するのが Strict Tool Use、と役割を明示しています。
言い換えれば、出力の行き先がプログラムの内部なのか、それとも外部の道具なのかという違い。
しかも両者は同じリクエストで併用できるので、対立する機能ではありません。
道具を呼ぶ側の仕組みは、こちらで整理しています。
スキーマには書けないことがある
便利な機能ほど、限界を先に知っておきたいところ。
構造化出力では、JSON Schema の仕様がまるごと使えるわけではありません。
Anthropic のドキュメントは、非対応の項目として次のようなものを挙げています。
- 再帰的なスキーマ
minimum/maximum/multipleOfといった数値の範囲制約minLength/maxLengthといった文字列の長さ制約- 外部ファイルを指す
$ref
つまり「型と必須項目はモデル側で保証できるが、値の妥当性までは保証されない」という線引き。
年齢フィールドが整数で返ってくることは保証されても、それが 0 以上 150 以下かどうかは自前で検証するしかありません。
OpenAI 側も、パフォーマンス上の理由でスキーマの一部機能に制限があると明記しており、required と additionalProperties の指定は厳密さを求められます。
さらに安全上の理由で応答が拒否される場合があり、その検出のために refusal フィールドが用意されている点も覚えておきたいところ。
そして最大の注意点は、形式が正しくても内容が正しいとは限らないという当たり前の事実。
スキーマは嘘を弾いてくれません。
ローカルLLMでも同じ考え方が使える
この仕組みはクラウドの API に限った話ではありません。
Ollama にも出力形式を指定するパラメータがあり、Pydantic のモデル定義からスキーマを渡す形で同じことができます。
手を動かして確かめたい方は、以前まとめた記事が入り口になるはずです。
対応モデルはベンダーごとに違うので、採用前には自分が使うモデルが対象かどうかを公式のモデル一覧で確認するのが確実。
ベータヘッダーやパラメータ名も更新されることがあるため、手元のSDKのバージョンと公式ドキュメントを突き合わせる手間は惜しまないほうが安全でしょう。
最後に
構造化出力は、AI の出力を「読み取れたらラッキー」から「必ず読み取れる」へ変える機能です。
支えているのは制約付きデコーディングという、候補トークンを削って形を強制する仕組み。
ファンクションコーリングとは競合せず、応答の形を決める側とツールの引数を決める側で役割が分かれます。
ただし型は守られても値の妥当性と内容の正しさは守られないので、検証層は残しておきたいところ。
AI をアプリケーションの部品として組み込むなら、真っ先に押さえておきたい土台だと感じます。
以上です。











コメントを残す