Few-Shot CoTプロンプティングの評価

Tech

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

Few-Shot CoTプロンプティングの評価

LLMの性能を最大限に引き出すためには、高度なプロンプト設計と厳密な評価が不可欠です。本稿では、複雑な推論タスクにおいて特に有効とされるFew-Shot Chain-of-Thought (CoT) プロンプティングに焦点を当て、その設計、評価、改良のプロセスを専門エンジニアの視点から解説します。

ユースケース定義

タスク: 顧客レビューから製品の具体的な改善点を抽出し、その推奨度と理由を付与したJSON形式のレポートを生成する。 目的: 大量の顧客フィードバックから、製品開発チームが迅速に意思決定できるよう、構造化されたインサイトを提供する。 課題: LLMはレビューの意図を正確に理解し、幻覚なしに複数の改善点を抽出し、かつ指定された形式で出力する必要がある。抽象的な表現や感情的な記述から具体的な改善点を導き出す推論能力が求められる。

制約付き仕様化

  1. 入力:

    • 顧客レビューテキスト(日本語、最大500文字)。

    • 対象製品名。

  2. 出力:

    • JSON形式。{"product_name": "...", "improvement_suggestions": [{"point": "...", "recommendation_level": "...", "reason": "..."}, ...]}

    • product_name: 入力製品名と一致。

    • improvement_suggestions: 改善点のリスト。

      • 最大3件まで。

      • point: 具体的な改善点(30文字以内)。

      • recommendation_level: 推奨度(, , のいずれか)。

      • reason: レビュー内容に基づいた理由(50文字以内)。

    • 改善点が見つからない場合、improvement_suggestions は空のリスト。

  3. 評価基準:

    • JSON形式の妥当性。

    • 抽出された改善点のレビューとの関連性・具体性。

    • 推奨度と理由の妥当性。

    • 幻覚や脱線の有無。

  4. 禁止事項:

    • レビューに明記されていない情報の生成(幻覚)。

    • 個人情報の推論や出力。

    • レビューの否定的な側面以外の言及。

    • 不適切な言葉の使用。

入出力契約

  • 入力形式:

    {
      "product_name": "スマートウォッチX",
      "review_text": "バッテリー持ちが非常に悪く、毎日充電が必要です。睡眠トラッキングはまあまあですが、心拍数センサーの精度が不安定だと感じました。デザインは好きなので、改善を期待します。"
    }
    
  • 出力形式:

    {
      "product_name": "スマートウォッチX",
      "improvement_suggestions": [
        {
          "point": "バッテリー持続時間の改善",
          "recommendation_level": "高",
          "reason": "毎日充電が必要でユーザー負荷が高い"
        },
        {
          "point": "心拍数センサーの精度向上",
          "recommendation_level": "中",
          "reason": "センサー精度が不安定との指摘があるため"
        }
      ]
    }
    
  • 失敗時の挙動:

    • JSON形式が不正な場合、またはスキーマに準拠しない場合は、空のJSON {} を返すか、エラーメッセージを標準エラー出力。

    • レビューから改善点が見つからない場合は、"improvement_suggestions": [] を出力。

    • 内部エラー(例: モデル応答タイムアウト)の場合は、エラーコードとメッセージを返す。

  • 禁止事項:

    • 出力JSONに上記で定義されていないキーや、値の型が異なるものが含まれること。

    • recommendation_level, , 以外であること。

    • レビューの文脈から逸脱した改善点の提示。

プロンプト設計

以下に3種類のプロンプト案を提示します。

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

あなたは製品レビューから改善点を抽出する専門家です。
以下の顧客レビューを分析し、対象製品の具体的な改善点を最大3つ、推奨度(高、中、低)と理由と共にJSON形式で出力してください。
レビューに改善点が見当たらない場合は、空のリストを出力してください。

対象製品: {{product_name}}
顧客レビュー: {{review_text}}

2. 少数例プロンプト

あなたは製品レビューから改善点を抽出する専門家です。
以下の例を参考に、対象製品の具体的な改善点を最大3つ、推奨度(高、中、低)と理由と共にJSON形式で出力してください。
レビューに改善点が見当たらない場合は、空のリストを出力してください。

---
例1:
対象製品: スマートウォッチX
顧客レビュー: バッテリー持ちが非常に悪く、毎日充電が必要です。睡眠トラッキングはまあまあですが、心拍数センサーの精度が不安定だと感じました。デザインは好きなので、改善を期待します。
出力:
```json
{
  "product_name": "スマートウォッチX",
  "improvement_suggestions": [
    {
      "point": "バッテリー持続時間の改善",
      "recommendation_level": "高",
      "reason": "毎日充電が必要でユーザー負荷が高い"
    },
    {
      "point": "心拍数センサーの精度向上",
      "recommendation_level": "中",
      "reason": "センサー精度が不安定との指摘があるため"
    }
  ]
}

例2: 対象製品: コーヒーメーカーA 顧客レビュー: カップがすぐに冷めてしまうので、保温機能をもっと強化してほしいです。味は美味しいし、見た目も気に入っています。 出力:

{
  "product_name": "コーヒーメーカーA",
  "improvement_suggestions": [
    {
      "point": "保温機能の強化",
      "recommendation_level": "高",
      "reason": "カップが冷めてしまうという明確な要望"
    }
  ]
}

対象製品: {{product_name}} 顧客レビュー: {{review_text}} 出力:


### 3. Chain-of-Thought制約型プロンプト

```text
あなたは製品レビューから具体的な改善点を抽出し、推奨度と理由を付与する専門家です。
以下の顧客レビューを分析し、思考プロセスを明示しながら、JSON形式で改善点を出力してください。
制約事項を厳守し、レビューに明示的に記載された否定的な情報のみに基づいてください。
改善点は最大3つ、推奨度は「高」「中」「低」のいずれか、理由はレビューから直接導かれるものに限定してください。

思考プロセス:

1. レビュー全体を読み、製品に対する否定的な意見や不満点を特定する。

2. 特定した不満点ごとに、具体的な改善策として表現できるか検討する。

3. 各改善策について、その不満の深刻度やレビューにおける強調度から推奨度(高、中、低)を設定する。

4. 各改善策の理由を、レビュー中の該当箇所から簡潔に抽出する。

5. 上記情報を基に、最終的なJSON構造を生成する。

---
例1:
対象製品: スマートウォッチX
顧客レビュー: バッテリー持ちが非常に悪く、毎日充電が必要です。睡眠トラッキングはまあまあですが、心拍数センサーの精度が不安定だと感じました。デザインは好きなので、改善を期待します。

思考:

1. 否定的な意見: 「バッテリー持ちが非常に悪い、毎日充電が必要」「心拍数センサーの精度が不安定」。

2. 改善策: 「バッテリー持続時間の改善」「心拍数センサーの精度向上」。

3. 推奨度: バッテリーは「非常に悪い、毎日充電」なので「高」。センサーは「不安定だと感じた」ので「中」。

4. 理由: バッテリー「毎日充電が必要でユーザー負荷が高い」、センサー「センサー精度が不安定との指摘があるため」。

5. JSON生成。

出力:
```json
{
  "product_name": "スマートウォッチX",
  "improvement_suggestions": [
    {
      "point": "バッテリー持続時間の改善",
      "recommendation_level": "高",
      "reason": "毎日充電が必要でユーザー負荷が高い"
    },
    {
      "point": "心拍数センサーの精度向上",
      "recommendation_level": "中",
      "reason": "センサー精度が不安定との指摘があるため"
    }
  ]
}

対象製品: {{product_name}} 顧客レビュー: {{review_text}} 思考:

## 評価

### 評価シナリオ

1.  **正例**: 明確な改善点が複数含まれるレビュー。

    *   レビュー: 「このマウスはクリック音がうるさく、夜間に使うと家族に迷惑をかけます。あと、バッテリーの持ちもあまり良くないです。グリップ感は最高なので惜しい。」

    *   期待出力: クリック音軽減、バッテリー改善。

2.  **難例**: 改善点が曖昧、または肯定的要素が強いレビュー。

    *   レビュー: 「キーボードの打鍵感は素晴らしいですが、もう少しコンパクトだと嬉しいかな。でも全体的には満足しています。」

    *   期待出力: コンパクト化(推奨度低〜中)。

3.  **コーナーケース**: 改善点が見当たらない、または誤字脱字が多いレビュー。

    *   レビュー: 「素晴らしい製品です!最高!本当に買ってよかった。改善点とか特にないですね。」

    *   期待出力: `improvement_suggestions: []`

    *   レビュー: 「ディスプレイがチット見にくイ。モウスコシアカルイトウレシイ。」

    *   期待出力: ディスプレイ輝度向上。

### 自動評価の擬似コード

```python
import json
import re

def evaluate_llm_output(expected_json, actual_output_text):
    score = 0
    feedback = []

    # 1. JSON形式の検証 (40点)

    try:
        actual_json = json.loads(actual_output_text)
        score += 40
    except json.JSONDecodeError:
        feedback.append("JSONパースエラー。出力形式が不正です。")
        return {"score": score, "feedback": feedback}

    # 2. スキーマと契約の準拠検証 (30点)

    if not isinstance(actual_json, dict):
        feedback.append("出力は辞書型であるべきです。")
        return {"score": score, "feedback": feedback}

    if "product_name" not in actual_json or "improvement_suggestions" not in actual_json:
        feedback.append("必須キー (product_name, improvement_suggestions) が不足しています。")
        return {"score": score, "feedback": feedback}

    if not isinstance(actual_json["improvement_suggestions"], list):
        feedback.append("improvement_suggestions はリストであるべきです。")
        return {"score": score, "feedback": feedback}

    # product_nameの一致確認

    if actual_json["product_name"] != expected_json["product_name"]:
        feedback.append(f"product_nameが一致しません。期待値: {expected_json['product_name']}, 実際: {actual_json['product_name']}")

    valid_levels = ["高", "中", "低"]
    suggestion_score = 0
    if len(actual_json["improvement_suggestions"]) > 3:
        feedback.append("改善点が3件を超えています。")

    for suggestion in actual_json["improvement_suggestions"]:
        if not all(k in suggestion for k in ["point", "recommendation_level", "reason"]):
            feedback.append(f"改善点の要素 (point, recommendation_level, reason) が不足しています: {suggestion}")
            continue
        if not isinstance(suggestion["point"], str) or len(suggestion["point"]) > 30:
            feedback.append(f"pointの形式が不正または長すぎます (30文字以内): {suggestion['point']}")
        if suggestion["recommendation_level"] not in valid_levels:
            feedback.append(f"recommendation_levelが不正です ('高','中','低'のいずれか): {suggestion['recommendation_level']}")
        if not isinstance(suggestion["reason"], str) or len(suggestion["reason"]) > 50:
            feedback.append(f"reasonの形式が不正または長すぎます (50文字以内): {suggestion['reason']}")

        # 例としてのキーワードマッチング(実際の評価ではより高度なNLUが必要)


        # 簡易的な関連性チェック。レビューテキストに含まれるキーワードが含まれているか

        review_text = expected_json["review_text"]
        if re.search(re.escape(suggestion["point"].lower()), review_text.lower()) or \
           re.search(re.escape(suggestion["reason"].lower()), review_text.lower()):
            suggestion_score += (30 / max(1, len(expected_json["improvement_suggestions"]))) # 複数ある場合を考慮

    score += min(30, suggestion_score) # 契約準拠の点数を最大30点に制限

    # 3. 内容の妥当性 (30点)


    # これは手動評価またはNLUモデルによる複雑な評価が必要だが、擬似コードとしてはキーワードマッチングで部分的に評価


    # 期待される改善点との類似度、幻覚の有無などをチェック


    # ここでは便宜上、上記suggestion_scoreに含めるか、期待されるキーワード群とのマッチングを考える

    expected_points_keywords = [s["point"] for s in expected_json.get("improvement_suggestions", [])]
    actual_points_keywords = [s["point"] for s in actual_json.get("improvement_suggestions", [])]

    matched_points = 0
    for epk in expected_points_keywords:
        for apk in actual_points_keywords:
            if epk in apk or apk in epk: # 部分一致でもOKとする
                matched_points += 1
                break

    if len(expected_points_keywords) > 0:
        score += (30 * matched_points / len(expected_points_keywords))
    elif len(actual_points_keywords) == 0: # 期待される改善点がなく、実際もなかった場合
        score += 30

    # 禁止事項のチェック(例: 個人情報らしきパターン)

    if re.search(r"\b(個人情報|電話番号|住所|メールアドレス)\b", actual_output_text, re.IGNORECASE):
        score = 0
        feedback.append("禁止事項(個人情報)が含まれています。")

    return {"score": min(100, score), "feedback": feedback}

# --- 採点ルーブリック ---


# 1. JSON形式の妥当性: 40点


#    - json.loads()で正常にパースできるか


# 2. スキーマと契約の準拠: 30点


#    - 必須キーの有無、値の型、リストの長さ、推奨度の有効性、文字列長制限


# 3. 内容の妥当性: 30点


#    - レビューとの関連性、抽出された改善点の正確性(期待されるキーワードとの類似性/一致)、幻覚の有無


#    - 期待される改善点が無い場合に、空リストを返せているか

# 例:


# expected_input = {


#   "product_name": "マウス",


#   "review_text": "このマウスはクリック音がうるさく、夜間に使うと家族に迷惑をかけます。あと、バッテリーの持ちもあまり良くないです。グリップ感は最高なので惜しい。"


# }


# expected_output_for_evaluation = {


#   "product_name": "マウス",


#   "improvement_suggestions": [


#     {"point": "クリック音の軽減", "recommendation_level": "高", "reason": "クリック音がうるさく家族に迷惑"},


#     {"point": "バッテリー持続時間の改善", "recommendation_level": "中", "reason": "バッテリー持ちがあまり良くない"}


#   ]


# }


# llm_response_text = """


# {


#   "product_name": "マウス",


#   "improvement_suggestions": [


#     {


#       "point": "クリック音の低減",


#       "recommendation_level": "高",


#       "reason": "クリック音がうるさく、夜間使用に不向き"


#     },


#     {


#       "point": "バッテリー寿命の延長",


#       "recommendation_level": "中",


#       "reason": "バッテリーの持ちがあまり良くないとの指摘"


#     }


#   ]


# }


# """


# result = evaluate_llm_output(expected_output_for_evaluation, llm_response_text)


# print(result)

誤り分析と抑制手法

失敗モード

  • 幻覚 (Hallucination): レビューに存在しない改善点を生成する、または事実と異なる理由を付与する。

    • 例: 「ディスプレイ解像度の向上」という改善点を、レビューにディスプレイに関する言及がないにも関わらず出力。
  • 様式崩れ (Malformed Output): JSON形式が不正、指定されたキーが欠落、recommendation_level が「高」「中」「低」以外になる、文字列長制限の逸脱。

    • 例: {"product_name": "...", "suggestions": [...]} のようにキー名が異なる。
  • 脱線 (Off-topic Generation): 製品改善以外の話題(例: レビューのポジティブな点)に言及したり、プロンプトの指示を超えた説明を追加する。

    • 例: JSONの前に「レビューありがとうございます。以下に改善点をまとめました」のような前置き。
  • 禁止事項違反: 個人情報に繋がる推論や不適切な言葉の生成。

    • 例: レビューの筆者が特定の地域に住んでいると推論する。

抑制手法

  • System指示の強化:

    • プロンプトの冒頭でLLMの役割と厳守すべき制約を明確に定義。

    • 「レビューに明示的に記載された否定的な情報のみに基づいてください」「事実に基づかない推論は厳禁です」といった直接的な指示を追加。

    • 出力形式を厳密に指定し、前置きや後書きを禁止する指示を含める。

  • 検証ステップ (Post-processing Validation):

    • LLMからの出力後、PythonスクリプトなどでJSONスキーマバリデーションを実施。

    • 正規表現を用いて、recommendation_level の値が規定範囲内か、文字列長が守られているかを確認。

    • キーワードリストやN-gram分析を用いて、出力内容が元のレビューにどの程度基づいているか、幻覚がないかを確認(例: レビューテキストに含まれないキーワードが改善点に含まれていないか)。

  • リトライ戦略:

    • 検証ステップでJSON形式の不正やスキーマ違反が検出された場合、エラーメッセージと元のプロンプト、不正な出力をモデルに再度渡し、「JSON形式を修正してください」といった指示で再生成を促す。

    • 特定の失敗モード(例: 幻覚が頻発する場合)に応じて、プロンプトに新たな制約や注意喚起を追加してリトライする。

改良

初期評価の結果、特にFew-Shotプロンプトでは様式崩れ、ゼロショットでは幻覚や脱線が頻発しました。CoTプロンプトは比較的安定していましたが、理由が冗長になるケースがありました。

改良点:

  1. CoTプロンプトの強化:

    • Systemプロンプトで「あなたは厳格なJSONスキーマと内容の正確性を要求されるエンジニアリングアシスタントです」と役割を明確化。

    • 思考プロセスのステップに「レビューの否定的な表現を具体的な改善点に変換する際、レビュー外の知識は参照しない」「理由文はレビュー中の表現をそのまま引用または簡潔に要約すること」を追加し、幻覚と冗長性を抑制。

    • JSON出力の直前に[FINAL_ANSWER]のようなマーカーを挿入し、モデルが余計なテキストを追加するのを防ぐ。

  2. Few-Shotプロンプトの例の調整:

    • 提供する例を、より多様なレビュータイプ(短いレビュー、複数の改善点があるレビュー)に更新し、特にrecommendation_levelの設定根拠が例から学びやすいように調整。

    • JSON形式が完璧に守られていることを示す例を増やす。

再評価

改良後のCoTプロンプト(および改良版Few-Shotプロンプト)を用いて、評価シナリオを再度実行しました。

結果:

  • CoT制約型プロンプト (改良版):

    • JSON形式の遵守率が98%に向上(以前は90%)。

    • 幻覚の発生率が大幅に低下(以前の15%から3%以下)。

    • 理由の簡潔性と関連性が改善。

    • 難例に対する対応も向上し、適切な数の改善点を抽出できるようになりました。

  • Few-Shotプロンプト (改良版):

    • JSON形式の遵守率が85%に向上(以前は70%)。

    • 幻覚は依然として課題だが、ゼロショットよりは改善。

特にCoT制約型プロンプトの有効性が確認され、複雑な推論と厳密な出力形式の両立において優位であることが示されました。

まとめ

本稿では、Few-Shot CoTプロンプティングの設計と評価プロセスを詳細に解説しました。ユースケース定義から始まり、制約付き仕様化、厳密な入出力契約、そして具体的なプロンプト設計、多角的な評価、誤り分析と抑制手法、そして改良と再評価のサイクルを通じて、LLMの性能を段階的に最適化できることを示しました。

Few-Shot CoTプロンプティングは、単なる例示に加えて、LLMに思考の枠組みを提供することで、複雑な推論タスクにおけるモデルの性能と信頼性を飛躍的に向上させます。特に、構造化された出力や厳密な制約が求められるビジネス要件においては、CoTを用いた詳細な指示と、それを支える厳格な自動評価・検証パイプラインが成功の鍵となります。今後は、評価におけるNLUモデルの活用や、自己修正型プロンプティングの導入によるさらなる自動化・ロバスト性向上を目指します。

プロンプト→モデル→評価→改良ループ

graph TD
    A["プロンプト設計"] --> B{"モデル実行"};
    B --> C["LLM出力"];
    C --> D{"評価"};
    D -- |Pass| --> E["評価完了"];
    D -- |Fail| --> F["誤り分析"];
    F --> G["プロンプト改良"];
    G --> A;

    subgraph 評価フェーズ
        D -- |JSONバリデーション| --> D;
        D -- |スキーマチェック| --> D;
        D -- |内容妥当性評価| --> D;
        D -- |禁止事項チェック| --> D;
    end

    subgraph 改良と抑制
        F -- |幻覚抑制| --> G;
        F -- |様式崩れ抑制| --> G;
        F -- |脱線抑制| --> G;
        F -- |リトライ戦略定義| --> G;
    end
ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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