AI Agent规划实战:从任务拆解到动态重规划的工程指南

发布时间:2026/10/20 11:56:02
AI Agent规划实战:从任务拆解到动态重规划的工程指南
聊 AI Agent十个里有九个绕不开 Planning。但你要是追一句“你的规划具体是怎么做的”不少人的回答就飘了——要么把任务拆解当成规划要么觉得让模型一步一步想就是规划。说实话我一开始也是这么理解的直到在真实项目里翻了几次车才把“Planning 到底意味着什么”这件事真正想明白。这篇文章就站在一个踩过坑的人的角度把真正的 Agent 规划掰开揉碎讲清楚它和任务拆解有什么区别、在 Agent 里该以什么形态存在、一个能落地的 Planner 要怎么设计以及我在实操中反复踩过的那些坑。内容适合正在做多步骤 Agent、复杂工作流或者单纯想搞清楚“规划层到底在解决什么问题”的开发者。1. 先把话说清Planning 到底在解决什么问题1.1 任务拆解是把任务“切开”Planning 是把切好的部分“排好”先分清两个概念任务拆解Task Decomposition和规划Planning经常被混着用但它们完全是两码事。拆解是把一个大目标拆成若干子任务比如“做一顿三菜一汤”可以拆成“买菜、洗菜、切菜、炒菜、煮汤、上桌”“做一次房价分析”可以拆成“数据获取、数据清洗、特征工程、建模、可视化”。拆解是静态的它输出的是一份待办清单不体现先后顺序、依赖关系、资源约束。规划则是在这些子任务之间建立执行关系哪些必须在前面哪些可以并行哪些要等前一步的结果什么条件满足之后才能进入下一步。还用做饭举例真会做饭的人不会先炒菜再煮汤因为汤要熬很久也不会把所有菜都切好了才开始烧因为灶台就一个你得统筹。这个“先炖汤、趁炖汤的时间洗菜切菜、最后再爆炒”的过程就是规划。对 Agent 来说这个区别同样致命。只做拆解不规划Agent 大概率会机械地按模型输出顺序执行而模型输出的顺序未必是正确顺序。我见过不少 Agent 项目第一版规划就是简单让模型“列出步骤”结果频繁出现先调分析接口再取数、先查天气再定位城市、先发邮件再生成附件。步骤之间没有门控、没有串并行关系、没有资源约束任务越复杂越乱。1.2 从“一问一答”到“先谋后动”规划的三个真正目标再往深一层看。普通大模型对话是“用户的 query 进来模型给一个 answer 出去”它不需要对多步任务负责。Agent 不一样Agent 接到的往往是一个需要多轮动作的目标中间要调工具、查数据、做判断最后还得交结果。这时候如果每一步都临时想会遇到一个很现实的问题局部最优不等于全局最优。举个例子假设任务是“整理三个来源的数据生成一份对比报告”。走一步看一步的做法是先读 A 来源生成一段分析再读 B 来源发现格式不一样回头处理格式再读 C 来源发现缺失字段又回去补。整个过程像没看地图就开车每个路口才决定往哪拐最后绕了一大圈。而真正的规划是在开工前先看全貌三个来源各有什么字段、要统一成什么结构、清洗规则是什么、对比维度有哪些、报告用什么结构输出。执行过程中虽然也会遇到意外但大方向是定好的。所以 Planning 的第一个目标是“先谋后动”。第二个目标更重要给每一步设定前置条件和输出标准。规划不只是一张步骤清单更是一份“知道什么时候可以进入下一步”的契约。第三个目标是留出重规划的口子计划赶不上变化时要能局部修正而不是推倒重来或者硬着头皮走完。这三个目标——全局路径、条件门控、动态修正——合在一起才算把 Planning 讲完整。2. 规划在 Agent 里的三种常见姿态讲规划不能脱离 Agent 的整体架构。市面上常见的姿态有三种我各说一下它们的运行方式、优点和坑。2.1 ReAct 风格边想边做规划藏在推理里ReAct 是 Reasoning Acting 的缩写核心循环是模型观察当前状态输出一个想法决定一个动作拿到结果后再观察、再想、再做直到任务完成。这个思路非常流行因为它用最简单的方式让模型接上了外部工具。严格说 ReAct 不算显式规划它的“计划”内隐在思考过程里走一步算一步。优点是实现成本极低也适合任务比较轻、分支少的场景。比如单轮调用一个工具、查一次信息、算一道题ReAct 完全够用。缺点是任务一复杂“近视”问题就暴露出来了。我自己做一个多轮信息聚合 Agent 时发现纯 ReAct 风格的 Agent 经常执行到第三步就忘了最初目标或者在两个子任务之间来回横跳上下文里塞满中间过程真正有用的信息反而被淹没。它不是不能用而是做长链路任务时你得在记忆设计上花很大力气给它兜底。如果让我选长任务我基本不会只用纯 ReAct。2.2 Plan-and-Execute先把计划摆上台面再按计划执行Plan-and-Execute 的思路更符合直觉启动时先用一个 Planner 模块生成完整计划这个计划不是一句话而是有序列表或带依赖关系的任务图然后 Executor 按计划逐个执行每完成一步记录结果必要时回到 Planner 更新剩余步骤。它和 ReAct 最大的区别是“谋”和“动”分开了。Planner 负责逻辑推演Executor 负责行动。分开有什么好处第一执行前你就能检查计划本身省掉大量无效动作第二Planner 的上下文更干净不需要塞入执行过程里的海量噪声它只需要知道目标、已知条件和剩余步骤第三出问题时能定位是哪一环的问题是计划错了还是执行错了。代价也很明显第一步规划需要额外时间Plan 一长用户能明显感到“卡”了一下。还有如果计划生成质量不行后续执行会处处碰壁。我就见过一个极端例子Planner 凭空规划出一个“获取某系统用户列表”的步骤可那套系统根本没有这个接口执行器到那一步直接卡死。所以 Plan-and-Execute 对 Planner 的质量要求更高通常得给它明确的工具清单防止模型幻觉出不存在的工具能力。2.3 分层规划与动态重规划复杂任务的正解再往上就是分层规划和动态重规划。分层规划是把问题放到不同抽象层级思考顶层只规划“先做 A、再做 B、最后做 C”每个大步骤下面再展开自己的子步骤。好处是每个规划层的输出短上下文压力小而且高层计划稳定不会因为底层细节变化就全盘推翻。动态重规划则是把“计划是死的世界是活的”纳入设计。执行完一步如果发现结果和预期偏差大就触发一次重规划拿当前状态和剩余步骤重新生成新计划。注意不是重跑整个任务只修正剩余部分。这两者经常组合使用。一个比较稳的架构长这样顶层维护一个大计划大计划里的每一项挂一个子计划子计划指导具体执行执行结果进到一个状态追踪器状态追踪器发现前置条件不满足或结果异常就把信号反馈给顶层 Planner只更新后续步骤。这样既避免过度规划的浪费也避免计划僵化带来的失败。下面这张表总结一下三种姿态的适用边界架构姿态规划方式优势主要风险适合场景ReAct隐式边想边做实现简单响应快长任务容易迷失目标短链路、少工具调用Plan-and-Execute显式先计划后执行可解释、可检查首步延迟高计划易幻觉多步骤、多工具、流程相对稳定分层 动态重规划多层级 局部修正抗变化上下文可控实现复杂度高长链路、高不确定性的复杂任务3. 一个能落地的 Planner 是怎么设计出来的接下来进实操。给 Agent 加规划层不是简单在系统提示词里写一句“请先规划再执行”。我在实践中把 Planner 的设计拆成了四件事输入目标定义、输出格式约束、依赖推断与编排、重规划触发逻辑。3.1 输入先让目标变得可校验规划的第一步不是让模型立刻列步骤而是先给它一个“高质量目标”。什么叫高质量目标至少包含三件事一是任务要达成的最终状态也就是完成之后你拿什么来验证二是已知约束比如只能用哪些工具、哪些数据源、时间预算、不能做的事三是用户可接受的交付形式。我踩过的坑是Planner 的输入太随意把用户的自然语言直接丢进去。比如用户说“把这几份文档里的关键信息整理出来”Planner 直接规划“提取关键信息、生成报告”看起来没毛病但执行到一半才发现用户想要的是表格不是报告或者用户其实希望先做去重再提取。所以我会在 Planner 前面加一个小模块就叫“目标澄清器”哪怕只是让模型复述一遍目标再提取关键约束为结构化字段后续规划的准确率都会明显提升。这个澄清器不必复杂让它输出几个字段就行goal最终目标、constraints约束列表、output_format交付形式、known_steps用户已经明确要求的步骤。关键是让这些字段成为后续 Planner 和 Executor 共享的上下文。注意目标字段不是写给人看的是写给执行系统用的。如果 output_format 里写“报告”最好再补一句“报告为 Markdown包含对比表格和结论段落”越具体越好。3.2 输出用结构化格式锁住规划的“形状”第二个关键点是规划结果的表示形式。自然语言段落是最差的规划输出格式不好解析、不好校验还容易有歧义。建议用 JSON 数组或 YAML 承载每一条计划项。一条计划项至少要有这些字段id步骤编号、name动作描述、type是动作还是判断、depends_on依赖哪些前置步骤用 id 引用、tool_needed要调用什么工具没有就写 none、verification步骤完成后判断成功的标准、statuspending / in_progress / done / failed。用 depends_on 表达顺序而不是只给一个步骤序号。这一步非常关键。depends_on 写进依赖关系之后执行器才能真正做拓扑排序和并行判断。如果只有编号顺序规划出来的步骤永远是一条直线既不能表达并行也不能表达“宁可晚点做也不能提前做”的门控关系。我在 Prompt 里会给 Planner 一个明确的输出示例。下面是一个简化版[ { id: 1, name: 读取并解析数据文件, type: action, depends_on: [], tool: file_reader, verification: 成功解析出结构化字段字段列表与目标一致 }, { id: 2, name: 清洗缺失值和重复记录, type: action, depends_on: [1], tool: data_cleaner, verification: 无重复记录关键字段缺失率低于 5% }, { id: 3, name: 核对清洗结果是否符合预期, type: condition, depends_on: [2], tool: none, verification: 如果清洗率异常标记为需要重规划 } ]不是所有 Planner 都必须输出这么细但至少要包含“依赖”和“验证标准”两个字段。有依赖才能做并行调度和控制顺序有验证标准才能发现“看起来做了其实没做好”的情况。3.3 依赖推断与并行编排计划与执行之间的桥梁Planner 输出带依赖的任务图之后执行器要做的第一件事不是盲目执行而是先检查任务图有没有环、有没有孤立节点、有没有引用了不存在的 id、有没有某节点需要调用的工具根本不在可用清单里。这个检查人类做很容易但模型经常出错。尤其计划一长它可能把步骤 8 的验证标准写错或者让步骤 7 依赖步骤 12而步骤 12 根本不在计划里。检查通过后做调度。可以先做一层“层级划分”没有依赖的步骤归为第一层只依赖第一层步骤的归为第二层依此类推。执行时既可以按层并行也可以串行。对多数 Agent 场景我不建议一上来就搞复杂调度器先把拓扑序跑对再在每一层内按资源许可做并行已经能解决绝大多数效率问题。另外一个我后来才意识到的细节verification 要尽量客观、可程序化。比如“文件存在且大于多少字节”“返回状态码是 200”“解析出的记录数大于 0”。如果验证标准写得模棱两可比如“结果合理”执行器没法判断最后还是靠模型猜等于没写。验证标准越接近断言Agent 越容易知道该不该进入下一步。4. 实操记录给一个多工具任务 Agent 加上规划层这一节用一个我最近做的模拟项目来演示。背景是做一个跨系统的信息汇总 Agent用户一句话进来它能自动从几个来源收集信息、合并去重、生成对比分析。第一版没有规划层第二版加了 Planner跑出来的差异非常直观。4.1 场景设计一个跨系统信息整理任务任务描述是这样的“帮我收集三个系统里关于某项目的所有关联信息去除重复内容统一字段格式最终产出一份 Markdown 格式的对比分析报告。”第一版无规划的 Agent 拿到任务后按自然反应开始先调第一个系统的搜索接口拿到结果后直接提炼摘要然后调第二个系统发现字段名和第一个不一样又回头做字段映射再调第三个系统时发现数据量很大上下文已经快放不下只能草草结束。结果虽然出来了但过程混乱中间还漏掉一个关键数据源。第二版有规划先让 Planner 输出一份计划。计划大致是1确认三个系统的数据接口和权限2分别拉取数据落成统一中间格式3合并去重4确定对比维度5生成 Markdown 报告。这份计划看起来简单但注意第 2 步是带并行语义的三个系统的数据获取可以并发进行而不是一个完了再到下一个。第 4 步依赖第 3 步而且验证标准写的是“对比维度至少包含 3 个且每个维度都有三份数据可对照”。有了这份计划执行就清晰多了。4.2 Planner 与执行器的代码骨架下面给一个简化伪代码骨架重点不是完整实现而是展示规划层和执行层怎么配合。def plan_and_execute(user_request, tools): goals clarify_goal(user_request) # 目标澄清 plan planner(goals, available_toolstools) # 生成带 depends_on 的计划 validate_plan(plan) # 查环、查孤立节点、查非法工具引用 scheduler TopologicalScheduler(plan) # 按依赖关系生成可执行序列 context {} while scheduler.has_next(): step scheduler.next() if step.verification_met(context) or reconsider(step, context): result execute_step(step, context, tools) context[step.id] result scheduler.mark_done(step.id) else: plan replan(remaining_plan(), current_statecontext, goalgoals) scheduler TopologicalScheduler(plan) return finalize_output(context, goals.output_format)骨架里有几个动作值得展开说。validate_plan 我会用很简单的规则排除问题遍历所有 depends_on如果依赖了不存在的 id就把这条计划项标记为重规划候选用拓扑排序检查环排不出来就说明有环。reconsider 这个函数是我后来加的有些步骤虽然 verification 没过但失败原因无伤大雅比如字段名略有差异执行器可以判断“继续执行对后续影响不大”修正一下接着跑真正会把后续步骤带偏的失败才走 replan。replan 也不需要整个计划重来。多数时候我会把已完成步骤的结果压进当前上下文然后让 Planner 只看“剩余步骤 当前状态”重新生成后续计划。这样快也稳还能避免计划反复横跳。4.3 实测对比同一任务有规划和没规划的区别同一个任务在两版 Agent 上的差异主要体现在三个方面。第一动作次数。无规划版本一共调用了 11 次工具其中至少 4 次是无效调用回头补字段映射、重新搜索、重复拉取某个数据集。有规划版本总共 7 次调用6 次对应计划项1 次是中间修正。第二上下文占用。无规划版本因为反复回头最终上下文 token 数大约是有规划版本的两倍到第三阶段已经出现“信息淹没”——模型开始找不到最早拿到的关键数据。第三结果质量。有规划版本的报告明确覆盖了四个对比维度无规划版本只覆盖了两个维度还有一个维度因为数据源没拉全而空缺。这个对比让我想明白一件事规划层的价值不只是“少走弯路”更在于执行结果更可控因为你始终知道当前在执行计划里的哪一步。可观测性对 Agent 来说太重要了出了问题你能定位而不是面对一团乱麻。5. 高频翻车现场与排查经验再好的规划设计落地时也会遇到各种问题。这里把我反复踩过的坑整理成一份速查帮你少走弯路。5.1 规划得过于完美执行时处处碰壁这是最典型的问题。Planner 生成的计划看着非常完整步骤里全是“获取 xx 数据”“清洗 xx 内容”但执行器每一步都发现现实和预期对不上数据源没权限、接口换了返回结构、某个字段根本不存在。于是执行器频繁触发重规划Agent 反而比没规划时更慢。我现在的对策是在 Planner 的 Prompt 里明确写一句“你只能使用工具清单里的能力不要假设任何未经验证的前提”。同时如果规划时无法确定某个系统是否有某个接口把“确认接口可用性”本身当成一个早期步骤而不是假设它存在。规划里宁可先放验证类步骤也不要把整个计划建立在幻觉上。5.2 任务树太深上下文还是爆了分层规划缓解了“树深”的生成压力但没解决“树深带来的信息量”。我见过一个 Agent 计划嵌套到第五层每一层执行结果都往上下文里塞最后上下文爆炸早期关键信息被截断或遗忘。对策有三个。一是强制每一层只保留一个关键结论把中间原始结果写进本地或外置存储不全部留在上下文二是限制计划树深度超过三层的任务强制“先执行完一层再展开下一层”而不是一次性把所有细节都生成出来三是在每轮执行后做上下文压缩把已完成步骤的详细过程替换成摘要。5.3 循环依赖和“鬼打墙”式重规划循环依赖主要有两种来源。一种是 Planner 自己写错步骤 A 依赖 B、B 又依赖 A拓扑检查没做的话执行器会永远等不到能执行的节点。另一种更隐蔽重规划时新计划又把已经失败的步骤原样包含进去执行器跑一轮又失败再重规划来回几次就像“鬼打墙”。循环依赖靠 validate 能挡住大半挡不住的是第二种。我在重规划逻辑里加了一条规则同一个步骤 id 如果连续两次执行失败下一次必须换一种方式执行否则直接标记为“放弃”把原因写进最终报告不再无限重试。这个止损规则看着简单实际运行中省下的时间非常可观。5.4 评测规划质量的一个土办法最后聊怎么衡量规划好不好。严格的做法是人工标注计划质量但日常迭代时我常用一个更快的手段把每个计划的执行结果和计划项逐条对照算“计划覆盖率”。覆盖率等于“执行后真正完成、且结果验证通过的计划项数量”除以“计划项总数”。再配合一个“无效动作率”也就是执行过程中有多少动作不属于当前计划或与最终目标无关。这两个指标不用算得很精确就能帮你快速判断改动方向。覆盖率低说明计划生成有毛病无效动作率高说明执行层控制不到位或者重规划太频繁。我每次改 Planner 的 Prompt 或调度逻辑都会拿同一组测试任务跑一遍对比这两个数字改动效果立刻能看出来。我个人体会最深的一点是规划不是 Agent 里一个孤立的模块它和记忆、工具选择、结果验证是一整套联动。很多团队把规划做成“仅仅让模型多输出几步”然后抱怨没效果其实是因为其他配套没跟上。如果你正准备给 Agent 加规划层我建议从最轻量的 Plan-and-Execute 开始先把依赖关系和验证标准写进计划里再逐步引入重规划。别一上来就搞全自动复杂调度先让计划可解释、可回放、可调整这个底子打好了后面再上强度都来得及。