構造化出力:モデルから信頼できるJSONを得る
コードが散文ではなくデータを必要とするとき、モデルは毎回きれいでパース可能な構造を返さなければなりません。希望ではなく信頼できるJSONを得る方法を解説します。
きれいな段落を書く言語モデルは、人間にとって有用です。別のプログラムに供給する言語モデルには、もっと難しいことが求められます。あなたのコードがパースできる形のデータを、毎回、驚きなしに返すことです。自分で出力を読むのをやめてjson.parseに渡し始めた瞬間、散文は負債となり、構造が要件となります。本ガイドは、「モデルはたいていJSONっぽい何かを返す」と「私のパイプラインはモデルの出力に依存できる」の間のギャップを埋めることについてです——なぜなら本番では、「たいてい」は「定期的に壊れる」と同じだからです。
なぜ散文では不十分なのか
モデルが人に答えるとき、小さな不完全さは人間の柔軟さの中に消えていきます。答えが「もちろん、こちらです:」で始まろうと、データをコードフェンスで囲もうと、前回とは少し違う言葉でフィールドにラベルを付けようと、読み手は気にしません。パーサーはそのすべてを気にします。データの前のたった一つの余計な文、末尾のカンマ、ときに数値でときに「unknown」という単語になるフィールド——これらのどれか一つが、動いていたパイプラインをスタックトレースに変えてしまいます。
核心的な問題は、モデルがもっともらしいテキストを生成するよう訓練されており、もっともらしいテキストは妥当な構造化データと同じではない、ということです。放っておけば、モデルはまさにあなたが厳格で機械可読であってほしいときに、親切で会話的であろうとして漂流します。信頼できる構造化出力を得るとは、その漂流を取り除く作業のことです。レバーはいくつかあり、それらは積み重なります。
レバー1:正確に求める
最も安価な改善は、最もよく省かれもします。望む形を正確に伝え、それを示すことです。曖昧な指示は曖昧な構造を生みます。「詳細をJSONで返して」というリクエストは、フィールド名、ネスト、型をモデルに発明させることになり、しかも呼び出しごとに違う発明をします。
代わりに、スキーマを具体的に指定しましょう。すべてのフィールドに名前を付け、その型を述べ、必須かどうかを明示し——そしてこれが省かれがちな部分ですが——妥当な応答の完全な例を示すのです。モデルは例へのパターンマッチングが並外れて得意で、一つの整った見本は、段落の説明よりも形式を確定させる働きをします。重要なルールも明示的に述べましょう。出力は周囲のテキストなしのJSONだけであること、欠損値は省略したり推測したりするのではなく特定の方法で表現すべきこと、そしてフィールドの集合は固定であること。リクエストにおける正確さは、他のすべてが積み上がる土台です。これを省くと、後のレバーはあなた自身が作った問題に当て布をすることになります。
レバー2:モデルの構造化出力機能を使う
丁寧に頼むことは役立ちますが、指示だけではモデルがさまよう余地が残ります。今や本格的なLLMプロバイダーの多くは、構造化出力のための機能を具体的に提供しており、それを使うことはプロンプトだけに頼るより大きな一歩です。
これらの機能にはいくつかの種類があります。軽い形は、出力を妥当なJSONに制約するモードです——モデルは構文的に整っていないものを出すことを妨げられ、「文を加えた」「コードフェンスを使った」という失敗のカテゴリ全体が消えます。より強い形は、出力が従うべきスキーマを与えることを可能にし、結果は単に妥当なJSONであるだけでなく、正しいフィールドと型を持つ、あなたが求めた正確な形の妥当なJSONになります。
これらの機能が存在する場所では、手作りのプロンプトよりもそれらを優先しましょう。それらは保証を「モデルが頼まれた」から「システムが強制する」へと動かし、その転換こそがすべてです。何が利用でき、どう呼び出すかは仕様が異なるため、プロバイダーのドキュメントを確認してください。ただし原則は一定です。モデルの善意に頼るのではなく、プラットフォームに構造を強制させること。
レバー3:信頼する前に検証する
最良のプロンプトと最強の構造化出力機能をもってしても、検証するまではモデルの出力を信頼できないものとして扱いましょう。これは被害妄想ではなく、あらゆる外部入力に適用するのと同じ規律です。検証には2つの層があり、両方が欲しいところです。
- 構造的検証は、出力がパースでき、スキーマに一致することを確認します。正しいフィールドが存在し、型が正しく、必須の値が欠けていないこと。これは上流のすべてをすり抜ける不正な応答を捕まえます。
- 意味的検証は、その内容があなたのドメインで筋が通ることを確認します。日付が実在する日付であること、カテゴリが許可された値の一つであること、数量がもっともらしい範囲にあること、参照された識別子が実際に存在すること。応答は完璧に妥当なJSONでありながら、なお無意味であり得ます。それを捕まえられるのはドメインのチェックだけです。
検証を後付けではなく、関門として走らせましょう。関門を通らない出力は、問題ないかのようにシステムの残りに届くべきではありません。関門で何をするかは、次のレバーの主題です。
レバー4:それでも起きる失敗に対処する
上記のどの組み合わせも完璧ではないため、起きないふりをするのではなく、残る失敗を見越して設計しましょう。検証が失敗したとき、いくつかの健全な選択肢があり、おおむね好ましい順に並べられます。
一つ目は有界の再試行です。多くの構造化出力の失敗は一回限りのもので、単にもう一度頼むだけ——理想的には前回の試みの何が問題だったかをモデルに伝えて——うまくいきます。再試行に上限を設け、しつこい失敗が永遠にループしないようにします。二つ目は、軽微で予測可能な問題に対する修復です。余計なコードフェンスを削る、明らかな書式を直す、惜しいものを期待される形に矯正する。修復は狭く保守的に保ちましょう。攻撃的な自動修正は本当の問題を隠し、データを破損させかねないからです。三つ目は、再試行と修復が尽きたときのきれいに記録された失敗です——不正なデータを下流に流すのではなく、フォールバック経路や人間によるレビューへ回し、パターンが見えるようログに残します。絶えず検証に失敗するフィールドは、別の修復ルールを足せではなく、プロンプトかスキーマを直せと告げているのです。
スキーマは仕事が許す限り単純に保つ
それ自体言及に値する、より静かなレバーがあります。求めるものの複雑さは、それをどれだけ信頼して得られるかに直接影響します。深くネストしたオブジェクト、任意フィールドの長いリスト、入り組んだ条件付き構造は、平坦で小さく必須フィールドだけの形よりも、モデルが一貫して生成するのが難しくなります。バロックなスキーマを御するために巧妙なプロンプトに手を伸ばす前に、スキーマがそこまでバロックである必要があるのかを問いましょう。多くの場合、一つの複雑な抽出を二つの単純なものに分けられたり、必要ではなく整然さのためにネストされた構造を平坦化できたりします。最も信頼できる構造化出力とは、あなたが複雑にしすぎなかった構造のことです。
まとめ
モデルから信頼できるJSONを得ることは、単一の小技ではなく、互いを補強する習慣の積み重ねです。正確に求め、例を示すこと。プロバイダーの構造化出力機能を使い、モデルが単に意図するのではなくプラットフォームが形を強制するようにすること。あらゆる応答を——構造的にも意味的にも——検証し、通るまで信頼できないものとして扱うこと。残る失敗に対しては、有界の再試行、狭い修復、きれいなフォールバックで設計すること。そしてスキーマを仕事が要求する以上に複雑にしないこと。これらを一緒に行えば、本番で重要な一線を越えます。たいていパース可能なデータを返すモデルから、実際にそれに依存できるパイプラインへと。
