Chain-of-Thoughtプロンプティング実践

Tech

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

Chain-of-Thoughtプロンプティング実践

Chain-of-Thought(CoT)プロンプティングは、大規模言語モデル(LLM)が複雑な推論タスクを段階的に分解し、より正確な結論を導き出すための強力な手法です。このアプローチは、モデルが思考プロセスを明示的に表現することで、その推論能力を向上させます。本記事では、CoTプロンプティングの実践的な設計、評価、改良サイクルについて解説します。

ユースケース定義

本記事のCoTプロンプティングのユースケースとして、「複数の条件を考慮した複雑なデータ変換と正当性の説明」を設定します。具体的には、与えられたJSON形式の顧客データと複数の変換ルールに基づき、データを整形し、その変換理由をCoT形式で詳細に説明するタスクとします。このタスクは、データクレンジング、個人情報匿名化、特定フォーマットへの変換など、多岐にわたるビジネスシーンで応用可能です。

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

LLMとのインタラクションにおいて、入出力契約を明確に定義することは、期待通りの挙動を引き出し、失敗モードを抑制するために不可欠です。

入力契約

  • フォーマット: 以下の構造を持つJSON文字列。

    {
      "customer_data": [
        {"id": "C001", "name": "John Doe", "email": "john.doe@example.com", "age": 30, "status": "active", "purchase_history": [100, 200]},
        {"id": "C002", "name": "Jane Smith", "email": "jane.smith@example.com", "age": null, "status": "inactive", "purchase_history": []}
      ],
      "transformation_rules": [
        {"rule_id": "R1", "description": "ageがnullの場合は25に設定", "target_field": "age", "condition": {"age": null}, "action": {"set_value": 25}},
        {"rule_id": "R2", "description": "emailドメインがexample.com以外は匿名化", "target_field": "email", "condition": {"email_domain_not_in": ["example.com"]}, "action": {"anonymize_domain": true}},
        {"rule_id": "R3", "description": "statusがinactiveの場合は削除", "condition": {"status": "inactive"}, "action": {"delete_record": true}}
      ]
    }
    
  • 必須項目: customer_data(配列)、transformation_rules(配列)。

  • データ型: 各フィールドはJSONの基本データ型(文字列、数値、真偽値、null、配列、オブジェクト)に準拠。

  • 制約: transformation_rulesは最大5つ。

出力契約

  • フォーマット: 以下の構造を持つJSON文字列。

    {
      "transformed_data": [
        // 変換後の顧客データオブジェクトの配列
      ],
      "reasoning_process": "CoT形式による詳細な変換プロセス説明"
    }
    
  • transformed_data: 変換後の顧客データオブジェクトの配列。

  • reasoning_process:

    • CoT形式のMarkdown文字列。

    • 各顧客データと各変換ルール適用について、以下の要素を含む。

      1. 対象レコードのid

      2. 適用されたrule_idまたは適用されなかった理由

      3. 変換前後の値

      4. 変換の正当性(ルールに基づく説明)

    • 最終的なtransformed_dataに至るまでの思考プロセスを明示的に記述する。

失敗時の挙動

  • 入力JSONの構文が無効な場合: {"error": "Invalid input JSON format."} を返す。

  • 変換ルールに矛盾がある場合: {"error": "Conflicting transformation rules detected. Please resolve rule_id: [ID1], [ID2]."} を返す。

  • 内部エラー(例: 幻覚、様式崩れ): {"error": "An internal processing error occurred. Please try again or refine your prompt."} を返す。

禁止事項

  • 入力データに存在しないフィールドの新規作成(変換ルールで明示的に指示された場合を除く)。

  • 個人を特定できる情報(PII)の意図しない開示(匿名化ルールが適用された場合は匿名化すること)。

  • 無関係な情報の出力や、タスクからの脱線。

プロンプト設計

複雑なタスクに対してLLMの推論能力を最大限に引き出すため、CoT(Chain-of-Thought)プロンプティングは非常に有効です[1]。ここでは、3種類のプロンプト案を提示します。

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

CoTの指示を含まず、モデルの一般的な推論能力に依存します。

あなたはデータ変換専門家です。
与えられた顧客データと変換ルールに基づき、データを変換し、最終的なJSON形式のデータを出力してください。

入力:
{{入力JSON文字列}}

出力:
{
  "transformed_data": [
    // 変換後の顧客データオブジェクトの配列
  ],
  "reasoning_process": "ここに変換プロセスを記述"
}

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

いくつかの具体例を提供することで、モデルに望ましい出力形式とCoTのスタイルを示します。

あなたはデータ変換専門家です。
与えられた顧客データと変換ルールに基づき、データを変換し、その変換理由をChain-of-Thought形式で詳細に説明したJSON形式のデータを出力してください。

---
例1:
入力:
{
  "customer_data": [{"id": "C001", "age": null}],
  "transformation_rules": [{"rule_id": "R1", "description": "ageがnullの場合は25に設定", "target_field": "age", "condition": {"age": null}, "action": {"set_value": 25}}]
}

出力:
{
  "transformed_data": [{"id": "C001", "age": 25}],
  "reasoning_process": "# 変換プロセス\n\n- **C001**: \n  - **適用ルール**: R1\n  - **詳細**: `age`が`null`であったため、ルールR1に従い`age`を`25`に設定しました。\n  - **変換前**: `age: null` -> **変換後**: `age: 25`"
}
---
例2:
入力:
{
  "customer_data": [{"id": "C002", "email": "test@other.com"}],
  "transformation_rules": [{"rule_id": "R2", "description": "emailドメインがexample.com以外は匿名化", "target_field": "email", "condition": {"email_domain_not_in": ["example.com"]}, "action": {"anonymize_domain": true}}]
}

出力:
{
  "transformed_data": [{"id": "C002", "email": "test@anonymized.com"}],
  "reasoning_process": "# 変換プロセス\n\n- **C002**: \n  - **適用ルール**: R2\n  - **詳細**: `email`のドメインが`other.com`であり、`example.com`ではなかったため、ルールR2に従いドメインを匿名化しました。\n  - **変換前**: `email: test@other.com` -> **変換後**: `email: test@anonymized.com`"
}
---

入力:
{{入力JSON文字列}}

出力:

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

システム指示でCoTの思考プロセスを詳細に指示し、ステップバイステップの推論を強制します[2]。

SYSTEM:
あなたは極めて正確なデータ変換を実行するエキスパートです。
以下の手順に従って、顧客データの変換と詳細な理由付けを行ってください。

1.  **入力の解析**: 提供されたJSON入力(顧客データと変換ルール)を厳密に解析します。

2.  **ルール評価の計画**: 各顧客レコードに対し、すべての変換ルールを定義された順序で評価する計画を立てます。

3.  **ステップバイステップの変換**:

    *   各顧客レコードを個別に処理します。

    *   各ルールについて、そのレコードに条件が合致するかを判断します。

    *   条件が合致した場合、指定されたアクション(値の設定、匿名化、レコード削除など)を実行します。

    *   条件が合致しない場合、その理由を明記します。

4.  **理由付けの構築**: 各レコードの各変換ステップについて、以下の情報をMarkdown形式で記述します。

    *   対象レコードのID (`id`)

    *   適用された`rule_id`とその理由、または適用されなかった`rule_id`とその理由

    *   変換前後の値

    *   最終的な状態に至るまでの具体的なステップを論理的に記述します。

5.  **最終出力の生成**: 変換された顧客データの配列と、詳細な理由付けプロセスを、指定されたJSONスキーマに厳密に従って出力します。

    *   `transformed_data`には最終的なレコードのみを含めます。

    *   `reasoning_process`は、各レコードの変換履歴を網羅的に記載します。

    *   出力は、正確なJSON形式でなければなりません。

USER:
以下のJSONデータを変換し、そのプロセスを詳細に説明してください。

入力:
{{入力JSON文字列}}

出力:

評価

CoTプロンプティングの評価は、出力の正確性だけでなく、推論プロセスの品質も測る必要があります。

評価シナリオ

  1. 正例: 全てのルールがシンプルに適用されるデータ。

    • C001: ageがnull -> 25に設定。

    • C002: emailドメインがother.com -> 匿名化。

  2. 難例: 複数のルールが競合・依存するデータ。

    • C003: ageがnull、かつstatusinactiveinactiveなら削除されるため、ageのルールは適用されない(または適用されるべきではない)。

    • C004: emailinvalidageがnullだが、まずemailが匿名化され、その後のageルールが適用される。

  3. コーナーケース: 無効な入力、矛盾するルール。

    • 入力JSONの構文エラー。

    • transformation_rulesで同じフィールドに対して矛盾するset_valueが定義されている。

自動評価の擬似コード

LLMの出力を自動的に評価するためには、採点ルーブリック、正規表現、関数評価を組み合わせます。

import json
import re

def evaluate_cot_output(llm_output: str, expected_data: list, expected_reasoning_keywords: list) -> dict:
    """
    LLMのCoT出力と期待値を比較し、評価スコアと詳細を返す擬似コード。

    Args:
        llm_output (str): LLMから得られた出力JSON文字列。
        expected_data (list): 期待されるtransformed_dataのリスト。
        expected_reasoning_keywords (list): reasoning_processに存在すべきキーワードのリスト。

    Returns:
        dict: 評価結果 (スコア、エラーメッセージなど)。
    """
    score = 0
    feedback = []

    # 1. JSON構文チェック

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

    # 2. キー存在チェック

    if "transformed_data" not in output_json:
        feedback.append("キー 'transformed_data' が出力JSONに存在しません。")
    else:
        score += 5
    if "reasoning_process" not in output_json:
        feedback.append("キー 'reasoning_process' が出力JSONに存在しません。")
    else:
        score += 5

    if "transformed_data" not in output_json or "reasoning_process" not in output_json:
        return {"score": score, "feedback": feedback}

    # 3. transformed_dataの正確性評価


    # ここでは簡易比較。実際にはIDでソートしてディープ比較を行うべき


    # 計算量: O(N log N + M log M) if sorting, O(N*M) worst case for direct comparison


    # メモリ条件: O(N + M) for storing data

    if sorted(output_json["transformed_data"], key=lambda x: x.get('id', '')) == \
       sorted(expected_data, key=lambda x: x.get('id', '')):
        score += 40
        feedback.append("transformed_data は期待値と一致しています。")
    else:
        feedback.append(f"transformed_data が期待値と異なります。出力: {output_json['transformed_data']}, 期待: {expected_data}")

    # 4. reasoning_processの品質評価 (キーワード、構造、論理性)

    reasoning = output_json["reasoning_process"]

    # CoTの必須キーワードチェック

    all_keywords_found = True
    for keyword in expected_reasoning_keywords:
        if keyword not in reasoning:
            all_keywords_found = False
            feedback.append(f"reasoning_process に必須キーワード '{keyword}' が見つかりません。")
    if all_keywords_found:
        score += 20
        feedback.append("reasoning_process に必須キーワードが全て含まれています。")

    # CoTの構造チェック (例: "- **CXXX**:" のような行が存在するか)

    if re.search(r"^- \*\*C\d{3}\*\*:.*", reasoning, re.MULTILINE):
        score += 10
        feedback.append("reasoning_process にCoTの構造(レコードごとの説明)が見られます。")
    else:
        feedback.append("reasoning_process にCoTの構造が見られません。")

    # 5. 全体的な評価

    if score >= 80:
        feedback.append("全体的に優れたCoTプロンプト出力です。")
    elif score >= 50:
        feedback.append("CoTプロンプト出力は許容範囲ですが、改善の余地があります。")
    else:
        feedback.append("CoTプロンプト出力は不十分です。大幅な改善が必要です。")

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

# --- 評価関数の利用例 ---


# llm_response = '{"transformed_data": [...], "reasoning_process": "# 変換プロセス..."}'


# expected_transformed = [{"id": "C001", "age": 25}]


# expected_cot_elements = ["C001", "R1", "age", "null", "25"]


# result = evaluate_cot_output(llm_response, expected_transformed, expected_cot_elements)


# print(result)

誤り分析と抑制手法

CoTプロンプティングにおいても、LLMは様々な失敗モードを示す可能性があります。

失敗モード

  • 幻覚(Hallucination):

    • 変換ルールを誤って解釈したり、存在しないデータフィールドを参照したりする。

    • 推論プロセス中に事実と異なる情報を生成する[3]。

  • 様式崩れ(Format Deviation):

    • 出力JSONが不正な構造になる。

    • reasoning_processがMarkdown形式でなかったり、指定されたCoTのステップを踏んでいなかったりする。

  • 脱線(Task Deviation):

    • 変換タスクと無関係な情報を出力に含める。

    • 指定されていない情報源から情報を引き出し、余計な説明を追加する。

  • 禁止事項違反:

    • 匿名化ルールがあるにも関わらず、個人を特定できる情報をそのまま出力する。

    • 倫理的に問題のある内容や不適切な表現を生成する。

抑制手法

  • System指示による役割と制約の明確化:

    • プロンプトの冒頭でLLMの役割(例: 「あなたは極めて正確なデータ変換を実行するエキスパートです」)と、従うべき厳格なルールを明確に定義する。

    • 出力形式、禁止事項、思考プロセスを箇条書きなどで具体的に指示する。

  • 少数例学習(Few-shot Learning):

    • 望ましい入出力ペアと、詳細なCoTの例を複数提示し、モデルに学習させる。

    • 特に、複雑な推論やコーナーケースの例は有効です。

  • 自己検証(Self-Correction)ステップの導入:

    • モデルに一度出力を生成させた後、「上記の変換プロセスに誤りがないか、もう一度確認し、誤りがあれば訂正してください」といった指示を追加する。

    • モデルが自身の推論を評価し、修正する能力を活用します[4]。

  • 外部ツールとの連携:

    • 出力JSONの構文チェックやスキーマバリデーションを、LLMの出力後に外部のコードで実行する。

    • 変換後のデータの整合性チェックをプログラマブルに行い、問題があればLLMにフィードバックする。

  • リトライ戦略:

    • 自動評価で出力が失敗した場合、プロンプトにエラーメッセージや具体的な修正指示を加えて再試行する。

    • 例: 「JSON構文エラーが発生しました。出力は必ず有効なJSON形式でお願いします。」

改良

誤り分析で特定された失敗モードに基づいて、プロンプトを具体的に改良します。

  1. 幻覚対策: Systemプロンプトに「与えられたルール以外の情報を推測したり、追加したりしないでください。」と追記。複雑なルールに対しては、ステップごとに中間検証を促す指示を追加。

  2. 様式崩れ対策: 出力JSONのスキーマをプロンプト内でより具体的に定義するか、JSON Schemaを直接与えることを検討。CoTのMarkdown形式も「各レコードごとにH2ヘッダーで区切り、その下に箇条書きでステップを記述する」など、厳格化する。

  3. 脱線対策: Systemプロンプトで「厳密にデータ変換と理由付けのみに焦点を当て、それ以外の情報は一切含めないでください。」と強調。

  4. 禁止事項対策: 「個人を特定できる情報(PII)は、変換ルールが明示的に匿名化を指示した場合を除き、厳重に秘匿してください。」といった具体的な警告を追加。

これらの改良は、プロンプトの冗長性を増す可能性はありますが、LLMの信頼性と予測可能性を大幅に向上させます。

再評価

改良されたプロンプトに対して、以前と同じ評価シナリオと自動評価の擬似コードを用いて再評価を実施します。これにより、改良が意図した効果を発揮しているか、新たな問題が発生していないかを確認します。特に、失敗モードとして顕在化していたケースが改善されているかを重点的に検証します。この反復的なプロセスが、堅牢なCoTプロンプトを構築する上で不可欠です。

まとめ

Chain-of-Thoughtプロンプティングは、LLMの複雑な推論能力を引き出し、透明性の高い出力をもたらす強力な手法です。本記事では、具体的なユースケース定義から、制約付き入出力契約の策定、ゼロショット、少数例、CoT制約型のプロンプト設計、そして自動評価の擬似コードまでを網羅しました。また、幻覚や様式崩れといった失敗モードを分析し、それらを抑制するための実践的な手法と改良サイクルについても詳述しました。

CoTプロンプティングを効果的に活用することで、LLMは単なる情報生成ツールから、信頼できる推論パートナーへと進化します。今後も、自己修正メカニズムの統合[4]や、より洗練されたCoTの構造化によって、LLMの推論能力はさらなる高みを目指すでしょう。


参考文献

[1] Xiangyu Liu et al. “Chain-of-Thought Reasoning with Large Language Models: A Survey”. arXiv preprint arXiv:2407.01859, 2024年7月2日. [2] Shon F. K. et al. “The Effect of Step-by-Step Reasoning on Model Performance”. arXiv preprint arXiv:2405.15875, 2024年5月23日. [3] Google AI Blog. “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models”. 2022年5月10日. https://ai.googleblog.com/2022/05/chain-of-thought-prompting-elicits.html [4] Xiangyu Liu et al. “Chain-of-Thoughts with Self-Correction for Multi-hop Reasoning”. arXiv preprint arXiv:2404.09886, 2024年4月15日. [5] Google Cloud Blog. “Gemini API と Chain-of-Thought (CoT) プロンプティングで複雑なタスクを処理する”. 2024年1月19日. https://cloud.google.com/blog/ja/products/ai-machine-learning/handle-complex-tasks-with-gemini-api-and-cot-prompting

graph TD
    A["プロンプト設計"] --> B{"LLMの実行"}
    B --> C["出力生成"]
    C --> D{"自動評価/人手評価"}
    D -- 出力失敗/要改善 --> E["誤り分析"]
    E --> F["プロンプト改良"]
    F --> A
    D -- 出力成功/承認 --> G["システムデプロイ/活用"]
ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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