Chain-of-Thought (CoT) プロンプティングの活用と評価

Tech

Chain-of-Thought (CoT) プロンプティングの活用と評価

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

はじめに

Chain-of-Thought (CoT) プロンプティングは、大規模言語モデル(LLM)が複雑な推論タスクを解く際に、中間的な思考ステップを出力させることで、その性能を飛躍的に向上させる手法です[1, 2]。特に算術推論、常識推論、記号推論といった多段階の思考を要する問題解決において有効性が示されており、LLMの「思考」を可視化することで、その推論過程を理解し、デバッグする手助けにもなります[3]。本稿では、CoTプロンプティングの具体的な活用方法から、プロンプト設計、評価、そして改良に至る一連のプロセスについて詳述します。

ユースケース定義

CoTプロンプティングは、以下のような複雑な推論が必要なシナリオで特に有効です。

  • 複雑な多段階推論を要する質問応答システム: 複数の情報源からデータを統合し、段階的に結論を導き出すような質問(例: 「A社の最新の四半期決算と、競合B社の過去3年の成長率を比較し、市場の今後のトレンドについて分析せよ」)。

  • 数理的・論理的思考を要する問題解決: 複雑な計算問題や、論理パズル、コードのデバッグなど、明確な中間ステップを必要とするタスク。

  • 意思決定支援: 特定の条件に基づき、複数の選択肢を評価し、その根拠と推奨事項を段階的に説明するタスク。

入出力契約(制約付き仕様化)

LLMとのインタラクションにおいて、入出力の契約を明確にすることで、予測可能な挙動を確保し、システム全体の堅牢性を高めます。

入力フォーマット

入力はJSON形式またはフリーテキストを許容しますが、複雑なタスクではJSONを推奨します。

{
  "task": "財務データ分析",
  "question": "A社の2023年Q4の売上高成長率と、競合B社の過去3年の平均売上高成長率を比較し、A社の投資判断について結論と理由を提示せよ。",
  "context": {
    "companyA_data": {
      "name": "A社",
      "2023_Q4_revenue": "100億ドル",
      "2022_Q4_revenue": "80億ドル"
    },
    "companyB_data": {
      "name": "B社",
      "2021_revenue_growth": "15%",
      "2022_revenue_growth": "10%",
      "2023_revenue_growth": "20%"
    }
  },
  "output_format_constraint": "思考過程と最終結論をJSONで出力すること。最終結論は'結論'フィールド、思考過程は'思考過程'フィールドに格納すること。"
}

出力フォーマット

出力もJSON形式とし、思考過程と最終的な回答を明確に分離します。

{
  "思考過程": [
    "A社の2023年Q4売上高成長率を計算します。",
    "B社の過去3年の平均売上高成長率を計算します。",
    "両社の成長率を比較します。",
    "比較結果に基づき、A社の投資判断に関する結論を導き出します。",
    "結論に至った理由を詳述します。"
  ],
  "結論": {
    "投資判断": "中立",
    "理由": "A社の成長率は堅調であるものの、競合B社の平均成長率と比較すると若干見劣りするため、さらなる市場動向の観察が必要です。"
  }
}

失敗時の挙動

  • 入力形式エラー: 不正なJSON形式の場合、{"error": "Invalid input format", "details": "Expected JSON, received text."} のようなエラーメッセージを返します。

  • 処理不能: 質問がLLMの能力を超える、または必要な情報がコンテキストにない場合、{"status": "failure", "message": "質問に必要な情報が不足しているか、タスクが複雑すぎます。", "思考過程": "不明なため、推論を中断しました。"} を返します。

  • 思考過程のみ出力: 最終結論を導き出せなくても、可能な限りの思考過程を出力します。

禁止事項

  • 不正確な情報の生成(幻覚): 事実に基づかない情報を生成しない。

  • 様式崩れ: 定義されたJSON出力フォーマット以外の形式で出力しない。

  • 脱線: 指示されたタスクから逸脱した内容や、無関係な情報を出力しない。

  • 不適切な内容: 差別的、暴力的、その他公序良俗に反する内容を生成しない。

  • 外部参照の無断使用: 提供されたコンテキスト以外の、学習データに含まれる可能性のある特定の情報を、出典を明記せずに利用しない。

プロンプト設計

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

1. ゼロショットプロンプト (Zero-shot Prompt)

追加の例示なしにタスクを指示します。複雑なタスクでは精度が低くなりがちですが、実装が最も容易です。

あなたは金融アナリストです。
以下の財務データと質問に基づき、A社の投資判断について結論と理由を提示してください。
思考過程も出力してください。

質問: A社の2023年Q4の売上高成長率と、競合B社の過去3年の平均売上高成長率を比較し、A社の投資判断について結論と理由を提示せよ。
コンテキスト:
会社A: 2023年Q4売上高: 100億ドル, 2022年Q4売上高: 80億ドル
会社B: 2021年売上高成長率: 15%, 2022年売上高成長率: 10%, 2023年売上高成長率: 20%
出力はJSON形式で、「思考過程」と「結論」の2つのキーを含めてください。

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

タスクの指示に加え、いくつかの入出力例(プロンプトと期待されるCoTプロセス、最終回答)を提供します。これにより、LLMはタスクの意図とCoTの出力形式をより良く理解できます。

あなたは金融アナリストです。
以下の例を参考に、質問に回答し、A社の投資判断について結論と理由を提示してください。
思考過程も出力してください。
出力はJSON形式で、「思考過程」と「結論」の2つのキーを含めてください。

---
例1:
質問: XYZ社の2023年Q3の利益成長率を計算し、市場平均(5%)と比較して投資判断を示せ。
コンテキスト:
XYZ社: 2023年Q3利益: 200億円, 2022年Q3利益: 180億円

出力:
```json
{
  "思考過程": [
    "XYZ社の2023年Q3利益成長率を計算します: (200 - 180) / 180 * 100 = 11.11%",
    "市場平均5%と比較します。",
    "XYZ社の成長率11.11%は市場平均5%より高いと判断します。",
    "この結果に基づき、投資判断を提示します。"
  ],
  "結論": {
    "投資判断": "買い",
    "理由": "XYZ社の2023年Q3の利益成長率は11.11%であり、市場平均の5%を大きく上回っているため、投資妙味があると考えられます。"
  }
}

質問: A社の2023年Q4の売上高成長率と、競合B社の過去3年の平均売上高成長率を比較し、A社の投資判断について結論と理由を提示せよ。 コンテキスト: 会社A: 2023年Q4売上高: 100億ドル, 2022年Q4売上高: 80億ドル 会社B: 2021年売上高成長率: 15%, 2022年売上高成長率: 10%, 2023年売上高成長率: 20%

### 3. Chain-of-Thought制約型プロンプト (Zero-shot CoT with specific instruction)

ゼロショットCoTの原則\[4]に基づき、「段階的に考える」指示を明示的に含めることで、LLMの推論能力を引き出します。JSON出力の制約も強化します。

```text
あなたは金融アナリストです。
以下の財務データと質問に基づき、A社の投資判断について結論と理由を提示してください。
重要なのは、**必ず思考過程を具体的なステップで記述し、その後で最終結論を導き出すこと**です。
出力は厳密にJSON形式で、「思考過程」と「結論」の2つのトップレベルキーを含め、それぞれの内容は詳細に記述してください。

質問: A社の2023年Q4の売上高成長率と、競合B社の過去3年の平均売上高成長率を比較し、A社の投資判断について結論と理由を提示せよ。
コンテキスト:
会社A: 2023年Q4売上高: 100億ドル, 2022年Q4売上高: 80億ドル
会社B: 2021年売上高成長率: 15%, 2022年売上高成長率: 10%, 2023年売上高成長率: 20%

さあ、段階的に考えましょう。

評価

LLMの出力評価は、自動評価と手動評価を組み合わせるのが理想的です。

評価シナリオ

  • 正例 (Happy Path): 明確なデータと質問で、正しい推論と結論が期待されるケース。

    • 例: 上記のA社・B社比較問題。
  • 難例 (Challenging Cases): データが曖昧、または計算が複雑なケース。

    • 例: 複数の要因(市場成長率、為替変動、季節性)を考慮する必要がある投資判断。

    • 例: 欠損データがある場合の推論。

  • コーナーケース (Edge Cases): 矛盾するデータや、倫理的な判断を伴うケース。

    • 例: 成長率が非常に高いが、社会貢献度が低い企業の評価。

    • 例: 数字の誤植が含まれている場合の対応。

自動評価の擬似コード

Pythonで実装する自動評価の擬似コードを示します。

import json
import re

def evaluate_llm_output(output_json: str, expected_answer: dict) -> dict:
    """
    LLMの出力を評価する擬似コード。
    Args:
        output_json (str): LLMから返されたJSON文字列。
        expected_answer (dict): 期待される結果を含む辞書。
                                 例: {"A_growth_rate": 25.0, "B_avg_growth_rate": 15.0, "investment_decision_keyword": "中立"}
    Returns:
        dict: 評価結果とスコア。
    """
    score = 0
    feedback = []

    try:
        parsed_output = json.loads(output_json)
    except json.JSONDecodeError:
        feedback.append("出力が有効なJSON形式ではありません。")
        return {"score": 0, "feedback": feedback}

    # 1. 出力フォーマットの検証 (20点)

    if "思考過程" in parsed_output and isinstance(parsed_output["思考過程"], list) and \
       "結論" in parsed_output and isinstance(parsed_output["結論"], dict):
        score += 20
        feedback.append("出力フォーマットは正しく、思考過程と結論のキーが存在します。")
    else:
        feedback.append("出力フォーマットが期待通りではありません。(思考過程または結論のキーがない、または型が不正です)")

    # 2. 思考過程の検証 (30点)


    # 思考過程が存在し、ステップが含まれているかを正規表現やキーワードで評価

    thought_process = " ".join(parsed_output.get("思考過程", []))
    if "売上高成長率を計算します" in thought_process and "比較します" in thought_process:
        score += 15
        feedback.append("思考過程に主要なステップ(計算、比較)が含まれています。")
    if len(parsed_output.get("思考過程", [])) >= 3: # 最低3ステップのCoTを期待
        score += 15
        feedback.append("思考過程が複数のステップで構成されています。")
    else:
        feedback.append("思考過程が詳細でないか、ステップ数が不足しています。")

    # 3. 結論の検証 (50点)

    conclusion = parsed_output.get("結論", {})
    a_q4_revenue = 100
    a_prev_q4_revenue = 80
    b_growths = [15, 10, 20]

    # A社成長率の計算

    a_growth = ((a_q4_revenue - a_prev_q4_revenue) / a_prev_q4_revenue) * 100

    # B社平均成長率の計算

    b_avg_growth = sum(b_growths) / len(b_growths)

    # 期待される計算結果との比較


    # 許容誤差を設ける

    if abs(a_growth - expected_answer["A_growth_rate"]) < 0.1:
        score += 10
        feedback.append(f"A社の成長率計算が正確です ({a_growth:.2f}% vs 期待値 {expected_answer['A_growth_rate']:.2f}%).")
    else:
        feedback.append(f"A社の成長率計算が不正確です ({a_growth:.2f}% vs 期待値 {expected_answer['A_growth_rate']:.2f}%).")

    if abs(b_avg_growth - expected_answer["B_avg_growth_rate"]) < 0.1:
        score += 10
        feedback.append(f"B社の平均成長率計算が正確です ({b_avg_growth:.2f}% vs 期待値 {expected_answer['B_avg_growth_rate']:.2f}%).")
    else:
        feedback.append(f"B社の平均成長率計算が不正確です ({b_avg_growth:.2f}% vs 期待値 {expected_answer['B_avg_growth_rate']:.2f}%).")

    # 投資判断キーワードの評価

    if expected_answer["investment_decision_keyword"] in conclusion.get("投資判断", ""):
        score += 30
        feedback.append("投資判断のキーワードが期待と一致しています。")
    else:
        feedback.append(f"投資判断のキーワードが期待と異なります。(期待: '{expected_answer['investment_decision_keyword']}', 実際: '{conclusion.get('投資判断', '')}')")

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

# 例として、LLM出力と期待値を定義

llm_output_example = """
{
  "思考過程": [
    "A社の2023年Q4売上高成長率を計算します: (100 - 80) / 80 * 100 = 25.0%。",
    "B社の過去3年の平均売上高成長率を計算します: (15 + 10 + 20) / 3 = 15.0%。",
    "両社の成長率を比較します。A社25.0% vs B社15.0%。",
    "A社の成長率はB社を上回ります。",
    "比較結果に基づき、A社の投資判断を「買い」と提示します。"
  ],
  "結論": {
    "投資判断": "買い",
    "理由": "A社の成長率(25.0%)は競合B社の平均成長率(15.0%)を上回っており、非常に良好なパフォーマンスを示しているため、積極的に投資を検討すべきです。"
  }
}
"""
expected_eval_result = {
    "A_growth_rate": 25.0,
    "B_avg_growth_rate": 15.0,
    "investment_decision_keyword": "買い" # この例では「買い」を期待する
}

# 評価の実行


# eval_results = evaluate_llm_output(llm_output_example, expected_eval_result)


# print(eval_results)
  • 計算量: O(N)、Nは出力JSONの文字数。正規表現マッチングやリストの走査が線形時間で処理されます。

  • メモリ条件: JSONパース後のオブジェクトサイズと、フィードバックリストのサイズに依存します。通常は非常に小さいです。

LLM開発における評価・改良ループ

CoTプロンプティングの性能向上には、継続的な評価と改良のサイクルが不可欠です。

graph TD
    A["タスク定義/データ収集"] --> B["初期プロンプト設計"];
    B -- プロンプト入力 --> C{"LLMモデル"};
    C -- モデル出力 --> D["評価
(自動/手動)"]; D -- 評価結果と誤り分析 --> E{"改良
(プロンプト/データ)"}; E -- 新しいプロンプト/データ --> B; C -- 失敗モード --> D;
  • A[タスク定義/データ収集]: 解決したい問題と、それに必要なデータを明確にします。

  • B[初期プロンプト設計]: ゼロショット、Few-shot、CoT制約型などのプロンプトを設計します。

  • C{LLMモデル}: 実際のLLMモデル(例: Gemini)にプロンプトを投入し、出力を生成させます。

  • D[評価
    (自動/手動)]
    : 生成された出力を、定義したルーブリックや擬似コード、または人間の目で評価します。失敗モードもここで検出します。

  • E{改良
    (プロンプト/データ)}
    : 評価結果と誤り分析に基づき、プロンプトや提供するデータ(Few-shot例など)を改善します。

誤り分析と抑制手法

CoTプロンプティングは強力ですが、完璧ではありません。主な失敗モードとその抑制手法を以下に示します。

失敗モード

  1. 幻覚(Hallucination):

    • 説明: 事実に基づかない、または入力コンテキストに存在しない情報を生成してしまう現象。CoTプロセス中の中間ステップで誤った前提を置くことがある。

    • 具体例: 「A社の最新の買収情報」を尋ねられた際に、架空の買収話を作り出す。

  2. 様式崩れ(Style/Format Deviation):

    • 説明: 指定された出力フォーマット(例: JSON)や文体(例: 金融アナリストの口調)から逸脱してしまう現象。

    • 具体例: JSON形式ではなく、フリーテキストで回答を返したり、思考過程と結論が混在したりする。

  3. 脱線(Off-topic):

    • 説明: 質問の意図から外れた情報や、無関係な詳細を出力してしまう現象。CoTプロセスが複雑化するにつれて発生しやすくなる。

    • 具体例: 投資判断を問われているのに、企業のCSR活動について長々と語り始める。

  4. 禁止事項違反:

    • 説明: 不適切な内容の生成や、個人情報の漏洩など、事前に定義された禁止事項に抵触する出力。

抑制手法

  1. System指示の強化:

    • 手法: プロンプトの先頭に、LLMの役割、厳守すべき制約、禁止事項を明確に記述する。

    • : あなたは公平な金融アナリストです。事実のみに基づき、推論ステップを厳密に踏んでください。架空の情報を生成することは厳禁です。

  2. 検証ステップの導入:

    • 手法: LLMの出力後に、外部ツールや正規表現、スキーマバリデーターを用いて出力を検証する。特にJSON形式の出力にはスキーマバリデーションが有効。

    • : 出力JSONをパースし、キーの存在、値の型、計算結果の妥当性などをPythonスクリプトで自動チェックする。

  3. リトライ戦略:

    • 手法: 検証ステップでエラーが検出された場合、エラーフィードバックとともにプロンプトを再投入し、LLMに修正を促す。

    • : あなたの出力はJSON形式のバリデーションに失敗しました。以下のエラーメッセージを確認し、修正してください: [エラーメッセージ]。

  4. Few-shot例の厳選と多様化:

    • 手法: 提供するFew-shot例が多様なシナリオをカバーし、正しい推論パスと期待される出力形式を明示する。負例(誤った推論と修正例)も検討する。
  5. 自己改良(Self-Refine)戦略:

    • 手法: LLM自身に、一度生成したCoTと結論を評価させ、より良い解釈や解決策を生成させる[5]。

    • : 初回の出力に対して「この推論に欠陥はないか?別の視点から考えるとどうなるか?」といった質問を投げかけ、自己批判と改善を促す。

改良と再評価

誤り分析で特定された問題点に基づき、プロンプトを改良します。

  • プロンプトの調整: 誤りの種類に応じて、ゼロショットCoTの指示を具体化したり(例: まずA社の成長率を計算し、次にB社の平均成長率を計算し、その後に比較し、最後に投資判断を下すこと)、Few-shot例を追加・修正したりします。

  • 温度(Temperature)の調整: 推論タスクにおいては、温度を低めに設定し、より確定的で再現性の高い出力を促します。

  • セルフリファイン導入: 幻覚や推論の誤りが多い場合、上記で触れた自己改良の仕組みを導入し、LLMが自身の出力を客観的に評価・修正するプロセスを組み込みます。

  • モデルの選択: 使用しているLLMのバージョンや、より推論能力に特化したモデルへの変更も検討します。

改良後、再度定義した評価シナリオ(特に難例やコーナーケース)を用いて再評価を実施し、性能改善が数値として現れるかを確認します。この評価・改良ループを繰り返すことで、プロンプトの堅牢性とLLMの信頼性を高めます。

まとめ

Chain-of-Thoughtプロンプティングは、LLMの複雑な推論能力を引き出すための極めて有効な手法です。本稿では、その活用事例、厳密な入出力契約、具体的プロンプト設計、自動評価の擬似コード、そして重要な評価・改良ループまでを解説しました。幻覚や様式崩れといった課題に対し、System指示の強化、検証ステップ、リトライ戦略、そして自己改良などの抑制手法を組み合わせることで、より信頼性の高いLLMアプリケーションを構築できます。Chain-of-Thoughtを体系的に活用し、継続的な評価と改良を行うことで、LLMのポテンシャルを最大限に引き出し、現実世界の多様な問題解決に貢献できるでしょう。


[1] Li, Y., Zhao, S., Zhang, C., et al. (2024). “A Survey on Chain-of-Thought Prompting: Recent Advances, Challenges, and Opportunities”. arXiv:2403.02450. (2024年3月4日 JST) [2] Zhao, Y., Shen, B., Ding, C., et al. (2024). “Chain-of-Thought Reasoning with Large Language Models: A Survey”. arXiv:2403.11184. (2024年3月18日 JST) [3] Google Cloud. (n.d.). “Chain-of-thought prompting”. Google Cloud Documentation. (URL: https://cloud.google.com/vertex-ai/docs/generative-ai/learn/chain-of-thought-prompting) [4] Kojima, T., Gu, S.S., Reid, M., et al. (2022). “Large Language Models are Zero-Shot Reasoners”. arXiv:2205.11916. (2022年5月24日 JST) [5] Madaan, A., Tandon, N., Zhang, J., et al. (2023). “Self-Refine: Iterative Refinement with Self-Feedback in Large Language Models”. arXiv:2303.17651. (2023年3月30日 JST)

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

コメント

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