从能聊到能干:Agent任务规划与Planner-Executor闭环
发布时间:2026/10/21 7:38:33 锦皓数字建站

做Agent 30课程开发时我最怕的就是“第7课魔咒”——前六课大家练得兴致勃勃到了第七课学生的Agent开始真正面对“多步骤任务”时突然变得又蠢又乱要么在一个子任务里反复打转要么把整段上下文塞进一次请求要么生成了一堆看起来很努力但根本不可执行的步骤。第7课恰好是整个30讲系列里“从能聊到能干”的分水岭。这篇文章想跟你聊聊我把这节课做成了一堂可落地的任务规划实操课从原理拆解、Planner-Executor最小闭环代码到课堂案例和评分标准全程都是自己备课时反复验证过的方案。如果你是正在设计Agent课程的人可以直接把这篇文章当成备课参考如果你是自学Agent开发的爱好者也能沿着这条路径走一遍知道当Agent面对复杂目标时究竟该往哪个方向使劲。1. 这门30讲课程走到第7课正好是“从能聊到能干”的分水岭1.1 前六课让Agent先会对话、会记忆、会调用工具Agent 30课程并不是从第1课就直接让学员写Agent。我把前六课设计成一组能力铺垫第一课讲Agent的总体认知第二课搭开发环境并跑通最基本的“提问-回答”循环第三课重点练提示词设计尤其是角色设定和输出格式控制第四课讲上下文窗口和对话记忆的取舍第五课进入工具调用让Agent能查询数据库、调计算器、搜文件第六课接入一个简单的知识库让Agent能基于外部资料回答定向问题。走到第六课结束时学生的Agent已经拥有三个核心零件能理解指令的大脑、能保存状态的工作台、能操作外部世界的几把工具。但真正的缺口也很明显面对“帮我写一份季度复盘报告”这种需要五六个动作串联的目标大多数学生的Agent会一股脑把所有资料塞进提示词或者一只工具反复乱调。缺的不是零件是“先想清楚分几步做”的规划层。于是第7课就出现了。1.2 第7课的教学目标一份可量化的任务规划能力第7课我给这节课起了个内部代号叫“从指令到路线”核心目标只有一个让学生掌握任务分解的基本模式并能在自己写的Agent里加入“规划-执行-反思”的最小闭环。为了不让学生觉得这里又是一堆抽象概念我把教学目标压缩成三条可验收的标准能把一个模糊的、多步骤的自然语言目标拆成3到6个边界清楚的子任务子任务之间有正确的依赖顺序而不是想到哪写到哪当某个子任务执行失败时Agent能基于失败信息调整计划而不是原地崩溃。设计课程时我特别有意避开“规划能力很强”这种不可评价的目标。课程验收时的作业里学生必须提交运行日志日志里能看到完整的任务列表、每步执行结果和失败后的再规划记录。没有日志一律视为无效作业。这门课教的不是纸面上的方法论而是能被日志证明的行为模式。2. 为什么任务规划是Agent从“玩具”走向“工具”的关键动作2.1 不做规划的Agent为什么会崩漫游式执行的三个副作用很多学生在前六课觉得Agent已经能用是因为他们的测试任务都只是单步任务查一个数据、回答一个问题、写一段文案。一旦任务变成“多步、多工具、有中间产物”的复合目标不做规划的Agent立刻暴露问题。第一个副作用是上下文失控。Agent如果不预先规划会习惯性地把所有可用资料、全部对话历史一起塞进模型很快就撞上上下文窗口的限制。就像一个人进超市不写购物清单只能把所有货架都逛一遍最后购物车塞到爆。第二个副作用是工具调用频率异常。没有规划的Agent会反复调用同一个工具来试探结果因为它不知道哪一步才是终点。我让学生做过实验让一个未加规划层的Agent整理一份用户反馈表格结果它在同一个查询接口上连续调用了13次每次只是换了个措辞问同样的问题。第三个副作用是结果不可复现。不规划的执行过程充满了随机性同样一个目标跑三次可能得到三条完全不同的路径。这对真实业务是无法接受的。规划层本质上是把执行路径“显式化”每一步做什么、输出什么、下一步依赖什么都写成结构化数据这样即使某一步出了问题也能定位到具体环节。2.2 任务分解的三种基础模式顺序、并行、条件分叉第7课里我把任务分解的模式总结成三种配着案例讲学生理解起来很快。顺序模式是最常见的。任务B必须用到任务A的结果所以步骤天然排成先后顺序。比如写季度复盘报告先得拉数据再分析数据最后写结论。这种模式的关键是“依赖关系优先不是习惯顺序优先”。并行模式适合那些互相不依赖的子任务。比如写一份产品调研报告可以同时查竞品信息、收集用户评价、整理内部数据。但在具体实现时我会提醒学生不要真的同时开好几个并发请求先学会把并行步骤“平铺”成列表后续再考虑并发优化。课程阶段追求的是逻辑清晰不是性能极致。条件分叉模式下后续步骤取决于前面某步的输出。负责人工审核的Agent收到一份表单如果表单里有高风险字段就走人工复核分支否则走自动通过分支。这种模式在手写规则的时代叫分支处理在Agent里变成规划层根据当前状态动态生成“下一步候选集合”。我用一个表格帮学生快速对齐三种模式模式适用场景必经条件典型反例顺序模式后一步依赖前一步的输出依赖关系明确先写结论再拉数据并行模式子任务之间无依赖结果可合并硬把相互纠缠的任务拆成并行条件分叉下一步取决于当前结果有可检测的分支状态把分叉当顺序执行导致反复走错2.3 规划粒度过粗则失控过细则低效讲任务分解时学生最常见的困惑是“一个子任务应该拆多细”。我给了一条经验性原则**一个子任务应该是一个人能独立完成、且完成后有明确产出物的动作单位。**拆到太细比如“先移动鼠标再打开文件”会让规划器陷入执行细节步骤列表动辄十几行反而拖垮效率拆到太粗比如“写报告”作为一个步骤执行时模型根本不知道从哪入手等同于没规划。实操中我用的检验方法叫“三分钟法则”如果Executior执行这个子任务时预计在三次工具调用以内能完成并产生中间结果这个粒度就是合理的。超过三次调用说明这个步骤还有继续拆分的空间。这个法则听起来不够精确但它特别好用尤其给基础薄弱的学生当判断标准时比任何泛泛而谈都有指导意义。课时里我还会拿几个不同的任务当场拆一遍让学生自己感受粗与细的边界。3. 最小可用规划闭环我用二三百行Python带学生搭出Planner-Executor3.1 数据结构设计从哪里开始记录规划状态这一节是第7课的动手核心。我要求学生不依赖任何现成的Agent编排框架直接用Python标准库写一个最小的规划闭环。原因很简单一旦用了重框架很多关键机制就被封装在黑盒里学生根本看不出规划状态的流转过程。自己写二三百行代码才能撞见真实的问题。我先带学生定义三个核心数据结构。第一个叫Step代表一个子任务from dataclasses import dataclass, field from typing import List, Optional dataclass class Step: id: str description: str status: str pending # pending / running / done / failed result: Optional[str] None retries: int 0第二个是Plan它就是整个任务的“路线图”dataclass class Plan: goal: str steps: List[Step] field(default_factorylist) current_index: int 0 def next_pending_step(self) - Optional[Step]: for step in self.steps: if step.status pending: step.status running return step return None def is_finished(self) - bool: return all(s.status done for s in self.steps)不要小看这个极度简化的结构它解决了一个关键问题Agent终于有了“当前做到哪一步”的显式记忆。过去学生的Agent一进入多轮执行就像没有书签的浏览器刷新一下又回到起点。有了Plan对象每一步的进度、结果、状态都落在确定的数据结构里。3.2 规划、执行、反思的三个循环函数有了数据结构下一步是规划器。我的方案很直接先让LLM根据目标生成步骤列表。课程里我不纠结于把规划做得又花哨又复杂而是聚焦在有步骤列表之后随后要如何执行、如何反馈。def plan_goal(goal: str, llm_func) - List[dict]: prompt ( 请把以下目标拆解为3到6个可执行的子任务。\n f目标{goal}\n 输出JSON数组每个元素包含description字段即可不要输出任何多余内容。 ) raw llm_func(prompt) # 这里要写一个容错解析函数把LLM输出的JSON解析成列表 steps_data parse_llm_json_array(raw) return [ {id: fstep_{i}, description: item[description]} for i, item in enumerate(steps_data) ]这段代码里注释提到的parse_llm_json_array是我专门设计的一个容错函数。因为LLM不总是乖乖输出纯JSON它会多写一段“以下是结果”。这个函数要能剥离多余文字、提取JSON块甚至修复少一个括号的常见错误。我特意把这个函数当做一个小的课堂挑战让学生自己先写再对照我的实现。这个环节非常考验细节经常有学生漏掉“从JSON块开始截取”的逻辑结果解析一直报错。执行循环的第2版是这一课里最重要的部分def run_pipeline(goal: str, llm_func, tools, max_retries2) - Plan: step_dicts plan_goal(goal, llm_func) plan Plan(goalgoal, steps[Step(**s) for s in step_dicts]) while not plan.is_finished(): step plan.next_pending_step() if step is None: break print(f 执行 {step.id}: {step.description}) prompt ( 你是执行器。请完成这个子任务\n f{step.description}\n 你可以调用以下工具并输出工具名称和参数。如果任务已完成请输出【完成】。 ) response llm_func(prompt) result execute_tool_response(response, tools) if result.get(success): step.status done step.result result.get(output) else: step.retries 1 if step.retries max_retries: step.status failed # 触发反思重规划 new_steps replan(goal, plan, llm_func) if new_steps: plan.steps new_steps else: # 允许换个措辞重试一次 retry_prompt f刚才的执行结果是{result}\n请换一种方法重新调用工具。 retry_response llm_func(retry_prompt) retry_result execute_tool_response(retry_response, tools) # ... 同样的流程 return plan这版代码刻意写得“朴素”它没有令人炫目的技巧但我发现正是这种朴素让学生的思路特别清楚一个循环四个状态失败就重试重试超过阈值就重新规划。课堂里80%的学生动手后能自己复现出类似的逻辑这说明它的设计是合理的。3.3 让Agent在失败后自主修正的“回退缓冲”接下来是“反思机制”。这一个机制是第7课远远超出其余内容的地方——Agent的规划层级能不能真正解决失败问题靠的往往是这里的设计。初始方案里一整个失败就触发“全盘重新规划”这看起来合理但在实操中灾难性。因为重新规划往往会推翻前面已完成的子任务导致同样的步骤反复重做。真正的解法是做局部回退把失败点之前三步都标记为“需复核”让Agent只针对这段区域重试或重规划。我把它称作“回退缓冲”这个概念来自版本控制里的回滚思想。失败不意味着全盘皆输只意味着某个局部假设不成立。回退缓冲的实现可以简单到给Plan增加一个checkpoint字段不断记录已成功步骤的摘要。当第N步失败时把current_index调整到max(0, N-3)并清空这三个Step的结果再让Planner基于这些Step的失败原因重新生成更精细的替换步骤。这个机制我单独用一个课时后补段来讲因为学生会发现Agent的成功率明显提升。原来一次全盘重规划能救回30%的失败任务回退缓冲能把救回率拉到75%以上。学生在日志里看到同一段失败任务被局部修复后继续执行完整流程通常才会真正理解“规划的粒度”是什么意思。3.4 一次完整运行示例和中断条件为了让学生对“什么算跑通”有清晰感知我准备了一个迷你演示。目标设定为“统计目录下所有日志文件中的错误行数”并限制工具只有两个list_files和count_lines。规划器生成步骤列出目录下所有.log文件读取每个文件的错误关键字行数汇总得到总数。执行器按步执行日志会输出每一步的进度。如果第二步写错工具参数会触发重试两次后走回退缓冲重新生成针对该文件的拆分步骤。课堂演示时我会故意让一个工具降速到5秒响应让学生看到“等待”和“卡死”之间的区别并解释为什么要给工具调用设置超时上限以及如何用超时作为失败条件。中断条件是必须讲的不是所有任务都该无限滚动下去。我规定三个硬性退出条件——所有步骤完成超过最大重试次数累计调用模型次数超过50次。第三种退出是为了防止学生在实验时烧掉太多调用预算也是逼他们写出更高效的规划而不是让Agent随机漫游到天荒地老。4. 课堂实操案例用规划Agent完成一次季度复盘报告4.1 需求拆解把一句话目标变成可执行的任务树第7课的实操案例选得很考究一个复杂到需要规划、但又贴近办公场景的任务——生成季度项目复盘报告。目标描述故意模糊“根据团队最近的开发记录生成一份季度复盘说明进展、问题和我下一步计划。”这个案例能炸出所有规划问题就是因为目标里出现了三个级别的任务读取记录、分析并决策、写文档顺序。学生刚拿到题时很多人试图一次提示词解决全部跑出来就是一篇毫无数据支撑的空话。我把拆解后的参考版本以任务树的形式贴在课堂文档里让学生对着改任务生成季度项目复盘报告 ├─ 1. 收集本季度关键记录 │ ├─ 1.1 扫描本地提交记录统计合并次数 │ └─ 1.2 汇总里程碑完成状态 ├─ 2. 数据分析 │ ├─ 2.1 识别进展亮点完成率、平均耗时 │ └─ 2.2 识别风险点延期、未关闭问题 ├─ 3. 生成报告大纲 │ └─ 3.1 确定结构进展-问题-计划 ├─ 4. 分块撰写正文 │ ├─ 4.1 写进展部分 │ ├─ 4.2 写问题部分 │ └─ 4.3 写下一步计划 └─ 5. 整体校验 ├─ 5.1 核对数据一致性 └─ 5.2 统一语气与格式这个任务树的妙处在于每个叶子节点都对应一个或几个工具调用而且层级分明。学生照着这个结构写Plan代码时基本不会出现上下文塞爆的问题因为每一阶段只需要关注局部产出的结果摘要。4.2 学生作业里反复出现的三个典型问题批改第7课作业时我发现学生的规划Agent往往会犯三类典型错误而且它们几乎每次都出现。第一类是“把工具当秘书”。学生给规划器设计了太多无意义的工具调用比如把“分析数据”也定义成一个工具但工具本身只是把参数直接传给LLM并没有真正执行任何数据分析动作。这种设计让步骤列表看起来丰富执行质量却极差。我的建议是工具必须有真实的外部动作否则就不叫工具叫花架子。第二类是“中间产物不落地”。很多学生的步骤执行完只是把返回的结果打印出来并不记录到Step.result里导致下一步执行器面对一片空白。用一个内存变量缓存中间结果是提高多步Agent稳定性的重要习惯。第三类是“失败后只会换提示词重试不会换个解”。如果工具返回“未找到文件”重试时系统提示词再怎么说“认真找”也改变不了文件不存在的现实。真正该做的是回到规划层把“查找文件”改成“查找同名文件或询问用户”。我在代码演示里特意放了一个这种失败日志让学生看到重试和重新规划的分界线在哪里。4.3 为什么这个案例适合作为第7课的课后作业季度复盘案例能火过整个课有一个原因它的产出物是文本报告学生可以直观对比“有规划”和“没规划”的质量差距。没有规划层的Agent跑出来的报告结构松散数据错位前后观点矛盾有规划层的Agent即便代码写得一般报告至少是从分块产物拼接而来的逻辑清晰得多。我还给这个案例设计了一个可验证的断言报告里出现的每个数字都必须能在中间步骤结果里找到来源。学生写完作业自己先用代码脚本检查一遍再提交。这个约束保证了规划闭环真正起了作用——事实来源可追溯而这一点正是Agent从“会聊天”变成“能交差”的重要标记。5. 评估一道规划题做得好不好四个维度与课程评分标准5.1 覆盖度、顺序合理性、资源约束、风险兜底给学生的作业打分我不用“看上去不错”这种主观标准而是固定看四个维度。每个维度又都有明确的不合格表现与合格表现。维度考察点不合格表现合格表现覆盖度目标涉及的关键动作是否齐全明显遗漏步骤比如忘了数据来源核心步骤无遗漏叶子节点可执行顺序合理性步骤之间的依赖关系是否可靠先写正文后拉数据或并行依赖有依赖关系的步骤严格先后执行资源约束是否考虑上下文与调用成本一次把所有记录塞进单次请求分块处理只传摘要与结论风险兜底失败后的处理方案是否完备失败即崩溃或反复重试同一操作有重试阈值有局部回退方案每项20分总分80分剩余20分看运行日志质量即步骤状态的流转有没有完整记录、失败是否可回溯。这个评分标准第5课就发给学生了所以第7课没有人来问“老师怎样才能拿高分”因为标准已经很明确。5.2 一套可复制的课堂验收模板我把课堂验收做成一个四层追问模板学生写完作业后要先对照模板自查再提交。第一层追问目标是单一还是复合如果目标是复合的有没有形成步骤列表第二层追问每个步骤是否有明确的成功标准比如“拉数据”成功不等于拿到数据而是拿到且格式正确、字段完整。第三层追问失败后怎么办有没有触发重试或回退还是直接丢弃结果继续往下走第四层追问最终结果能否串联到最初的原始目标中间产物是否能作为证据保留这四个问题基本上过滤掉所有“假装做了规划”的代码。我还开玩笑地跟学生说你要是能把这四个问题答清楚以后面试讲项目经验都多了一份底气。Agent规划能力没有面试题库但这套追问模板就是最接近实战的考察清单。6. 备课中的经验黑白名单哪些设计有效哪些老师容易想偏6.1 有效设计小步快跑、样例驱动、留半成品第7课备课时我第一次试讲就发现如果只讲原理不给代码学生听完觉得懂了一到作业就崩。后来改成“原理一段、代码一段、跑通再走下一段”的小步快跑结构吸收效果明显好转。每一小节控制在20分钟以内配套一个十几行的代码片段节奏不会拖沓。样例驱动也特别重要。我给每个任务分解模式都配了两个对比样例一个错误版本一个正确版本。错误版本的规划日志真实来自某学生作业我会先把日志投到屏幕上让学生一起“诊断”看问题出在哪一步。这个过程很费时间但课堂效果非常好因为学生会突然发现原来自己的Agent也有同样问题不是只有他一个人踩坑。留半成品也是一招。我不把第7课的关键代码全部给全而是刻意少给replan和retry那段的细节让学生自己补齐。半成品式教学能防止学生“抄完即走”逼着他们理解循环控制流。不过代价是课堂节奏变缓花在答疑上的时间增加需要预留一些弹性区间。6.2 三种容易想偏的认知误区作为备课复盘我还想记录下来第一次把自己也给困住的三个误区。第一个误区是“把规划当成高级提示词”。刚开始我设计了超级长的规划提示模板里面塞满角色说明、输出格式、示例。结果学生稍一微调目标规划质量瞬间崩掉。后来我简化了规划提示只保留结构约束效果反而稳定。第二个误区是“跳过执行层只练规划层”。我一度觉得任务分解本身才是第7课的重点于是专门准备了一整节纯提示词练习。但作业统计显示只练规划不改执行学生的整体系统能力并没有明显提升。后来才想明白规划的质量只有在执行时才能被真正检验规划和执行必须作为闭环一起练分开练都是空中楼阁。第三个误区是“让Agent一次生成所有内容”。有学生在作业里让规划器负责大段报告的生成一步就跨过了“分块写入”的边界。这种做法的结果跟没有规划几乎一样因为单步生成的上下文负担和漫游式执行并无本质差异。在第7课的课堂里我会多次强调规划的作用是把大问题切成一串小问题而不是把小问题强行合成一个大问题。再回头看第7课之所以在整个30讲里卡在“能聊到能干”的这个位置是因为它第一次要求学生的代码在结构上同时承担判断、执行、记忆、纠错四种职责。规划是整个系统的“脊椎”没有它工具再多也只是堆零件。而通过一套能跑、可验证、可复盘的最小闭环每个学生都能在运行日志里亲眼看到自己的Agent从一团乱麻变成逐步收敛的执行序列这个过程本身就比任何理论讲解都更有说服力。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。