最近在做 Agent 项目时有个问题一直绕不开大模型本身是一个“思考引擎”但怎么让它在现实任务里靠谱地“做事”而不是东拉西扯地胡说一通这背后核心就是 Agent 的决策与规划机制。目前社区里讨论最多、实战用得最多的四条路线——ReAct、Plan-and-Execute、Reflexion 和 Tree of Thoughts——我前后都试过一轮这中间踩了不少坑也理出了一些门道。这篇就把我对这四种架构的理解、实现要点和选型思路完整梳理一遍内容偏实操适合正在做 AI Agent、想要让智能体更可控的开发者和研究者参考。1. 整体认知为什么 Agent 需要一套决策与规划机制1.1 大模型的“一步式输出”根本不够用先看一个最基本的场景你让大模型“帮我在 GitHub 上找一个 star 最多、最近一周有更新的 OCR 项目并写一份对比报告”。直接丢给 GPT-4 这类模型它能输出一个像模像样的回答但大概率是编的——它不会真的去 GitHub 上搜索不会真的对比 star 数甚至可能把已经不维护的项目说得天花乱坠。原因很简单大模型的知识停留在训练数据那一刻而且它有“幻觉”倾向。Agent 的核心价值恰恰在于给大模型装上“手”和“眼睛”让它可以调用工具、读取真实世界的数据、执行代码并根据结果调整下一步。但问题来了——一个多步骤任务先做什么、后做什么、遇到错误怎么办、中间结果怎么评估这些如果全靠一次性 prompt 写死那就是死板的剧本根本没法应对真实世界的随机性。所以Agent 的“决策与规划”本质上就是回答三件事下一步做什么行动选择做完之后怎么评判结果评估失败了怎么办纠错与重启。ReAct、Plan-and-Execute、Reflexion、Tree of Thoughts就是目前最主流的四套回答方式。它们不是互相替代的关系而是针对不同任务特点的差异化方案甚至可以在生产环境里组合使用。1.2 四种模式的思路差异用一个生活类比说清楚我用一个“做饭”的类比来帮大家快速建立直觉。ReAct边想边做像是“看着菜谱做菜”。每做一步看一眼菜谱判断火候对不对再决定下一步。菜谱就是你给模型的 few-shot 示例实时锅里的状态工具返回结果决定接下来的动作。它的特点是行动与推理交替进行信息实时反馈。Plan-and-Execute先计划再执行像是“提前写好完整菜单”。先想清楚今天要做几道菜、每道菜的步骤、需要什么食材然后一次性把采购和烹饪流程安排到位再去逐项执行。特点是先规划全局再逐步落地计划先于行动。Reflexion反思再重试像是“做完一道菜后尝一口发现咸了写下‘下次少放盐’然后重新做”。它不是改变做菜的流程框架而是在失败后给系统增加一个“记忆修正”环节。特点是善于从失败中学习具备过程记忆。Tree of Thoughts多路树状推演像是“同时模拟三条做菜路线想象哪条最可能成功再选一条执行”。每一步都生成多个候选方案评估优劣必要时回溯换路。特点是在搜索空间中做决策适合难问题。有了这个直觉我们再深入看每一种模式的实际实现和适用场景。2. ReAct 深度拆解让推理与行动交替驱动2.1 ReAct 的核心思想与 Prompt 构造ReActReasoning Acting来自 2022 年 Google Research 的论文《ReAct: Synergizing Reasoning and Acting in Language Models》核心思路非常朴素把“思考过程”和“行动调用”交错写入模型上下文模型每推理一句就跟着调用一次工具工具返回后继续推理直到任务完成。一个最简化的 ReAct 数据流是这样的Thought: 我需要查找用户问的这家公司的创始人信息 Action: search[OpenAI 创始人] Observation: 搜索结果返回 Sam Altman 等人 Thought: 找到了我可以直接回答 Action: finish[OpenAI 创始人是 Sam Altman]在这个流程中模型并不是一次性输出答案而是每一步都输出结构化的文本由 Agent 框架解析出 Action执行工具再把工具结果作为 Observation 继续塞回上下文。关键细节在于Thought 是模型自由生成的推理轨迹不需要额外训练属于纯 prompt 工程范畴Action 必须格式稳固通常定义为工具名[参数]的格式方便代码做字符串解析或函数调用Observation 是程序自动追加的模型不控制这段内容的生成。我在实际项目里发现ReAct 的成败首先取决于 few-shot 示例的质量。你需要给模型展示至少 2-3 组完整的 Trajectory轨迹示例而且要刻意包含一次失败后的纠正路径——比如说搜索某个关键词返回空模型换了个关键词再搜。没有失败示例模型遇到边缘情况时容易不知所措。2.2 ReAct 的实现示例与工具解析现在主流的 LangChain、LlamaIndex 都已经把 ReAct 封装好了但为了讲清楚机制我建议你自己手写一个极简版本。下面这个伪代码展示了核心循环逻辑def react_agent(question, tools, max_steps10): prompt build_react_prompt(question, tools) with_history # 用于存放 Thought/Action/Observation 的历史 for step in range(max_steps): response llm(prompt with_history) thought, action parse_response(response) observation execute_action(action, tools) with_history fThought: {thought}\nAction: {action}\nObservation: {observation}\n if action.type finish: return thought # 或者返回最终答案 return max_steps exceeded注意这里有几个容易踩的坑stop token 要配好。如果 llm 是 OpenAI 的建议在调用时传stop[Observation]防止模型自己往下续写 Observation 导致轨迹混乱。解析 Action 时要容忍格式扰动。模型偶尔会输出多余的空格、换行甚至把 Action 写成Action :解析器要做归一化处理否则 Action 被误判成无效调用后Agent 会在死循环里打转。max_steps 一定要设上限。我见过一个电商客服 Agent 因为没有步数上限在“查询物流-无查询结果-换方式查询-又无结果”之间翻来覆去跑了几十轮token 费用直接爆表。经验值是简单任务 5 步、中等任务 10 步、复杂任务 20 步封顶。ReAct 的适用场景非常清晰需要工具调用、且信息在过程中逐步获取的任务。比如资料检索、API 调用、数据库问答、网页浏览等。它的优点是对动态变化的环境适应性强缺点是每步都要等 LLM 生成推理轨迹延迟和 token 开销都偏大而且长时间运行可能积累很长的上下文导致模型“忘记”最初的指令。2.3 ReAct 的上下文化与记忆问题我实际跑一个“多轮网页抓取”任务时ReAct 到第十步左右模型明显开始“失忆”——它忘了用户最初要的是什么格式的答案反而专注于抓取过程中的琐碎分支。这其实是lost in the middle现象的表现LLM 对超长上下文的注意力分布不均匀。缓解办法我在项目里试过三种每轮把目标指令压缩后醒目插在 prompt 开头辅助模型记住终点将历史轨迹用摘要替代当轨迹超过一定长度时用另一个 LLM 调用把前 N 轮的 Observation 浓缩成一段摘要替换原始轨迹控制上下文长度引入“里程碑检查点”在轨迹中添加一个额外的结构化标记例如Status: 第3/5步, 已完成信息收集, 正在生成报告让模型对当前进度有清晰的锚定点。顺带一提ReAct 这个名字在搜索引擎里经常和“React 前端框架”撞车也被一些人拿去造了不少 Agent 框架图。如果你搜资料建议关键词带上“ReAct agent”“reasoning acting”否则容易搜到一堆前端教程。3. Plan-and-Execute先谋后动的全局规划派3.1 两步走架构Planner 与 ExecutorPlan-and-Execute 的思路最早可以追溯到 2023 年 LangChain 的一篇博客后来也被很多 Agent 框架吸收。核心是把“决策”和“执行”切成两个独立模块Planner规划器接收用户目标拆解成一份完整、有序的子任务清单可能需要调用外部工具来补全计划细节Executor执行器按计划逐项执行每项执行完返回结果全部完成后由 Planner或一个单独的 Responder生成最终输出。一个典型的计划长这样Plan: 1. 搜索用户提到的竞品名称及其官网 2. 抓取竞品官网的定价页面 3. 抓取竞品官网的功能列表 4. 对比功能与价格生成对比表 5. 输出 Markdown 格式的对比报告实现上Planner 和 Executor 可以是同一个 LLM也可以不同模型比如用更强的模型做规划用更便宜的模型做执行。LangChain 里封装好的PlanAndExecuteAgent 我试用过但生产环境下我更推荐自己手动拆管因为默认实现里 Executor 返回的结果往往只是个摘要丢失了大量原始信息后续规划调整缺乏依据。3.2 计划为什么有时候会“纸上谈兵”Plan-and-Execute 最大的优点是对复杂长任务的稳定性和可解释性。计划可以展示给用户看用户能提前确认方向对不对Agent 也不容易在中途跑偏。但它的弱点也很明显计划本身是大模型生成的一旦外部环境与预期不符整个计划就可能失效。举一个我实际遇到的场景让 Agent 从某个电商网站抓取商品价格Planner 给出的计划是“先访问商品列表页 → 点击进入详情页 → 提取价格”但执行时网站改版了详情页 URL 结构变化第一步就失败。如果 Executor 只是机械地报错整个任务就停摆。所以我在用 Plan-and-Execute 时一定会给 Executor 加上“子任务级容错重试”与“Plan 动态修订”两个机制Plan Revision Policy计划修订策略: 1. 如果某个子任务连续失败两次调用 replanner 重新生成剩余计划 2. 新计划必须保留已完成子任务的结果避免重复劳动 3. 修订次数上限建议 2-3 次防止进入“计划-失败-再计划-再失败”的死循环。另外关于 Executor 的形态我踩过一个坑默认把每个子任务都丢给一个 ReAct agent 去执行结果是双重决策开销本来一个 ReAct 顶多十几步就能完成的任务变成“计划 多个 ReAct”时间翻了两三倍。最终我的做法是子任务简单就直连工具函数只有复杂子任务才开 ReAct。这是典型的“杀鸡不用牛刀”。3.3 适合的场景与必要前提Plan-and-Execute 非常适合以下场景任务流程相对固定如周报生成、定时数据采集、标准项目复盘等需要向用户展示进度条每完成一个子任务就更新一个勾选状态产品体验会好很多多 Agent 协作Planner 把计划分发到多个专用 Executor 并行执行这是它相对 ReAct 的另一大优势。但前提是你对任务环境有足够的预先了解。如果任务本身就是高度开放式的探索比如“帮我想一个创意、并分析可行性”Plan-and-Execute 的计划阶段往往就陷入平庸反而不如 Tree of Thoughts 这类能枚举多种思路的方案。4. Reflexion让 Agent 从失败里长记性4.1 从“失败就重来”到“带着反思重来”Reflexion 是 2023 年 Shunyu Yao也是 ReAct 作者之一等人提出的框架。核心思想Agent 执行失败后不仅简单地重试而是先在语言层面自我反思生成一条经验教训然后带着这条经验重新尝试。这里有个关键术语区分Reflexion 里的记忆分两层。Working Memory工作记忆当前这次尝试的完整轨迹相当于上下文窗口Episode Memory事件记忆之前每次尝试的反思总结跨轮次保留。举个例子一个 Agent 写代码反复出错。第一次运行报TypeErrorReflexion 会生成一条反思“这次失败是因为把字符串当数字计算下次运行前先检查数据类型。”下一条轨迹开头就把这条经验塞进 prompt于是后续生成代码时模型就会主动做类型转换。伪代码是这样def reflexion_agent(task, max_attempts5): episode_memory [] for attempt in range(max_attempts): result, trajectory run_with_memory(task, episode_memory) if is_success(result): return result reflection llm_reflect(trajectory, previous_reflectionsepisode_memory) episode_memory.append(reflection) return max attempts exceeded4.2 反思质量的三个维度调试技巧与血泪教训Reflexion 的效果几乎完全取决于反思生成这一步的质量。我实测下来让模型凭空“反思”很容易产生空话比如“下次我要更仔细”“我要避免语法错误”这种完全没有操作性的废话。要提升反思质量建议强制反思内容包含三个维度具体原因Cause明确指出是哪一步、哪个假设/工具/逻辑导致失败修正动作Fix给出可执行的下一步动作而不是抽象建议验证信号Signal说明重试时依据什么判断“这次没有重蹈覆辙”。下面是我在项目里常用的一种反思 prompt 模板你刚才执行任务失败了请根据以下轨迹进行反思输出 3-4 句话 - 本attempt失败的直接原因是什么 - 哪一步决策导致了偏差 - 下一次重试时你要采取什么具体动作 - 如果重试时看到什么信号可以判断问题已规避实际上我在 HumanEval 类代码生成任务和分类爬虫任务上都测过。没有 Reflexion 的 ReAct 大约 30 步能跑通加 Reflexion 后一次失败后第二次基本就跑通整体步数和 token 消耗下降 30%-40%。不过 Reflexion 不适合用在一路都很顺利的任务上——它本身的反射调用是有额外开销的。4.3 记忆污染问题反思写入上下文要过期这里必须强调一个我在生产环境遇到的严重问题Reflexion 的记忆是“可过期”的。当 Agent 跨多个任务复用时上一次任务中“不要抓取那个网站因为反爬太强”的反思可能会被错误地带入下一次任务反而导致 Agent 畏手畏脚。解决办法有两种按任务域隔离记忆给记忆打上 task name 或 embedding 标签只有相似任务才召回给反思加时效与置信度明确标注“这个反思是在 XX 数据下的结论”如果后续尝试成功提升置信度如果后续证明经验无效降权或删除。我现在偏向的做法是加一个轻量记忆管理模块类似Memory {task_id: [reflections]}每次新任务打开独立记忆跨任务共享只发生在同一 playbook 内部。这样既保留 Reflexion 的跨尝试学习能力又避免上下文污染。5. Tree of Thoughts在选项分支中搜索最优路径5.1 从“单线直走”到“搜索树分叉”Tree of ThoughtsToT是 Princeton 和 DeepMind 等机构在 2023 年提出的方法通俗地说就是把大模型的推理过程显式做成一棵搜索树。普通 CoT 或 ReAct 是单一路径走到底而 ToT 在每一步同时生成多个候选思路给每个候选打分评估然后选择最优分支继续探索遇到死路还可以回溯。这对解决“难题”特别有效比如逻辑推理、数学应用题、创意写作等。以一道 24 点游戏为例普通 CoT 可能直接写一行算式错了就没法重来ToT 则是生成好几种中间方案评估每种方案离目标有多远选中可能性最大的分支继续算。实现上ToT 有两种主要搜索策略BFS广度优先每层保留 Top-K 个节点继续展开适合状态空间中需要“稳扎稳打”的任务DFS深度优先深入一个分支直到结束不行再回溯适合分支不太多但单分支内部逻辑深的场景。一个最简化伪代码BFS 版本def tree_of_thoughts(question, k3, max_depth5): frontier [initial_state] for depth in range(max_depth): candidates [] for state in frontier: # 为每个节点生成 k 个候选下一步 next_steps llm_generate_candidates(question, state, k) # 给候选打分排序 scored [ (llm_evaluate(question, ns), ns) for ns in next_steps ] candidates.extend(scored) candidates.sort(reverseTrue) frontier [ns for score, ns in candidates[:k]] if is_solution(frontier[0]): return frontier[0] return best_state(frontier)5.2 评估器设计ToT 的命门所在ToT 的评估环节也就是判断哪个候选分支更优极其关键但我发现有太多教程只讲流程不讲评估。评估方式一般有两类模型自评self-evaluation直接让 LLM 对候选方案打分简单但对复杂方案容易产生评价偏差规则/工具评估heuristic/tool-based evaluation比如代码任务直接跑测试集看通过率数学任务验证中间步骤是否符合已知公式。我在项目里强烈建议能用真实工具评估就优先用真实工具。比如在“写自动化测试脚本”这类任务里候选脚本直接跑一遍看几个核心断言是否通过这比让 LLM 打分可靠得多。没法用工具评估的开放任务再用模型打分而且要给出评分理由避免拍脑袋打 1-10 分。另一个实用的技巧是候选生成时要求多样性。如果 k 个候选都是同一个思路的微小变体搜索树的信息增益就很小。我在提示词里明确写“请给出 N 种策略差异明显的方案”或者干脆给候选生成阶段注入不同的“启发式提示词”比如“请从时间最优的角度设计路径”与“请从成本最优的角度设计路径”效果会好很多。5.3 ToT 的实际成本与工程权衡必须坦诚说ToT 是这四种方案里 token 开销最大、最复杂的一种。每一步要生成 k 个候选还要给每个候选评分成本几乎是 CoT 的数倍。如果是带宽和成本敏感的生产任务我不建议全局使用 ToT更推荐的做法是先跑 CoT/ReAct 试一次失败了再进入 ToT 模式只在“难以一次到位”的推理节点开 ToT比如法律问题分析这种从多个维度权衡的场景而不是每个查询都树状展开。另外ToT 和“思维链 Self-Consistency”经常被混淆。Self-Consistency 是采样多条链投票取多数而 ToT 是有结构地搜索和回溯两者的差别本质是“并行多路投票”和“串行搜索 评估 回溯”的区别。如果搞混了工程实现上会南辕北辙。6. 四种决策模式的横向对比与实战选型建议6.1 一图看懂四个方案的核心差异特性ReActPlan-and-ExecuteReflexionTree of Thoughts核心机制推理与行动交替先规划后执行失败后反思重试搜索树 候选评估动态适应力高中需要 replan中高中受限于搜索宽度可解释性中轨迹长但透明高计划可见高反思可见中评估过程复杂Token 开销中中低中高最高实现复杂度低中中低高典型场景信息检索/工具调用任务流程固定/需进度展示代码生成/多轮试错逻辑难题/创意规划6.2 场景匹配你到底该用哪个从我踩过的坑出发我会按下面的思路来选型大多数工具调用型 Agent 任务ReAct 是首选简单直接框架成熟。当你发现失败率高时叠加 Reflexion而不是换掉 ReAct通常是成本最低的优化方式。长周期、流程明确的业务任务选 Plan-and-Execute。它能把任务拆成可独立验收的模块还方便对接人工审批环节比如“第2步预算审批通过后再继续”。代码生成与调试类任务ReAct Reflexion 组合非常强。先跑代码报错后反思再生成修正版这个循环在 HumanEval 之外的真实场景里也稳。复杂推理、策略设计类任务ToT 效果好但成本高。建议封装成“困难任务专用通道”只在普通路径失败之后启用。6.3 实战中反复踩过的五个坑最后梳理五个我在生产环境里亲身经历的问题每条都是真金白银换来的教训。坑一Prompt 格式不稳定导致解析失败。无论 ReAct 还是 ToT解析 Action、解析候选都必须容错。我的建议是不要把解析逻辑写得太死宁可先做一层正则归一化再做字段提取并对解析失败的情况给模型一个明确的反馈比如“你的 Action 格式不正确请输出工具[参数]格式再试”。这个反馈本身就构成了一个新的 Observation 信号。坑二死循环检测缺失。Agent 可能重复执行同一个失败动作而不自知。我在框架里加了去重检测如果某个 Action 的参数列表和上次完全相同且 Observation 也相同直接判定本轮无效强制 Agent 更换策略而不是让它继续空转。坑三上下文无限膨胀。ReAct 跑得越长上下文越大模型越容易“迷失”。解决方案是前面提到的轨迹摘要化。我实测过一个 20 步的轨迹压缩成 200 token 摘要后模型对目标的记忆保持度明显提升答案相关性也更好。坑四评估器代替了任务验证。ToT 里如果只用 LLM 打分评价分支很容易出现“高分错误分支”。最好给所有候选设计一个最小可验证关卡。比如代码任务先跑单测数据分析任务先检查数据流关键字段的完整性。验证关卡越硬树搜索越可靠。坑五忽略 Agent 安全边界。这一点很容易被忽视但非常重要。当 Agent 有了行动和工具调用能力后实际就是在替你做真实世界的操作——发 HTTP 请求、修改文件、调数据库。任何决策框架都必须有底层安全护栏工具的权限最小化、敏感操作人工确认、日志可审计、执行沙箱隔离。决策规划解决的是“做什么更聪明”安全护栏解决的是“做错事也不致命”两者缺一不可。6.4 我现在的工程范式先正交再组合经过多轮项目实践我目前的常态架构是基础执行走 ReAct失败自动触发 Reflect 提升长任务用 Plan-and-Execute 套壳超难推理节点定点启用 ToT。这不是一个“四选一”而是一个“正交组合”的架构。决策层按任务难度动态切换底层工具与记忆模块共用这样既控制了成本又保留了对复杂任务的兜底能力。每次给团队分享这段经验时我都会提醒一句话不要在概念里打转先拿一个真实任务把四种模式分别跑一遍记录 token 消耗、成功率、平均步数你对该用哪个的直觉自然就准了。决策规划的最终目的从来不是选定某个花哨框架而是让 Agent 在真实世界的混乱中找到一条高成功率的通路。
阅读完成 · 觉得有帮助?