LLMプロンプト評価指標と改善ループ

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

LLMプロンプト評価指標と改善ループ

大規模言語モデル(LLM)の性能を最大限に引き出すためには、プロンプトの設計と評価が不可欠です。本記事では、プロンプト評価の主要指標と、反復的な改善ループの構築方法について解説します。

ユースケース定義:製品レビューからの情報抽出

、オンライン製品レビューテキストから以下の情報を抽出するタスクを想定します。

  1. 感情 (Sentiment): ポジティブ、ネガティブ、ニュートラル

  2. 評価点 (Rating): 1から5の整数

  3. 主要な特徴 (KeyFeatures): レビューで言及されている製品の具体的な特徴(最大3つ)

入出力契約と制約付き仕様化

LLMへの期待値を明確にするため、入出力契約を定義します。これにより、出力の予測可能性と品質を向上させます。

入力契約

  • 形式: 純粋なテキストデータ。

  • 文字数: 100文字以上500文字以内。

  • 言語: 日本語。

出力契約

  • 形式: JSONオブジェクト。

  • 必須キー: sentiment (string), rating (integer), key_features (array of strings)。

  • 値の制約:

    • sentiment: “ポジティブ”, “ネガティブ”, “ニュートラル” のいずれか。

    • rating: 1から5の整数。

    • key_features: 最大3つの文字列要素を持つ配列。各要素は10文字以内。

  • 失敗時の挙動: 抽出できなかった情報やエラーが発生した場合、対応する値は null または空の配列とし、error_message キーに理由を記述する。

  • 禁止事項: 出力にレビューテキストの引用や、分析以外の余計なテキストを含めない。

プロンプト設計

以下に、ユースケースに対する3種類のプロンプト案を提示します。

1. ゼロショットプロンプト

基本的な指示のみで、モデルに自律的に推論させるプロンプトです。

あなたは製品レビュー分析の専門家です。以下のレビューテキストから、感情、評価点、主要な特徴をJSON形式で抽出してください。

出力形式:
{
  "sentiment": "ポジティブ|ネガティブ|ニュートラル",
  "rating": 1-5の整数,
  "key_features": ["特徴1", "特徴2", "特徴3" (最大3つ)]
}

レビューテキスト:
{{REVIEW_TEXT}}

2. 少数例 (Few-Shot) プロンプト

いくつかの具体例(入出力ペア)を提示し、モデルにタスクのパターンを学習させるプロンプトです。

あなたは製品レビュー分析の専門家です。以下のレビューテキストから、感情、評価点、主要な特徴をJSON形式で抽出してください。出力は提供された例に従ってください。

---
例1:
レビューテキスト: このカメラは画質が非常に良く、旅行に持っていくのに最適でした。バッテリーの持ちも素晴らしいです。
出力:
{
  "sentiment": "ポジティブ",
  "rating": 5,
  "key_features": ["画質", "バッテリー持ち", "携帯性"]
}

例2:
レビューテキスト: デザインは良かったのですが、動作が遅く、フリーズすることがよくありました。期待外れです。
出力:
{
  "sentiment": "ネガティブ",
  "rating": 2,
  "key_features": ["動作速度", "フリーズ"]
}
---

レビューテキスト:
{{REVIEW_TEXT}}

3. Chain-of-Thought (CoT) 制約型プロンプト

思考のプロセスを明示させることで、推論能力を向上させ、特定の制約を遵守させるプロンプトです。

あなたは製品レビュー分析の専門家です。以下のレビューテキストから、感情、評価点、主要な特徴をJSON形式で抽出してください。抽出プロセスを段階的に思考し、その上で最終結果を出力してください。

思考プロセス:

1. まずレビュー全体のトーンを読み取り、感情を判断する。

2. 次に、具体的な評価や満足度を表す記述から評価点を判断する。

3. 最後に、製品の具体的な言及部分から最大3つの主要な特徴を特定する。各特徴は10文字以内とする。

出力形式:
{
  "sentiment": "ポジティブ|ネガティブ|ニュートラル",
  "rating": 1-5の整数,
  "key_features": ["特徴1", "特徴2", "特徴3" (最大3つ)]
}

レビューテキスト:
{{REVIEW_TEXT}}

評価シナリオと自動評価

プロンプトの性能を客観的に評価するため、多様なシナリオを用意し、自動評価を行います。

評価シナリオ

  • 正例 (Positive Case): 感情、評価点、特徴が明確に記述されているレビュー。

    • 例: 「このスマホはバッテリーが一日中持ち、カメラの性能も最高です。文句なしの5点です。」
  • 難例 (Difficult Case): 感情が混在している、評価点が曖昧、特徴が複数あるが優先順位が不明瞭なレビュー。

    • 例: 「デザインは素敵だけど、起動が少し遅いのが残念。でも全体的には悪くないかな。3点。」
  • コーナーケース (Corner Case): 特定の特徴にのみ言及し、感情や評価点が暗黙的、または全く言及されていないレビュー。

    • 例: 「このノートPCはキーボードの打鍵感が非常に良い。それ以外は普通です。」

    • 例2: 「何事もなく届いた。特にコメントなし。」

自動評価の擬似コード

自動評価は、LLMの出力をプログラム的に検証し、スコアを算出します。

import re
import json

def evaluate_llm_output(llm_output: str, expected_output: dict) -> dict:
    """
    LLMの出力と期待される出力に基づき、自動評価を行う関数。
    :param llm_output: LLMから返されたJSON文字列
    :param expected_output: 期待される出力の辞書(参照用)
    :return: 評価結果を示す辞書
    """
    score = 0
    feedback = []

    try:

        # 1. JSON形式の検証

        parsed_output = json.loads(llm_output)
        score += 10 # JSON形式が正しい
    except json.JSONDecodeError:
        feedback.append("JSON形式エラー: 出力が有効なJSONではありません。")
        return {"score": score, "feedback": feedback, "details": {}}

    details = {}

    # 2. 必須キーの検証

    required_keys = ["sentiment", "rating", "key_features"]
    if all(key in parsed_output for key in required_keys):
        score += 10
    else:
        feedback.append(f"必須キー不足: {', '.join([k for k in required_keys if k not in parsed_output])}")

    # 3. sentimentの検証 (値の制約)

    sentiment_options = ["ポジティブ", "ネガティブ", "ニュートラル"]
    if "sentiment" in parsed_output and parsed_output["sentiment"] in sentiment_options:
        score += 10
        details["sentiment_valid"] = True
    else:
        feedback.append(f"sentimentの値が不正: {parsed_output.get('sentiment', 'なし')}")
        details["sentiment_valid"] = False

    # 4. ratingの検証 (整数かつ範囲内)

    if "rating" in parsed_output:
        if isinstance(parsed_output["rating"], int) and 1 <= parsed_output["rating"] <= 5:
            score += 10
            details["rating_valid"] = True
        else:
            feedback.append(f"ratingの値が不正: {parsed_output.get('rating', 'なし')}")
            details["rating_valid"] = False
    else:
        details["rating_valid"] = False

    # 5. key_featuresの検証 (配列、要素数、文字数)

    if "key_features" in parsed_output:
        if isinstance(parsed_output["key_features"], list):
            if len(parsed_output["key_features"]) <= 3:
                score += 10
                all_features_valid = True
                for feature in parsed_output["key_features"]:
                    if not isinstance(feature, str) or len(feature) > 10:
                        all_features_valid = False
                        feedback.append(f"key_featuresの要素が不正(型または文字数): {feature}")
                        break
                if all_features_valid:
                    score += 10 # 各要素も有効
                    details["key_features_valid"] = True
                else:
                    details["key_features_valid"] = False
            else:
                feedback.append(f"key_featuresの要素数が多すぎます: {len(parsed_output['key_features'])}")
                details["key_features_valid"] = False
        else:
            feedback.append("key_featuresがリスト形式ではありません。")
            details["key_features_valid"] = False
    else:
        details["key_features_valid"] = False

    # 6. コンテンツの正確性評価(簡易版: 期待値とのキーワードマッチング)


    # この部分は、実際のタスクに合わせてセマンティック類似度やLLMによる評価を組み合わせる


    # ここでは、期待される特徴がどの程度含まれているかで加点

    if expected_output.get("key_features") and parsed_output.get("key_features"):
        matched_features = set(expected_output["key_features"]) & set(parsed_output["key_features"])
        score += len(matched_features) * 5 # マッチした特徴ごとに加点 (最大3*5=15点)
        details["matched_features"] = list(matched_features)

    # 総スコアは最大 (10+10+10+10+10+10+15) = 75点

    return {"score": score, "feedback": feedback, "details": details}

# --- 使用例 ---


# review_text = "このヘッドホンは音質は素晴らしいが、長時間つけると耳が痛くなる。バッテリーは長持ちする。3点。"


# llm_output_example = """


# {


#   "sentiment": "ニュートラル",


#   "rating": 3,


#   "key_features": ["音質", "装着感", "バッテリー持ち"]


# }


# """


# expected_output_for_evaluation = {


#     "sentiment": "ニュートラル",


#     "rating": 3,


#     "key_features": ["音質", "装着感", "バッテリー持ち"]


# }


# result = evaluate_llm_output(llm_output_example, expected_output_for_evaluation)


# print(result)

プロンプト評価と改善ループ

効果的なプロンプトは一朝一夕には完成しません。以下の評価と改善のサイクルを回すことが重要です。

graph TD
    A["プロンプト設計"] --> B{"LLMモデル"};
    B --> C["LLM出力"];
    C --> D["評価指標定義"];
    D --> E{"自動評価"};
    E --> F{"評価結果"};
    F -- 失敗/低品質 --> G["誤り分析"];
    G --> A;
    F -- 成功/高品質 --> H["最終プロンプト確定"];
    subgraph 改善ループ
        A --> B
        B --> C
        C --> D
        D --> E
        E --> F
        F -- 失敗/低品質 --> G
        G --> A
    end
  1. プロンプト設計: 初期プロンプト(ゼロショット、Few-Shot、CoTなど)を考案します。

  2. LLMモデル: 設計したプロンプトをLLMに入力し、出力を生成させます。

  3. LLM出力: 生成された出力を収集します。

  4. 評価指標定義: 入出力契約に基づき、正解率、フォーマット遵守率、F1スコアなどの評価指標を定義します。

  5. 自動評価: 定義した評価指標とテストデータセットを用いて、自動的にLLMの出力を評価します[4]。上記の擬似コードのような関数がこれにあたります。

  6. 評価結果: 自動評価により、プロンプトのスコアやフィードバックを得ます。

  7. 誤り分析: スコアが低い場合や特定の失敗モードが検出された場合、手動でLLMの出力とプロンプトを詳細に分析します。どの部分でモデルが誤ったか、なぜ誤ったかを特定します。

  8. プロンプト修正: 誤り分析の結果に基づき、プロンプトの指示の明確化、例の追加、制約の強化などを行います。例えば、幻覚を防ぐために「レビューに明示的に記載されている情報のみを抽出すること」といった指示を追加します。

  9. 最終プロンプト確定: 目標とする評価スコアを達成するまで、このループを繰り返します。

この反復的なプロセスにより、プロンプトの性能は継続的に向上します。Google Cloudのドキュメント(2024年3月15日更新)やOpenAIのガイド(2024年2月28日更新)でも、プロンプトの反復的な改善が重要であると強調されています[1, 2]。

失敗モードと抑制手法

LLMは強力ですが、完璧ではありません。プロンプト設計では、一般的な失敗モードとその抑制手法を理解しておくことが重要です。

失敗モード

  • 幻覚 (Hallucination): レビューに存在しない事実や情報をモデルが生成してしまう現象[5]。

    • 例: 「このスマホは指紋認証が速い」と出力したが、レビューには言及がない。
  • 様式崩れ (Format Deviation): JSON形式の違反、キーの欠落、値の型が異なるなど、入出力契約で定義したフォーマットを遵守しない[4]。

    • 例: sentimentが「良い」となる、key_featuresが文字列ではなくカンマ区切りの単一文字列となる。
  • 脱線 (Off-topic/Derailment): タスクの範囲を超えて、無関係な情報を含めたり、質問に直接答えないこと。

    • 例: 「この製品は素晴らしいですが、競合他社のA製品も検討する価値があります」と余計なコメントを付与する。
  • 禁止事項違反: 指示で明確に禁止された行為(例: 個人情報の出力、レビューテキストの引用)を行ってしまう。

    • 例: 出力に{{REVIEW_TEXT}}の一部をそのまま含めてしまう。

抑制手法

  • System指示の活用: システムプロンプトでモデルの役割、遵守すべきルール、禁止事項を明確に定義する。例えば、「あなたは誠実で、指定された情報のみを抽出するAIです。存在しない情報を生成してはいけません。」

  • 具体的かつ明確な指示: 曖昧な表現を避け、具体的な行動や制約を指示する。「最大3つの特徴を抽出する」や「1から5の整数で評価点を記述する」のように明示します。

  • Few-Shot学習: 適切な入出力例を複数提供することで、期待される挙動をモデルに示します。特にフォーマット遵守に有効です。

  • Chain-of-Thought (CoT) プロンプティング: モデルに思考プロセスを段階的に出力させることで、複雑なタスクでの推論ミスを減らし、幻覚を抑制する効果があります。

  • 出力検証ステップ (Post-processing): LLMの出力後に、自動評価の擬似コードのようなスクリプトを用いて、JSON形式、値の範囲、キーワード、禁止事項の有無などをプログラム的に検証します。これにより、様式崩れや禁止事項違反を検出・修正できます。

  • リトライ戦略: 出力検証でエラーが検出された場合、エラーメッセージとともにLLMに再度プロンプトを送信し、修正を促す戦略です。例えば、「出力がJSON形式ではありませんでした。正しいJSON形式で再度出力してください。」

  • 温度 (Temperature) 設定の調整: 創造性が不要なタスクでは、temperatureを0に近い低い値に設定し、モデルの出力のばらつきを抑えます。

まとめ

LLMプロンプトの評価と改善は、効果的なAIアプリケーション開発の要です。入出力契約の明確化から始まり、多様なプロンプト設計、厳格な自動評価、そして継続的な誤り分析と修正のループを通じて、LLMの性能を最適化できます。本記事で示した評価指標、自動評価の擬似コード、改善ループの概念、そして失敗モードとその抑制手法は、プロンプトエンジニアリングの実践において強力な指針となるでしょう。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました