本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
プロンプト入出力契約と安定化
大規模言語モデル(LLM)を活用したアプリケーション開発において、プロンプトの入出力契約を明確にし、その動作を安定させることはシステムの信頼性と保守性を確保する上で極めて重要です。本記事では、プロンプトの入出力契約の定義から、具体的な設計手法、評価、失敗モード分析、そして継続的な改良プロセスについて解説します。
ユースケース定義
本稿では、カスタマーサポートシステムにおける問い合わせの分類タスクを例とします。ユーザーからの自然言語での問い合わせ文を、事前に定義されたカテゴリ(例: 注文、配送、支払い、技術サポート、その他)に分類し、さらに重要度(高、中、低)を付与するものです。このタスクは、後続の自動ルーティングやエスカレーションの効率化に貢献します。
制約付き仕様化と入出力契約
LLMの出力を予測可能で機械処理可能な形式にするため、厳密な入出力契約を定義します。
入力契約
形式: ユーザーからの自然言語テキスト。最大500文字。
内容: カスタマーサポートへの問い合わせ内容。
禁止事項: 個人を特定できる情報(PII)、違法な内容、またはモデルの安全ポリシーに違反する内容。これらが含まれる場合は、分類を拒否し、特定のメッセージを返す。
出力契約
形式: JSONオブジェクト。以下のキーと値を厳密に保持する。
category: 文字列。定義済みのカテゴリリスト(”注文”, “配送”, “支払い”, “技術サポート”, “その他”)のいずれか一つ。importance: 文字列。定義済みの重要度リスト(”高”, “中”, “低”)のいずれか一つ。reasoning: 文字列。分類結果に至った簡潔な理由(50文字以内)。
失敗時の挙動:
モデルがカテゴリを判断できない場合、
categoryは”その他”とし、importanceは”低”とする。入力契約に違反する内容が検出された場合、JSONオブジェクトではなく、エラーメッセージ
{"error": "Invalid or prohibited input."}を返す。
プロンプト設計
定義された入出力契約に基づき、異なるアプローチでプロンプトを設計します。
ゼロショットプロンプト
モデルにタスクの概要と出力形式のみを指示します。
あなたはカスタマーサポートの問い合わせを分類するAIアシスタントです。
以下のユーザーからの問い合わせを「category」と「importance」に分類し、その理由「reasoning」をJSON形式で出力してください。
カテゴリ: 注文, 配送, 支払い, 技術サポート, その他
重要度: 高, 中, 低
不明な場合はcategoryを「その他」、importanceを「低」としてください。
禁止された内容が含まれる場合は、JSONではなく{"error": "Invalid or prohibited input."}を返してください。
---
問い合わせ:
[USER_QUERY]
---
出力:
少数例(Few-shot)プロンプト
具体的な入出力例を示すことで、モデルに期待される挙動をより明確に伝えます。
あなたはカスタマーサポートの問い合わせを分類するAIアシスタントです。
以下のユーザーからの問い合わせを「category」と「importance」に分類し、その理由「reasoning」をJSON形式で出力してください。
カテゴリ: 注文, 配送, 支払い, 技術サポート, その他
重要度: 高, 中, 低
不明な場合はcategoryを「その他」、importanceを「低」としてください。
禁止された内容が含まれる場合は、JSONではなく{"error": "Invalid or prohibited input."}を返してください。
---
例1:
問い合わせ: 注文した商品がまだ届きません。追跡番号はABC123です。
出力:
{"category": "配送", "importance": "中", "reasoning": "商品の配送状況に関する問い合わせのため。"}
例2:
問い合わせ: 先月分の請求書に誤りがあるようです。詳細を確認したいです。
出力:
{"category": "支払い", "importance": "高", "reasoning": "請求内容の誤りに関する重要な問い合わせのため。"}
例3:
問い合わせ: アカウントのパスワードを忘れてしまいました。リセット方法を教えてください。
出力:
{"category": "技術サポート", "importance": "中", "reasoning": "アカウントの技術的な問題に関する問い合わせのため。"}
---
問い合わせ:
[USER_QUERY]
---
出力:
Chain-of-Thought(CoT)制約型プロンプト
モデルに思考プロセスを段階的に出力させ、その後に最終的なJSON出力を生成させます。これにより、複雑な判断の透明性を高め、誤りを減らすことを目指します。
あなたはカスタマーサポートの問い合わせを分類するAIアシスタントです。
以下のユーザーからの問い合わせを分析し、まずカテゴリと重要度を判断してください。
次に、その判断に至った理由を簡潔に述べ、最後にその結果をJSON形式で出力してください。
カテゴリ: 注文, 配送, 支払い, 技術サポート, その他
重要度: 高, 中, 低
不明な場合はcategoryを「その他」、importanceを「低」としてください。
禁止された内容が含まれる場合は、JSONではなく{"error": "Invalid or prohibited input."}を返してください。
---
問い合わせ:
[USER_QUERY]
---
思考プロセス:
1. 問い合わせ内容を理解する。
2. 最も適切なカテゴリを上記のリストから選定する。
3. 問い合わせの緊急性や影響度から重要度を上記のリストから選定する。
4. 上記の判断に至った根拠をreasoningとしてまとめる。
JSON出力:
評価
プロンプトの有効性を評価するため、複数のシナリオと自動評価の仕組みを構築します。
評価シナリオ
正例: 各カテゴリの明確な問い合わせ文。
- 例: 「新しい商品の注文方法が知りたいです。」 (期待: 注文, 低)
難例: 複数のカテゴリにまたがる可能性のある問い合わせ、または曖昧な表現。
- 例: 「ウェブサイトで見た新製品について質問があります。まだ注文はしていません。」 (期待: その他/技術サポート, 中)
コーナーケース:
非常に短い/長い問い合わせ文。
誤字脱字が多い問い合わせ文。
悪意のある、または禁止事項に該当する問い合わせ文(例: 「私のクレジットカード情報を教えてください。」)。
自動評価(擬似コード)
Pythonでの擬似コードを用いて、生成された出力が契約に準拠しているかを自動で採点します。
import json
import re
def evaluate_llm_output(output_str: str, expected_category: str, expected_importance: str) -> dict:
"""
LLMの出力文字列を評価し、採点結果を返す関数。
出力契約への準拠度と内容の正確性を評価する。
"""
score = 0
feedback = []
# 1. 禁止事項検出時のエラーメッセージチェック
if '{"error": "Invalid or prohibited input."}' in output_str:
if expected_category == "prohibited": # 評価シナリオ側で禁止入力とマークしている場合
score += 100
feedback.append("禁止入力に対する正しいエラー応答。")
else:
feedback.append("予期しないエラー応答。")
return {"score": score, "feedback": feedback, "parsed_output": None}
# 2. JSON形式の検証
try:
parsed_output = json.loads(output_str)
score += 20 # JSON形式が正しい
feedback.append("JSON形式が有効です。")
except json.JSONDecodeError:
feedback.append("JSON形式が無効です。")
return {"score": score, "feedback": feedback, "parsed_output": None}
# 3. キーの存在と型の検証
required_keys = {"category", "importance", "reasoning"}
if required_keys.issubset(parsed_output.keys()):
score += 20
feedback.append("必須キーが全て存在します。")
else:
feedback.append(f"必須キーが不足しています: {required_keys - parsed_output.keys()}")
return {"score": score, "feedback": feedback, "parsed_output": parsed_output} # キー不足で後の評価は難しい
if not all(isinstance(parsed_output[k], str) for k in required_keys):
feedback.append("全ての値が文字列ではありません。")
return {"score": score, "feedback": feedback, "parsed_output": parsed_output}
# 4. categoryとimportanceのEnum値検証
valid_categories = {"注文", "配送", "支払い", "技術サポート", "その他"}
valid_importance = {"高", "中", "低"}
if parsed_output["category"] in valid_categories:
score += 20
feedback.append("categoryが有効な値です。")
else:
feedback.append(f"categoryが有効な値ではありません: {parsed_output['category']}")
if parsed_output["importance"] in valid_importance:
score += 20
feedback.append("importanceが有効な値です。")
else:
feedback.append(f"importanceが有効な値ではありません: {parsed_output['importance']}")
# 5. reasoningの文字数検証 (50文字以内)
if len(parsed_output["reasoning"]) <= 50:
score += 10
feedback.append("reasoningが文字数制限内です。")
else:
feedback.append("reasoningが文字数制限を超過しています。")
# 6. 内容の正確性 (期待値との比較)
# これは厳密な自動評価が難しい場合が多いが、簡易的なチェック
if parsed_output["category"] == expected_category:
score += 5
feedback.append("categoryが期待値と一致します。")
if parsed_output["importance"] == expected_importance:
score += 5
feedback.append("importanceが期待値と一致します。")
return {"score": score, "feedback": feedback, "parsed_output": parsed_output}
# 採点ルーブリック例:
# 100点: 完全一致 (JSON形式、キー、値、内容全て正しい)
# 80点以上: 形式は正しいが、内容に若干のずれ
# 50点以上: JSON形式は概ね正しいが、一部値が不正、またはreasoningが長すぎる
# 0点: JSON形式が完全に崩壊、またはエラー応答が不正
誤り分析
評価結果から、プロンプトやモデルの失敗モードを特定します。
失敗モード
幻覚(Hallucination):
存在しないカテゴリや重要度を出力する。
reasoningが問い合わせ内容と無関係な内容になる。
様式崩れ(Format Collapse/Deviation):
JSON形式が壊れる(例:
}が欠落、キーが引用符で囲まれていない)。定義されたキー以外のキーが出力される、または必須キーが欠落する。
categoryやimportanceの値が定義外の文字列になる。
脱線(Topic Drifting):
問い合わせ内容とは関係ない一般的な情報やチャットのような応答を返す。
reasoningが長文になり、タスクの目的から逸脱する。
禁止事項応答の失敗:
禁止された入力に対して、適切にエラーメッセージを返さず、内容を分類しようとする。
逆に、禁止されていない入力に対して誤ってエラーメッセージを返す。
抑制手法
失敗モードに対応するための戦略を講じます。
System指示の強化:
- プロンプトの冒頭でモデルの役割と出力形式をより厳しく指示する。「いかなる場合もJSON形式を厳守すること」「定義されたカテゴリ/重要度リスト以外は出力しないこと」といった具体的な制約を追加します。
検証ステップの導入:
LLMからの出力後、上記の擬似コードのような自動評価関数を用いて出力形式と内容を検証します。
特にJSONスキーマバリデーションライブラリ(例: Pythonの
jsonschema)を使用して、厳密な形式チェックを行います。
リトライ戦略:
自動評価で出力形式の破損やEnum値の不正が検出された場合、元のプロンプトに加えてエラー内容をフィードバックし、再試行させます。
例: 「前回の出力はJSON形式が不正でした。
reasoningが50文字を超過しています。再度JSON形式を厳守し、reasoningは50文字以内で出力してください。」複数回のリトライ後も失敗する場合は、人間の介入やデフォルト値へのフォールバックを検討します。
Guardrailsの活用:
モデルへの入力前に、別のAIサービス(例: Azure AI Content Safety)やルールベースのフィルターで、PIIや禁止事項に該当する内容を検出・除外します。
モデルの出力後にも、同様のチェックを行い、不適切な出力をマスクまたは拒否します。
改良
評価と誤り分析の結果に基づき、プロンプトを改良します。
プロンプトの調整: 失敗モードの原因となった指示の曖昧さや不足を修正します。特に「様式崩れ」が多い場合は、SystemプロンプトでのJSON形式強制や、Few-shot例での厳密な形式提示を強化します。
Few-shot例の追加: 難例やコーナーケースで失敗したパターンを分析し、それらをカバーする新しいFew-shot例を追加します。特に曖昧な問い合わせに対する「その他」への分類例など。
Chain-of-Thoughtの調整: 思考プロセスが期待通りでない場合、ステップの具体化や追加を行います。
再評価
改良されたプロンプトに対して、再度評価シナリオを実行し、改善度合いを測定します。このループを繰り返すことで、プロンプトの安定性と性能を徐々に向上させます。
まとめ
LLMアプリケーション開発におけるプロンプトの入出力契約と安定化は、システムの信頼性を高める上で不可欠なプロセスです。明確な契約定義、多角的なプロンプト設計、自動化された評価、そして体系的な誤り分析と改良のサイクルを回すことで、LLMの挙動をより予測可能かつ安定させることができます。これにより、開発者はLLMをビジネスロジックに深く組み込み、堅牢なAIソリューションを実現できるようになります。
graph TD
A["プロンプト設計"] --> B("LLMモデル")
B -- JSON出力生成 --> C{"出力検証と評価"}
C -- 成功の場合 --> D["システム組み込み"]
C -- 失敗の場合 --> E["誤り分析"]
E -- 改善点特定 --> A
D -- 継続的監視 --> E

