Menu

ReAct 提示:大模型的推理与行动循环

ReAct 提示让模型在推理任务和调用工具之间交替进行,每拿到一个结果都先读完,再决定下一步。大多数 AI 智能体背后都是这个循环。

下面的每条提示词都可以编辑:改好后,在 ChatGPT、Claude 或其他 AI 应用中打开。

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 行是程序执行上一个行动后贴回去的内容。按顺序读这些回复。

System prompt
通过反复执行 Thought、Action 和 Observation 来完成任务。 Thought: 推理你已经知道什么,以及下一步该做什么。 Action: 必须是以下其中之一 run_tests[path] 对一个文件运行 pytest 并返回输出 read_file[path] 返回一个文件的内容 finish[answer] 用你的答案结束任务 写一个 Thought 和一个 Action,然后停下。Observation 会由我们提供给你。
任务:昨天合并之后,tests/test_cart.py 开始失败。找出原因。
Try it
Chat apps receive the system prompt as the start of your message, so this tries its wording, not its priority. To test it as standing instructions, put it in the app's custom instructions or the API's system field.
Example replyReplies vary between models and runs.

Thought: 我还不知道是哪个测试失败、怎么失败的。先运行测试,在读代码之前就能看到具体是哪条断言出错。 Action: run_tests[tests/test_cart.py]

注意两点。第一个行动是运行测试,而不是猜测:模型在读代码之前先收集了证据。另外,每个 Thought 都引用了最新的 Observation,这正是思维链做不到的。

在聊天应用里试试 ReAct

不写代码也能看到这个循环。在 ChatGPT、Claude 或 Gemini 中,你可以自己充当工具:模型请求一个行动,你去执行,再把结果贴回去。

充当模型的工具
Fill in
我想回答这个问题:为什么我什么都没做,笔记本电脑的风扇还转得很响? 请用循环的方式帮我。每次回复只包含一个 Thought(你目前知道什么、还缺什么)和一个 Action:让我去检查、运行或查找的一件具体的事,并写明确切的步骤。然后停下来等我。我会用“Observation: ...”回复结果。等你有了足够的证据,再用“Answer:”开头给出你的结论。在拿到证据之前,不要猜答案。
Try it
Example replyReplies vary between models and runs.

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 贴回去,如此反复。这样做很慢,但能清楚地看到智能体是如何决定下一步的。

Coddy 编程语言插图

和 Coddy 一起学编程

开始