资讯动态

Agent决策与规划:ReAct、计划-执行、反思、思维树框架解析

发布时间:2026/10/26 6:22:12 来源:尧图企业网站定制
我最早做 Agent 的时候最纠结的地方不是怎么把模型接上工具而是模型动不动就把流程跳过去直接回答一个看起来合理、实际上完全是编出来的结果。相信不少人在跑 LangChain 或自研 Agent 时也遇到过这个问题任务没执行完它已经给出“答案”了而且你还很难第一时间发现它在胡说。后来的经验告诉我Agent 能不能用关键不在模型多强而在决策与规划这一层做得够不够稳。这几年被反复验证的四个框架——ReAct、Plan-and-Execute、Reflexion、Tree of Thoughts就是解决这个问题的核心答案。它们分别对应“怎么边想边做”“怎么先计划再执行”“怎么从失败里复盘重试”“怎么把推理变成搜索”市面上绝大多数 Agent 架构、Agent 框架、Agent 面试题里提到的方案基本都能追溯到这四套思路。这篇文章围绕这四个框架逐个拆解我会把它们的核心机制、适用场景、落地写法和我实际踩过的坑都讲一遍。主要适合两类人一类是正在搭建 Agent 应用、但不想停留在 demo 阶段的开发者另一类是准备 Agent 相关岗位面试、需要把“决策与规划”讲清楚的学习者。内容不预设你已经很懂底层原理但我会默认你会写一点 Python、知道 LangChain 这类框架大概长什么样。1. 决策与规划难在哪先看清四个框架分别治什么病1.1 生成式模型的天然短板把“下一步”当成“下一句话”来生成大模型本质上是一个“根据上文预测下一个 token”的系统它擅长补全文本但补全文本不等于规划行动。拿一个真实任务举例让 Agent 帮你“查一下北京今天的天气然后给出合适的穿搭建议”。如果直接用普通 Prompt 去问模型它大概率会给出一个像模像样的回答“今天晴12 到 22 度早晚温差大建议穿薄外套。”看起来没问题但它压根没有去查实时天气。这就是生成式模型的典型问题它没法区分“说出一句话”和“执行一个动作”也不知道自己哪些话需要外部信息来校验。Agent 要做决策与规划本质上就是在模型外面套一层循环让模型在每个节点上决定现在该继续推理还是调用工具还是直接给出最终答案。这层循环如果没设计好模型就会一路“放飞自我”。所以在讨论 ReAct、Plan-and-Execute、Reflexion、Tree of Thoughts 之前得先把心智模型切换一下Agent 不是一个“更聪明的提示词”而是一个由模型驱动的小型状态机。所谓决策与规划就是状态转移的规则。1.2 四个框架的定位差异没有一个万金油这四个框架经常被放在一起讲但它们其实治的是不同的病ReAct 解决“推理与行动如何交错”的问题。它让模型在每一步先产生思考再决定调用什么工具然后根据观察结果继续推理。适合需要外部信息、多步操作的交互式任务。Plan-and-Execute 解决“长任务如何不跑偏”的问题。它把“制定计划”和“执行计划”拆成两个角色先列出完整步骤再逐步执行避免每一步都重新想一遍。Reflexion 解决“失败之后如何自我改进”的问题。它让 Agent 在失败后生成一段文字反思存进记忆下次重试时参考这段反思。Tree of Thoughts 解决“单一思路容易走入死胡同”的问题。它不再让模型沿着一条推理链走到底而是同时维护多个候选分支像搜索一样尝试、评估、回溯。理解这层差异之后你会发现它们根本不是竞争关系。实际生产中经常是把其中两到三种组合在一起用。比如外层用 Plan-and-Execute 做任务规划内层每一步用 ReAct 调工具跑完效果不满意再套一层 Reflexion 做复盘重试。很多成熟的 Agent 框架本质都是在做这种组合。1.3 从“提示词工程”转向“决策循环设计”我一开始做 Agent 也走了弯路总觉得效果不好就是 Prompt 写得不细拼命往系统提示词里堆约束。后来发现对 Agent 来说光靠 Prompt 约束“你要一步一步来”是没用的。模型可能嘴上答应了实际还是直接跳到最后一步。真正有效的是把决策过程结构化。你要明确告诉模型当前有哪些工具可用工具输入输出长什么样每步最多调用几次工具什么条件下必须继续、什么条件下可以停止。这些不是提示词能完全兜住的需要代码层面的循环和校验。ReAct、Plan-and-Execute、Reflexion、Tree of Thoughts 能成为主流就是因为它们提供了可复制的结构化模板让模型在每一步都有一个清晰的“决策接口”。从工程角度说你写的其实是一个循环模型输出文本解析出意图和动作执行工具把观察结果拼回去再喂给模型。剩下的问题就是循环的每一步让模型想多深、怎么处理失败、怎么避免重复、怎么在多个选项之间取舍。这四个框架正好就是在回答这些问题。2. ReAct边想边做最经典的 Agent 决策循环2.1 Thought-Action-Observation 三轮驱动ReAct 这个名字来自 Reason Act它的核心循环是三个字母Thought思考、Action动作、Observation观察。Thought模型用自然语言描述当前对问题的理解以及下一步应该做什么。Action模型输出一个具体动作通常是调用某个工具比如 search、calculator、代码执行器。Observation工具返回的结果被拼接回上下文成为下一轮思考的输入。这个循环会一直持续直到模型认为信息足够输出一个特殊动作 Final Answer。关键点在于思考如果发现信息不足就必须执行动作获取新信息动作的结果如果和预期不符思考要能识别出来并调整方向。这就是 ReAct 比普通 CoT思维链扎实的原因——CoT 只是一段文字推理ReAct 把推理和外部反馈绑在一起。我常用一个简单的 ReAct 提示模板基本上就是把流程写明你是一个需要解决用户问题的 Agent。请严格遵守以下循环 1. 先输出一个 Thought说明你对当前情况的分析和下一步计划。 2. 如果还需要信息输出一个 Action格式为 Action: 工具名[输入]可用工具search、calculator。 3. 工具返回后会给出 Observation请基于 Observation 继续思考。 4. 当信息足够时输出 Action: Final Answer[答案]。 每一步最多执行一次动作不要连续编造多个动作。别小看那句“每一步最多执行一次动作”很多 Agent 跑飞就是因为模型一次输出里塞了三四个 Action导致解析混乱。2.2 一个可以直接照抄的 ReAct 交互示例拿“查北京天气并给穿搭建议”来说一个标准 ReAct 轨迹大概是这样的Thought: 用户需要的是北京实时天气和穿搭建议我不掌握实时数据需要先查询天气。 Action: search[北京 今日天气] Observation: 晴气温 12-22 度风力 3 级空气质量良。 Thought: 天气信息已获取早晚温差较大应当建议分层穿衣。 Action: Final Answer[北京今天晴12到22度风力3级。早晚较凉建议穿薄外套加长袖午间可脱掉外套。]看起来简单但这条轨迹里有两个容易被忽略的细节。第一“Action: search[北京 今日天气]”之前的 Thought 必须说明“我不掌握实时数据”这能显著降低模型直接编造答案的概率。第二Observation 之后的 Thought 要给出判断逻辑比如“早晚温差较大”而不是机械重复观察结果这说明模型真的在利用工具返回的信息做推理。在实现上我建议把每轮 Thought/Action/Observation 都追加到对话历史里而不是只传最后一轮。模型作决策时非常依赖完整轨迹尤其是多步任务少了前面的观察结果后面很可能自说自话。2.3 ReAct 的坑循环、噪声、动作空间ReAct 看起来优雅实际跑起来至少有三个坑。第一是循环停不下来。模型拿到 Observation 之后如果无法得出结论就会反复调用同一个工具。我在项目里见过搜索同一个关键词四遍还不罢休的循环。处理办法是加最大步数限制并且检测重复动作如果同一动作连续出现直接强制终止或换一种策略。比如设置 max_steps6超过就要求模型基于已有信息作答。第二是工具返回噪声太大。网页搜索结果经常是几千个字符的摘要模型能读但很容易被无关信息带偏。建议在把 Observation 喂回模型之前做一层截断或清洗只保留和查询词相关的部分。这个步骤看着不起眼但对决策质量影响非常大。第三是动作空间别给太大。工具越多模型选错的概率越高。不是所有 Agent 都需要十几个工具很多时候两三个就够。工具描述也要写清楚参数和返回值不然模型会猜参数格式。我见过模型把日期参数写成中文就是因为工具描述里没给示例。3. Plan-and-Execute把“计划”和“执行”拆成两个角色3.1 Planner 负责想Executor 负责干ReAct 的一个问题是每走一步都要思考一遍短任务没问题一旦任务有五个以上步骤思考开销会变得很大而且容易中途跑偏。Plan-and-Execute 的思路很直接先让模型一次性生成完整计划再逐条执行执行完再检查要不要调整计划。这个架构里有两个角色。Planner 负责把用户目标拆成有序步骤输出一个编号列表Executor 负责执行每一步每一步通常也可以使用 ReAct 循环来调工具最后还有一个 Controller 角色负责检查执行结果和计划是否匹配决定继续、重试还是重新规划。举个例子任务“生成一份竞品分析的周报”Planner 可能输出1. 确定竞品名单至少包含头部的三个产品 2. 搜索每个竞品的最新功能动态 3. 整理竞品功能对比表 4. 基于对比结果写结论 5. 生成 Markdown 文件然后 Executor 一步步执行。第 2 步如果发现某竞品近三个月没有任何公开动态Controller 就要决定是跳过这个竞品还是换一个研究对象而不是硬着头皮继续。3.2 执行中什么时候必须重新规划计划是静态的世界是动态的。Plan-and-Execute 最关键的环节就是“重新规划”的触发条件。我在实际项目里总结出三个必须触发的情况当前步骤用到的新信息和计划前提矛盾。比如计划里假设数据在某张表里实际查不到。某一步连续重试多次仍然失败且原因不是临时故障。用户中途修改了需求后续步骤全部失效。我把这些判断写成一个专门的检查 Prompt每执行完一步就调用一次让模型输出一个状态标记continue、retry、replan 或者 done。这比让模型自己闷头继续要稳得多。重新规划也不是把整个计划推倒重来而是保留已完成步骤的结果只调整后续步骤。3.3 省 token 的代价Plan-and-Execute 最大的优势是省 token因为执行阶段不需要每步都重新分析全局任务。比如一个十步任务ReAct 每一步都要重新读完整历史再思考上下文会越来越长Plan-and-Execute 每步只关注当前步骤上下文更可控。但代价是如果计划一开始就错了后面会错得很彻底。比如一个调研任务Planner 把“统计用户画像”漏掉了Executor 会老老实实执行完所有错误步骤最后交付一个缺胳膊少腿的结果。这也是为什么 Controller 的角色不能省掉。所以 Plan-and-Execute 适合任务边界清晰、步骤相对固定、执行链条长的场景典型的如周报生成、数据分析流水线、批处理任务。反过来如果任务本身发散、需要大量自由探索比如“帮我研究一下某个新兴领域”Plan-and-Execute 就会显得笨重。4. Reflexion用复盘把失败变成下一步的输入4.1 没有梯度就用“文字反馈”做自我修正Reflexion 这个框架的核心想法很有意思模型没有反向梯度但它有语言能力所以可以让它把失败原因写成一段文字反思作为下一次尝试的参考。这相当于用文本做“强化信号”。它的架构一般包含三个组件Actor执行任务的模型负责输出行动。Evaluator评估器判断 Actor 的输出是否成功。可以是硬性规则比如单元测试是否通过也可以让另一个模型打分。Memory记录 Actor 的历史轨迹和反思。每次失败后生成一条反思存储在记忆里下一次尝试前把反思注入上下文。整个流程是一个 episode 循环跑完一轮评估失败就反思带着反思再跑下一轮。和 ReAct 那种单轮循环不同Reflexion 的重点是跨轮次的学习它不在乎你这一轮怎么走而在乎你下一轮能不能走得更聪明。我做过一个简单的实践让 Agent 写一个处理 CSV 数据的 Python 脚本有明确测试用例。第一轮模型用了过时的 pandas 写法测试报错Evaluator 判定失败并返回具体错误Reflexion 生成一句反思“不要用 df.append2.0 版本已经移除改用 pd.concat。另外注意 read_csv 时指定 encoding。”第二轮带着这句反思重新写一次通过。4.2 一次完整的 Reflexion 闭环实际操作中我把闭环设计成四个阶段比论文里的原型更工程化一点。第一阶段是环境准备定义成功标准明确 Evaluator。这一步最容易糊弄。比如“写一个脚本处理 CSV”什么叫成功我通常要求同时满足能运行、输出结果正确、代码结构可读。没有明确的 evaluator反思就是无源之水。第二阶段是执行Actor 带着当前记忆里的历史反思去执行任务得到一条完整轨迹。这条轨迹要完整记录每轮 Thought 和 Action因为后续反思需要回顾。第三阶段是评估与反思Evaluator 输出成功或失败失败时给出一段具体反馈。然后让一个 Reflection 模型生成反思格式我习惯固定为三行“失败原因是什么”“哪些动作导致了失败”“下一次应该改用什么策略”。固定格式的反思比自由发挥更容易被下一轮 Actor 利用。第四阶段是重试把反思加入记忆最多重试 N 次N 我一般设 3。超过 N 次还失败就放弃并输出当前进度和失败总结而不是无限循环。4.3 复盘不是万能药Reflexion 看起来很美但它有一个致命前提Evaluator 必须可靠。如果你用一个模型去评估另一个模型的输出而这个评估模型本身就会漏判那么反思内容就会建立在错误判断上越改越偏。我在项目里遇到过更尴尬的情况反思质量太差导致第二轮不如第一轮。比如第一轮用了正确方案但代码有个小语法错误反思却写成“方案完全错误应该换一种数据结构”第二轮直接被带沟里去了。解决办法是给反思 Prompt 增加约束要求“只针对导致失败的具体技术细节进行修正不要推翻未经证实的部分”。同时限制反思的篇幅不要让模型长篇大论讲道理一两句话点到痛点就够。另外一个容易踩的坑是记忆污染。如果不做隔离上一轮任务的反思可能会污染下一轮任务。我习惯用一个 task-specific memory 变量在每个新任务开始时清空只保留通用的工具使用技巧。有些人还想把反思积累成长期知识库这个方向可以尝试但要有长期记忆的验证机制不然错误的反思会被越传越大。5. Tree of Thoughts当一条思路不够就用搜索来凑5.1 从单链推理到多分支搜索ReAct 和 CoT 本质上都是单条推理链模型走一步看一步一旦某个中间决策错了后面全部跟着错而且没有回头路。Tree of ThoughtsToT就是为了治这个病。ToT 的核心是让模型同时维护多个“部分想法”每个想法生成若干候选的下一步然后评估这些候选留下有希望的剪掉没希望的。这其实把模型推理变成了一次树搜索常见策略是 BFS广度优先或 DFS深度优先每一步用模型做“启发式评分”。它的适用范围不是所有任务而是那些“中间步骤决策空间大、需要探索”的任务。经典案例有数学问题、创意写作、复杂路线规划。比如“用 3、4、6、10 四个数算 24 点”直接思维链可能走到某一步就死锁而 ToT 会在每一步保留多个算式分支发现当前分支没法继续时就换一个。5.2 分支、打分、回溯ToT 的三个核心动作一道 24 点题目的 ToT 过程大概是这样的当前状态是 [3, 4, 6, 10]第一步让模型生成几个候选操作比如“347”“6-42”“10-64”“4*624”每个候选代表一个新的状态。接下来不是全部展开而是让模型给每个状态打分评估“继续算下去有多大希望”。分数高的分支保留分数低的分支直接剪掉。一个稍微抽象一点的 Python 伪代码大概长这样def tot_solve(state, beam3, max_depth4): # state 是当前问题的中间表示 states [state] for depth in range(max_depth): candidates [] for s in states: nexts generate_candidates(s) # LLM 根据当前状态生成候选步骤 scores evaluate_candidates(nexts) # LLM 给候选步骤打分 candidates.extend(zip(nexts, scores)) # 保留分数最高的 beam 个候选 states top_k(candidates, beam) if is_solved(best_state(states)): break return best_state(states)这里面最关键的是 generate_candidates 和 evaluate_candidates 两个 Prompt。生成候选时不能只让模型给一个答案而是让它给多个“下一步”打分时不能用笼统的“好不好”要点明评分维度。比如“这个分支离目标还差几步有没有明显的死路迹象继续走下去的前景如何”打分 Prompt 里的维度不清晰整个搜索就会变成随机游走。5.3 ToT 的性价比边界ToT 的代价非常直接token 消耗是 ReAct 的数倍甚至十几倍。因为每一步要生成多个候选每个候选还要单独评分最终只留下少数分支。很多任务根本不值得这么折腾。日常的问答、信息查询、工具调用用 ReAct 就够了任务步骤固定用 Plan-and-Execute 更省只有那些“单条路走下去很难成功”的任务才值得上 ToT。我自己的经验是先跑一次普通 ReAct观察它失败的模式。如果失败原因是“选了一个错误方向然后无法回头”再考虑 ToT。如果失败原因是“工具没用好”或者“信息不足”ToT 帮不上忙应该去调工具和检索。另外ToT 可以和 ReAct 结合把思考的每一步变成一个可操作的动作比如“用搜索验证某个假设”。这样 Agent 既有树的全局视野又有工具的现实支撑。但复杂度也会成倍增加需要你在工程投入上做权衡。6. 选型指南四个框架不是互相替代而是互相补充6.1 一张表看清四套方案的差异我把四个框架放到一张表里对比这样选型时直接照着套就行框架核心机制适合的任务token 成本典型风险ReAct思考-行动-观察循环信息查询、工具调用、多步交互中容易循环、跑偏Plan-and-Execute先规划后执行长流程、固定步骤、批处理低计划僵硬错了难纠Reflexion失败后反思重试有明确评估标准的任务中高评估器不可靠会误导Tree of Thoughts多分支搜索与回溯组合优化、创意规划、数学推理高分支爆炸、token 爆表如果你是做面试准备这张表基本能帮你回答“这几个方法有什么区别”这类问题。核心话术是ReAct 解决单步行动与反馈的耦合Plan-and-Execute 解决长时间跨度任务的规划衰减Reflexion 解决失败后的经验复用ToT 解决单一推理路径的局部最优问题。6.2 组合使用我最推荐的三种混合模式实际项目中几乎没有只用单一框架的场景我常用的混合模式有三种你可以直接借鉴。第一种是“Plan-and-Execute 外层 ReAct 内层”。外层 Planner 把任务拆成步骤Executor 执行每一步时用 ReAct 调工具。这是我最推荐的通用配置适合大多数业务 Agent兼顾成本和可控性。比如“分析一个月度销售数据”计划拆三步每步用 ReAct 完成检索和计算。第二种是“ReAct Reflexion 重试”。任务有明确测试标准时比如代码生成、SQL 查询第一遍 ReAct 跑完评估失败就反思重试。等于给 ReAct 加了一个自动驾驶的纠错机制。我就经常用这个模式跑复杂 SQL 生成效果比单纯加大模型参数明显。第三种是“ToT 做规划 Plan-and-Execute 做执行”。任务属于“规划本身就很有难度”的类型比如“设计一个三天的团建方案”先用 ToT 生成几个候选方案并评分选一个最好的再用 Plan-and-Execute 把它拆成可执行的日程步骤。这样不会一上来就动辄几十个步骤而是先把“做什么”想明白。6.3 一个通用的落地闭环如果你想把四者统一到一个 Agent 架构里我建议用下面这个闭环做骨架接收任务后先判断类型简单查询走 ReAct长流程走 Plan-and-Execute。Planner 出计划后每一步交给 Executor 执行Executor 内部用 ReAct。每步执行完Controller 判断结果状态为 done / retry / replan。整个任务失败且有明确评估标准时触发 Reflexion 生成反思重新执行。只有规划本身容易走进死胡同时才用 ToT 做候选方案评估。这里每个角色的 Prompt 都尽量独立方便单独调试。我习惯把 Planner、Executor、Controller、Reflection 四种角色拆成不同的函数或类而不是写在一个巨大的 Agent 类里。这个习惯对排查问题帮助很大后面还会提到。7. 踩坑实录Agent 决策模块最容易翻车的地方7.1 循环停不下来怎么办做 Agent 决策模块我遇到最多的一个问题就是循环。Agent 反复调用同一个工具不仅浪费 token还会让用户觉得产品疯了。我的排查思路是先看轨迹日志。如果循环发生在同一动作上说明模型没有从 Observation 中获取足够信息此时要检查 Observation 是否被正确拼回上下文。如果循环发生在不同动作之间反复横跳说明模型在“不确定走哪条路”这时候增加一个“信息收集状态总结”的 Thought 往往有效。工具层也要加保险。我给搜素工具返回结果加一个全局去重如果某条结果已经出现过标记为“重复结果”。模型看到重复标记后通常就会收敛。同时设置硬性最大轮数超过后强制触发 Final Answer 或 replan这是最后的兜底。7.2 计划执行两步之后就对不上了Plan-and-Execute 最常见的崩溃点是计划和执行脱钩。比如 Planner 说“调用 CRM 接口获取客户数据”Executor 去调接口才发现权限不足于是整个后续计划全部作废。如果 Controller 没有意识到这一点Agent 就会继续硬跑。我建议在每步执行后不但要记录结果还要记录执行的前提。每条计划步骤从“前置条件、动作、产出物、验证条件”四个维度描述执行时逐项对照。一旦发现前置条件不满足立即交给 Controller 决定是否 replan。另外要注意的是replan 时不要把 Old Plan 完全丢掉。很多失败后的新计划会参考旧计划的错误所以要把旧计划和失败原因一起传给 Planner。我在实践中发现有了失败上下文的新计划质量通常远高于完全重写的计划。7.3 复盘之后反而越改越差Reflexion 不是每次都能带来正向效果我踩过一次很典型的坑。当时做一个网页信息提取任务第一轮 Agent 用正则提取失败原因是某字段格式变化。反思让 Agent 改用 CSS 选择器结果 CSS 选择器也写错了反而比第一轮更差。后来我加了两个约束。第一个是反思生成时必须基于 Evaluator 的具体错误信息而不是模型泛泛而谈地总结经验。第二个是重试范围限制如果失败原因是“某个具体字段解析失败”反思只能修正这一步不允许模型推翻其他已验证正确的步骤。我还把每次重试的结果都记录下来。如果连续两次重试都没有改进就直接终止输出已经完成的部分和剩余问题而不是无限“自我修正”。这对用户是更负责任的做法因为 Agent 的失败必须可归因。7.4 树开太大token 先爆了ToT 的分支数一旦没控制好token 消耗是非常恐怖的。我最初做 ToT 时 beam 设了 5深度设了 5结果一个任务跑了几万 token最后结果还没比单条 ReAct 好多少。我的调参经验是beam 越小越好一般 2 到 3 就够对大多数推理任务深度超过 4 意义不大因为再深前面的分支分数已经严重不可靠了。打分 prompt 也要明确“只根据当前状态和少量历史评估不要重新计算过去所有步骤”否则打分本身就会变成一个大模型任务。另外ToT 的分支里可以保留“放弃”这个选项。比如某个分支被打成 only 1 分且连续两轮没有进展那就直接剪掉不要为了凑 beam 数量强留。剪枝比展开重要一个干净的小树往往好过一棵枝叶杂乱的大树。最后分享一个我养成的习惯每次跑 Agent 任务我都会把完整轨迹 dump 成 JSON 日志包含每一轮 Thought、Action、Observation、状态标记以及 token 消耗。排查问题时先看轨迹再改 Prompt最后才动代码逻辑。这个习惯帮我省了很多时间“看日志而不是猜原因”是我在 Agent 决策与规划这条路上学到的最大经验。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑