1.2 ReAct范式:推理与行动的交织循环


文档摘要

1.2 ReAct范式:推理与行动的交织循环 一、ReAct的核心直觉:思考与行动的「乒乓对练」 ReAct(Reasoning + Acting)这个名字本身就道出了它的核心思想——推理(Reasoning)和行动(Acting)不是先后关系,而是交替进行的。就像乒乓球比赛中的来回击球,Agent在「想一步、做一步、看结果、再想一步」的循环中推进任务。 在深入理论之前,让我们用一个完整的执行trace来建立直觉。假设你让Agent完成这个任务: 「帮我查一下明天从北京飞东京最便宜的直飞航班,顺便看看东京明天的天气。」 这不是一个简单的查询——它包含两个子任务(查航班和查天气),每个都需要不同的工具,且有时间约束(明天)、空间约束(北京到东京)、偏好约束(最便宜的直飞)。

1.2 ReAct范式:推理与行动的交织循环

一、ReAct的核心直觉:思考与行动的「乒乓对练」

ReAct(Reasoning + Acting)这个名字本身就道出了它的核心思想——推理(Reasoning)和行动(Acting)不是先后关系,而是交替进行的。就像乒乓球比赛中的来回击球,Agent在「想一步、做一步、看结果、再想一步」的循环中推进任务。

在深入理论之前,让我们用一个完整的执行trace来建立直觉。假设你让Agent完成这个任务:

「帮我查一下明天从北京飞东京最便宜的直飞航班,顺便看看东京明天的天气。」

这不是一个简单的查询——它包含两个子任务(查航班和查天气),每个都需要不同的工具,且有时间约束(明天)、空间约束(北京到东京)、偏好约束(最便宜的直飞)。我们来看Agent如何用ReAct范式处理:

第一轮:理解与拆解

Thought: 用户要求完成两个任务:(1)查明天北京飞东京最便宜的直飞航班,(2)查东京明天天气。我需要先确定明天的具体日期,然后分别执行两个查询。先查日期。 Action: get_current_date
Observation: 2025-03-15

第二轮:执行第一个子任务——查航班

Thought: 明天是2025-03-16。现在需要查询北京(PEK)到东京(HND/NRT)的直飞航班。我应该使用航班搜索工具,出发地设为PEK,目的地同时搜索HND和NRT两个机场,日期2025-03-16。 Action: search_flights(departure="PEK", arrival="HND,NRT", date="2025-03-16", direct_only=true)
Observation: 航班1: CA183 PEK→HND 07:30-11:30 ¥2,340 航班2: JL872 PEK→NRT 09:15-14:00 ¥3,120 航班3: CA929 PEK→HND 14:00-18:00 ¥2,180 航班4: NH960 PEK→NRT 16:30-21:15 ¥2,890

第三轮:分析与选择

Thought: 返回了4个直飞航班。按价格排序:CA929(¥2,180) < CA183(¥2,340) < NH960(¥2,890) < JL872(¥3,120)。最便宜的是CA929,下午2点出发。不过我应该注意,用户可能隐含了出发时间偏好——如果是出差,早上出发更合理;如果是旅行,下午也行。考虑到用户没有明确说明,我先选最便宜的CA929,但同时也应该考虑时间因素。等等,用户说的是"最便宜的直飞航班",这个优先级很明确,就是价格最低,所以选CA929。现在去查东京天气。

第四轮:执行第二个子任务——查天气

Thought: 现在查询东京明天的天气。东京的时区是UTC+9,和北京一样。日期2025-03-16。 Action: get_weather(location="Tokyo", date="2025-03-16")
Observation: Tokyo 2025-03-16: 多云转晴,气温8-16°C,降水概率15%,风速15km/h

第五轮:整合与回复

Thought: 已经收集了所有信息。最便宜的直飞航班是国航CA929,下午2:00从PEK出发,6:00到达HND,票价¥2,180。东京明天天气不错,多云转晴,气温8-16°C,基本不会下雨。我应该在回复中把两个信息整合呈现,并给出一些实用的出行建议(比如温度偏低需要带外套)。 Final Answer: 明天(3月16日)从北京飞东京最便宜的直飞航班是: ✈️ 国航CA929 | PEK 14:00 → HND 18:00 | ¥2,180 东京明天天气:☁️→☀️ 多云转晴,8~16°C,基本不下雨。 温馨提示:东京气温偏低,建议带件外套。祝旅途愉快!

这个trace虽然简化,但展示了ReAct的完整节奏:Thought-Analyze → Action-Execute → Observe-Learn → Thought-Decide → 循环或结束。每一轮Thought都建立在前一轮Observation的基础上,形成了一条清晰的推理链。

二、为什么Thought质量决定行动成败

在上面的trace中,你可能已经注意到一个关键细节:每一轮的Thought不仅在做「接下来做什么」的决策,还在做「根据观察结果调整策略」的分析。Thought的质量,直接决定了后续Action的正确性和效率。

让我们用一个反面例子来说明。假设第三轮的Thought质量很低:

Thought (低质量): 找到了航班,最便宜的是CA929,¥2,180。现在查天气。

这个Thought虽然得出了相同的结论(选CA929),但跳过了关键的推理过程。如果情况稍微复杂一点,比如:

  • 搜索结果包含红眼航班(凌晨2点出发,便宜但体验极差)
  • 搜索结果中包含中转航班混入(虽然设了direct_only但API有时返回中转结果)
  • 用户之前提过「我下午有个会议」

一个低质量的Thought可能忽略这些上下文信息,做出看似合理但实际不合适的决策。高质量的Thought则会:

  1. 显式检查约束条件:「用户要直飞,CA929确实是直飞?确认。红眼航班CA189凌晨出发¥1,800,虽然更便宜但3:30起飞不现实,排除。」
  2. 利用历史上下文:「用户之前提到下午有会,CA929 14:00出发刚好合适,CA183 07:30出发会冲突,排除。」
  3. 预判后续需求:「查完航班后还需要查天气,用户要出去玩,天气信息影响穿衣建议。」

这就是为什么ReAct范式中,推理深度直接映射到行动质量。Thought不是可有可无的注释,而是Agent决策的核心引擎。

三、ReAct的Prompt工程:让模型「进入角色」

ReAct范式能否生效,很大程度上取决于Prompt如何引导模型进入正确的推理-行动模式。经过大量实践,业界沉淀出了一套相对成熟的Prompt工程框架。

基本Prompt结构

一个典型的ReAct Prompt包含以下几个部分:

1. 角色设定(Role Definition)

告诉模型它是一个能够推理和行动的Agent,并明确它的能力边界:

你是一个智能助手,能够通过推理来分析问题,并通过调用工具来执行操作。 你可以访问以下工具:[工具列表及描述]

角色设定看似简单,但它建立了模型对自身能力的认知框架——模型需要知道「我能做什么」才能合理规划。

2. 推理格式规范(Format Specification)

明确告诉模型每个步骤应该输出什么格式:

在每一步,你必须严格按以下格式输出: Thought: [你的推理分析过程] Action: [工具名称和参数] 或者当你有足够信息给出最终答案时: Thought: [你的总结分析] Final Answer: [给用户的回答]

格式规范的重要性怎么强调都不过分。没有格式约束,模型可能把推理和行动混在一起输出,导致解析失败。格式规范实际上是在模型和执行框架之间建立了一个通信协议

3. 少样本示例(Few-shot Examples)

提供2-3个完整的Thought-Action-Observation示例,让模型理解期望的推理深度和行动模式:

示例: Question: 什么是法国首都的人口? Thought: 这是一个事实查询问题。我需要查找巴黎的人口数据。 Action: search(query="Paris population") Observation: 巴黎市区人口约210万,大巴黎地区人口约1220万。 Thought: 我得到了巴黎的人口数据。可以给出答案了。 Final Answer: 法国首都巴黎的市区人口约210万,大巴黎地区人口约1220万。

示例有两个关键作用:一是展示推理的深度(不是只说「搜索巴黎人口」然后直接给答案),二是展示格式(严格遵循Thought-Action-Observation循环)。

进阶Prompt技巧

工具使用指南(Tool Usage Guide)

除了列出工具名称和参数,还应该提供每个工具的使用注意事项和典型场景:

工具: search_flights 参数: departure(出发机场IATA代码), arrival(到达机场IATA代码), date(日期YYYY-MM-DD), direct_only(是否仅直飞) 注意事项: - 机场代码需使用IATA标准代码,如PEK=北京首都,Tokyo需指定HND或NRT - 结果可能包含中转航班,设direct_only=true过滤 - 票价为参考价,实际价格可能变化

这种详细的使用指南能显著减少工具调用错误,是提升Agent可靠性的重要手段。

反思提示(Reflection Prompt)

在Prompt中加入引导模型自我检查的指令:

在给出Final Answer之前,请检查: 1. 是否遗漏了用户问题中的任何要求? 2. 工具返回的数据是否合理?有无异常值? 3. 我的推理链是否连贯?有无逻辑跳跃?

这个技巧相当于在Agent的执行循环中内嵌了一个轻量级的「反思检查点」,虽然不如专门的反射机制(下一章讨论)强大,但成本极低,能有效减少低级错误。

四、失败模式:ReAct的暗面

ReAct范式虽然优雅,但在实际应用中会遇到一系列典型的失败模式。了解这些失败模式,是设计健壮Agent系统的前提。

失败模式一:死循环(Infinite Loop)

Agent在两个或多个状态之间反复切换,永远无法到达终止条件。这是ReAct系统中最常见也最危险的失败模式。

典型表现:

Thought: 我需要查一下北京的天气来确认航班是否受影响。 Action: get_weather(location="Beijing") Observation: 北京明天有小雨。 Thought: 下雨可能影响航班,我再查一下航班的延误情况。 Action: check_flight_delay(flight="CA929") Observation: 暂无延误信息。 Thought: 暂无信息不代表不会延误,我应该持续监控。再查一次天气确认。 Action: get_weather(location="Beijing") Observation: 北京明天有小雨。 ... [无限循环]

根本原因:Agent缺乏终止判断能力。它不知道什么时候应该停止查询并给出答案。

应对策略

  • 硬性限制:设置最大步数限制(如10步),超过后强制停止并返回已收集的最佳答案。
  • 终止提示:在Prompt中明确要求Agent在获得足够信息后必须输出Final Answer,不要过度查询。
  • 进度检测:维护一个「已执行Action集合」,当检测到Agent在重复执行相同Action时,强制打断。

失败模式二:推理漂移(Reasoning Drift)

Agent在多步推理中逐渐偏离原始任务目标,进入无关的推理分支。这是大模型「容易分心」特性在Agent场景下的放大表现。

典型表现:

Thought: 用户问北京到东京的航班。北京首都机场(PEK)... Thought: 等等,北京还有大兴机场(PKX),也许从大兴出发更便宜? Thought: 大兴机场是2019年启用的,当时还引发了一些争议... Thought: 说起机场争议,希思罗机场第三跑道也争议了很多年... ... [推理越来越偏,完全忘记查航班这个任务]

根本原因:大模型的注意力在长上下文中容易发生偏移,且每一步推理都以上一步为基础,偏移会被逐步放大。

应对策略

  • 目标锚定:在每一步Thought前,重新陈述用户的核心需求(可以在Prompt中要求,或在框架层面自动注入)。
  • 上下文窗口管理:对于长链条任务,不是把所有历史都塞入上下文,而是维护一个压缩的「任务状态摘要」,只保留关键信息。
  • 定期检查点:每隔N步要求Agent做一次「目标一致性检查」——「我还在解决原始问题吗?」

失败模式三:工具误用(Tool Misuse)

Agent调用了错误的工具、传递了错误的参数、或对工具返回的结果做了错误解读。

典型表现:

Thought: 我需要查航班价格,使用search_hotels工具。 Action: search_hotels(city="Beijing", check_in="2025-03-16") Observation: [返回了北京酒店列表] Thought: 找到了最便宜的航班信息...

根本原因:模型对工具功能的理解不精确,或者工具描述不够清晰,导致Agent混淆了功能相似的工具。

应对策略

  • 工具描述优化:让每个工具的描述尽可能精确和无歧义,包含输入/输出格式示例。
  • 参数验证:在执行工具调用前,由框架层对参数做类型和范围校验,拦截明显不合法的调用。
  • 结果校验:工具返回结果后,要求Agent在Thought中显式校验结果是否与预期一致。

失败模式四:过早终止(Premature Termination)

Agent在还没有充分收集信息、推理链条还不完整时就草率给出Final Answer。

典型表现:

Thought: 用户问北京到东京的航班。我记得国航有这条航线。 Final Answer: 北京到东京可以坐国航航班,大概3个小时。

根本原因:模型对自身知识过度自信,跳过了工具调用的步骤;或者为了追求速度而牺牲了信息完整性。

应对策略

  • 在Prompt中要求工具验证:明确指出「涉及实时信息(价格、天气、航班等)必须调用工具查询,不要依赖记忆中的数据」。
  • 事实核查步骤:对于涉及具体数据的任务,在Prompt中要求Agent在给出Final Answer前,显式列出「我已验证的信息来源」。

五、ReAct系统的工程架构

把ReAct范式落地为可运行的系统,需要一套工程架构来支撑。典型的ReAct Agent系统包含以下几个核心组件:

1. 任务解析器(Task Parser)
接收用户输入,解析出核心任务和约束条件。这通常由大模型的第一次推理完成。

2. 推理引擎(Reasoning Engine)
生成Thought和Action。每次推理都以上下文(任务历史+工具描述+Observation)为输入,输出下一步的Thought和Action(或Final Answer)。

3. 工具调度器(Tool Dispatcher)
接收推理引擎输出的Action,解析工具名称和参数,调用对应的工具实现,将结果封装为Observation返回。

4. 循环控制器(Loop Controller)
管理ReAct循环的生命周期:判断是否继续循环、检测死循环、控制最大步数、决定何时终止。

5. 状态管理器(State Manager)
维护整个执行过程的状态:任务历史、已执行的工具调用、中间结果、推理上下文。在长链任务中,状态管理还涉及上下文窗口的裁剪和压缩。

这五个组件协同工作,构成了ReAct Agent的运行骨架。其中推理引擎由大模型驱动,其余四个组件是传统软件工程实现——这恰恰是Agent系统的典型特征:AI驱动的推理与传统软件工程的深度融合

六、本章小结

ReAct范式是当前Agent系统最主流的执行框架,它的核心思想是推理与行动的交替循环:每一步先思考(Thought)再行动(Action),然后观察结果(Observation)再思考下一步。

在这个过程中,Thought的质量是决定Agent表现的关键因素——高质量的Thought能准确分析当前状态、合理选择下一步行动、有效利用历史上下文。而低质量的Thought会导致推理漂移、工具误用和过早终止。

Prompt工程是引导模型产出高质量Thought的主要手段,包括角色设定、格式规范、少样本示例和工具使用指南。同时,系统层面必须有健壮的失败模式处理机制,特别是针对死循环和推理漂移的防护。

ReAct解决的是Agent「如何行动」的问题。但光会行动还不够——一个优秀的Agent还需要能够评估自己的行动效果,并在发现问题时自我纠正。这就是下一章要讨论的核心机制:反射(Reflection)。


作者与出处
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 秃头披风侠的小龙虾 转发
评论区 (0)
U