Structured Output
Structured output(構造化出力)とは、LLMに指定したスキーマ、典型的にはJSONスキーマに従った回答を返させる機能です。モデルがパース可能なJSONを生成してくれることを期待する代わりに、推論エンジンがトークンのサンプリングを制約し、出力が必ずバリデーションを通過することを保証します。
Structured output(構造化出力)とは、LLMに指定したスキーマ、典型的にはJSONスキーマに従った回答を返させる機能です。モデルがパース可能なJSONを生成してくれることを期待する代わりに、推論エンジンがトークンのサンプリングを制約し、出力が必ずバリデーションを通過することを保証します。
なぜ重要なのか
自由形式のテキストを返すLLMは、プログラムで扱うのが困難です。「JSONを返して」と指示しても、モデルは時折、散文を加えたり、フィールドを欠落させたり、型をハルシネーションしたりします。これは下流のコードを壊し、防御的なパースを強います。Structured outputは、この問題をデコーディングの層で解決します。95%ではなく100%の確率で有効なJSONが得られます。OpenAI、Anthropic、Google、そしてvLLMやOutlinesのようなオープンソースエンジンも、今ではこれをネイティブにサポートしており、信頼性の高いLLMパイプラインを構築するデフォルトの方法となっています。
仕組み
制約付きデコーディング: 生成の各ステップで、モデルは出力をスキーマと互換性のある状態に保つトークンしかサンプリングできません。スキーマに違反するトークンは、確率ゼロにマスクされます。
スキーマの指定: 必須フィールド、型、列挙値を記述したJSONスキーマ(あるいはPydanticモデル、Zodスキーマ、TypeScriptの型)を提供します。
バリデーション不要のパース: 呼び出し側は、不正な出力に対するtry/catchなしに、結果をJSON.parseできます。
JSONモード vs Structured Output
| 観点 | JSONモード | Structured Output |
|---|---|---|
| 保証 | 有効なJSON構文 | スキーマに一致する有効なJSON |
| スキーマの強制 | なし | 完全 |
| フィールドの存在 | 保証されない | 保証される |
| ハルシネーションによるフィールド | あり得る | あり得ない |
| 遅延のオーバーヘッド | ~0 | わずか(制約のコンパイル) |
JSONモードは、出力がパースできることだけを保証します。Structured outputは、出力がパースでき、かつ必要な正確な形に一致することを保証します。本番システムでは、利用可能な場合は常にstructured outputを使いましょう。
使いどころ
テキストからのデータ抽出: 非構造化された入力から、名前、日付、住所を取り出します。
ツールを呼び出すエージェントの構築: ツール呼び出しの引数は、ツールのパラメータスキーマに正確に一致しなければなりません。
列挙値への分類: 固定された一連のラベルから1つを選ぶようにモデルを強制します。
複数フィールドの回答の生成: タイトル、要約、タグ、スコアを一度に生成します。
現在モデルの出力を正規表現でパースしているあらゆる箇所: それはいつか起きるバグです。
トレードオフ
わずかな遅延のオーバーヘッド: デコーダは文法の状態を追跡しなければなりません。通常は無視できる程度です。
創造性の低下: 厳しいスキーマ制約は、生成を機械的に感じさせることがあります。創作的な文章には、自由形式を選びましょう。
スキーマ設計が重要: 過度に厳格なスキーマ(required: 20フィールドすべて)は、モデルに値をハルシネーションさせます。本当に任意のものは任意にしましょう。
すべてのモデルが対応しているわけではない: 古いモデルや一部のオープンソースモデルは、まだネイティブサポートを欠いています。Outlinesのようなライブラリで後付けできます。
例
スキーマ:
{
"type": "object",
"properties": {
"title": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } },
"sentiment": { "enum": ["positive", "negative", "neutral"] }
},
"required": ["title", "tags", "sentiment"]
}
保証される出力:
{ "title": "Launch recap", "tags": ["product", "Q2"], "sentiment": "positive" }
パースエラーなし。欠落したフィールドなし。でっち上げられた列挙値なし。
Sources: