GEO

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: