<p><!--META
{
"title": "LLM出力のJSON/YAML形式制御:確実な構造化データ生成のためのプロンプト戦略と評価",
"primary_category": "LLM",
"secondary_categories": ["プロンプト工学", "M LOps"],
"tags": ["LLM", "JSON", "YAML", "プロンプト", "構造化出力", "評価", "Gemini", "OpenAI"],
"summary": "LLMからJSON/YAML形式の構造化データを確実に出力させるためのプロンプト設計、評価、誤り分析、改良戦略を解説。入出力契約から自動評価までを網羅。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"LLMのJSON/YAML出力制御は、API連携や自動化の要。確実な構造化データ生成のためのプロンプト戦略、評価、改良のサイクルを徹底解説します。
#LLM #プロンプト工学
#JSON", "hashtags":["#LLM","#プロンプト工学"]},
"link_hints": ["https://developers.google.com/gemini/docs/reference/rest/v1beta/models/generateContent#response_mime_type", "https://platform.openai.com/docs/guides/text-generation/json-mode"]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">LLM出力のJSON/YAML形式制御:確実な構造化データ生成のためのプロンプト戦略と評価</h1>
<p>大規模言語モデル(LLM)は、自然言語の生成だけでなく、構造化されたデータ形式であるJSONやYAMLの生成においても優れた能力を発揮します。これにより、LLMの応用範囲は大幅に広がり、外部APIの呼び出し、設定ファイルの動的生成、非構造化テキストからのデータ抽出など、多様な自動化ユースケースに利用可能になります。本記事では、LLMからJSON/YAML形式の構造化データを確実に出力させるためのプロンプト設計、評価、誤り分析、および改良戦略について解説します。</p>
<h2 class="wp-block-heading">ユースケース定義</h2>
<p>LLMがJSON/YAML形式で出力する主要なユースケースは以下の通りです。</p>
<ul class="wp-block-list">
<li><p><strong>API呼び出しパラメータの自動生成</strong>: ユーザーの自然言語指示を元に、外部APIに必要なJSON形式の引数を生成する。</p></li>
<li><p><strong>設定ファイルの動的作成</strong>: システム設定やアプリケーションのデプロイ設定(YAML形式など)を、特定の要件に基づいてLLMに生成させる。</p></li>
<li><p><strong>非構造化テキストからのデータ抽出</strong>: ニュース記事やドキュメントから特定のエンティティ(人物名、地名、日付、数値など)を抽出し、構造化されたJSONオブジェクトとして出力する。</p></li>
<li><p><strong>構造化されたレポートやログの生成</strong>: LLMの分析結果を、機械処理が容易なJSON形式で出力し、後続システムとの連携を可能にする。</p></li>
</ul>
<h2 class="wp-block-heading">制約付き仕様化(入出力契約)</h2>
<p>LLMからの構造化出力の信頼性を高めるためには、厳格な入出力契約を定義することが不可欠です。</p>
<h3 class="wp-block-heading">フォーマットの定義</h3>
<ul class="wp-block-list">
<li><p><strong>JSON</strong>: RFC 8259準拠の有効なJSONオブジェクトまたはJSON配列とします。特定のJSONスキーマに従うことを必須とします。例えば、<code>{"type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "integer"}}}</code> のようなスキーマ定義を出力指示に含めます。</p></li>
<li><p><strong>YAML</strong>: YAML 1.2準拠の有効なドキュメントとします。JSONと同様に、特定のスキーマや構造に従うことを指示します。YAMLはJSONのスーパーセットであるため、JSONとほぼ同様の戦略が適用可能です [5]。</p></li>
</ul>
<h3 class="wp-block-heading">失敗時の挙動</h3>
<ul class="wp-block-list">
<li><p>モデルが指定された形式(JSON/YAML)に準拠できない場合、有効なJSON/YAMLの最小限の構造(例: <code>{}</code> または <code>[]</code>)を返すか、エラーを示す特定の構造を返します。</p></li>
<li><p>Gemini APIでは <code>response_mime_type="application/json"</code> パラメータ [1] を、OpenAI APIでは <code>response_format={"type": "json_object"}</code> パラメータ [2] を設定することで、モデルが有効なJSONを出力しない場合にAPIがエラーを返すように強制できます。これにより、無効な出力をアプリケーション層でハンドリングする手間を省き、エラー応答に対するリトライ戦略を講じることが推奨されます。</p></li>
</ul>
<h3 class="wp-block-heading">禁止事項</h3>
<ul class="wp-block-list">
<li><p>JSON/YAMLのブロック前後に、モデルによる説明文やコメントを付加することを禁止します。</p></li>
<li><p>プロンプトで指示されていない、無関係な情報や幻覚を含むデータを生成することを禁止します。</p></li>
<li><p>出力は、指定されたスキーマの範囲に厳密に限定されるべきであり、余計なフィールドを含めてはなりません。</p></li>
</ul>
<h2 class="wp-block-heading">プロンプト設計</h2>
<p>確実なJSON/YAML出力を得るために、以下の3種類のプロンプト設計を検討します。特に、モデルの温度(temperature)は低めに設定(0.0~0.3程度)し、出力の一貫性を高めることが重要です。</p>
<h3 class="wp-block-heading">1. ゼロショットプロンプト(Zero-shot Prompt)</h3>
<p>出力形式とスキーマを明確に指示する最も基本的なアプローチです。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
以下の情報をJSONオブジェクトとして出力してください。
対象テキスト: "東京の天気は晴れで、気温は28度です。明日は雨が降るでしょう。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string", "description": "都市名"},
"weather": {"type": "string", "description": "現在の天気"},
"temperature": {"type": "integer", "description": "現在の気温(摂氏)"},
"forecast_tomorrow": {"type": "string", "description": "明日の天気予報"}
},
"required": ["city", "weather", "temperature", "forecast_tomorrow"]
}
出力JSON:
</pre>
</div>
<h3 class="wp-block-heading">2. 少数例プロンプト(Few-shot Prompt)</h3>
<p>期待する入出力ペアをいくつか提示し、モデルに学習させます。特に複雑な構造や曖昧な指示の場合に有効です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
---
対象テキスト: "大阪のイベント情報:来月10日にAIカンファレンスが開催されます。"
JSONスキーマ:
{
"type": "object",
"properties": {
"event_name": {"type": "string"},
"location": {"type": "string"},
"date": {"type": "string", "format": "date"}
},
"required": ["event_name", "location", "date"]
}
出力JSON:
{
"event_name": "AIカンファレンス",
"location": "大阪",
"date": "2024-08-10"
}
---
対象テキスト: "札幌の特産品はラーメンとビールです。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string"},
"specialties": {"type": "array", "items": {"type": "string"}}
},
"required": ["city", "specialties"]
}
出力JSON:
{
"city": "札幌",
"specialties": ["ラーメン", "ビール"]
}
---
対象テキスト: "京都の観光名所は金閣寺と清水寺、開催中の祭りは祇園祭です。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string"},
"attractions": {"type": "array", "items": {"type": "string"}},
"current_festival": {"type": "string"}
},
"required": ["city", "attractions"]
}
出力JSON:
</pre>
</div>
<h3 class="wp-block-heading">3. Chain-of-Thought制約型プロンプト(CoT-constrained Prompt)</h3>
<p>モデルに思考プロセスを段階的に記述させ、最終的にJSON/YAMLを生成させることで、複雑な抽出や変換の精度を高めます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
以下の思考プロセスを経て、最終的にJSONを出力してください。
1. 対象テキストから必要な情報を特定します。
2. 特定した情報をJSONスキーマの各フィールドにマッピングします。
3. マッピングした情報でJSONオブジェクトを構築します。
対象テキスト: "プロジェクトAは現在進行中。担当は田中。期限は2024年12月末。"
JSONスキーマ:
{
"type": "object",
"properties": {
"project_name": {"type": "string"},
"status": {"type": "string", "enum": ["進行中", "完了", "保留"]},
"assigned_to": {"type": "string"},
"due_date": {"type": "string", "format": "date"}
},
"required": ["project_name", "status", "assigned_to", "due_date"]
}
思考プロセス:
1. 対象テキストから「プロジェクトA」「進行中」「田中」「2024年12月末」を特定。
2. project_name: プロジェクトA, status: 進行中, assigned_to: 田中, due_date: 2024-12-31 にマッピング。
3. 上記情報でJSONを構築。
出力JSON:
</pre>
</div>
<h2 class="wp-block-heading">評価</h2>
<p>LLMの構造化出力は、その後のシステム連携において非常に重要であるため、厳密な評価が必要です。</p>
<h3 class="wp-block-heading">評価シナリオ</h3>
<ul class="wp-block-list">
<li><p><strong>正例</strong>: 簡単なデータ構造、一般的な入力。</p>
<ul>
<li>例: 人物名と年齢の抽出、単純な天気情報。</li>
</ul></li>
<li><p><strong>難例</strong>: 複雑なネスト構造、エッジケース、特殊文字を含む入力。</p>
<ul>
<li>例: 複数のイベント情報を含むテキストからの配列抽出、ヌル値や空リストが期待されるケース、<code>"</code>や<code>\</code>を含む文字列。</li>
</ul></li>
<li><p><strong>コーナーケース</strong>: 曖昧な入力、情報不足な入力。</p>
<ul>
<li>例: 必須フィールドがテキストに存在しない場合、複数の解釈が可能なテキスト。</li>
</ul></li>
</ul>
<h3 class="wp-block-heading">自動評価の擬似コード</h3>
<p>自動評価は、以下のステップで実施できます(Pythonを想定)。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">import json
import jsonschema
import re
import yaml # YAMLの場合
def validate_json_output(output_string: str, json_schema: dict) -> dict:
"""
LLMのJSON出力を検証する関数
Args:
output_string: LLMから得られたJSON文字列
json_schema: 期待されるJSONスキーマ
Returns:
検証結果を含む辞書
例: {"is_valid_format": True, "is_valid_schema": True, "details": "Success"}
"""
result = {
"is_valid_format": False,
"is_valid_schema": False,
"content_matches": {},
"details": ""
}
# 1. JSON構文の検証
try:
parsed_json = json.loads(output_string)
result["is_valid_format"] = True
except json.JSONDecodeError as e:
result["details"] = f"JSON形式が不正: {e}"
return result
# 2. JSONスキーマの検証
try:
jsonschema.validate(instance=parsed_json, schema=json_schema)
result["is_valid_schema"] = True
except jsonschema.exceptions.ValidationError as e:
result["details"] = f"JSONスキーマ検証エラー: {e.message}"
return result
# 3. 特定のビジネスロジック/内容の検証(例: 特定のキーの有無、値の範囲、正規表現)
# 例: "city"キーが存在し、かつ文字列であるか
if "city" in parsed_json and isinstance(parsed_json["city"], str):
result["content_matches"]["city_exists_and_is_string"] = True
else:
result["content_matches"]["city_exists_and_is_string"] = False
result["details"] += " 'city'フィールドが期待通りではありません。"
# 例: "temperature"が数値であり、-50から50の範囲内か
if "temperature" in parsed_json and isinstance(parsed_json["temperature"], (int, float)) and -50 <= parsed_json["temperature"] <= 50:
result["content_matches"]["temperature_in_range"] = True
else:
result["content_matches"]["temperature_in_range"] = False
result["details"] += " 'temperature'フィールドが期待通りではありません。"
if result["is_valid_format"] and result["is_valid_schema"] and all(result["content_matches"].values()):
result["details"] = "すべての検証に成功しました。"
return result
# YAMLの場合の例
def validate_yaml_output(output_string: str, yaml_schema: dict) -> dict:
# YAMLの構文検証 (PyYAMLを使用)
try:
parsed_yaml = yaml.safe_load(output_string)
# 以降はJSONスキーマ検証と同様のロジックを適用可能(YAMLスキーマバリデータも存在するが、ここでは省略)
except yaml.YAMLError as e:
pass # エラーハンドリング
return {"is_valid_format": False, "is_valid_schema": False, "details": ""} # 仮の戻り値
# 評価の擬似ルーブリック
# 採点基準:
# - JSON構文の有効性: 40点
# - JSONスキーマへの準拠: 40点
# - 特定のキーの存在と値の妥当性(ビジネスロジック): 20点
# 合計100点
</pre>
</div>
<ul class="wp-block-list">
<li><p><strong>入力</strong>: LLM出力文字列、期待されるJSON/YAMLスキーマ。</p></li>
<li><p><strong>前提</strong>: <code>json</code>モジュール、<code>jsonschema</code>ライブラリ(<code>pip install jsonschema</code>)、<code>PyYAML</code>ライブラリ(<code>pip install PyYAML</code>)。</p></li>
<li><p><strong>計算量</strong>: JSONパースとスキーマ検証は入力サイズに比例。ビジネスロジック検証は定数時間または入力サイズに比例。</p></li>
<li><p><strong>メモリ条件</strong>: 入力JSON/YAMLのサイズに依存。</p></li>
</ul>
<h2 class="wp-block-heading">誤り分析と抑制手法</h2>
<p>LLMからの構造化出力は、いくつかの失敗モードに陥る可能性があります。これらの分析と抑制手法を以下に示します。</p>
<h3 class="wp-block-heading">失敗モード</h3>
<ul class="wp-block-list">
<li><p><strong>幻覚(Hallucination)</strong>: プロンプトにない情報や事実に基づかない情報をJSON/YAMLに含める。</p></li>
<li><p><strong>様式崩れ(Format Deviation)</strong>: 有効なJSON/YAML形式ではない文字列(例: 末尾のカンマ忘れ、括弧の不一致)を出力する。</p></li>
<li><p><strong>脱線(Off-topic Generation)</strong>: JSON/YAMLブロックの前後に追加の説明文や無関係なテキストを生成する。</p></li>
<li><p><strong>禁止事項違反</strong>: 定義されたスキーマにないフィールドを含めたり、必須フィールドを欠落させたりする。</p></li>
</ul>
<h3 class="wp-block-heading">抑制手法</h3>
<ul class="wp-block-list">
<li><p><strong>System指示の厳格化</strong>: Systemプロンプトで「JSON形式のみを出力すること。余計なテキストは一切含めないこと」など、厳格な制約を明示的に指示します。</p></li>
<li><p><strong>JSONモードの利用</strong>: Google Gemini APIの <code>response_mime_type="application/json"</code> [1] や OpenAI APIの <code>response_format={"type": "json_object"}</code> [2] のように、モデル固有のJSON出力強制機能を利用します。これにより、モデルは有効なJSONのみを生成するように強く誘導されます。</p></li>
<li><p><strong>出力検証ステップ</strong>: 上記の自動評価擬似コードのように、LLMの出力を受け取った後、必ずJSON/YAML構文とスキーマの検証を実行します。</p></li>
<li><p><strong>リトライ戦略</strong>: 検証に失敗した場合、エラーメッセージとともにプロンプトを再構築し、再度LLMに生成を要求するリトライ戦略を実装します。例えば、「無効なJSONが出力されました。<code>[エラーメッセージ]</code> に注意し、厳密にJSONスキーマに従って再出力してください。」</p></li>
<li><p><strong>構造化された少数例</strong>: 特に複雑な出力の場合、期待する正確なJSON/YAML構造を示す少数例を提供することで、モデルの理解を深めます。</p></li>
<li><p><strong>より高性能なモデルの利用</strong>: JSON/YAML出力の品質はモデルの能力に依存するため、Gemini 1.5 ProやGPT-4oなど、より高性能なモデルの使用を検討します。</p></li>
</ul>
<h2 class="wp-block-heading">改良</h2>
<p>誤り分析の結果に基づき、以下の点を中心にプロンプトやシステムを改良します。</p>
<ul class="wp-block-list">
<li><p><strong>プロンプトの具体性向上</strong>: 失敗したケースについて、プロンプトの指示をより具体的、かつ明確に修正します。特にスキーマ定義をより詳細に記述します。</p></li>
<li><p><strong>禁止事項の強調</strong>: 失敗モードで確認された禁止事項違反に対し、プロンプト内でその禁止事項を強く明示し、ペナルティを課すような指示を追加することも検討します。</p></li>
<li><p><strong>モデル選択の調整</strong>: 特定のモデルで頻繁に形式崩れが発生する場合、別のモデルへの切り替えや、ファインチューニングの検討を行います。</p></li>
<li><p><strong>評価基準の洗練</strong>: 誤り分析で発見された新たな失敗パターンに対応するため、自動評価のルーブリックや正規表現、スキーマ定義を更新します。</p></li>
</ul>
<h2 class="wp-block-heading">再評価</h2>
<p>改良されたプロンプトやシステムは、既存の評価シナリオ(正例、難例、コーナーケース)に加えて、特に改良対象となった失敗ケースを重点的に再評価します。この反復的なプロセスにより、LLMの構造化出力の信頼性と堅牢性を継続的に向上させます。</p>
<h2 class="wp-block-heading">まとめ</h2>
<p>LLMからのJSON/YAML形式制御は、高度な自動化とシステム連携を実現する上で不可欠です。本記事では、入出力契約の定義から、ゼロショット、少数例、Chain-of-Thought制約型プロンプトの設計、そして構文・スキーマ・内容検証を組み合わせた自動評価、さらには失敗モードの分析と抑制手法まで、一連のプロセスを詳細に解説しました。これらの戦略を組み合わせ、継続的な評価と改良のループを回すことで、LLMが常に期待通りの構造化データを出力し、その価値を最大限に引き出すことが可能になります。</p>
<hr/>
<h3 class="wp-block-heading">プロンプト→モデル→評価→改良のループ</h3>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph LR
A["要件定義"] --> B("入出力契約定義");
B --> C["プロンプト設計"];
C --> D("LLMによる出力生成");
D --> E["出力結果"];
E --> F{"評価フェーズ"};
F -- |検証成功| --> G["目的達成"];
F -- |検証失敗| --> H["誤り分析"];
H --> I["プロンプト改良"];
I --> C;
</pre></div>
<hr/>
<p><strong>参考文献</strong>
[1] Google Developers. “models.generateContent method”. <a href="https://developers.google.com/gemini/docs/reference/rest/v1beta/models/generateContent#response_mime_type">https://developers.google.com/gemini/docs/reference/rest/v1beta/models/generateContent#response_mime_type</a> (更新日: 2024-05-15, Google Developers)
[2] OpenAI Platform. “JSON mode”. <a href="https://platform.openai.com/docs/guides/text-generation/json-mode">https://platform.openai.com/docs/guides/text-generation/json-mode</a> (更新日: 2023-11-06, OpenAI Platform)
[3] LlamaIndex Documentation. “JSON Output Parser”. <a href="https://docs.llamaindex.ai/en/stable/module_guides/prompting/output_parsing/json_output_parser/">https://docs.llamaindex.ai/en/stable/module_guides/prompting/output_parsing/json_output_parser/</a> (更新日: 2024-07-20, LlamaIndex Project)
[4] LangChain Documentation. “JSON output parser”. <a href="https://python.langchain.com/docs/modules/model_io/output_parsers/json">https://python.langchain.com/docs/modules/model_io/output_parsers/json</a> (更新日: 2024-07-18, LangChain Project)
[5] TechTarget. “What is YAML? Understanding Yet Another Markup Language”. <a href="https://www.techtarget.com/whatis/definition/YAML">https://www.techtarget.com/whatis/definition/YAML</a> (更新日: 2023-11-20, TechTarget)</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
LLM出力のJSON/YAML形式制御:確実な構造化データ生成のためのプロンプト戦略と評価
大規模言語モデル(LLM)は、自然言語の生成だけでなく、構造化されたデータ形式であるJSONやYAMLの生成においても優れた能力を発揮します。これにより、LLMの応用範囲は大幅に広がり、外部APIの呼び出し、設定ファイルの動的生成、非構造化テキストからのデータ抽出など、多様な自動化ユースケースに利用可能になります。本記事では、LLMからJSON/YAML形式の構造化データを確実に出力させるためのプロンプト設計、評価、誤り分析、および改良戦略について解説します。
ユースケース定義
LLMがJSON/YAML形式で出力する主要なユースケースは以下の通りです。
API呼び出しパラメータの自動生成: ユーザーの自然言語指示を元に、外部APIに必要なJSON形式の引数を生成する。
設定ファイルの動的作成: システム設定やアプリケーションのデプロイ設定(YAML形式など)を、特定の要件に基づいてLLMに生成させる。
非構造化テキストからのデータ抽出: ニュース記事やドキュメントから特定のエンティティ(人物名、地名、日付、数値など)を抽出し、構造化されたJSONオブジェクトとして出力する。
構造化されたレポートやログの生成: LLMの分析結果を、機械処理が容易なJSON形式で出力し、後続システムとの連携を可能にする。
制約付き仕様化(入出力契約)
LLMからの構造化出力の信頼性を高めるためには、厳格な入出力契約を定義することが不可欠です。
フォーマットの定義
JSON: RFC 8259準拠の有効なJSONオブジェクトまたはJSON配列とします。特定のJSONスキーマに従うことを必須とします。例えば、{"type": "object", "properties": {"name": {"type": "string"}, "age": {"type": "integer"}}} のようなスキーマ定義を出力指示に含めます。
YAML: YAML 1.2準拠の有効なドキュメントとします。JSONと同様に、特定のスキーマや構造に従うことを指示します。YAMLはJSONのスーパーセットであるため、JSONとほぼ同様の戦略が適用可能です [5]。
失敗時の挙動
モデルが指定された形式(JSON/YAML)に準拠できない場合、有効なJSON/YAMLの最小限の構造(例: {} または [])を返すか、エラーを示す特定の構造を返します。
Gemini APIでは response_mime_type="application/json" パラメータ [1] を、OpenAI APIでは response_format={"type": "json_object"} パラメータ [2] を設定することで、モデルが有効なJSONを出力しない場合にAPIがエラーを返すように強制できます。これにより、無効な出力をアプリケーション層でハンドリングする手間を省き、エラー応答に対するリトライ戦略を講じることが推奨されます。
禁止事項
JSON/YAMLのブロック前後に、モデルによる説明文やコメントを付加することを禁止します。
プロンプトで指示されていない、無関係な情報や幻覚を含むデータを生成することを禁止します。
出力は、指定されたスキーマの範囲に厳密に限定されるべきであり、余計なフィールドを含めてはなりません。
プロンプト設計
確実なJSON/YAML出力を得るために、以下の3種類のプロンプト設計を検討します。特に、モデルの温度(temperature)は低めに設定(0.0~0.3程度)し、出力の一貫性を高めることが重要です。
1. ゼロショットプロンプト(Zero-shot Prompt)
出力形式とスキーマを明確に指示する最も基本的なアプローチです。
あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
以下の情報をJSONオブジェクトとして出力してください。
対象テキスト: "東京の天気は晴れで、気温は28度です。明日は雨が降るでしょう。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string", "description": "都市名"},
"weather": {"type": "string", "description": "現在の天気"},
"temperature": {"type": "integer", "description": "現在の気温(摂氏)"},
"forecast_tomorrow": {"type": "string", "description": "明日の天気予報"}
},
"required": ["city", "weather", "temperature", "forecast_tomorrow"]
}
出力JSON:
2. 少数例プロンプト(Few-shot Prompt)
期待する入出力ペアをいくつか提示し、モデルに学習させます。特に複雑な構造や曖昧な指示の場合に有効です。
あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
---
対象テキスト: "大阪のイベント情報:来月10日にAIカンファレンスが開催されます。"
JSONスキーマ:
{
"type": "object",
"properties": {
"event_name": {"type": "string"},
"location": {"type": "string"},
"date": {"type": "string", "format": "date"}
},
"required": ["event_name", "location", "date"]
}
出力JSON:
{
"event_name": "AIカンファレンス",
"location": "大阪",
"date": "2024-08-10"
}
---
対象テキスト: "札幌の特産品はラーメンとビールです。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string"},
"specialties": {"type": "array", "items": {"type": "string"}}
},
"required": ["city", "specialties"]
}
出力JSON:
{
"city": "札幌",
"specialties": ["ラーメン", "ビール"]
}
---
対象テキスト: "京都の観光名所は金閣寺と清水寺、開催中の祭りは祇園祭です。"
JSONスキーマ:
{
"type": "object",
"properties": {
"city": {"type": "string"},
"attractions": {"type": "array", "items": {"type": "string"}},
"current_festival": {"type": "string"}
},
"required": ["city", "attractions"]
}
出力JSON:
3. Chain-of-Thought制約型プロンプト(CoT-constrained Prompt)
モデルに思考プロセスを段階的に記述させ、最終的にJSON/YAMLを生成させることで、複雑な抽出や変換の精度を高めます。
あなたはユーザーの指示に基づいて、厳格にJSON形式で情報を提供するアシスタントです。
JSONブロック以外に余計なテキストは含めないでください。
以下の思考プロセスを経て、最終的にJSONを出力してください。
1. 対象テキストから必要な情報を特定します。
2. 特定した情報をJSONスキーマの各フィールドにマッピングします。
3. マッピングした情報でJSONオブジェクトを構築します。
対象テキスト: "プロジェクトAは現在進行中。担当は田中。期限は2024年12月末。"
JSONスキーマ:
{
"type": "object",
"properties": {
"project_name": {"type": "string"},
"status": {"type": "string", "enum": ["進行中", "完了", "保留"]},
"assigned_to": {"type": "string"},
"due_date": {"type": "string", "format": "date"}
},
"required": ["project_name", "status", "assigned_to", "due_date"]
}
思考プロセス:
1. 対象テキストから「プロジェクトA」「進行中」「田中」「2024年12月末」を特定。
2. project_name: プロジェクトA, status: 進行中, assigned_to: 田中, due_date: 2024-12-31 にマッピング。
3. 上記情報でJSONを構築。
出力JSON:
評価
LLMの構造化出力は、その後のシステム連携において非常に重要であるため、厳密な評価が必要です。
評価シナリオ
自動評価の擬似コード
自動評価は、以下のステップで実施できます(Pythonを想定)。
import json
import jsonschema
import re
import yaml # YAMLの場合
def validate_json_output(output_string: str, json_schema: dict) -> dict:
"""
LLMのJSON出力を検証する関数
Args:
output_string: LLMから得られたJSON文字列
json_schema: 期待されるJSONスキーマ
Returns:
検証結果を含む辞書
例: {"is_valid_format": True, "is_valid_schema": True, "details": "Success"}
"""
result = {
"is_valid_format": False,
"is_valid_schema": False,
"content_matches": {},
"details": ""
}
# 1. JSON構文の検証
try:
parsed_json = json.loads(output_string)
result["is_valid_format"] = True
except json.JSONDecodeError as e:
result["details"] = f"JSON形式が不正: {e}"
return result
# 2. JSONスキーマの検証
try:
jsonschema.validate(instance=parsed_json, schema=json_schema)
result["is_valid_schema"] = True
except jsonschema.exceptions.ValidationError as e:
result["details"] = f"JSONスキーマ検証エラー: {e.message}"
return result
# 3. 特定のビジネスロジック/内容の検証(例: 特定のキーの有無、値の範囲、正規表現)
# 例: "city"キーが存在し、かつ文字列であるか
if "city" in parsed_json and isinstance(parsed_json["city"], str):
result["content_matches"]["city_exists_and_is_string"] = True
else:
result["content_matches"]["city_exists_and_is_string"] = False
result["details"] += " 'city'フィールドが期待通りではありません。"
# 例: "temperature"が数値であり、-50から50の範囲内か
if "temperature" in parsed_json and isinstance(parsed_json["temperature"], (int, float)) and -50 <= parsed_json["temperature"] <= 50:
result["content_matches"]["temperature_in_range"] = True
else:
result["content_matches"]["temperature_in_range"] = False
result["details"] += " 'temperature'フィールドが期待通りではありません。"
if result["is_valid_format"] and result["is_valid_schema"] and all(result["content_matches"].values()):
result["details"] = "すべての検証に成功しました。"
return result
# YAMLの場合の例
def validate_yaml_output(output_string: str, yaml_schema: dict) -> dict:
# YAMLの構文検証 (PyYAMLを使用)
try:
parsed_yaml = yaml.safe_load(output_string)
# 以降はJSONスキーマ検証と同様のロジックを適用可能(YAMLスキーマバリデータも存在するが、ここでは省略)
except yaml.YAMLError as e:
pass # エラーハンドリング
return {"is_valid_format": False, "is_valid_schema": False, "details": ""} # 仮の戻り値
# 評価の擬似ルーブリック
# 採点基準:
# - JSON構文の有効性: 40点
# - JSONスキーマへの準拠: 40点
# - 特定のキーの存在と値の妥当性(ビジネスロジック): 20点
# 合計100点
入力: LLM出力文字列、期待されるJSON/YAMLスキーマ。
前提: jsonモジュール、jsonschemaライブラリ(pip install jsonschema)、PyYAMLライブラリ(pip install PyYAML)。
計算量: JSONパースとスキーマ検証は入力サイズに比例。ビジネスロジック検証は定数時間または入力サイズに比例。
メモリ条件: 入力JSON/YAMLのサイズに依存。
誤り分析と抑制手法
LLMからの構造化出力は、いくつかの失敗モードに陥る可能性があります。これらの分析と抑制手法を以下に示します。
失敗モード
幻覚(Hallucination): プロンプトにない情報や事実に基づかない情報をJSON/YAMLに含める。
様式崩れ(Format Deviation): 有効なJSON/YAML形式ではない文字列(例: 末尾のカンマ忘れ、括弧の不一致)を出力する。
脱線(Off-topic Generation): JSON/YAMLブロックの前後に追加の説明文や無関係なテキストを生成する。
禁止事項違反: 定義されたスキーマにないフィールドを含めたり、必須フィールドを欠落させたりする。
抑制手法
System指示の厳格化: Systemプロンプトで「JSON形式のみを出力すること。余計なテキストは一切含めないこと」など、厳格な制約を明示的に指示します。
JSONモードの利用: Google Gemini APIの response_mime_type="application/json" [1] や OpenAI APIの response_format={"type": "json_object"} [2] のように、モデル固有のJSON出力強制機能を利用します。これにより、モデルは有効なJSONのみを生成するように強く誘導されます。
出力検証ステップ: 上記の自動評価擬似コードのように、LLMの出力を受け取った後、必ずJSON/YAML構文とスキーマの検証を実行します。
リトライ戦略: 検証に失敗した場合、エラーメッセージとともにプロンプトを再構築し、再度LLMに生成を要求するリトライ戦略を実装します。例えば、「無効なJSONが出力されました。[エラーメッセージ] に注意し、厳密にJSONスキーマに従って再出力してください。」
構造化された少数例: 特に複雑な出力の場合、期待する正確なJSON/YAML構造を示す少数例を提供することで、モデルの理解を深めます。
より高性能なモデルの利用: JSON/YAML出力の品質はモデルの能力に依存するため、Gemini 1.5 ProやGPT-4oなど、より高性能なモデルの使用を検討します。
改良
誤り分析の結果に基づき、以下の点を中心にプロンプトやシステムを改良します。
プロンプトの具体性向上: 失敗したケースについて、プロンプトの指示をより具体的、かつ明確に修正します。特にスキーマ定義をより詳細に記述します。
禁止事項の強調: 失敗モードで確認された禁止事項違反に対し、プロンプト内でその禁止事項を強く明示し、ペナルティを課すような指示を追加することも検討します。
モデル選択の調整: 特定のモデルで頻繁に形式崩れが発生する場合、別のモデルへの切り替えや、ファインチューニングの検討を行います。
評価基準の洗練: 誤り分析で発見された新たな失敗パターンに対応するため、自動評価のルーブリックや正規表現、スキーマ定義を更新します。
再評価
改良されたプロンプトやシステムは、既存の評価シナリオ(正例、難例、コーナーケース)に加えて、特に改良対象となった失敗ケースを重点的に再評価します。この反復的なプロセスにより、LLMの構造化出力の信頼性と堅牢性を継続的に向上させます。
まとめ
LLMからのJSON/YAML形式制御は、高度な自動化とシステム連携を実現する上で不可欠です。本記事では、入出力契約の定義から、ゼロショット、少数例、Chain-of-Thought制約型プロンプトの設計、そして構文・スキーマ・内容検証を組み合わせた自動評価、さらには失敗モードの分析と抑制手法まで、一連のプロセスを詳細に解説しました。これらの戦略を組み合わせ、継続的な評価と改良のループを回すことで、LLMが常に期待通りの構造化データを出力し、その価値を最大限に引き出すことが可能になります。
プロンプト→モデル→評価→改良のループ
graph LR
A["要件定義"] --> B("入出力契約定義");
B --> C["プロンプト設計"];
C --> D("LLMによる出力生成");
D --> E["出力結果"];
E --> F{"評価フェーズ"};
F -- |検証成功| --> G["目的達成"];
F -- |検証失敗| --> H["誤り分析"];
H --> I["プロンプト改良"];
I --> C;
参考文献
[1] Google Developers. “models.generateContent method”. https://developers.google.com/gemini/docs/reference/rest/v1beta/models/generateContent#response_mime_type (更新日: 2024-05-15, Google Developers)
[2] OpenAI Platform. “JSON mode”. https://platform.openai.com/docs/guides/text-generation/json-mode (更新日: 2023-11-06, OpenAI Platform)
[3] LlamaIndex Documentation. “JSON Output Parser”. https://docs.llamaindex.ai/en/stable/module_guides/prompting/output_parsing/json_output_parser/ (更新日: 2024-07-20, LlamaIndex Project)
[4] LangChain Documentation. “JSON output parser”. https://python.langchain.com/docs/modules/model_io/output_parsers/json (更新日: 2024-07-18, LangChain Project)
[5] TechTarget. “What is YAML? Understanding Yet Another Markup Language”. https://www.techtarget.com/whatis/definition/YAML (更新日: 2023-11-20, TechTarget)
コメント