【ReAct vs CoT】LLMの思考プロセスを可視化・制御する実践的アプローチ
1. はじめに:AIツールの舞台裏で起こっていること
こんにちは。Binary Techでソフトウェアエンジニアをしております、Chiと申します。
普段はAI機能を組み込んだ製品開発と、そのクラウドインフラ構築を担当しています。
社内でAIを活用したツールを開発・提供する中で、よく耳にする質問があります。
「なぜこのAIは、単純な四則演算を間違えるのに、複雑な推論はできたりするの?」
「同じモデルを使っているのに、ツールによって答えの精度が大きく違うのはなぜ?」
その答えの鍵は、「プロンプトエンジニアリング」、特に思考プロセスを如何に設計するかにあります。
本稿では、大規模言語モデル(LLM)の推論能力を飛躍的に高める2つの手法、Chain of Thought (CoT) と ReAct (Reasoning + Acting) について、実装コードとベンチマークを交えながら、その舞台裏と実践的な導入方法を深掘りします。
2. 技術概要:CoTとReActの基本概念
まずは、それぞれの手法のコアコンセプトを理解しましょう。
Chain of Thought (CoT) - 思考の連鎖
CoTは、モデルに最終答えだけでなく、答えに至るまでの思考ステップも順番に生成させる手法です。人間が頭のなかで「まずは...、次に...、だから...」と考えるプロセスを模倣します。
- 特徴: 比較的シンプルで導入しやすい。複数ステップの推論が必要な問題(数学の文章題、複雑な読解問題など)の精度向上に極めて有効。
-
プロンプトのキーワード:
考えましょう
、ステップバイステップで
、まずは
など。
ReAct (Reasoning + Acting) - 推論と行動の融合
ReActは、CoTの推論ステップに**「行動(Action)」**を加え、外部ツール(API、関数、検索エンジン等)と連携できるようにした発展形です。推論→行動→観察のサイクルを繰り返します。
- Reason(推論): 現在の状況を元に、次に何をすべきか思考する。
- Act(行動): 思考結果に基づき、外部のツールを実行する。
- Observe(観察): ツールの実行結果を観察し、次の推論の材料とする。
- 特徴: モデル内部の知識に依存せず、最新・正確な情報を取得できる。動的な計画立案やツール操作が可能。
-
プロンプトのキーワード:
Thought:
,Action:
,Observation:
というフォーマットが一般的。
3. 実装例:Python + LangChain で両者を比較する
理論だけではわかりにくいので、実際にコードを使って比較してみましょう。今回はLLMアプリケーション構築のデファクトスタンダードである LangChain フレームワークを使用します。
共通の設定
まずは必要なライブラリをインストールし、LLM(ここではAzure OpenAIのGPT-4)をセットアップします。
pip install langchain langchain-openai
# 環境設定とLLMの初期化
import os
from langchain_openai import AzureChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 環境変数にAPIキーなどを設定
os.environ["AZURE_OPENAI_API_KEY"] = "your_api_key"
os.environ["AZURE_OPENAI_ENDPOINT"] = "your_endpoint"
# LLMの初期化
llm = AzureChatOpenAI(
deployment_name="gpt-4",
temperature=0 # 創造性を抑え、再現性を高める
)
例1: Chain of Thought (CoT) の実装
課題: 「マイクとスピーカーがそれぞれ2つと3つあります。合計でいくつの電子機器がありますか?」
# CoT用のプロンプトテンプレート
cot_prompt = PromptTemplate(
input_variables=["question"],
template="""
以下の質問に答えてください。答えに至るまでの理由もステップバイステップで説明してください。
質問: {question}
理由:
"""
)
# チェーンを作成
cot_chain = LLMChain(llm=llm, prompt=cot_prompt)
# 実行
question = "マイクとスピーカーがそれぞれ2つと3つあります。合計でいくつの電子機器がありますか?"
result = cot_chain.run(question)
print("CoT 結果:")
print(result)
想定される出力:
理由:
まず、マイクが2つあります。
次に、スピーカーが3つあります。
電子機器はマイクとスピーカーの両方を指すので、これらを合計します。
2つ + 3つ = 5つです。
したがって、合計で5つの電子機器があります。
例2: ReAct の実装 (シンプルな関数をツールとして使用)
ReActを実装するには、LangChainの Agent
と Tool
の概念を使用します。
ここでは、簡単な乗算を行う関数を「ツール」として定義し、エージェントに利用させます。
課題: 「123 × 45 の答えは何ですか? 計算が必要です。」
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
# ツールとして使う簡単な乗算関数を定義
def multiply(a, b):
"""二つの数値を乗算します。"""
return a * b
# ツールのリストを設定
tools = [
Tool(
name="Multiplier",
func=lambda args: multiply(int(args["a"]), int(args["b"])),
description="二つの数値 a と b を掛け合わせるときに使います。入力は 'a, b' の形式にすべきです。",
)
]
# ReAct のエージェントを初期化
# LangChainは内部的にReAct用のプロンプトテンプレートを持っている
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # ReActスタイルのエージェント
verbose=True, # 思考プロセスを詳細に表示
handle_parsing_errors=True
)
# 実行
query = "123 × 45 の答えは何ですか? 計算が必要です。"
result = agent.run(query)
print("\nReAct 結果:")
print(result)
想定される出力 (verbose=True の場合):
> Entering new AgentExecutor chain...
質問は123 × 45の計算だ。私は内部で計算する能力がないので、計算ツールを使う必要がある。
Thought: 乗算ツールを使うべきだ。
Action: Multiplier
Action Input: {"a": 123, "b": 45}
Observation: 5535
ツールの結果から、答えは5535であることがわかった。
Thought: 答えが得られたので、ユーザーに返答する。
Action: Final Answer
Action Input: 123 × 45 の答えは 5535 です。
> Finished chain.
ReAct 結果:
123 × 45 の答えは 5535 です。
このように、ReActエージェントは「内部で計算できない」と推論し、乗算ツールを行動として呼び出し、その結果を観察して最終答えを導いています。
4. 実践的なティップスとよくある落とし穴
実際の業務で適用する際に役立つノウハウと注意点を共有します。
✅ ティップス
-
CoTはまずお試しあれ: 複雑な推論タスクでは、まずシンプルなCoT (
ステップバイステップで考えてください
) を追加するだけでも精度が大幅に向上します。 -
ツールの説明は詳細に: ReActで使用するツールの
description
は、エージェントがツールを選択する唯一の手がかりです。「いつ」「何を」するツールなのかを非常に明確かつ具体的に記述しましょう。 -
エージェントの暴走を防ぐ: ReActエージェントは思考ループに陥ったり、無関係なツールを呼び出したりする可能性があります。
max_iterations
パラメータで思考ステップ数の上限を設定することを強く推奨します。 - プロンプトの反復改良: 出力結果が思わしくなければ、プロンプトの指示文を少し変えるだけで改善されることが多々あります。A/Bテストの感覚で試行錯誤しましょう。
⚠️ よくある落とし穴
- 過剰な期待: CoT/ReActは魔法ではありません。基礎モデルの能力上限を超えるタスクは解けません。適切なモデル選択が大前提です。
- コストとレイテンシ: ReActは外部ツールを何度も呼び出し、LLM itselfも複数回呼び出されます。そのため、処理時間とAPIコストが増加します。本当に必要な場合にのみ採用すべきです。
- ツールの誤った使用: エージェントがツールに入力を正しい形式で渡せない場合があります。ツールの入力パース処理は堅牢に作り、エラーハンドリングを徹底しましょう。
- セキュリティリスク: エージェントが自由にAPIを呼び出せることは、削除操作や高コストな操作を誤って実行するリスクがあります。ツールの実行権限は最小権限の原則に基づいて厳しく制限してください。
5. 応用と発展:より強力なエージェントへ
CoTとReActは組み合わせて進化しています。
- Plan-and-Execute: 超大規模なタスクを、まず全体の計画(Plan)を立案し、その後実行(Execute) するエージェント。ReActのスケールアップ版。
- ReWOO (Reasoning WithOut Observation): 観察(ツール実行結果)を待つために生じるレイテンシを削減するため、推論と行動の計画を全て先に立ててから、一括してツールを実行する手法。効率性が大幅に向上。
- マルチモーダルエージェント: ツールとして画像生成モデルや音声認識モデルを組み込むことで、テキスト以外の入出力も扱えるエージェントの構築が可能になります。
これらの発展形は、LangChainやLlamaIndexなどのフレームワークが積極的にサポートを進めており、今後さらに手軽に利用できるようになるでしょう。
6. 結論:それぞれのメリット・デメリットと未来
手法 | メリット | デメリット | 適したユースケース |
---|---|---|---|
Chain of Thought (CoT) | 実装が簡単、高速、低コスト、推論過程が可視化される | モデルの知識外や最新情報は扱えない、動的計画が苦手 | モデル知識内の複雑な推論(数学、読解、コード解説) |
ReAct | 外部情報と連携可能、動的計画が可能、実世界と相互作用できる | 実装が複雑、高コスト、レイテンシが高い、暴走リスクがある | 検索を伴うQA、データ分析、動的な作業の自動化 |
未来展望
LLMの基盤モデル自体の推論能力が向上しても、それを如何に制御し、安全かつ効率的に現実のタスクに適用するかという「制御技術」としてのプロンプトエンジニアリングの重要性はますます高まっていくでしょう。ReActの考え方は、AIエージェントが自律的に複雑な作業を完了するための重要な基礎技術となっています。
どちらを選ぶべき?
- モデルの知識だけで答えが導ける静的な問題 → CoT
- 外部の情報やAPIと連携する動的な問題 → ReAct
まずは簡単なCoTから試し、必要に応じてReActやより高度なエージェントへと発展させていくのが、実業務への導入における現実的で堅実なアプローチです。
本記事が、皆さんがAIの思考プロセスを設計する際の一助となれば幸いです。何か質問や気づきがあれば、コメントでぜひお聞かせください。