ReAct 提示是一种让语言模型在推理和行动之间交替进行的模式:它先写下对该做什么的思考,再执行一个行动,比如搜索或调用工具,读取结果,然后才决定下一步。这个名字是 Reason + Act(推理加行动)的缩写,出自 Yao 等人 2022 年的论文《ReAct: Synergizing Reasoning and Acting in Language Models》。它和用来构建用户界面的 JavaScript 库 React 毫无关系。
大多数能浏览网页、运行代码或编辑文件的 AI 智能体,运行的都是这个循环的某种版本。亲手走一遍这个循环之后,你会发现智能体的行为变得容易预测,也更容易调试。
Thought、Action、Observation 循环
ReAct 提示词要求模型写三种行。
- Thought: 对当前情况的推理,以及还缺少哪些信息。
- Action: 一次固定格式的工具调用,比如
search[query]、read_file[path]或finish[answer]。 - Observation: 这个行动的结果。这一行不由模型来写,而是由你的程序(或你本人)执行行动,再把真实的输出加进记录里。
然后,整个记录被发回给模型,模型再写出下一个 Thought。当模型选择结束行动时,循环终止。模型自己从不运行工具,它只是提出请求,由外层的代码决定是否执行。
为什么推理加行动胜过单独任何一种
论文把 ReAct 与只推理的提示词(思维链)和只行动的提示词(不写推理、只调用工具)做了比较。在使用维基百科搜索工具的问答和事实核查任务中,只推理的方式常常基于模型自己编造的事实构建出一套自信的论证,而 ReAct 可以去查证这个事实;问答的最佳结果来自 ReAct 与思维链的结合,当其中一种给不出有把握的答案时,就退回到另一种。只行动的方式难以拆解目标,也难以把找到的信息整合成答案;写下来的思考则让计划始终清晰可见。在两个交互式任务中,一个是基于文本的家务游戏,另一个是模拟购物网站,只用一两个示例提示的 ReAct,表现就超过了作者对比的经过训练的基线方法。
总的经验是:思维链帮助模型做计划,工具给模型提供事实,对于两者都需要的任务,只有其中一个是不够的。
一个 ReAct 智能体,一轮一轮地看
下面是一个带两个工具的小型编程智能体。每个标签页都是同一段对话往后推进一步:Observation 行是程序执行上一个行动后贴回去的内容。按顺序读这些回复。
Thought: 我还不知道是哪个测试失败、怎么失败的。先运行测试,在读代码之前就能看到具体是哪条断言出错。 Action: run_tests[tests/test_cart.py]
注意两点。第一个行动是运行测试,而不是猜测:模型在读代码之前先收集了证据。另外,每个 Thought 都引用了最新的 Observation,这正是思维链做不到的。
在聊天应用里试试 ReAct
不写代码也能看到这个循环。在 ChatGPT、Claude 或 Gemini 中,你可以自己充当工具:模型请求一个行动,你去执行,再把结果贴回去。
Thought: 电脑空闲时风扇很响,通常意味着有东西在后台占用 CPU,或者风扇散热吃力(积灰、通风口被挡住)。先检查 CPU 占用,就能区分这两种情况。
Action: 打开任务管理器(Windows 上按 Ctrl+Shift+Esc)或活动监视器(Mac 上在“应用程序 > 实用工具”里)。按 CPU 对进程排序,等 30 秒,然后告诉我排在前三的进程及其 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."
有四个细节很重要。记录在 Action 之后立刻截断,因为模型有时会继续往下写,自己编造一个 Observation。工具出错(比如文件名写错)会变成一条模型可以应对的 Observation,而不会让循环崩溃。步数上限可以阻止模型无限循环。测试输出也被截短了,因为巨大的测试日志会占满上下文窗口;决定什么内容进入这个窗口,就是上下文工程。
这两个工具在做任何事之前也会先检查参数。参数是模型选的,所以限制应该放在工具里:inside_project 拒绝任何解析后位于项目文件夹之外的路径(包括 ../ 这类花招),run_tests 只接受 test_*.py 文件,read_file 只打开几种源代码和文本文件类型,并且最多返回 20,000 个字符,这样 .env 文件或 SSH 密钥永远不会进入下一次请求。运行 pytest 仍然会执行你项目自己的代码,所以要在容器或一次性的检出副本中运行这样的智能体,绝不要在存有机密信息的机器上运行。
OpenAI、Anthropic 和 Google 当前的 API 也提供原生的工具调用:你用名称和 JSON schema 描述每个工具,模型返回一个结构化的工具调用,而不是一行 Action:。循环完全一样,只是省去了解析这一步。
ReAct 循环中的安全问题
智能体会读取不是它自己写的文本:网页、文件、工具输出。其中任何一处都可能藏着针对模型的指令,这就是提示词注入。只给每个工具它需要的最小权限(尽可能只读),任何删除、发送或付款的操作都要请人工确认,并把模型请求的行动当作需要检查的不可信输入,而不是闭着眼睛执行的命令。
常见问题
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 和思维链有什么区别?
思维链只根据模型已经知道的东西来推理,所以链条前面的一个错误事实会一直错下去。ReAct 把推理和获取真实信息的行动交替进行,模型可以先核实一个事实、看到一个测试失败或读完一个文件,再继续。ReAct 论文发现,让推理以搜索结果为依据,能减少单用思维链时编造的事实。
现在的 AI 智能体还在用 ReAct 吗?
循环还在用。如今大多数智能体都是推理、调用工具、读取结果、再做决定,这就是 ReAct 的循环。变的是格式:现在的 API 不再从文本中解析 Action: 行,而是提供原生的工具调用,模型返回一个结构化的工具请求,你的代码再把结果发回去。
不写代码,能在 ChatGPT 里用 ReAct 吗?
能,你自己来充当工具就行。让模型每次只回复一个 Thought 和一个 Action,然后停下。你去执行这个行动(搜索、打开文件、运行命令),把结果作为 Observation 贴回去,如此反复。这样做很慢,但能清楚地看到智能体是如何决定下一步的。