構造化出力パターン
毎回機械可読な結果を得る:明示的契約、列挙の規律、NULL の逃げ道、検証・修復ループ。
「JSON で返して」の問題
JSON を要求しても JSON が返るとは限りません。バッククォートで囲まれた散文、末尾のカンマ、オブジェクト前の雑談、あるいはほぼ一致するキー名が返ってきます。
3 つの手でほぼ解決します。
1. 契約を「記述」せず「提示」する
型込みで正確な形を示します:
次の構造に一致する JSON オブジェクトのみを返し、前後に説明を書かないこと:
{
"verdict": "pass" | "fail",
"severity": 1 | 2 | 3,
"issues": [{ "file": string, "line": number, "note": string }],
"confidence": number // 0.0 - 1.0
}
「pass または fail のいずれか」と書くより "pass" | "fail" と列挙する方が効きます。モデルが出力すべきリテラルトークンを目にするからです。
2. 棄権を許可する
逃げ道がないと、モデルは不確実さを認めず捏造します。明示的な選択肢を足します:
情報が不足している場合は次を返す:
{ "verdict": "unknown", "missing": ["<何が要るか>"] }
これだけで自信過剰な捏造が大きく減ります。「分からない」が正当な答えになるからです。
3. 検証して修復するが、上限を設ける
1 回目 → 解析失敗 → エラーを添えて再試行
2 回目 → 解析失敗 → エスカレートまたは null を返す
解析エラーはそのまま渡します(Unexpected token } at position 214)。モデルは具体的なエラーの修正が得意です。無限ループは禁止、2 回で打ち切ります。
強制レベルの選び方
| 手法 | 信頼性 | コスト | 使いどころ |
|---|---|---|---|
| プロンプト契約 | 良い | 無料 | 既定の出発点 |
| 検証+再試行 | とても良い | 失敗時 +1 呼 | 大半のケース |
| ネイティブ JSON/スキーマ | ほぼ完全 | わずかな負荷 | 厳密なパイプライン |
まずプロンプト契約。検証は即座に足します。安価で残りを捕まえられます。解析失敗が許されない場面のみネイティブ化してください。
効果のある配置の工夫
- 形式指定は最後に、入力の直前に置く。末尾の指示は守られやすい。
- フィールド名を本文中でも繰り返す。例示だけでなく。冗長な手掛かりが効く。
- 深い入れ子を避ける。 3 段を超えると精度が落ち始める。平らにできるなら平らに。
- 任意文字列キーのオブジェクトより、フラットなオブジェクトの配列 を選ぶ。
構造化出力を使わない方がいい場面
チャットの返信、説明、人が直接読むものには不要です。会話ターンに JSON を強制すると回答が劣化し、統合も楽になりません。構造が要るなら 会話の後に抽出 します。