文章として読む結果と、プログラムが利用する結果
前の節では、モデルからメッセージを受け取る流れを説明しました。回答を人が読むだけなら、自然な文章で返せば十分な場合があります。しかし、問い合わせの分類や申請項目の抽出では、後続の処理が使える形で受け取りたくなります。そこで利用するのが構造化出力です。
たとえば「来週大阪へ出張します。交通費の申請方法を知りたいです」という入力から、分類、行き先、質問内容を取り出すとします。文章で「これは出張に関する問い合わせで……」と返されるより、分類は「出張」、行き先は「大阪」、質問内容は「交通費の申請方法」と分かれている方が、振り分けや保存に利用しやすくなります。
構造化出力は、このように項目と形式を決めて結果を受け取る方法です。生成モデルを使う範囲は同じですが、自由な文章を返す場合よりも、アプリケーションとの接続を明確にできます。
スキーマは、結果に求める形の定義
どんな項目があり、どんな値を入れるかを定義したものをスキーマと呼びます。項目名だけでなく、文字列なのか数値なのか、値が無いことを許すのか、選べる分類は何か、といった条件を表せます。文字列は「大阪」のような文字の並び、数値は計算に使える数です。こうした値の種類を型と呼びます。
JSONは、「項目名と値」の組などを使ってデータを表す書式です。たとえば{"行き先": "大阪", "出張日": null}は、行き先が大阪で、具体的な出張日は値が無いと表した例です。nullは値が無いことを表します。JSON Schemaは、そのJSONにどんな項目や型を求めるかを書くための規格です。データ本体と、その形の定義を区別してください。
ここで大切なのは、不明な項目も表現できるようにすることです。入力に具体的な出張日が書かれていないのに、必ず日付を埋めることを求めると、推測が混ざるおそれがあります。「入力にない場合は未確定として扱う」という設計なら、不明なことを不明なまま次の処理へ渡せます。
スキーマは項目の説明書でもあります。「分類」という名前だけでは、人事・経理・その他なのか、出張・休暇・給与なのか分かりません。選択肢や説明を具体化することで、モデルへの要求と受け取る側の期待をそろえます。
先ほどの問い合わせを、説明用の項目へ整理した例が次の表です。入力には「来週」という範囲だけが書かれており、具体的な日付は未確定としています。
| 項目 | 求める値の形 | 整理した結果の例 |
|---|---|---|
| 分類 | 決めた分類から一つ | 出張 |
| 行き先 | 文字列、または未確定 | 大阪 |
| 出張日 | 日付、または未確定 | 未確定 |
| 質問内容 | 文字列 | 交通費の申請方法 |
後続の処理は「分類が出張なら担当窓口を選ぶ」「日付が必要な手続きなら追加確認する」といった判断ができます。自由な文章の言い回しを毎回読み取るより、何を条件として処理を進めるかを明示しやすくなります。
JSONに見えること、形式が合うこと、正しいこと
モデルに「JSONで返してください」と文章で指示するだけの方法と、スキーマを指定して結果を検証する方法は同じではありません。JSONらしい文字列でも、必須の項目が抜けていたり、数値の項目へ文章が入っていたりすれば、後続の処理で扱えません。
さらに、形式の検証に通っても、内容が正しいとは限りません。行き先が文字列として「京都」になっていれば、型は正しくても、元の文章が大阪への出張なら抽出内容は誤っています。構造の検証と、入力や根拠に照らした意味の確認を分ける必要があります。
申請の可否も同様です。結果に「承認可能」という項目が入ったからといって、業務上の承認が行われたことにはなりません。モデルの分類結果、業務ルールによる判定、人による承認は、それぞれ別の段階です。
LangChainでは、どこで構造化するのか
LangChainでは、with_structured_outputでスキーマを指定し、モデルの応答を項目として受け取る構成を作れます。Pydanticという、データの形や値を検査するライブラリも利用できます。「Pydanticのモデル」はデータの項目を定義するもので、文章を生成するLLMとは別の意味の「モデル」です。
内部では、提供会社の構造化出力機能を利用する方法や、ツール呼び出しの形式を利用する方法があります。使える方法や検査のされ方は、スキーマの表し方と接続先によって異なります。ここでは「形を指定する」「結果を検査する」「意味を確認する」という役割を押さえます。エージェントで最終出力の形を指定する方法は、第14節で、その仕組みを学んでから扱います。
失敗を扱えることも、構造化の利点
応答を解釈できない場合や、必要な項目がそろわない場合は、その結果を無理に利用せず、追加の確認や保留へ進めます。再度モデルへ依頼する方法もありますが、繰返せば必ず正しくなるわけではありません。回数の上限と、失敗した場合の扱いを決めておきます。
構造化出力を学ぶ意味は、モデルにきれいなJSONを書かせることだけではありません。何を受け取れると処理を進められ、何が不足すると止めるべきかを明確にすることにあります。次の節では、このような部品を順に組み合わせるChainとRunnableを説明します。