ReAct プロンプティングとは、言語モデルが推論と行動を交互に行うパターンです。何をするかについての考えを書き、検索やツール呼び出しのような行動をとり、その結果を読んで、それから初めて次のステップを決めます。名前は Reason + Act の略で、Yao らの2022年の論文「ReAct: Synergizing Reasoning and Acting in Language Models」から来ています。ユーザーインターフェースを作るための JavaScript ライブラリの React とは関係ありません。
Web を閲覧したり、コードを実行したり、ファイルを編集したりする AI エージェントの多くは、このループの何らかの版を動かしています。一度手作業でたどってみれば、エージェントのふるまいはずっと予測しやすく、デバッグしやすくなります。
Thought、Action、Observation のループ
ReAct のプロンプトは、モデルに3種類の行を書かせます。
- Thought:現在の状況と、足りない情報についての推論。
- Action:
search[query]、read_file[path]、finish[answer]のような決まった形式で書く、1つのツール呼び出し。 - Observation:その行動の結果。この行はモデルが書くものではありません。あなたのプログラム(またはあなた)が行動を実行し、本物の出力を記録に加えます。
そして記録全体がモデルに戻され、モデルが次の Thought を書きます。モデルが終了の行動を選んだところでループは終わります。モデルが自分でツールを実行することはありません。頼むだけで、その依頼を実行するかどうかはまわりのコードが決めます。
推論と行動の組み合わせがどちらか一方に勝つ理由
論文は ReAct を、推論だけをするプロンプト(Chain-of-Thought)と、行動だけをするプロンプト(推論を書かないツール呼び出し)と比べました。Wikipedia の検索ツールを使った質問応答とファクトチェックでは、推論だけの場合、モデルがでっち上げた事実の上に自信ありげな議論を組み立ててしまうことがよくありましたが、ReAct はその事実を調べられました。質問応答で最も良い結果が出たのは ReAct と Chain-of-Thought を組み合わせ、一方で自信のある答えが出なかったらもう一方に切り替える方法でした。行動だけの場合は目標を分解したり、見つけたものを答えにまとめたりするのに苦戦しました。書き出された思考が、計画を見える状態に保っていたのです。テキストベースの家事ゲームと、シミュレーションのショッピングサイトという2つの対話型タスクでは、1つか2つの例だけでプロンプトした ReAct が、著者たちが比較した学習済みのベースラインを上回りました。
一般的な教訓はこうです。Chain-of-Thought はモデルが計画を立てるのを助け、ツールは事実を与えます。両方が必要なタスクには、どちらか一方だけでは足りません。
ReAct エージェントを1ターンずつ
下のブロックは、ツールを2つ持つ小さなコーディングエージェントです。各タブは同じ会話の1ステップあとの状態です。Observation の行は、前の行動を実行したあとでプログラムが貼り付けたものです。返答を順番に読んでください。
Thought: どのテストがどう失敗しているのかがわからない。コードを読む前にテストを実行すれば、どのアサーションで落ちているかが正確にわかる。 Action: run_tests[tests/test_cart.py]
2つのことに注目してください。最初の行動は当て推量ではなくテストの実行です。モデルはコードを読む前に証拠を集めました。そして各 Thought は直前の Observation に触れています。これは Chain-of-Thought にはできない部分です。
チャットアプリで ReAct を試す
ループを見るのにコードは要りません。ChatGPT、Claude、Gemini では、あなたがツールになれます。モデルが行動を頼み、あなたがそれを実行して結果を貼り付けます。
考え:何もしていないのにファンがうるさいのは、たいていバックグラウンドで何かが CPU を使っているか、ファンが冷却に苦労している(ほこり、通気口のふさがり)かのどちらかです。まず CPU の使用率を確認すれば、この2つを切り分けられます。
行動:タスクマネージャー(Windows なら Ctrl+Shift+Esc)か、アクティビティモニタ(Mac なら「アプリケーション」>「ユーティリティ」)を開いてください。プロセスを CPU の順に並べ替えて30秒待ち、上位3つのプロセスとその CPU 使用率を教えてください。
同じ指示の各行動の裏に本物の関数を置けば、それがエージェントです。
Python での最小限の ReAct ループ
コードでは、プログラムが記録を送り、返答の中の Action の行を見つけ、対応する関数を実行し、Observation を追加して繰り返します。このスケッチは OpenAI の Python SDK と、上のコーディングエージェントのシステムプロンプトを使っています。
import re
import subprocess
from pathlib import Path
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
PROJECT = Path.cwd().resolve()
READABLE = {".py", ".md", ".txt", ".toml", ".cfg"}
def inside_project(arg):
# The model chooses arg: resolve it and refuse anything outside the project.
path = (PROJECT / arg).resolve()
if not path.is_relative_to(PROJECT):
raise ValueError(f"{arg} is outside the project")
return path
def run_tests(arg):
path = inside_project(arg)
if not (path.is_file() and path.name.startswith("test_") and path.suffix == ".py"):
raise ValueError(f"{arg} is not a test file")
result = subprocess.run(["pytest", str(path), "-q"], capture_output=True, text=True)
return result.stdout[-2000:]
def read_file(arg):
path = inside_project(arg)
if not path.is_file() or path.suffix not in READABLE:
raise ValueError(f"{arg} is not a file this agent may read")
return path.read_text()[:20000]
TOOLS = {"run_tests": run_tests, "read_file": read_file}
def react(task, system_prompt, max_steps=8):
transcript = f"Task: {task}\n"
for _ in range(max_steps):
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": transcript},
],
temperature=0,
)
text = response.choices[0].message.content
match = re.search(r"Action: (\w+)\[(.*)\]", text)
if not match:
return text
transcript += text[: match.end()] + "\n"
tool, arg = match.groups()
if tool == "finish":
return arg
try:
observation = TOOLS[tool](arg) if tool in TOOLS else f"Unknown tool: {tool}"
except Exception as error: # a missing file becomes an observation, not a crash
observation = f"Error: {error}"
transcript += f"Observation:\n{observation}\n"
return "Stopped after max_steps without an answer."
重要な点が4つあります。記録は Action の直後で切っています。モデルがときどき書き続けて、自分で Observation をでっち上げるからです。失敗したツール(たとえばファイル名の間違い)は、ループをクラッシュさせる代わりに、モデルが反応できる Observation になります。ステップの上限が、永遠にループするモデルを止めます。そしてテストの出力は切り詰めています。巨大なテストログはコンテキストウィンドウを埋めてしまうからです。そのウィンドウに何を入れるかを決めることがコンテキストエンジニアリングです。
2 つのツールは、何かを実行する前に引数もチェックします。引数を選ぶのはモデルなので、制限はツール側に置きます。inside_project はプロジェクトフォルダーの外を指すパス(../ を使ったものも含む)を拒否し、run_tests は test_*.py ファイルだけを受け付け、read_file は一部のコードとテキストのファイル形式だけを開いて最大 20,000 文字を返します。これで .env ファイルや SSH 鍵が次のリクエストに乗ることはありません。それでも pytest はプロジェクト自体のコードを実行するので、このようなエージェントはコンテナか使い捨てのチェックアウトで動かし、秘密情報のあるマシンでは動かさないでください。
OpenAI、Anthropic、Google の現在の API には、ネイティブのツール呼び出し(Function Calling)もあります。各ツールを名前と JSON スキーマで記述すると、モデルは Action: の行ではなく構造化されたツール呼び出しを返します。ループはまったく同じで、解析の部分がなくなるだけです。
ReAct ループの安全性
エージェントは、自分が書いたのではないテキストを読みます。Web ページ、ファイル、ツールの出力です。そのどれにも、モデルに向けた指示が含まれている可能性があります。これがプロンプトインジェクションです。各ツールには必要最小限のアクセス権だけを与え(できるだけ読み取り専用に)、削除、送信、支払いを伴うものは人間に確認してもらい、モデルが求める行動は、何も考えずに実行するコマンドではなく、確認すべき信頼できない入力として扱ってください。
よくある質問
ReAct プロンプティングとは何ですか?
ReAct(Reason + Act の略)とは、言語モデルが短い推論を書き、次に検索やツール呼び出しのような行動を書き、その結果を読んでから再び推論する、というプロンプトのパターンです。Yao らの2022年の論文「ReAct: Synergizing Reasoning and Acting in Language Models」から来ています。JavaScript のライブラリの React とは関係ありません。
ReAct の Thought、Action、Observation とは何ですか?
Thought(思考)は、次に何をするかについてのモデルの推論です。Action(行動)は、search[python 3.13 release notes] のように決まった形式で書いたツール呼び出しです。Observation(観察)はツールの出力で、あなたのプログラムが行動を実行して得たものをプロンプトに加えます。モデルが答えを持って終了の行動をとるまで、このループを繰り返します。
ReAct と Chain-of-Thought の違いは何ですか?
Chain-of-Thought はモデルがすでに知っていることから推論するので、推論の初めのほうで間違った事実があれば、それは間違ったままです。ReAct は推論と、本物の情報を取ってくる行動を交互に行うので、モデルは続ける前に事実を確かめたり、テストの失敗を見たり、ファイルを読んだりできます。ReAct の論文は、推論を検索結果に根ざさせることで、Chain-of-Thought だけのときに出てきたでっち上げの事実が減ることを確かめました。
AI エージェントは今も ReAct を使っていますか?
ループは使っています。今日のエージェントの多くは、推論し、ツールを呼び出し、結果を読み、また判断します。これが ReAct のサイクルです。変わったのは形式です。テキストから Action: の行を解析する代わりに、現在の API にはネイティブのツール呼び出しがあり、モデルが構造化されたツールのリクエストを返し、あなたのコードが結果を送り返します。
コードなしで ChatGPT で ReAct を使えますか?
自分でツールの役をすれば使えます。1つの Thought と1つの Action を書いたら止まるようモデルに頼みます。あなたがその行動(検索、ファイルを開く、コマンドの実行)を実行し、結果を Observation として貼り付け、繰り返します。時間はかかりますが、エージェントが次に何をするかをどう決めているかがはっきりわかります。