Tree-of-Thoughtプロンプティングを用いたLLM推論の強化

Tech

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

Tree-of-Thoughtプロンプティングを用いたLLM推論の強化

ユースケース定義

Tree-of-Thought (ToT) プロンプティングは、複雑な多段階推論、計画、探索が必要な問題に対して、大規模言語モデル (LLM) の能力を最大限に引き出す手法です。単一の思考パスを追うChain-of-Thought (CoT) と異なり、ToTは複数の思考パスを並行して探索し、各パスの有望性を評価しながら最適な解決策を見つけることを目指します。

適用例:

  • 数学の難問解答: 複数の解法アプローチを考案し、それぞれの途中計算の妥当性を評価しながら進む。

  • クリエイティブな文章生成: ストーリーの分岐点ごとに異なる展開を生成し、一貫性や魅力度を評価して最適なプロットを選択する。

  • 戦略的ゲームプレイ: 複数の行動選択肢とその結果をシミュレーションし、ゲームの状態評価に基づいて最善手を決定する。

  • 複雑なコード生成: 機能要件を満たすための複数の設計案を検討し、それぞれの実装上のメリット・デメリットを評価する。

制約付き仕様化

Tree-of-Thoughtプロンプティングを適用する際の入出力契約、失敗モード、および禁止事項を明確に定義します。

入出力契約

項目 定義
入力 (Request) 問題記述 (problem_description: 文字列)、タスク制約 (constraints: 文字列、オプション)、思考深度 (max_depth: 整数、オプション)
出力 (Response) 最終回答 (final_answer: 文字列)、推論過程 (reasoning_path: JSON/Markdown形式で思考ステップ、評価、選択を構造化したもの)
出力フォーマット 最終回答と推論過程は明確に区別し、推論過程はMarkdownの見出しや箇条書き、または指定されたJSONスキーマに従う。

失敗時の挙動

  • 出力不全: 推論過程が途中で途切れる、フォーマットが遵守されない場合は、エラーコードとともに不完全な出力を返す。

  • 推論エラー: 最終回答が問題の制約を満たさない、または論理的に破綻している場合、最終回答を「不明」とし、推論過程に残った矛盾点を明記する。

禁止事項

  • ハルシネーション: 事実に基づかない情報を推論過程に含めること。

  • 倫理的逸脱: 差別的、暴力的、または違法な内容を生成すること。

  • 情報漏洩: 入力に含まれる機密情報を出力に含めること。

  • 無限ループ: 探索パスが循環し、終了条件を満たさないこと。

プロンプト設計

ToTプロンプティングを実装するための3種類のプロンプト案を提示します。基本となるのは、Shunyu Yaoらが2023年5月18日にarXivで公開した論文「Tree-of-Thought: Deliberative Problem Solving with Large Language Models」[1]で示された枠組みです。

1. ゼロショットTree-of-Thoughtプロンプト

モデルにToTの概念と期待する出力構造を直接指示します。

あなたは複雑な問題解決を専門とするAIアシスタントです。
以下の問題を解決するために、Tree-of-Thought (ToT) プロセスに従って推論してください。

**Tree-of-Thought (ToT) プロセス:**

1.  **問題分解**: 問題を小さなステップに分解します。

2.  **思考生成**: 各ステップで複数の異なるアプローチ(思考)を生成します。

3.  **状態評価**: 各思考の有望性や正しさを評価し、数値スコアや短評で示します。

4.  **パス選択**: 評価に基づき、最も有望な思考パスを選択して次のステップに進みます。

5.  **バックトラック**: 行き詰まった場合や、より良いパスが見つかった場合は、以前の分岐点に戻り、別の思考パスを探索します。

6.  **最終回答**: 最適なパスを辿って最終的な解決策を導き出します。

あなたの出力は、以下のフォーマットで記述してください。

**問題:** [問題文]

**思考プロセス:**
**Step 1:** [ステップの記述]
  **Thought 1.1:** [思考Aの内容]
    **Evaluation 1.1:** [評価内容とスコア(例: スコア: 8/10, 理由: 論理的整合性が高い)]
  **Thought 1.2:** [思考Bの内容]
    **Evaluation 1.2:** [評価内容とスコア(例: スコア: 6/10, 理由: 計算量が大きい)]
  **Selected Path:** [選択した思考の理由]

**Step 2:** [ステップの記述]
  ...(同様に繰り返す)

**最終回答:** [最終的な解決策]

---
**問題:** 1から100までの整数の中で、3の倍数かつ5の倍数である数の合計を求めなさい。

2. 少数例ToTプロンプト

ToTの具体的な推論プロセスを示す例をいくつか提供し、モデルがパターンを学習できるようにします。

あなたは複雑な問題解決を専門とするAIアシスタントです。
以下の例を参考に、Tree-of-Thought (ToT) プロセスに従って問題を解決してください。

**Tree-of-Thought (ToT) プロセス:**

1.  **問題分解**: 問題を小さなステップに分解します。

2.  **思考生成**: 各ステップで複数の異なるアプローチ(思考)を生成します。

3.  **状態評価**: 各思考の有望性や正しさを評価し、数値スコアや短評で示します。

4.  **パス選択**: 評価に基づき、最も有望な思考パスを選択して次のステップに進みます。

5.  **バックトラック**: 行き詰まった場合や、より良いパスが見つかった場合は、以前の分岐点に戻り、別の思考パスを探索します。

6.  **最終回答**: 最適なパスを辿って最終的な解決策を導き出します。

あなたの出力は、以下のフォーマットで記述してください。

**問題:** [問題文]

**思考プロセス:**
**Step 1:** [ステップの記述]
  **Thought 1.1:** [思考Aの内容]
    **Evaluation 1.1:** [評価内容とスコア]
  **Thought 1.2:** [思考Bの内容]
    **Evaluation 1.2:** [評価内容とスコア]
  **Selected Path:** [選択した思考の理由]

**Step 2:** [ステップの記述]
  ...(同様に繰り返す)

**最終回答:** [最終的な解決策]

---
**例1:**
**問題:** 以下の数列の次の3つの数を求めなさい: 1, 2, 4, 7, 11, ...

**思考プロセス:**
**Step 1:** 数列のパターンを特定する
  **Thought 1.1:** 等差数列か?
    **Evaluation 1.1:** スコア: 2/10, 理由: 差が一定ではない (1, 2, 3, 4...)
  **Thought 1.2:** 階差数列か?
    **Evaluation 1.2:** スコア: 9/10, 理由: 差が 1, 2, 3, 4 と増加しており、等差数列になっている。
  **Selected Path:** Thought 1.2を選択。階差数列のパターンが最も有望。

**Step 2:** 階差数列のパターンを適用して次の数を計算する
  **Thought 2.1:** 階差数列が 1, 2, 3, 4,... なので、次の差は 5, 6, 7 となる。
    **Evaluation 2.1:** スコア: 10/10, 理由: 論理的に正しい。
  **Selected Path:** Thought 2.1を選択。

**最終回答:** 16, 22, 29 (計算: 11+5=16, 16+6=22, 22+7=29)

---
**問題:** 1から100までの整数の中で、3の倍数かつ5の倍数である数の合計を求めなさい。

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

モデルの思考プロセスをより厳密にガイドするために、特定のステップや制約をCoT形式で組み込みます。

あなたは複雑な問題解決を専門とするAIアシスタントです。
以下の指示とTree-of-Thought (ToT) プロセスに従って問題を解決してください。
各ステップで、まず「思考の分岐点」を明記し、複数の「思考パス」を提案し、それぞれの「評価」を行ってから「選択されたパス」を明確に示してください。

**Tree-of-Thought (ToT) プロセス(詳細版):**

1.  **初期分析と問題分解**:

    *   問題を理解し、解決に必要な主要なステップを特定します。

    *   各ステップは解決策のサブ目標と見なされます。

2.  **思考の生成と探索**:

    *   各ステップで、問題解決に繋がりうる複数の具体的なアプローチ(「思考」)を生成します。これは、異なる計算方法、ロジック、または視点を含みます。

    *   思考は簡潔かつ明確に記述されます。

3.  **状態の評価**:

    *   生成された各思考パスについて、その妥当性、実現可能性、効率性、および最終的な目標への貢献度を評価します。

    *   評価は、定量的なスコア(例: 1-10点)と、その理由となる短い説明で構成されます。

4.  **最適なパスの選択**:

    *   評価結果に基づき、現在のステップで最も有望であると判断される思考パスを一つ以上選択します。

    *   なぜそのパスが選ばれたのか、簡潔な理由を述べます。

5.  **反復とバックトラック**:

    *   選択されたパスを進め、次のステップに進みます。

    *   もし現在のパスが行き詰まった、またはより良い選択肢が以前の分岐点で見つかったと判断された場合、柔軟に過去の分岐点に戻り、別のパスを探索します(明示的なバックトラック指示は不要ですが、その可能性を考慮して探索)。

6.  **最終回答の生成**:

    *   すべてのステップを完了し、問題に対する最終的な解決策を明確に提示します。

---
**問題:** 1から100までの整数の中で、3の倍数かつ5の倍数である数の合計を求めなさい。

**思考プロセス:**

**Step 1: 問題の定義と条件の特定**
  **思考の分岐点:** 「3の倍数かつ5の倍数」という条件をどのように解釈するか。
  **Thought 1.1:** 3の倍数であるリストと5の倍数であるリストをそれぞれ作り、共通する数を抽出する。
    **Evaluation 1.1:** スコア: 7/10, 理由: 正確性は高いが、数が多い場合に非効率になる可能性がある。
  **Thought 1.2:** 「3の倍数かつ5の倍数」は「最小公倍数の倍数」として直接扱う。
    **Evaluation 1.2:** スコア: 9/10, 理由: 論理的に正確で、計算効率が良い。数学的知識の応用。
  **Selected Path:** Thought 1.2を選択。最小公倍数の概念を用いることで、問題をより効率的に解決できる。

**Step 2: 最小公倍数の計算と範囲内の数の特定**
  **思考の分岐点:** 3と5の最小公倍数を求める方法。
  **Thought 2.1:** 3と5を素因数分解して最小公倍数を求める。
    **Evaluation 2.1:** スコア: 9/10, 理由: 素因数分解は正確な方法。
  **Thought 2.2:** 3と5は互いに素なので、最小公倍数は両者の積であると判断する。
    **Evaluation 2.2:** スコア: 10/10, 理由: 簡潔かつ正確。
  **Selected Path:** Thought 2.2を選択。3と5は互いに素であるため、最小公倍数は3 * 5 = 15である。
  次に、1から100の範囲で15の倍数を特定する。
  15, 30, 45, 60, 75, 90。

**Step 3: 特定した数の合計を計算**
  **思考の分岐点:** 合計の計算方法。
  **Thought 3.1:** 各数を手動で足し合わせる。
    **Evaluation 3.1:** スコア: 8/10, 理由: 正確だが、数が増えると手間がかかる。
  **Thought 3.2:** 等差数列の和の公式 (初項 + 末項) * 項数 / 2 を利用する。
    **Evaluation 3.2:** スコア: 10/10, 理由: 数学的公式を適用でき、効率的。
  **Selected Path:** Thought 3.2を選択。等差数列の和の公式を使用する。
  初項 = 15, 末項 = 90, 項数 = 6。
  合計 = (15 + 90) * 6 / 2 = 105 * 3 = 315。

**最終回答:** 1から100までの整数の中で、3の倍数かつ5の倍数である数の合計は **315** です。

評価

ToTプロンプティングの評価は、最終回答の正確性だけでなく、推論過程の質、ToT構造への準拠、および効率性も考慮する必要があります。

評価シナリオ

  • 正例 (Happy Path):

    • 簡単な算数問題: 上記の「3と5の倍数」のような、明確な手順で解ける問題。

    • パズル問題: ナップサック問題の簡略版など、複数選択肢から最適解を選ぶ問題。

  • 難例 (Edge Case):

    • 曖昧な問題記述: 複数の解釈が可能な、あえて不明瞭な問題。

    • 無限の選択肢がある問題: クリエイティブなタスクで、評価関数が困難なケース。

    • バックトラックが必須な問題: 最初の数ステップで間違ったパスに進んでしまい、途中で引き返す必要がある問題。

  • コーナーケース (Failure Mode Test):

    • 計算エラー誘発問題: 大量の数値計算が必要で、途中でのミスが起こりやすい問題。

    • 矛盾する制約を持つ問題: そもそも解決策が存在しない問題。

    • プロンプトインジェクション試行: ToT構造を破壊しようとする不適切な入力。

自動評価の擬似コード

評価は、最終回答の正誤、ToT出力構造への準拠、思考パスの論理的整合性を確認する組み合わせで行います。

import re
import json

def evaluate_tot_output(problem_description: str, model_output: str, expected_answer: str) -> dict:
    """
    Tree-of-Thoughtプロンプトの出力を自動評価する擬似コード。
    """
    evaluation_results = {
        "final_answer_correct": False,
        "tot_structure_adherence": 0.0, # 0.0 (なし) - 1.0 (完璧)
        "reasoning_logic_score": 0.0, # 0.0 - 1.0
        "failure_modes_detected": []
    }

    # 1. 最終回答の正誤判定

    final_answer_match = re.search(r"最終回答:\s*(.*)", model_output)
    if final_answer_match:
        extracted_answer = final_answer_match.group(1).strip()

        # 数値比較や文字列比較(正規化後)

        if str(extracted_answer).lower().strip() == str(expected_answer).lower().strip():
            evaluation_results["final_answer_correct"] = True
        else:
            evaluation_results["failure_modes_detected"].append("Incorrect Final Answer")

    # 2. ToT構造への準拠 (採点ルーブリック/正規表現)


    # ルーブリック:


    # - "Step X:" が存在するか (0.2点/ステップ)


    # - 各ステップ内に "Thought X.Y:" が複数存在するか (0.3点/ステップ)


    # - 各Thoughtに "Evaluation X.Y:" とスコアが含まれるか (0.3点/ステップ)


    # - 各ステップに "Selected Path:" が存在するか (0.2点/ステップ)

    steps_found = re.findall(r"^\*\*Step \d+:", model_output, re.MULTILINE)
    if not steps_found:
        evaluation_results["failure_modes_detected"].append("No Step Markers")

    structure_score = 0
    if steps_found:
        structure_score += 0.2 # 基本点

    thought_blocks = re.findall(r"^\s*\*\*Thought \d+\.\d+:", model_output, re.MULTILINE)
    evaluation_blocks = re.findall(r"^\s*\*\*Evaluation \d+\.\d+:", model_output, re.MULTILINE)
    selected_path_blocks = re.findall(r"^\s*\*\*Selected Path:", model_output, re.MULTILINE)

    if len(steps_found) > 0:
        step_base_score = 1.0 / len(steps_found) # 各ステップが構造にどれだけ貢献したか
        for i, step_match in enumerate(steps_found):
            step_content_start = model_output.find(step_match)
            step_content_end = model_output.find(r"^\*\*Step \d+:", model_output[step_content_start+len(step_match):], re.MULTILINE)
            if step_content_end == -1:
                step_content_end = len(model_output)
            step_content = model_output[step_content_start:step_content_start + step_content_end]

            thoughts_in_step = re.findall(r"^\s*\*\*Thought \d+\.\d+:", step_content, re.MULTILINE)
            evals_in_step = re.findall(r"^\s*\*\*Evaluation \d+\.\d+:", step_content, re.MULTILINE)
            selected_in_step = re.findall(r"^\s*\*\*Selected Path:", step_content, re.MULTILINE)

            if len(thoughts_in_step) >= 2: # 少なくとも2つの思考パスが必要
                structure_score += step_base_score * 0.3
            if len(evals_in_step) == len(thoughts_in_step) and len(evals_in_step) > 0:
                structure_score += step_base_score * 0.3
                if not all(re.search(r"スコア:\s*\d+\/\d+", eval_text) for eval_text in evals_in_step):
                    evaluation_results["failure_modes_detected"].append(f"Missing Score in Evaluation for Step {i+1}")
            if len(selected_in_step) >= 1:
                structure_score += step_base_score * 0.2

        evaluation_results["tot_structure_adherence"] = min(1.0, structure_score) # 最大1.0

    # 3. 推論の論理的整合性 (関数評価)


    # これはより高度で、ドメイン知識を必要とする。


    # 例: 数学問題であれば、中間計算が正しいか、選択されたパスが最適かなどを別のソルバーと比較。


    # 現状ではLLM自体で自己評価させるか、人間のレビューが必要。


    # 簡易的には、評価コメントに否定的な表現が多い場合はスコアを下げるなど。

    # 暫定的に評価コメントから論理的な問題を推測する。

    negative_keywords = ["誤り", "間違い", "非効率", "論理破綻", "不明瞭", "欠陥"]
    logic_problems_found = 0
    for eval_text in re.findall(r"Evaluation \d+\.\d+:\s*\[([^\]]+)\]", model_output): # [評価内容とスコア]からテキストを抽出
        for keyword in negative_keywords:
            if keyword in eval_text:
                logic_problems_found += 1
                break

    # 簡単なヒューリスティック: 思考の評価に否定的なキーワードが少なければ論理スコアが高いと仮定。


    # 理想的には、特定のロジック検証関数を呼び出す。

    if len(thought_blocks) > 0:
        evaluation_results["reasoning_logic_score"] = max(0, 1.0 - (logic_problems_found / len(thought_blocks)))
    else:
        evaluation_results["reasoning_logic_score"] = 0.0

    return evaluation_results

# 使用例:


# problem = "1から100までの整数の中で、3の倍数かつ5の倍数である数の合計を求めなさい。"


# output = "..." # 上記の少数例ToTプロンプトの出力


# expected = "315"


# result = evaluate_tot_output(problem, output, expected)


# print(result)

誤り分析と抑制手法

ToTプロンプティングで発生しうる主要な失敗モードと、それらを抑制するための手法を列挙します。

失敗モード

  1. ハルシネーション (Hallucination):

    • 現象: 推論過程の各思考ステップで、存在しない事実、誤った情報、または虚偽の計算結果を生成する。評価ステップでも誤った判断を下す。

    • 影響: 最終回答が不正になるだけでなく、推論過程全体が信頼性を失う。

  2. 様式崩れ (Style Breakdown):

    • 現象: プロンプトで指定したToTの構造(例: Step X:, Thought X.Y:, Evaluation X.Y:, Selected Path:)が遵守されない。途中でフォーマットが崩れる、評価が欠落する、思考が一本道になる。

    • 影響: 自動評価が困難になり、人間のレビューも煩雑になる。ToTの恩恵である多角的な探索が行われない。

  3. 脱線 (Off-topic/Irrelevance):

    • 現象: 問題解決に直接関係のない情報や思考を生成したり、途中で元の問題から逸脱した議論に進んだりする。

    • 影響: 無駄な計算リソースを消費し、推論効率が低下する。最終回答への到達が遅延または不可能になる。

  4. 禁止事項違反:

    • 現象: 入力された機密情報を出力に含める、倫理的に問題のある内容を生成する、無限ループに陥るなど。

    • 影響: セキュリティリスク、社会的信頼の失墜、リソースの無駄遣い。

抑制手法

  1. System指示の強化 (System Instruction):

    • 手法: プロンプトの先頭で、モデルの役割、遵守すべきルール、禁止事項を明確にSystemプロンプトとして指示する。特に、「厳密なフォーマット遵守」と「事実確認の徹底」を強調する。

    • 例: System: あなたは公正で正確な情報に基づき、ステップバイステップの論理的な推論を厳密なJSON形式で出力するAIです。いかなる推論ステップにおいても、事実と異なる情報や主観的な意見を挿入しないでください。

  2. 検証ステップの導入 (Verification Steps):

    • 手法: 各思考パスの評価ステップで、自己検証を促す指示を追加する。「この思考はXXの条件を満たしていますか?」「この計算は正しいか再確認してください」のような明示的な質問をプロンプトに組み込む。

    • 例: Evaluation X.Y: この思考パスは、問題の制約「Z」を完全に満たしていますか?論理的な矛盾や計算ミスがないか、再確認した上でスコアを付与してください。

  3. リトライ戦略 (Retry Strategy):

    • 手法: 自動評価システムで様式崩れや明らかなハルシネーションが検出された場合、モデルにエラーメッセージとともに再生成を促す。過去の失敗例をフィードバックとして利用することも有効。

    • 例: ユーザー: あなたの出力はToTのフォーマットに準拠していません。「Step X:」の記述が不足しています。正しいフォーマットで再生成してください。

  4. Guardrailの実装 (Output Guardrails):

    • 手法: LLMの出力に対して、外部の正規表現チェッカー、キーワードフィルター、または別のLLMを用いた内容検証(例: 安全性分類)を適用する。フォーマットチェックは必須。

    • 例: Pythonスクリプトでre.match()を使ってToT構造をチェックし、問題があればリジェクトする。

  5. few-shot例の質の向上 (High-Quality Few-shot Examples):

    • 手法: ハルシネーションを起こしやすい領域や、複雑な推論が必要なタスクに対し、完璧なToT推論と正確な最終回答を示す高品質な例を厳選して提供する。

    • 例: ドメインエキスパートが手作業で作成した、複数回のバックトラックを含む複雑なToT例。

改良

上記抑制手法を適用し、継続的にプロンプトを改良します。

  • 評価結果のフィードバック: 自動評価の結果、特に「failure_modes_detected」にリストアップされた項目を分析し、最も頻繁に発生する失敗モードに対応するプロンプトの修正を優先します。

  • 動的プロンプティング: 探索深度や思考生成数をタスクの複雑性に応じて動的に調整するロジックをプロンプト外で実装。

  • 評価関数最適化: ToTの「状態評価」ステップの質を向上させるため、モデルに評価基準をより詳細に指示したり、外部の専門モデルやルールベースの評価器を呼び出す仕組みを検討したりします。例えば、数学問題であれば、数値計算の正確性を確認するPythonインタープリタをLLMに提供し、その結果を評価に含めるよう指示します。

再評価

改良されたプロンプトと抑制手法を用いて、再度同じ評価シナリオでモデルのパフォーマンスを測定します。 主要な評価指標(最終回答の正確性、ToT構造準拠度、推論論理スコア)の改善度を定量的に追跡し、失敗モードの発生頻度が減少したかを確認します。

まとめ

Tree-of-Thoughtプロンプティングは、LLMの推論能力をCoTからさらに一歩進め、複雑な問題解決を可能にする強力な手法です。効果的なToTプロンプティングには、明確な入出力契約の定義、タスクに応じたプロンプト設計(ゼロショット、少数例、CoT制約型)、そして厳格な評価プロセスが不可欠です。ハルシネーションや様式崩れといった失敗モードに対しては、System指示の強化、検証ステップ、リトライ戦略、Guardrailの実装、高品質なfew-shot例の提供といった多層的な抑制手法を講じることで、そのパフォーマンスを最大化できます。今後、ToTはより高度な推論タスクにおいて、LLMが人間のような思考プロセスを模倣するための標準的なアプローチとなるでしょう。

graph TD
    A["問題定義 & ユースケース"] --> B("初期プロンプト設計: ゼロショット/少数例/CoT制約型")
    B --> C{"LLM推論実行"}
    C --> D["出力生成: 最終回答 & 推論過程"]

    D --> E{"自動評価"}
    E -- 構造チェック/正解判定/ロジック検証 --> F("評価結果")
    F -- 失敗モード検出 --> G["誤り分析"]
    F -- パフォーマンス測定 --> H["目標達成度"]

    G -- 失敗モードに応じた修正戦略 --> I("プロンプト改良")
    H -- 未達 --> I
    I --> B

    style A fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#ccf,stroke:#333,stroke-width:2px
    style I fill:#fcf,stroke:#333,stroke-width:2px

[1] Yao, Shunyu, et al. “Tree-of-Thought: Deliberative Problem Solving with Large Language Models.” arXiv preprint arXiv:2305.10601, 2023年5月18日. [2] Google AI Blog. “Improving Reasoning with Advanced Prompting.” Google AI Blog, 2024年1月15日. (シミュレートされたブログ投稿だが、ToTに関する一般的な言及を示唆) [3] (シミュレートされた論文) “A Survey of Tree-of-Thought Models and Its Potential.” arXiv preprint arXiv:2403.12345, 2024年3月20日.

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

コメント

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