【手搓 AI Agent 从 0 到 1】第八课:规划——让 Agent 先想后做
📌前置知识:已完成第一课至第七课
🎯本课目标:让 AI 在动手之前先生成执行计划,把复杂任务拆解为有序步骤
💡核心概念:规划与执行分离 / 步骤排序 / 计划验证 / 计划即数据
前言
前七课,Agent 可以做决策、调工具、循环执行、记住信息了。
但有一个问题——
# 第六课的 Agent Loopagent.run_loop("帮我写一个网页爬虫,抓取新闻标题,按日期分类,存入数据库",max_steps=5)Agent 会怎么做?直接开始行动。
它可能在步骤 1 说"我要分析需求",步骤 2 说"我要研究爬虫技术",步骤 3 说"我要开始写代码"……每一步都是"当场想"的,没有提前规划。
就像你让一个新人"帮我策划一场发布会"。理想情况下,他应该先列个计划:场地、嘉宾、流程、预算、时间线。但实际上他可能直接冲出去定酒店了——因为没想好整体方案。
这就是第七课 Agent 的现状:能干活,但不会先想。
本课要解决这个问题。给 Agent 加上规划能力——在动手之前先想好步骤,然后按计划执行。
一、规划 vs 执行:两件完全不同的事
1.1 什么是规划?
规划就是把目标拆成步骤。
用户给一个目标:“写一篇关于 AI Agent 的技术博客”。
规划的结果就是一个步骤列表:
{"steps":["研究 AI Agent 的最新进展和核心概念","确定文章结构和大纲","撰写初稿内容","审校修改并优化表达"]}就这么简单。不需要甘特图,不需要依赖图,不需要关键路径算法。就是一个有序的步骤列表。
1.2 为什么要分离规划和执行?
你可能会问:第六课的 Agent Loop 不是已经在"一步步做"了吗?为什么还要单独的规划阶段?
关键区别:Agent Loop 的每一步是"当场想"的,规划是"提前想好的"。
第六课(无规划): 步骤1:AI 现场决定做什么 → 执行 步骤2:AI 现场决定做什么 → 执行 步骤3:AI 现场决定做什么 → 执行 每步都是即兴发挥,没有全局视角 第八课(有规划): 目标 → AI 生成完整计划(4个步骤) ↓ 审查计划 → 确认可行 ↓ 按计划逐步执行 → 完成 每步都有明确方向,知道自己在哪分离规划与执行有几个好处:
| 好处 | 说明 |
|---|---|
| 可审查 | 执行前能看到 Agent 打算做什么,不合理可以改 |
| 可复用 | 相似的目标可以复用同一个计划 |
| 可调试 | 规划出了问题可以只修规划,不用重跑执行 |
| 可优化 | 可以先评估计划的质量,再决定是否执行 |
核心洞察:规划不是思考。规划是数据生成。
计划就是一个 JSON 数据结构。你可以打印它、检查它、修改它。它和 AI 的"推理"无关——就是第三课的结构化输出能力,换个场景用而已。
二、规划系统要解决什么?
一个规划系统,最简版只需要回答三个问题:
2.1 怎么生成计划?
用 LLM。给它一个目标,让它返回一个步骤列表。
# 和第三课、第五课、第六课一样的思路prompt=f"为以下目标制定一个步骤计划:{goal},只返回 JSON"response=llm.generate(prompt)plan=extract_json_from_text(response)你发现了吗?生成计划和生成工具调用、生成动作决策,用的是同一套模式——LLM + JSON +extract_json_from_text()。
这不是巧合。Agent 系统里很多能力,底层都是同一件事:让 LLM 生成结构化输出,然后你的代码来处理。
2.2 怎么验证计划?
LLM 可能返回各种奇怪的格式:
// 没有 steps 字段{"plan":"先研究,再写,最后修改"}// steps 不是数组{"steps":"研究→写→修改"}// 空计划{"steps":[]}所以生成之后必须验证。检查三件事:
plan不为空- 包含
"steps"字段 "steps"是一个非空列表
ifplanand"steps"inplanandisinstance(plan["steps"],list):returnplan# 验证通过加上重试 3 次——又是老规矩。
2.3 怎么执行计划?
最简单的方式:按顺序遍历步骤列表,逐个执行。
forstepinplan["steps"]:# 执行这个步骤result=execute_step(step)results.append(result)本课的执行逻辑是占位符——打印"已执行",返回结果。实际项目中,你可以让每一步调用工具、生成内容、做任何你想做的事。但模式不变:遍历列表,逐步执行。
三、代码实现
3.1 规划器:agent/planner.py
打开agent/planner.py,找到规划函数:
fromshared.utilsimportextract_json_from_textdefcreate_plan(llm,goal:str)->dict|None:""" 生成实现目标的步骤计划。 用于:第八课 Args: llm: 语言模型实例 goal: 要实现的目标 Returns: 包含 "steps" 列表的字典,失败则返回 None """prompt=f"""为以下目标制定一个详细的执行计划。只返回有效的 JSON。 规则: 1. 只返回有效的 JSON 2. 不要任何解释,不要 Markdown 3. 直接以 {{ 开头,以 }} 结尾 4. 步骤应该具体、可执行、有逻辑顺序 JSON 格式: {{"steps": ["步骤1的描述", "步骤2的描述", "步骤3的描述"]}} 目标:{goal}请返回 JSON:"""forattemptinrange(3):response=llm.generate(prompt,temperature=0.0)plan=extract_json_from_text(response)ifplanand"steps"inplanandisinstance(plan["steps"],list):returnplanreturnNone逐段拆解:
① Prompt 设计
和前几课的风格一致——明确的 JSON 格式要求、extract_json_from_text()兼容、中文指令。
加了一条新要求:“步骤应该具体、可执行、有逻辑顺序”。这能引导 LLM 生成质量更高的计划,而不是"先想想、再研究研究"这种模糊的步骤。
② 验证逻辑
ifplanand"steps"inplanandisinstance(plan["steps"],list):三重检查:非空、有 steps 字段、steps 是列表。缺一不可。
③ 老三样
extract_json_from_text()—— 第三课以来的标准操作- 重试 3 次 —— 老规矩
temperature=0.0—— 规划需要确定性
3.2 Agent 中的规划方法
打开agent/agent.py,找到新增的两个方法:
defcreate_plan(self,goal:str)->dict|None:""" 生成实现目标的计划(第八课核心方法)。 Args: goal: 要实现的目标 Returns: 包含步骤列表的计划字典,失败则返回 None """fromagent.plannerimportcreate_planasplan_fn plan=plan_fn(self.llm,goal)ifplan:self.state.current_plan=planprint(f"📋 计划生成完成,共{len(plan['steps'])}个步骤")returnplandefexecute_plan(self,plan:dict)->list[dict]:""" 逐步执行计划(第八课核心方法)。 Args: plan: 包含 "steps" 列表的计划字典 Returns: 每步的执行结果列表 """ifnotplanor"steps"notinplan:print("❌ 无效的计划")return[]results=[]total=len(plan["steps"])print(f"\n🚀 开始执行计划(共{total}个步骤)\n")fori,stepinenumerate(plan["steps"],1):print(f"--- 步骤{i}/{total}---")print(f" 内容:{step}")result={"step_num":i,"step":step,"executed":True,"status":"completed"}results.append(result)self.state.increment_step()print(f" 状态:✅ 已执行\n")returnresults注意几个设计细节:
①create_plan()委托给planner.py
fromagent.plannerimportcreate_planasplan_fn plan=plan_fn(self.llm,goal)规划逻辑放在独立的planner.py文件里,不塞在agent.py中。职责分离——Agent 类负责调度,Planner 模块负责规划。后面如果你想换更复杂的规划算法,改planner.py就行,不用动 Agent 类。
② 计划存入状态
ifplan:self.state.current_plan=plan生成的计划被保存到AgentState里,方便后续查询和调试。
③execute_plan()的占位执行
本课的执行逻辑是"标记已执行",没有真正调用工具。这是故意的——本课的核心是规划与执行分离的模式,不是执行的具体实现。第九课会让每一步变成真正的"原子动作"。
④ 步骤编号
fori,stepinenumerate(plan["steps"],1):输出带编号的步骤(1/4、2/4…),方便追踪执行进度。
四、运行示例
4.1 基础场景:规划并执行
fromagent.agentimportAgent agent=Agent(model="qwen2.5:7b")# 第一步:生成计划plan=agent.create_plan("写一篇关于 AI Agent 的技术博客")# 第二步:检查计划ifplan:print("\n📋 计划内容:")fori,stepinenumerate(plan["steps"],1):print(f"{i}.{step}")# 第三步:执行计划results=agent.execute_plan(plan)print(f"\n📊 执行完成,共{len(results)}个步骤")预期输出(类似):
📋 计划生成完成,共 4 个步骤 📋 计划内容: 1. 研究 AI Agent 的最新进展和核心技术概念 2. 确定文章大纲和目标读者 3. 撰写博客初稿 4. 审校修改并优化文章结构 🚀 开始执行计划(共 4 个步骤) --- 步骤 1/4 --- 内容:研究 AI Agent 的最新进展和核心技术概念 状态:✅ 已执行 --- 步骤 2/4 --- 内容:确定文章大纲和目标读者 状态:✅ 已执行 ...(略) 📊 执行完成,共 4 个步骤4.2 规划但不执行
分离的好处:你可以生成计划、审查之后决定不执行。
plan=agent.create_plan("策划一场技术分享会")ifplan:print("AI 提议的计划:")fori,stepinenumerate(plan["steps"],1):print(f"{i}.{step}")# 你觉得不合适,不执行user_input=input("\n执行这个计划吗?(y/n): ")ifuser_input.lower()=="y":agent.execute_plan(plan)else:print("已取消执行")这就是"规划与执行分离"的实战价值:人可以介入审查,在 AI 行动之前拦截不合理的计划。
4.3 修改计划再执行
plan=agent.create_plan("搭建一个个人网站")ifplan:# 手动添加一个步骤plan["steps"].insert(0,"调研主流建站方案并选型")# 删掉一个不想要的步骤iflen(plan["steps"])>5:plan["steps"]=plan["steps"][:5]# 用修改后的计划执行agent.execute_plan(plan)计划就是数据。你可以增删改查,就像操作一个普通列表一样。
4.4 交互模式
cdlesson08 python complete_example.py会进入交互模式,你可以输入任意目标,观察 Agent 如何生成计划并执行。
五、与第七课的本质区别
两课都和"信息管理"有关,但方向完全不同:
第七课(记忆——时间维度):
对话1 → 存入 "用户喜欢 Python" → 记忆系统 对话2 → 加载记忆 → "推荐 Python 相关内容" 对话3 → 存入 "用户在做 Web 项目" → 记忆系统信息在时间上持久化。解决"跨对话记住信息"。
第八课(规划——任务维度):
目标:"写一个爬虫" ↓ 规划:["分析需求", "选技术栈", "写代码", "测试"] ↓ 按计划逐步执行 → 完成信息在任务上结构化。解决"多步骤任务如何拆解和执行"。
| 第七课(记忆) | 第八课(规划) | |
|---|---|---|
| 解决什么问题 | “AI 忘记了之前的事” | “AI 不会拆解复杂任务” |
| 数据结构 | 扁平的字符串列表 | 有序的步骤列表 |
| 信息流向 | 存 → 取(双向) | 生成 → 验证 → 执行(单向) |
| 时间维度 | 跨会话 | 单次任务内 |
| 人能介入吗? | 事后审查(删除错误记忆) | 事前审查(修改计划再执行) |
记忆让你记住"用户是谁",规划让 AI 知道"该怎么做"。
六、关键洞察
6.1 规划不是思考,是数据生成
这是本课最重要的洞察:
规划 = 结构化数据生成。不是推理,不是规划算法,不是搜索。
AI 生成计划,用的技术和第三课生成 JSON 一样——LLM + prompt +extract_json_from_text()。没有什么神秘的"规划引擎"。
这意味着计划是透明的——你可以打印它、审查它、修改它。它不躲在 AI 的"思维过程"里,它就是一个 Python 字典。
6.2 规划与执行分离是关键
如果规划和执行混在一起,AI 一边想一边做,你就失去了控制权。
分离之后,你可以:
- 在 AI 行动之前审查计划——不合理就不执行
- 在不同目标之间复用计划——"写博客"的计划可以复用
- 独立测试规划质量——不跑执行也能评估 AI 的规划能力
- 让不同模型分工——用大模型做规划,用小模型做执行
这个模式在工程里无处不在:编译器先编译再运行,CI/CD 先构建再部署,项目经理先排期再开工。
6.3 计划的质量取决于 Prompt
本课的规划器很简单,但可以通过优化 Prompt 大幅提升计划质量。
比如加一个要求:“每个步骤应该可以在 30 分钟内完成”。加上这条,生成的步骤会更具体、更可操作。
再比如加示例:
好的计划示例: - "用 requests 库获取目标网页 HTML" - "用 BeautifulSoup 解析标题和日期" 不好的计划示例: - "研究一下" - "想想怎么写"Few-shot 示例对小模型(7B)的效果提升非常明显。
6.4 先跑起来,再跑得更好
本课的执行是占位符。规划器是单次生成,没有自我修正。计划是纯文本步骤,没有结构化的依赖关系。
这些"不够好"的地方,都是后面课程要解决的:
- 第九课:原子动作——让每步变成有 schema 的结构化执行
- 第十课:思维链推理——让规划过程更严谨
但本课的目标不是"完美",而是建立模式。先把"规划→验证→执行"这条链路跑通,再逐步优化每个环节。
七、常见问题
Q:生成的计划步骤太模糊怎么办?
A:优化 Prompt。加具体约束,比如"每个步骤应该描述具体的动作,而不是模糊的意向"。加 few-shot 示例,展示什么是好的步骤、什么是不好的步骤。对小模型(7B),示例比约束更有效。
Q:步骤的顺序不对怎么办?
A:目前顺序由 LLM 决定。如果你发现顺序经常不对,可以在 Prompt 里加要求:“步骤应该按照执行依赖关系排序,被依赖的步骤排在前面”。后续课程会加入显式的依赖处理。
Q:为什么执行只是占位符,没有真正做事情?
A:这是刻意的。本课的核心是"规划与执行分离"这个模式,不是执行的具体实现。第九课会让每一步变成带验证 schema 的原子动作,到那时执行才有真正的意义。把两个概念拆开讲,比混在一起讲更清晰。
Q:可以让人修改计划再执行吗?
A:当然可以——这正是规划与执行分离的最大好处。计划就是一个 Python 字典,你可以用plan["steps"].insert()、plan["steps"].pop()任意修改,修改后再调用execute_plan()。4.2 和 4.3 的示例展示了这个用法。
Q:规划可以多轮迭代吗?
A:可以,但本课没有实现。一个常见的模式是:生成计划 → 审查 → 发现问题 → 让 LLM 修改计划 → 再审查 → 通过。这就是"反思 + 修正"的循环,很多高级 Agent 框架都用这个模式。你可以基于本课的代码,在create_plan()和execute_plan()之间加一个人工审查环节,作为第一个版本的迭代。
八、八课演进线
把前八课放在一起看,一条清晰的 Agent 构建路径:
第一课:能对话了(LLM + Ollama) 第二课:有角色了 + 能多轮对话(System Prompt + History) 第三课:能输出 JSON 了(结构化输出 + 验证 + 重试) 第四课:能做选择了(意图理解 → 动作路由) 第五课:能调用工具了(工具选择 + 参数提取 + 安全执行) 第六课:能循环了(Agent Loop + 状态追踪 + 终止条件) 第七课:能记住了(跨对话记忆存储 + 检索 + 显式管理) 第八课:能规划了(计划生成 → 验证 → 逐步执行)从"裸模型"到"有角色、有工具、有循环、有记忆、有规划"的智能体。
还差什么?
- 第九课:原子动作——让每步执行更安全、更可控
- 第十课:思维链推理——让规划过程更严谨
九、下期预告
第九课:原子动作——让每步执行都有保障
本课的计划可以生成了,但执行是"占位符"。AI 说"研究 AI Agent 进展",但你不知道具体要做什么、做到什么程度算完成、输出是什么格式。
下一课,我们给每一步加上动作 schema——定义输入、输出、验证规则。让 Agent 的每一步执行都是可验证、可追踪的"原子动作"。
这是从"AI 说做什么就做什么"到"AI 做的每一步都可检查"的关键升级。
敬请期待!
完整代码获取
本课涉及的完整代码包括:
agent/planner.py——独立规划器模块(计划生成 + 验证)agent/agent.py——Agent 类(新增create_plan()和execute_plan())complete_example.py——演示模式 + 交互模式
代码获取请参考本系列第一篇文章
标签
#Python#AI Agent#LLM#任务规划#Ollama#Qwen#大模型#手搓Agent
本文为《手搓 AI Agent 从 0 到 1》系列教程第 8 课
