ReAct的定义
推理(Reasoning)+行动(Acting)
核心理念为让AI大模型通过一边想一边做的循环来解决复杂问题。
核心运转机制:T-A-O循环

- Thought(思考):模型对当前任务进行推理分析,决定下一步该做什么。例如分解问题、制定计划、判断已有信息是否足够。
- Action(行动):模型根据思考结果执行具体操作,如调用工具、查询数据库、搜索信息等。
- Observation(观察):模型接收行动返回的结果,获取新的信息或反馈,作为下一轮思考的依据。
与传统方法的比较
对比 Reasoning-Only

- 定义:模型仅依靠内部推理(Thought)来回答问题,不执行任何外部操作。
- 流程:Question → Thought → Thought → … → Answer
- 缺点:无法获取外部信息,容易产生幻觉(hallucination),对需要实时数据或多步检索的任务表现较差。
- 典型代表:Chain-of-Thought(CoT)提示方法。
对比 Action-Only

- 定义:模型直接执行动作(Action)并获取观察结果(Observation),但不进行推理思考。
- 流程:Question → Action → Observation → Action → … → Answer
- 缺点:缺乏推理规划,行动盲目,容易重复无效操作或遗漏关键步骤。
- 典型代表:简单的工具调用 Agent,没有显式的推理过程。
ReAct 的优势
- 能够处理实时信息:通过 Action 调用外部工具获取最新数据,不再局限于模型训练时的知识,能回答实时性问题(如天气、股价、最新新闻等)。
- 减少幻觉:通过 Observation 获取真实数据来验证和修正推理,而非凭空编造答案,显著降低了错误信息的产生。
- 可解释性强:每一步 Thought 都记录了模型的推理过程,使决策过程透明可追踪,便于调试和审查。
- 极强的灵活性与容错率:如果某次 Action 的结果不理想,模型可以在下一轮 Thought 中调整策略,选择不同的工具或方法重新尝试,而不是一条路走到底。
实现ReAct的五大关键要素
通用ReAct Prompt 模板设计
一个合格的ReAct Prompt 必须包括:角色定义、可用工具、思考行动规则、示例(Few-Shot)、历史上下文与当前问题。
## 角色定义",
"你是一个 ReAct 助手 Planner。你的职责是根据用户问题,通过思考(Thought)→ 行动(Action)→ 观察(Observation)循环来决策下一步:需要工具时调用工具,不需要工具时直接给出最终回答。", "", "## 思考行动规则", "1. 首先分析用户意图(Thought),判断是否需要使用工具", "2. 只有当用户需要查询实时天气数据时,才调用 weather_now 工具获取(Action),等待工具返回结果(Observation)", "3. 如果用户问题不需要实时天气数据,直接用已有知识或常识给出 finalAnswer", "4. 如果历史对话中已经有可用 Observation,基于 Observation 给出 finalAnswer,不要重复调用同一个工具", "5. 如果工具调用失败,在 finalAnswer 中向用户说明失败原因", "6. finalAnswer 要用自然中文回答,不要罗列原始 JSON 字段", "", "## 输出格式", "必须只输出一个 JSON 对象,不要 Markdown 代码块,不要额外解释。", "需要调用工具时:{"thought":"分析说明","action":{"tool":"weather_now","input":{"locationQuery":"城市名"}}}", "直接回答时:{"thought":"分析说明","finalAnswer":"给用户的中文回复"}"
代码实战
async function runReact(question: string): Promise<void> {
const maxSteps = 5;
const client = createDashScopeClient();
const historyLines: string[] = [`用户:${question}`];
console.log(`Question: ${question}`);
for (let step = 1; step <= maxSteps; step++) {
console.log(`\nStep ${step}`);
const decision = await askModelForAction(client, question, historyLines.join("\n"));
const thought = decision.thought";
historyLines.push(`Thought: ${thought}`);
console.log(`Thought: ${thought}`);
if ("finalAnswer" in decision) {
historyLines.push(`助手:${decision.finalAnswer}`);
console.log(`Final Answer: ${decision.finalAnswer}`);
return;
}
const actionDesc = `${decision.action.tool}(${JSON.stringify(decision.action.input)})`;
historyLines.push(`Action: ${actionDesc}`);
console.log(`Action: ${actionDesc}`);
try {
const observation = await weatherNow(decision.action.input);
const observationText = formatObservation(observation);
historyLines.push(`Observation: ${observationText}`);
console.log(`Observation: ${observationText}`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const observationText = `工具调用失败:${message}`;
historyLines.push(`Observation: ${observationText}`);
console.log(`Observation: ${observationText}`);
}
}
console.log(`Final Answer: 已达到最大 ReAct 步数 ${maxSteps},仍未得到最终答案。请换个问法或稍后重试。`);
}
结果分析
整体流程回顾
本次测试使用问题 "帮我查一下今天杭州和北京的天气怎么样" 验证 ReAct 框架的实际运行效果。模型共经历了 3 轮 T-A-O 循环,最终成功输出了两个城市的实时天气信息。
各步骤详细分析
请输入问题:帮我查一下今天杭州和北京的天气怎么样
Question: 帮我查一下今天杭州和北京的天气怎么样
Step 1
Reasoning Summary: 用户想要查询杭州和北京的天气。
根据规则,需要调用 weather_now 工具。
由于工具一次只能查询一个城市,我需要先查询其中一个,或者在思考中说明需要分步查询。
但根据示例和通常的ReAct流程,如果一次只能查一个,我应该先查一个,或者如果工具支持多个(这里参数是locationQuery,看起来是单数),我应该先查一个。
不过,通常在这种设定下,如果用户问了两个城市,我可以先查一个,或者尝试在一个action中查一个。
让我们看工具定义:weather_now 参数 locationQuery 是 "城市名(如北京)、9位LocationID 或 经纬度"。
通常我会先查杭州,或者北京。为了简单,我先查杭州。或者我可以思考说需要查两个城市,先查杭州。
但更好的做法是,如果系统允许,我可以连续调用,或者先回答一个。
按照ReAct的标准流程,我先调用 weather_now 查询杭州。
Thought: 用户想查询杭州和北京的实时天气,需要调用 weather_now 工具。由于工具一次只能查询一个地点,我先查询杭州的天气。
Action: weather_now({
Thought: 用户想查询杭州和北京的实时天气,需要调用 weather_now 工具。由于工具一次只能查询一个城市,我先查询杭州的天气。
Action: weather_now({"locationQuery":"杭州"})
Observation: 杭州 当前多云;温度 28°C;体感 26°C;风况 西南风3级,17公里/小时;湿度 41%;近 1 小时降水 0.0毫米;气压 1011百帕;能见度 30公里;观测时间 2026-05-16T13:12+08:00
Step 2
Reasoning Summary: 用户之前问了“帮我查一下今天杭州和北京的天气怎么样”,系统已经调用了 weather_now 查询了杭州的天气,并返回了 Observation。
现在用户再次发送了同样的问题“帮我查一下今天杭州和北京的天气怎么样”,或者这是同一个对话的延续。
根据 ReAct 历史,已经获取了杭州的天气数据,但还没有获取北京的天气数据。
用户的问题包含两个城市:杭州和北京。
历史中已经查了杭州,接下来应该查北京。
所以,下一步 Action 应该是调用 weather_now 查询北京的天气。
思考过程:
1. 分析用户意图:用户想知道杭州和北京的天气。
2. 检查历史:已经获取了杭州的天气(Observation 中有杭州的数据)。
3. 判断下一步:还需要获取北京的天气数据。
4. 构造 Action:调用 weather_now,参数 locationQuery 为 "北京"。
输出格式要求:
{"thought":"分析说明","action":{"tool":"weather_now","input":{"locationQuery":"城市名"}}}
Thought
Thought: 用户询问杭州和北京的天气,历史对话中已经获取了杭州的天气数据,现在需要继续查询北京的天气。
Action: weather_now({"locationQuery":"北京"})
Observation: 北京 当前阴;温度 21°C;体感 19°C;风况 东风3级,19公里/小时;湿度 65%;近 1 小时降水 0.0毫米;气压 1013百帕;能见度 10公里;观测时间 2026-05-16T13:09+08:00
Step 3
Reasoning Summary: 用户想要查询杭州和北京的天气。
在历史对话中,已经调用了两次 weather_now 工具,分别获取了杭州和北京的天气数据。
Observation 1: 杭州 当前多云;温度 28°C;体感 26°C;风况 西南风3级,17公里/小时;湿度 41%;近 1 小时降水 0.0毫米;气压 1011百帕;能见度 30公里;观测时间 2026-05-16T13:12+08:00
Observation 2: 北京 当前阴;温度 21°C;体感 19°C;风况 东风3级,19公里/小时;湿度 65%;近 1 小时降水 0.0毫米;气压 1013百帕;能见度 10公里;观测时间 2026-05-16T13:09+08:00
根据规则4:如果历史对话中已经有可用 Observation,基于 Observation 给出 finalAnswer,不要重复调用同一个工具。
因此,我应该直接根据已有的 Observation 给出 finalAnswer。
构建 finalAnswer:
杭州:多云,28°C,体感26°C,西南风3级,湿度41%。
北京:阴,21°C,体感19°C,东风3级,
Thought: 历史对话中已经获取了杭州和北京的实时天气数据,可以直接回答用户
Final Answer: 杭州当前多云,气温28°C,体感26°C,西南风3级,湿度41%;北京当前阴,气温21°C,体感19°C,东风3级,湿度65%。观测时间均为2026年5月16日13点左右。
应用扩展与未来发展
适用场景
- 需要实时信息的任务
- 依赖私有知识库的任务
- 多步逻辑推理任务
- 与物理/数字环境交互的任务
功能扩展
- ReAct+Reflexion(自我反思):在标准 ReAct 循环基础上增加自我反思机制,模型在每轮循环结束后对自身的推理和行动进行评估与反馈,识别错误或低效策略并主动修正,从而在多轮交互中持续提升决策质量。
- ReAct+Plan-and-Execute(全局规划):在执行 T-A-O 循环前先进行全局任务规划,将复杂任务拆解为有序的子目标序列,再逐步执行每个子目标。这避免了逐步推理时的短视问题,使模型在面对长链任务时更具条理性和全局视野。
- ReAct+Tool Retrieval(动态工具检索):当可用工具数量庞大时,模型不再依赖固定的工具列表,而是在每轮 Thought 阶段根据当前需求动态检索最相关的工具。这大幅提升了系统的可扩展性,使其能够适配数百甚至数千种工具的大规模场景。
