ReAct是Reasoning and Acting的缩写,由普林斯顿大学和谷歌的研究者在论文中提出。它的核心思想非常直观:让大语言模型在解决问题时,交替进行“思考(Thought)”“行动(Action)”“观察(Observation)”,而不是一口气把答案吐出来。思考负责拆解问题和规划下一步,行动负责真正去调用搜索、查数据库或执行一段代码,观察则是把外部环境返回的结果喂回给模型,让它据此修正推理路径。这三者串成一个循环,直到模型认为信息足够,输出最终答案为止。相比纯粹的思维链,ReAct能借助外部工具弥补模型的知识盲区;相比纯粹的函数调用,它多了显式的推理痕迹,出错时更容易定位问题出在哪一步。

一、ReAct的核心原理与循环机制
ReAct的本质是把一次复杂任务拆解成若干个可检查的小步骤。传统的一次性生成有个明显缺陷:模型一旦开始跑偏,后面的输出会沿着错误方向越走越远,而且你很难知道错在哪。ReAct通过显式的Thought字段强制模型先“说清楚自己打算干什么”,再执行动作。这个自我说明的过程本身就是一种约束,实验表明它能显著降低幻觉率,因为模型必须先给出理由,再去行动,理由站不住脚时往往自己就会修正。
一个完整的ReAct循环包含四个阶段:首先是Thought,模型分析当前掌握的信息,判断还缺什么;其次是Action,模型从预定义的工具列表中选择一个,并给出调用参数;然后是Observation,外部环境执行工具并把结果返回;最后模型综合所有历史信息进入下一轮思考,或者判定任务完成,输出Answer。整个过程形成一条轨迹,每一步都记录在上下文里,模型相当于在一块白板上不断追加自己的推理记录。
值得注意的细节是终止条件的设计。工具返回“未找到结果”或“无相关信息”时,一个鲁棒的ReAct模板应当允许模型选择换关键词重试,而不是死磕同一条路。实践中常见的做法是在系统提示里明确写入“如果连续两次搜索都没有获得有效信息,请基于已有信息给出最佳答案”,避免陷入无限循环烧token。
二、可直接套用的ReAct提示词模板
下面给出一个通用的ReAct系统提示模板,适用于带搜索工具的问答场景。使用时把工具说明部分替换成你自己的工具清单即可。注意格式约束必须严格,用固定分隔符区分不同字段,否则模型的输出会不稳定。
你是一个使用ReAct模式解决问题的智能助手。请严格按照以下格式工作:
Question: 需要回答的问题
Thought: 分析当前情况,思考下一步应该做什么
Action: 工具名称,只能从以下列表中选择:
search[关键词] - 搜索互联网获取信息
lookup[关键词] - 在上一次搜索结果中查找特定内容
finish[答案] - 信息足够时给出最终答案并结束
系统会在Action之后返回:
Observation: 工具执行的结果
你可以多次进行 Thought - Action - Observation 循环,
直到你有把握回答问题。最后必须以 Action: finish[最终答案] 结束。
要求:
1. 每轮只输出一个Thought和一个Action,不要自行编造Observation
2. Thought要简洁明确地说明为什么选择这个Action
3. 如果搜索两次仍无有效结果,请基于已有信息调用finish
现在开始:
Question: {你的问题}
Thought:这个模板的关键在于最后一句“Thought:”的引导。把它放在提示词末尾,模型会自然接续输出第一个思考步骤,省去了额外的格式说明。另外,工具列表用search[关键词]这种方括号语法描述参数,是目前兼容性最好的写法,GPT、Claude、国产模型基本都能正确解析。
如果项目里用的是OpenAI风格的函数调用接口,也可以把ReAct的思考过程保留在文本层,把Action映射成结构化的函数调用。这种混合模式下,Thought照常输出,但Action部分交给框架解析,稳定性更好。示例代码如下:
import json
SYSTEM_PROMPT = """你使用ReAct模式工作。每轮先输出Thought说明推理过程,
然后从工具列表中选择一个调用。可用工具:
- web_search(query): 搜索网页
- calculator(expression): 计算数学表达式
当信息足够时,不再调用工具,直接输出"Answer: ..."结束。"""
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
while True:
resp = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=[
{"type": "function", "function": {
"name": "web_search",
"parameters": {"type": "object",
"properties": {"query": {"type": "string"}}}}}
],
temperature=0
)
msg = resp.choices[0].message
messages.append(msg)
# 模型没有再调用工具,说明给出了最终答案
if not msg.tool_calls:
break
for call in msg.tool_calls:
result = dispatch(call.function.name,
json.loads(call.function.arguments))
messages.append({"role": "tool",
"tool_call_id": call.id,
"content": str(result)})三、完整示例:多跳问答的执行轨迹
用一个典型的多跳问题来看ReAct实际运行时的输出长什么样。问题是“《三体》的作者获得雨果奖的那部作品首次出版于哪一年”。这类问题无法一步回答,必须先确认作者,再确认获奖作品,最后查出版年份,正好体现ReAct的链式推理能力。
Question: 《三体》的作者获得雨果奖的那部作品首次出版于哪一年? Thought: 我需要先确认《三体》的作者是谁。 Action: search[三体 作者] Observation: 《三体》是刘慈欣创作的长篇科幻小说。 Thought: 刘慈欣获得雨果奖的作品需要进一步确认,可能就是《三体》本身, 但也可能是其他作品,我应该搜索验证。 Action: search[刘慈欣 雨果奖] Observation: 2015年,刘慈欣凭借《三体》英文版获得第73届雨果奖最佳长篇小说奖, 这是亚洲人首次获得该奖项。 Thought: 获奖作品确认为《三体》。现在需要查它首次出版的年份。 Action: search[三体 首次出版年份] Observation: 《三体》最早于2006年5月在《科幻世界》杂志上连载, 单行本于2008年1月由重庆出版社出版。 Thought: 信息已完整,可以给出答案。 Action: finish[《三体》首次出版于2008年(单行本),获奖作品即《三体》本身, 其作者为刘慈欣,于2015年获雨果奖]
从轨迹可以看出,每一步Thought都在引用前面Observation里的信息,形成了清晰的证据链。这种可追溯性是ReAct在Agent开发中流行的重要原因——当答案出错时,你可以逐行检查轨迹,准确定位是哪次搜索结果引入了错误信息,或者哪一步推理出现了跳跃。调试纯思维链输出时几乎不可能做到这一点。
四、ReAct与思维链、函数调用的对比及常见坑
把三种方案放在一起比较会更清楚各自的定位。思维链只做内部推理,不接触外部信息,适合数学题、逻辑题这类封闭问题;函数调用只关注动作本身,模型解释自己的部分被弱化了;ReAct则把两者缝合在一起,代价是每次交互消耗的token更多,因为完整的轨迹要一直保留在上下文里。
| 方案 | 外部工具 | 推理可追溯 | token消耗 |
|---|---|---|---|
| 思维链CoT | 不支持 | 中等 | 低 |
| 函数调用 | 支持 | 弱 | 中 |
| ReAct | 支持 | 强 | 高 |
实际落地时有几个坑值得提前规避。第一是温度参数,ReAct对随机性很敏感,temperature建议设为0或不超过0.2,否则模型可能在格式上出现漂移,比如把Action:写成别的词,导致解析器失效。第二是轨迹长度控制,多轮循环后上下文会迅速膨胀,一般限制最大轮数在5到8轮,超限就强制让模型总结现有信息作答。第三是提示词中的指令冲突,如果系统提示里既要求ReAct格式又要求“简洁回答”,模型会无所适从,务必保证指令单一明确。
最后一个建议是做好Observation的截断处理。搜索引擎返回的原始网页内容可能长达几千token,直接塞进上下文会快速吃掉预算。常见做法是工具层面对结果做摘要或截断到500字以内再返回,这样既保留了关键信息,又把单轮成本控制在合理范围。把这些细节处理好之后,ReAct模板在检索增强问答、自动化运维、数据分析等场景里都能稳定运行,是目前构建Agent应用性价比很高的起步方案。
修改时间:2026-09-13 10:28:38