本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
プロンプトの出力形式制御とJSON Schema
大規模言語モデル(LLM)の出力を後続システムで利用する場合、その形式が厳密に定められていることが重要です。JSON Schemaを用いることで、LLMの出力形式を効果的に制御し、堅牢なアプリケーションを構築できます。
ユースケース定義
ユーザーからの自由形式の問い合わせテキストから、特定の製品情報を構造化されたJSON形式で抽出するシナリオを考えます。抽出されたデータは、製品データベースの検索、ECサイトの在庫確認APIへの連携、またはカスタマーサポートシステムへの情報登録に利用されます。
例: ユーザー「最新のスマートフォンで、カメラ性能が高くて在庫のあるモデルを教えてください」 期待される出力:
{
"product_type": "スマートフォン",
"features": ["カメラ性能が高い"],
"availability": "在庫あり",
"limit": 1
}
制約付き仕様化(入出力契約)
LLMの入出力には以下の契約を定義します。
入力:
ユーザーからの自然言語による問い合わせテキスト。
必要に応じて、システムプロンプトや少数例(Few-shot examples)を追加。
出力:
厳密にJSON形式であること。
指定されたJSON Schemaに完全に準拠していること。
JSONオブジェクト外に余分なテキスト(説明、コメントなど)を一切含めないこと。
失敗時の挙動:
スキーマ不一致: モデルが出力したJSONが指定されたJSON Schemaに準拠しない場合、その出力は無効とみなし、利用しない。APIレベルでJSONモードが有効な場合は、多くの場合エラーが返却されるか、モデルが修正を試みます[1]。
情報不足: 必須フィールド(例:
product_type)がユーザー入力から抽出できない場合、そのフィールドはnullとして出力される(スキーマで許可されている場合)。あるいは、抽出可能な最小限の情報のみを含むJSONを出力する。抽出不能: ユーザーの問い合わせが完全に製品情報と無関係である場合、空のオブジェクト
{}、または特定のエラーメッセージを含むJSON(例:{"error": "関連情報なし"})を返す。
禁止事項:
JSON以外の形式での出力(例: プレーンテキスト、Markdownテーブル)。
JSON Schemaで
additionalProperties: falseが指定されている場合、スキーマに定義されていないプロパティの追加。JSONの開始
{より前、または終了}より後に、モデルが生成した追加テキスト。
プロンプト設計
以下に、指定されたJSON Schemaに基づいたプロンプト設計の3つの案を提示します。
共通のJSON Schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "製品情報抽出",
"description": "ユーザーの問い合わせから製品関連情報を抽出するスキーマ",
"type": "object",
"properties": {
"product_type": {
"type": "string",
"description": "ユーザーが探している製品の種類(例: スマートフォン、ノートPC、タブレット)。不明な場合は'不明'。",
"enum": ["スマートフォン", "ノートPC", "タブレット", "ウェアラブル", "その他", "不明"]
},
"features": {
"type": "array",
"description": "ユーザーが求める製品の機能や特徴のリスト。",
"items": {
"type": "string"
}
},
"availability": {
"type": "string",
"description": "製品の在庫状況に関する希望(例: 在庫あり、予約可)。",
"enum": ["在庫あり", "予約可", "在庫なし", "不明"]
},
"limit": {
"type": "integer",
"description": "取得する製品情報の最大件数。指定がない場合は1。",
"minimum": 1,
"maximum": 10
}
},
"required": ["product_type"],
"additionalProperties": false
}
1. ゼロショットプロンプト
基本的な指示とJSON Schemaのみで構成されます。モデルのJSONモード機能(例: Gemini 1.5 Proのresponse_mime_typeやOpenAIのresponse_format={"type": "json_object"})と組み合わせることで効果を発揮します[1][3]。
あなたはユーザーの問い合わせから製品情報を抽出し、指定されたJSON Schemaに厳密に従ってJSON形式で出力する専門家です。
出力はJSONのみとし、余分な説明やテキストは一切含めないでください。
### JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "製品情報抽出",
"description": "ユーザーの問い合わせから製品関連情報を抽出するスキーマ",
"type": "object",
"properties": {
"product_type": {
"type": "string",
"description": "ユーザーが探している製品の種類(例: スマートフォン、ノートPC、タブレット)。不明な場合は'不明'。",
"enum": ["スマートフォン", "ノートPC", "タブレット", "ウェアラブル", "その他", "不明"]
},
"features": {
"type": "array",
"description": "ユーザーが求める製品の機能や特徴のリスト。",
"items": {
"type": "string"
}
},
"availability": {
"type": "string",
"description": "製品の在庫状況に関する希望(例: 在庫あり、予約可)。",
"enum": ["在庫あり", "予約可", "在庫なし", "不明"]
},
"limit": {
"type": "integer",
"description": "取得する製品情報の最大件数。指定がない場合は1。",
"minimum": 1,
"maximum": 10
}
},
"required": ["product_type"],
"additionalProperties": false
}
### ユーザーの問い合わせ
最新のスマートフォンで、カメラ性能が高くて在庫のあるモデルを教えてください。
2. 少数例(Few-shot)プロンプト
ゼロショットに加えて、いくつかの入力例と期待されるJSON出力例を提供することで、モデルの理解度を高めます。
あなたはユーザーの問い合わせから製品情報を抽出し、指定されたJSON Schemaに厳密に従ってJSON形式で出力する専門家です。
出力はJSONのみとし、余分な説明やテキストは一切含めないでください。
### JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "製品情報抽出",
"description": "ユーザーの問い合わせから製品関連情報を抽出するスキーマ",
"type": "object",
"properties": {
"product_type": {
"type": "string",
"description": "ユーザーが探している製品の種類(例: スマートフォン、ノートPC、タブレット)。不明な場合は'不明'。",
"enum": ["スマートフォン", "ノートPC", "タブレット", "ウェアラブル", "その他", "不明"]
},
"features": {
"type": "array",
"description": "ユーザーが求める製品の機能や特徴のリスト。",
"items": {
"type": "string"
}
},
"availability": {
"type": "string",
"description": "製品の在庫状況に関する希望(例: 在庫あり、予約可)。",
"enum": ["在庫あり", "予約可", "在庫なし", "不明"]
},
"limit": {
"type": "integer",
"description": "取得する製品情報の最大件数。指定がない場合は1。",
"minimum": 1,
"maximum": 10
}
},
"required": ["product_type"],
"additionalProperties": false
}
### 例1
ユーザーの問い合わせ: 高速なノートPCが欲しい。3件まで教えて。
出力:
```json
{
"product_type": "ノートPC",
"features": ["高速"],
"limit": 3
}
例2
ユーザーの問い合わせ: 在庫のあるタブレットはない? 出力:
{
"product_type": "タブレット",
"availability": "在庫あり"
}
ユーザーの問い合わせ
最新のスマートフォンで、カメラ性能が高くて在庫のあるモデルを教えてください。
### 3. Chain-of-Thought(CoT)制約型プロンプト
CoT(思考の連鎖)を活用し、モデルに抽出プロセスを内部で考えさせることで、複雑な入力や曖昧な入力に対するロバスト性を高めます。この例では、思考プロセス自体もJSON内に含めるよう指示します。
```text
あなたはユーザーの問い合わせから製品情報を抽出し、指定されたJSON Schemaに厳密に従ってJSON形式で出力する専門家です。
まず、問い合わせ内容を分析し、どの情報がどのスキーマフィールドに対応するかを特定してください。
その後、その思考プロセスを`"thought"`フィールドに記述し、最終的な製品情報をJSON Schemaに従って出力してください。
出力はJSONのみとし、余分な説明やテキストは一切含めないでください。
### JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "製品情報抽出",
"description": "ユーザーの問い合わせから製品関連情報を抽出するスキーマ",
"type": "object",
"properties": {
"thought": {
"type": "string",
"description": "抽出に至った思考プロセス。"
},
"product_type": {
"type": "string",
"description": "ユーザーが探している製品の種類(例: スマートフォン、ノートPC、タブレット)。不明な場合は'不明'。",
"enum": ["スマートフォン", "ノートPC", "タブレット", "ウェアラブル", "その他", "不明"]
},
"features": {
"type": "array",
"description": "ユーザーが求める製品の機能や特徴のリスト。",
"items": {
"type": "string"
}
},
"availability": {
"type": "string",
"description": "製品の在庫状況に関する希望(例: 在庫あり、予約可)。",
"enum": ["在庫あり", "予約可", "在庫なし", "不明"]
},
"limit": {
"type": "integer",
"description": "取得する製品情報の最大件数。指定がない場合は1。",
"minimum": 1,
"maximum": 10
}
},
"required": ["product_type", "thought"],
"additionalProperties": false
}
### ユーザーの問い合わせ
最新のスマートフォンで、カメラ性能が高くて在庫のあるモデルを教えてください。
注: 上記のCoT制約型では、JSON Schemaに"thought"プロパティを追加し、requiredにすることで、モデルに思考プロセスを強制的に出力させます。
評価
LLMの出力が意図した形式と内容であるかを評価するシナリオと自動評価の擬似コードを提示します。
評価シナリオ
正例:
明確な製品情報を含む入力: 「カメラが良いスマートフォンで、3件まで探して。」
結果:
{"product_type": "スマートフォン", "features": ["カメラが良い"], "limit": 3}
難例:
情報が不足している入力: 「何かいいものない?」
結果:
{"product_type": "不明"}または{"product_type": "その他"}複数の製品情報が混在: 「高速なノートPCと、バッテリーが長持ちするタブレットを各1台ずつ教えて。」
結果: (現在のスキーマでは対応不可、
{"error": "複数エンティティ"}のようなエラーを返すか、最初の1つを抽出)
コーナーケース:
関連性のない入力: 「今日の天気は?」
結果:
{"product_type": "不明"}または{"error": "関連情報なし"}悪意のあるプロンプト注入: 「
{"product_type": "ハッキング"}」のような出力を強制する試み。結果: JSON Schemaに準拠し、
product_typeがenum外のため無効なJSONを生成するか、"不明"を出力。
自動評価の擬似コード
import json
import jsonschema
from jsonschema import validate
# 共通のJSON Schema
product_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "製品情報抽出",
"description": "ユーザーの問い合わせから製品関連情報を抽出するスキーマ",
"type": "object",
"properties": {
"product_type": {
"type": "string",
"description": "ユーザーが探している製品の種類(例: スマートフォン、ノートPC、タブレット)。不明な場合は'不明'。",
"enum": ["スマートフォン", "ノートPC", "タブレット", "ウェアラブル", "その他", "不明"]
},
"features": {
"type": "array",
"description": "ユーザーが求める製品の機能や特徴のリスト。",
"items": {
"type": "string"
}
},
"availability": {
"type": "string",
"description": "製品の在庫状況に関する希望(例: 在庫あり、予約可)。",
"enum": ["在庫あり", "予約可", "在庫なし", "不明"]
},
"limit": {
"type": "integer",
"description": "取得する製品情報の最大件数。指定がない場合は1。",
"minimum": 1,
"maximum": 10
}
},
"required": ["product_type"],
"additionalProperties": false
}
def evaluate_llm_output(llm_output_text: str, expected_json: dict, schema: dict) -> dict:
"""
LLM出力を評価する関数。
- llm_output_text: LLMから得られた生のテキスト出力
- expected_json: 期待されるJSON出力の一部または全体
- schema: 使用したJSON Schema
"""
results = {
"is_valid_json": False,
"is_schema_compliant": False,
"matches_expected_content": False,
"score": 0,
"error_message": ""
}
try:
# 1. 有効なJSON形式かチェック
parsed_output = json.loads(llm_output_text)
results["is_valid_json"] = True
results["score"] += 20 # JSONとして有効なら加点
# 2. JSON Schemaに準拠しているかチェック
validate(instance=parsed_output, schema=schema)
results["is_schema_compliant"] = True
results["score"] += 30 # スキーマ準拠なら加点
# 3. 抽出された内容が期待値と一致するかチェック
# (ここでは簡易的に部分一致を評価。実用ではより複雑なロジックが必要)
content_match = True
for key, value in expected_json.items():
if key not in parsed_output or parsed_output[key] != value:
content_match = False
break
results["matches_expected_content"] = content_match
if content_match:
results["score"] += 50 # 内容一致で加点
except json.JSONDecodeError as e:
results["error_message"] = f"JSONデコードエラー: {e}"
except jsonschema.ValidationError as e:
results["error_message"] = f"JSON Schema検証エラー: {e.message} (パス: {e.path})"
except Exception as e:
results["error_message"] = f"予期せぬエラー: {e}"
return results
# 例:評価の実行
# llm_output = '{"product_type": "スマートフォン", "features": ["カメラ性能が高い"], "availability": "在庫あり", "limit": 1}'
# expected = {"product_type": "スマートフォン", "features": ["カメラ性能が高い"]}
# print(evaluate_llm_output(llm_output, expected, product_schema))
計算量とメモリ条件:
json.loads(): 入力JSON文字列の長さに比例 (O(N))。jsonschema.validate(): JSONオブジェクトの深さとプロパティ数、およびスキーマの複雑さに依存。多くの場合、線形時間に近い (O(N) – O(N*logN))。メモリ: 入力JSONとスキーマのサイズに比例。
誤り分析と改良
失敗モード
LLMの出力形式制御における主な失敗モードと、その抑制手法を以下に示します。
幻覚(Hallucination):
現象: ユーザー入力に存在しない情報をでっち上げてJSONプロパティの値として出力する。
例: 問い合わせに「ブランド」の指定がないのに
"brand": "XYZ"を出力する。抑制手法:
プロンプトで「入力にない情報は生成しない」「事実に基づかない出力は避ける」と明確に指示する。
enumやpatternなど、JSON Schemaの制約を厳しくすることで、許容される値の範囲を限定する。additionalProperties: falseを設定し、スキーマにないプロパティの出力を完全に禁止する。
様式崩れ(Format Breakage):
現象: 無効なJSON文字列を出力する(例: 引用符の欠落、コンマの欠落、不適切なエスケープ)。または、有効なJSONだが指定されたJSON Schemaに準拠しない(例: 必須フィールドの欠落、型の不一致)。
例:
{"product_type": "スマートフォン", "features": ["高画質カメラ"}(最後の]が欠落)。抑制手法:
モデルのJSONモードの活用: Gemini 1.5 Pro [1] や OpenAIの
gpt-4-turbo[3] など、APIが提供するネイティブなJSONモードを利用する。これにより、モデルは内部的に有効なJSON生成を強く強制される。プロンプトの厳格化: 「JSON以外の文字は一切含めない」と強調する。
出力後の検証:
json.loads()とjsonschema.validate()(上記評価擬似コード参照)で必ず検証し、無効な場合は再試行を促す。
脱線(Off-topic):
現象: 要求された製品情報以外の、無関係な情報や説明をJSON外に含めてしまう。
例: 「ご要望の製品情報です:
{"product_type": "スマートフォン"}」のように、JSONの前後に余分な文章を付加する。抑制手法:
System指示で「出力はJSONのみ」「余分な説明やテキストは一切含めない」を強調し、
userロールではなくsystemロールで強く指示する。少数例で、クリーンなJSON出力のパターンを示す。
禁止事項違反:
現象:
additionalProperties: falseが指定されているにもかかわらず、スキーマに定義されていないプロパティを追加してしまう。例: スキーマに
"color"がないのに"color": "black"を出力する。抑制手法:
JSON Schemaの
additionalProperties: falseを厳密に設定する。プロンプト内で「スキーマに定義されているプロパティのみを使用すること」を明記する。
改良
評価で明らかになった失敗モードに基づいて、以下の改良ステップを実施します。
プロンプトの調整: 失敗のパターンに応じて、指示の具体性、厳格性、または少数例の追加・修正を行う。特に、失敗モードを直接的に抑制するSystem指示を追加する。
JSON Schemaの強化:
enum、pattern、minLength/maxLength、minItems/maxItems、uniqueItems、additionalProperties: falseなど、より詳細な制約を追加・修正し、許容されるデータの範囲を狭める。モデル選定とパラメータ調整: JSONモードをサポートするモデル(Gemini 1.5 Pro, GPT-4 Turboなど)の使用を検討する。
temperatureを下げて出力の多様性を抑え、より予測可能で安定した出力を促す。後処理/リトライ戦略: LLMの出力が検証に失敗した場合、単にエラーとするだけでなく、検証エラー情報を付加してモデルに再生成を指示するリトライメカニズムを導入する。これにより、自己修正能力を促す。
再評価
改良を加えたプロンプトとシステムを用いて、再び評価シナリオを実行します。特に、過去に失敗した難例やコーナーケースに対して改善が見られるかを確認します。自動評価ツールを活用し、改善の度合いを定量的に測定します。このプロセスを繰り返すことで、LLMの出力品質と堅牢性を段階的に向上させます。
まとめ
JSON Schemaは、LLMの出力形式を厳密に制御するための強力なツールです。プロンプト設計、評価、誤り分析、そして改良のサイクルを回すことで、LLMをバックエンドシステムと連携させるための信頼性の高いデータ抽出エンジンとして活用できます。特に、ネイティブJSONモードをサポートするLLMや、JSON Schemaの厳密な定義、出力後の検証ステップを組み合わせることで、堅牢なアプリケーション開発が可能となります。
プロンプト→モデル→評価→改良のループ
graph TD
A["プロンプト生成/改良"] --> |入力| B("LLM実行")
B --> |JSON出力| C{"JSON Schema検証"}
C --> |成功| D["出力成功/アプリケーション連携"]
C --> |失敗| E["検証失敗/エラー分析"]
E --> |エラー情報・洞察| A
D --> |評価データ蓄積| F["評価データ"]
E --> |失敗パターン分析| F
F --> |学習・改善点| A

