出力制御——構造化、JSON Schema、Markdown/XML
レッスン4:出力制御——構造化、JSON Schema、Markdown/XML
このレッスンで学ぶこと
- 構造化出力がなぜ実務で重要なのかを理解する
- JSON Schema による出力指定の基本を押さえる
- XML タグでの構造化(Anthropic 推奨パターン)を学ぶ
- Constrained Generation の仕組みと使いどころを区別できる
- 出力検証とリトライ設計の発想を持つ
- 長い出力での失敗パターンを把握する
前回のレッスンで、Few-shot と Chain-of-Thought を通じて「LLM に何を考えさせるか」を扱いました。今回は、「LLM の出力をどう受け取るか」に焦点を当てます。LLM を業務システムに組み込むとき、自然言語の自由形式では後段のプログラムが処理できません。構造化出力——JSON、XML、Markdown——をどう設計し、どう検証するかが、実務の中核技術になります。
なぜ構造化出力が必要か
LLM の自然な出力は「自然言語の文章」です。これは人が読むには便利ですが、システムが処理するには扱いにくい形です。
自然言語出力のままだとどうなるか
質問:「次の顧客レビューから、評価と理由を抽出してください」 LLM の出力:「このレビューは肯定的で、特に対応の早さを評価しています。ただ、価格については少し高いと感じているようです」
この出力は人が読めば理解できますが、
- 評価を「ポジティブ/中立/ネガティブ」のどれと判定したか
- 理由を箇条書きで抽出したいときに、どう取り出すか
- 後段の集計・通知・データベース保存にどう渡すか
が、システム的には曖昧です。100 件のレビューを処理しようとした瞬間、破綻します。
構造化出力にすると
出力:
{ "sentiment": "positive", "positive_points": ["対応の早さ"], "negative_points": ["価格が高い"] }
これなら、後段のプログラムが data.sentiment === "positive" のように判定でき、自動的に集計・通知・保存ができます。
💡 ポイント 「LLM を試しに使ってみる」段階では自然言語出力で十分ですが、「業務システムに組み込む」段階では構造化が必須です。本レッスンはその段階での設計を扱います。
JSON Schema による出力指定
JSON は構造化出力の最も標準的な形式です。「JSON Schema」を使って、期待する形を明示するのが推奨パターンです。
JSON Schema の基本
JSON Schema は、JSON の構造(プロパティ名、型、必須項目、列挙値など)を記述する仕様です。LLM に直接渡すこともあれば、API のパラメータとして渡すこともあります。
例:顧客レビュー分析の JSON Schema
{
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "neutral", "negative"]
},
"positive_points": {
"type": "array",
"items": { "type": "string" }
},
"negative_points": {
"type": "array",
"items": { "type": "string" }
}
},
"required": ["sentiment"]
}
このスキーマで「sentiment は 3 値の列挙、positive/negative_points は文字列の配列、sentiment は必須」が明示されます。
Structured Outputs(API レベルでの強制)
2024 年以降、OpenAI・Anthropic・Google などの主要 API は「Structured Outputs」と呼ばれる、JSON Schema に必ず従う出力モードを提供しています。プロンプトで「JSON で返してください」と書くだけより、API レベルで強制する方が、確実性が高くなります。
📝 補足 Structured Outputs は便利ですが、複雑なスキーマでは LLM が無理に値を埋めようとして、嘘の値を作る場合があります(「sentiment は必須だが、レビュー内容が判定不能」のときに、根拠なく "neutral" を返す等)。「埋められない場合は null を返す」「不明 enum を用意する」などの設計で対処します。
XML タグでの構造化(Anthropic 推奨)
Claude シリーズを開発する Anthropic は、入出力の構造化に「XML タグ」を使うことを公式ドキュメントで推奨しています。Claude シリーズは XML タグ構造に特に強い反応を示すよう訓練されているためです。
入力の構造化
長い指示やデータを LLM に渡すとき、XML タグで区切ることで、LLM が「どこからどこまでが何の情報か」を判別しやすくなります。
例:
以下の指示と文書から、要約と論点を抽出してください。
<instruction> 文書を 300 文字以内で要約し、論点を 3 つ箇条書きで抽出する </instruction>
<document> (ここに文書本文) </document>
出力の構造化
出力側でも XML タグで返させることができます。これは JSON より自然言語に近く、長文の入れ子に強いという利点があります。
例:
出力形式: <summary>...</summary> <key_points> <point>...</point> <point>...</point> <point>...</point> </key_points>
JSON と XML の使い分け
| 場面 | 推奨 |
|---|---|
| プログラムで機械処理する | JSON(パース容易、Schema 強制可能) |
| Claude シリーズに長文を渡す | 入力は XML タグで区切る |
| 入れ子・自由テキストが多い | XML タグ(パースより構造の柔軟性) |
| API の Structured Outputs を使う | JSON Schema |
💡 ポイント 入力を XML タグで区切り、出力は JSON Schema で受ける、というハイブリッドが現場では多く使われます。「入力は人と LLM が読みやすい、出力は機械が処理しやすい」という非対称な設計が、運用上で機能します。
Constrained Generation(制約付き生成)
Constrained Generation は、LLM の出力を「文法的に必ず正しい JSON や特定の構造に従わせる」技術です。
仕組みの概要
LLM は次トークンを確率で選びますが、Constrained Generation では「次に許される文字/トークン」をフィルタリングし、JSON 文法から外れるトークンを生成しないようにします。例えば、JSON のキー直後は : しか許されないなら、それ以外のトークン候補を強制的に除外します。
主要ベンダーでの提供
- OpenAI:Structured Outputs(JSON Schema に従う)
- Anthropic:Tool Use や Response Format による構造化
- Google:JSON Mode、Response Schema
効用と限界
効用:
- 出力がパースエラーで失敗することがなくなる(JSON は必ず妥当な JSON になる)
- 後段のリトライ・ハンドリングコードがシンプルになる
限界:
- スキーマが複雑すぎると、LLM が無理やり値を埋める
- 「妥当な JSON」と「正しい内容」は別問題で、内容のハルシネーションは別途検証が必要
⚠️ 注意 Constrained Generation は「形式の正しさ」を保証しますが、「内容の正しさ」は保証しません。形が正しくて内容が嘘の JSON も、平気で返ってきます。レッスン 5 で扱う評価セットと組み合わせて、内容の妥当性を別途検証する運用が必要です。
Markdown テンプレート
人が読む出力(メール、文書、Slack 投稿など)では、Markdown テンプレートを指定する方法が便利です。
例:
出力形式:
件名
(件名を 1 行で)
本文
(3 段落以内)
アクション
- (箇条書き 3 個以内)
Markdown は人が読みやすく、後段で HTML 変換や PDF 生成にも繋ぎやすい形式です。
📝 補足 Markdown 出力は「人が見る最終成果物」を作るタスクで効きます。Web のフロントエンドに渡してそのまま表示する、メールに整形して送る、社内 Wiki に投稿する、などの用途では JSON より Markdown が適します。
出力検証とリトライ設計
構造化出力を業務で使うときに、必ず必要になるのが「出力検証」と「リトライ設計」です。
出力検証の 3 層
| 層 | 検証内容 |
|---|---|
| ①形式の検証 | JSON が妥当か、スキーマに従っているか |
| ②値の検証 | 列挙値が許容範囲か、必須項目が空でないか、型が正しいか |
| ③意味の検証 | 内容が事実と矛盾しないか、入力と整合しているか |
①②はプログラムで自動検証できます。③は LLM-as-a-Judge(後のレッスン)や人手による確認が必要です。
リトライ設計
検証に失敗したとき、どう対処するか:
- シンプルリトライ:同じプロンプトで再実行(温度が 0 でない限り別の結果が出る可能性)
- エラーフィードバック付きリトライ:「前回の出力はこういう問題があった。次は……」をプロンプトに追加して再実行
- フォールバック:N 回失敗したら、別のモデル/別のプロンプト/人手対応に切り替える
💡 ポイント 業務システムでは「3 回失敗したら人手対応キューに送る」のような設計が現実的です。LLM は確率モデルなので、100% の成功は期待できません。失敗時のフォールバックを最初から組み込むのが、本気の運用設計です。
長い出力での失敗パターン
文脈窓が広がった 2026 年の主要モデルでも、長い出力には特有の失敗パターンがあります。
①出力打ち切り(truncation)
トークン上限に達して、JSON が途中で切れる。フォーマットエラーになります。
対策:上限値を余裕を持って設定/出力を分割設計(チャンク単位で生成)
②中間の品質低下(lost in the middle)
長い文脈や長い出力の「中盤」で、品質が下がる傾向(Liu らの 2024 年研究などで指摘)。冒頭・末尾は精度が高いが、真ん中で抜けや誤りが増える現象です。
対策:重要な指示は冒頭と末尾に重複させる/長い出力は分割する
③整合性の崩れ
長い出力の途中で、自分が前段で言ったことと矛盾する。
対策:出力を 2 段階で生成(要約→詳細)、整合性チェックを別プロンプトで実行
⚠️ 注意 「文脈窓が 100 万トークンあるから、長い文書もそのまま渡せる」のは半分本当で半分嘘です。物理的には収まりますが、中間部分の品質が下がる現象が確認されています。長い文書は、分割して処理し、後で統合する設計のほうが、安定することが多いです。
講師の現場メモ:「JSON が壊れる」問題を解いた話
私(西脇)が SaaS スタートアップ PM だった頃、社内 AI 機能でいちばん多かったバグ報告が「JSON が壊れて画面が真っ白になる」でした。
調査してみると、原因は次のように分散していました。
- 文字列内のクォートのエスケープ漏れ:30%
- 末尾のカンマ余分・抜け:25%
- 文脈窓上限で出力が途切れる:20%
- LLM が説明文を JSON の前後に付け加える:15%
- スキーマと違うキー名で返す:10%
最初に試したのは、プロンプトの末尾に「JSON 以外の文字を一切出力しないでください」と書く対策でした。これで「説明文の付加」は減りましたが、ほかの問題は残りました。
次に、JSON Schema をプロンプトに含めて期待形式を明示しました。これで「キー名違い」が減りました。
決定打になったのは、API レベルの Structured Outputs(当時はベータ)を採用したことでした。これで「文字列のエスケープ」「末尾のカンマ」「キー名違い」がほぼ消え、残ったのは「文脈窓上限による打ち切り」だけになりました。
打ち切り問題への対策として、私たちは「出力の上限を超えそうなときは、まず『概要だけ』を返し、続きは別呼び出しで詳細化する」設計に切り替えました。トータルのコストは少し上がりましたが、エラーレートは 5% から 0.3% に下がりました。
このときに改めて感じたのが、出力制御は「プロンプトだけ」「API 機能だけ」「設計だけ」の単一手段では解決せず、これらを組み合わせて初めて運用に耐えるということです。本レッスンで層別の検証・リトライ・分割設計に触れたのは、こうした経験が背景にあります。
まとめ
このレッスンでは、以下のことを学びました。
- 構造化出力は、LLM を業務システムに組み込むときの必須技術
- JSON Schema:プロパティ名・型・必須・列挙値を記述する標準仕様。Structured Outputs で API レベル強制も可能
- XML タグ(Anthropic 推奨):入力の区切り、長文・入れ子の自然な構造化に向く
- 使い分け:入力は XML タグで区切り、出力は JSON Schema で受けるハイブリッドが現場で多い
- Constrained Generation:形式の正しさは保証するが、内容の正しさは別途検証が必要
- Markdown テンプレート:人が読む最終成果物(メール・文書・Slack 投稿)に向く
- 出力検証の 3 層:形式/値/意味
- リトライ設計:シンプルリトライ/エラーフィードバック付きリトライ/フォールバック
- 長い出力の失敗パターン:打ち切り/中間の品質低下(lost in the middle)/整合性の崩れ
次のレッスンでは、本コースの中心テーマである「評価とイテレーション」を扱います。プロンプト評価セットの作り方、量的指標と質的レビュー、A/B 比較、エラー分析、LLM-as-a-Judge の限界と使い方を学びます。
確認クイズ
このレッスンの理解度をチェックしましょう。