何を重視するかで方法が決まる

最初に結論を示します。出力の形式を確実に守らせたいなら、Structured Outputsが第一候補です。ただし、使っているAPIとモデルが対応していることが条件です。複数のプロバイダやローカルモデルで同じコードを動かしたい場合は、プロンプトで指示し、受け取った側で検証する方法が最も広く使えます。LLMに外部の処理を呼ばせる設計なら、Function Calling(ツール呼び出し)がそのまま使えます。JSONモードは、JSONとして読み込めることは保証されますが、項目の構成は保証されません。Structured Outputsが使えない環境での中間策と考えるのが妥当です。

以下では、この結論の根拠を5つの観点で説明します。機能名やパラメータ名、対応モデルは執筆時点(2026年10月)の情報です。変更が頻繁にあるため、実装前に各社の公式ドキュメントを確認してください。

5つの観点での比較表

方法形式の確実さ使える範囲スキーマの制約失敗時の扱いトークンコスト
プロンプトで指示低〜中ほぼすべてのモデルなし(強制力もない)自前で解析・検証・再試行する説明や例の分だけ増える。再試行するとさらに増える
JSONモードJSONとして読めることだけ保証対応するAPIのみスキーマは指定できない項目の過不足を自前で検証するプロンプト指示とほぼ同じ
Function Calling流用中〜高主要な商用APIの多く。ローカルではモデル次第JSON Schemaで書ける。厳密モードには制限がある引数の検証が必要(厳密モードを使わない場合)ツール定義が入力トークンに加わる
Structured Outputs高(スキーマに沿うことを保証)対応するAPI・モデルのみ使えるJSON Schemaの機能が限られる拒否や出力途中の打ち切りは別に処理するスキーマが入力に加わる場合がある

各方法の仕組みと弱点

プロンプトで指示する

「次の形式のJSONだけを返してください」と書き、出力例を添える方法です。どのモデルでも使えて、追加の機能は要りません。そのため、複数のプロバイダを切り替えて使うコードや、古いモデルでも動かす必要がある場合に向きます。

弱点は、形式が守られる保証がないことです。JSONの前に説明文が付く、コードフェンスで囲まれる、閉じ括弧が抜ける、といった崩れが起こります。受け取る側では、解析、スキーマ検証、失敗時の再試行が必須です。PythonならPydantic、TypeScriptならZodといった検証ライブラリを組み合わせるのが一般的です。性能の高いモデルほど崩れにくいものの、大量に処理すると一定の割合で失敗が出る前提で設計してください。

JSONモード

APIのオプションで、出力をJSONとして読み込める文字列に限定する機能です。OpenAIのAPIでは、JSONモードを使う場合、メッセージのどこかに「JSON」という語を含める必要があります。執筆時点の仕様です。

弱点は、どんな項目を出すかを指定できないことです。項目名が変わる、必須の項目が抜ける、型が違う、といったことは防げません。出力が最大トークン数に達すると途中で切れ、JSONとして読めなくなることもあります。Structured Outputsに対応したAPIでは、JSONモードをあえて選ぶ理由は少なくなっています。

Function Callingを流用する

本来は、LLMに外部の関数を呼ばせるための機能です。関数の引数をJSON Schema(JSONの構造を記述する規格)で定義すると、モデルはその形式で引数を返します。使いたい関数を1つだけ定義し、その関数を必ず呼ぶよう指定すると、形式の決まった出力を受け取る手段として使えます。OpenAI、Anthropic、Googleの主要APIが対応しており、Structured Outputs専用の機能より先に広まったため、使えるモデルが多いのが利点です。

弱点は3つあります。1つ目に、厳密モードを使わない場合、引数がスキーマに完全には沿わないことがあります。2つ目に、関数を呼ばずに通常の文章で答えたり、複数の関数呼び出しを返したりすることがあります。呼び出しを強制する設定が必要です。3つ目に、本来の用途とは違う使い方なので、コードを読んだ人に意図が伝わりにくくなります。ローカルモデルでは、ツール呼び出しを学習したモデルでないと安定しません。

Structured Outputs

JSON Schemaを渡すと、モデルが文字を1つずつ生成する段階で、スキーマに合わない文字を選べないよう制限します。この仕組みを制約付きデコーディングと呼びます。生成の仕組みで制限するため、4つの方法の中で形式が最も確実に守られます。OpenAIとGoogleのGeminiのAPIが対応しており、Anthropicも執筆時点で同種の機能を提供しています。ローカルでは、llama.cpp、Ollama、vLLMなどがJSON Schemaや文法の指定による制約に対応しています。

弱点はスキーマの書き方の制約です。プロバイダによって、すべての項目を必須にすること、想定外の項目を禁止すること(additionalProperties: false)が求められたり、一部のキーワードが使えなかったりします。入れ子の深さや項目数にも上限があります。複雑なスキーマはエラーで受け付けられないことがあります。新しいスキーマを最初に使うときは、前処理のために応答が遅れる実装もあります。安全上の理由でモデルが回答を拒否した場合や、最大トークン数で出力が打ち切られた場合は、スキーマに沿わない応答が返ることがあります。この2つは別に処理してください。

どの方法でも、形式が正しいことと中身が正しいことは別

Structured Outputsで形式が保証されても、中身が正しいとは限りません。日付の項目にもっともらしい架空の日付が入る、元の文書にない情報で項目が埋められる、分類のラベルが間違う、といった誤りは防げません。すべての項目を必須にすると、情報がない場合でもモデルは何か値を入れようとします。「不明」を表す値(nullやunknown)を選択肢に含めておくと、こうした作り話を減らせます。

項目の説明(descriptionの記述)と、プロンプトでの指示は、どの方法でも省略できません。形式の保証は、受け取った後の処理を楽にするための仕組みです。抽出結果の正確さが重要な業務では、値の範囲チェックや、元の文書との照合を別に用意してください。

用途・使っているAPI・必要な確実さ別の選び方

1つの商用APIで本番運用し、形式の崩れを許容できない場合は、Structured Outputsを使います。データベースへの登録や、別のシステムへの受け渡しが該当します。スキーマが制約に収まらない場合は、スキーマを単純にするか、処理を複数回の呼び出しに分けます。

LLMが外部の処理を呼ぶエージェントや業務フローを作る場合は、Function Callingを本来の用途で使います。対応していれば厳密モードを有効にします。出力を受け取るためだけなら、Structured Outputsのほうが意図が明確です。

複数のプロバイダを切り替える場合や、古いモデル・小さなローカルモデルを使う場合は、プロンプトでの指示と検証・再試行を基本にします。ローカルで推論サーバーを自分で動かしているなら、llama.cppやvLLMの制約機能で確実さを上げられます。複数のプロバイダの違いを吸収するライブラリ(PythonのInstructorなど)を使う選択肢もあります。

試作や個人用のスクリプトで、失敗したらやり直せばよい場合は、プロンプトでの指示で十分です。JSONモードは、Structured Outputsに対応していないAPIで、JSONとして読めることだけでも確保したい場合に使います。

導入前に確かめる3つのこと

まず、使う予定のモデルが、Structured Outputsまたは厳密モードのFunction Callingに対応しているかを公式ドキュメントで確認します。対応していれば、使いたいスキーマがそのプロバイダの制約に収まるかを、実際のリクエストで試します。最後に、どの方法を選んでも、拒否、出力の打ち切り、値の誤りを検出する処理を入れます。この3つを確認すれば、方法を選んだ後に手戻りが出る可能性を減らせます。