AI Agent 实战避坑 04|别让 AI 边想边做:Planning/Execution 分离实战
AI Agent 实战避坑 04|别让 AI 边想边做:Planning/Execution 分离实战
让 Agent 实现一个 694 行的模块——real_wiring.py,负责活动系统的真实布线逻辑。Agent 上来就开始写代码,前 50 行还不错,到第 100 行发现前面的数据结构选错了,但它没有回头改,而是在错误的基础上继续往下堆了 400 行补丁逻辑。最后交出来的代码"能跑",但维护性为零。
这不是 Agent “笨”。这是自回归生成的结构性问题——每个 token 都基于前面已生成的内容,一旦前面走错了方向,后面要"推翻重来"在概率上极其困难。
解决方案出奇简单:让 Agent 先输出计划,不写代码。人审计后再执行。
边想边做 vs 先想后做
模式 A:边想边做(默认行为)
"实现 real_wiring.py"
→ Agent 直接开始写代码
→ 第 30 行选了 list 当数据结构
→ 第 100 行发现需要按 key 查找,但已经用 list 写了 70 行
→ 不回头,加一个 O(n) 的遍历凑合
→ 第 200 行发现需要并发安全,但整个设计没考虑锁
→ 不回头,在每个函数入口加 Lock
→ 最终:694 行,能跑,但架构像补丁摞补丁
模式 B:先想后做(Plan-then-Execute)
Step 1: "先输出实现计划,列出核心数据结构、模块划分、关键接口。不要写代码。"
Step 2: 人审计计划 → "dict 比 list 更适合这个场景,并发问题建议用 queue"
Step 3: "按照审计后的计划实现"
→ 最终:420 行,结构清晰,无补丁逻辑
效果对比:
| 指标 | 边想边做 | 先想后做 |
|---|---|---|
| 代码行数 | 694 | 420 |
| 首轮 review 通过率 | 40% | 75% |
| 返工轮数(平均) | 3.2 轮 | 1.4 轮 |
| 总 token 消耗 | 85K | 52K |
| 人工审计时间 | 45 min(读懂绕弯的代码) | 20 min(5 min 看计划 + 15 min 看代码) |
先想后做在几乎所有维度上都更好——包括 token 成本。虽然多了一次"输出计划"的调用,但减少了 2 轮返工,总计反而更省。
为什么默认模式是"边想边做"
这不是模型的设计缺陷,而是自回归生成的本质决定的。
GPT / Claude 系列模型的生成方式:一次输出一个 token,每个 token 的生成基于前面所有已生成的 token。这意味着:
token_1 → 确定了方向 A
token_1 + token_2 → 在方向 A 上更进一步
token_1 + token_2 + ... + token_100 → 深度锁定在方向 A
此时要切换到方向 B:
模型需要在第 101 个 token 的位置"否定"前面 100 个 token 的基础
这在概率上极其罕见——因为训练数据中"突然推翻自己"的模式很少
用人的比喻:这就像让一个人拿钢笔在纸上写论文——不能涂改、不能回退。第一句话决定了第一段的走向,第一段决定了全文的结构。如果第一句话走错了,后面要么硬着头皮继续,要么整张纸作废。
Planning/Execution 分离就是把"写论文"拆成"先列提纲 → 确认提纲 → 再正式写"。提纲阶段成本极低(输出几百 token),但能避免正式写作时的方向性错误。
实操:怎么做 Planning/Execution 分离
第一步:Planning Prompt
你的任务是为 real_wiring.py 设计实现方案。
不要写代码。输出以下内容:
1. 模块的核心职责(一句话)
2. 数据结构选型(用什么存储什么,为什么)
3. 关键函数列表(函数名 + 输入输出 + 职责)
4. 依赖关系(哪些外部模块,怎么交互)
5. 你预见的难点和风险
约束:
- 方案控制在 500 字以内
- 每个选型必须给出理由
- 如果有多种方案,列出你推荐的那种及理由
关键设计:
- “不要写代码” 是必须写的——否则模型会自动进入执行模式
- 结构化要求 防止模型输出一堆自由文本
- 字数限制 防止模型在计划阶段就开始展开细节
第二步:人工审计
Agent 的计划输出通常长这样:
1. 核心职责:管理活动模块的布线关系(谁连谁、什么条件触发)
2. 数据结构:
- 布线表:Dict[str, WiringEntry],key 为 module_id
理由:需要按 module_id 频繁查找,dict O(1) 优于 list O(n)
- 执行队列:collections.deque
理由:FIFO 顺序执行,deque 两端操作 O(1)
3. 关键函数:
- register(module_id, config) → None:注册一个模块的布线配置
- resolve(trigger) → List[WiringEntry]:给定触发条件,返回需要执行的布线
- execute(entries) → Result:按顺序执行布线列表
- validate() → List[Error]:校验全部布线配置的一致性
4. 依赖:
- config_loader:读取布线配置文件
- event_bus:接收触发事件
5. 风险:
- execute 可能需要处理超时,单个模块卡住不能阻塞全部
- 配置热更新时需要考虑 register 的并发安全
人看完这个计划(5 分钟),做两件事:
- 确认/修正选型:“数据结构选型合理,但 execute 需要加超时控制,建议用 concurrent.futures”
- 补充 Agent 没想到的:“还需要一个 dry_run 模式,不真正执行但验证流程”
第三步:带约束执行
按照以下计划实现 real_wiring.py:
[贴上审计后的计划]
执行约束:
- 严格按照计划的函数列表实现,不要添加计划外的函数
- 数据结构必须使用 Dict[str, WiringEntry] 和 deque
- execute 使用 concurrent.futures.ThreadPoolExecutor,超时 30s
- 必须包含 dry_run 参数
关键:把审计后的计划作为执行的"合同"。Agent 不需要在执行阶段重新做架构决策,只需要按合同实现。这大幅降低了执行阶段走偏的概率。
什么时候该用 Plan-then-Execute
不是所有任务都需要这个模式。判断标准:
任务复杂度 × 返工成本 = 是否需要 Planning
返工成本低 返工成本高
(改几行就行) (改了要连带改很多)
复杂度低 直接做 直接做
(修 typo) (简单 bug fix)
复杂度高 直接做 先计划再做 ✓
(重构一个函数) (实现新模块)
(跨文件改动)
(涉及并发/状态管理)
具体场景:
| 场景 | 是否需要 Planning | 理由 |
|---|---|---|
| 修一个空指针 bug | ❌ | 定位到行,改一行,跑测试 |
| 添加一个字段的 CRUD | ❌ | 模式固定,不需要架构决策 |
| 实现 694 行的核心模块 | ✅ | 数据结构、接口设计都需要提前确定 |
| 根因分析(Crashsight) | ✅ | 需要先推理再下结论,不能边猜边查 |
| 重构认证中间件 | ✅ | 影响面大,需要先画出变更范围 |
| 跑 grep 检查 | ❌ | 执行型任务,无需计划 |
进阶:多层 Planning
对于特别复杂的任务,可以做两层 Planning:
Layer 1: 架构计划(粗粒度)
"这个系统分几个模块?模块间怎么通信?"
→ 人审计 → 确认架构
Layer 2: 模块计划(细粒度)
对每个模块:"这个模块的函数列表和数据结构是什么?"
→ 人审计 → 确认设计
Layer 3: 执行
对每个模块逐个实现
但注意,不要过度——三层以上的 Planning 通常意味着任务应该被拆分成多个独立任务,而不是在一个 Agent session 里完成。
Planning 的一个副产品:审计效率
Planning/Execution 分离还有一个容易被忽视的好处:人审计代码的效率大幅提升。
没有计划时,审计者需要:
- 读完全部代码(可能 500+ 行)
- 在脑子里逆向推导"Agent 的设计意图是什么"
- 判断设计是否合理
- 判断实现是否符合设计
有了计划后,审计者只需要:
- 看计划(5 分钟,200 字)→ 判断设计是否合理
- 对照计划看代码 → 只需要检查"实现是否符合计划"
审计效率从"读懂 500 行代码的意图"变成"检查实现是否符合已确认的计划"。前者需要 45 分钟,后者需要 15 分钟。
Checkpoint 机制:Plan-then-Execute 的增强版
Planning 解决的是"开头方向对不对"的问题。但长任务中间也可能走偏。Checkpoint 机制是补充方案:
计划:实现 4 个函数 register / resolve / execute / validate
执行流程:
实现 register → ✅ Checkpoint: 跑 test_register → PASS → 继续
实现 resolve → ✅ Checkpoint: 跑 test_resolve → PASS → 继续
实现 execute → ❌ Checkpoint: 跑 test_execute → FAIL → 停下修复
修复 execute → ✅ Checkpoint: 跑 test_execute → PASS → 继续
实现 validate → ✅ Checkpoint: 跑 test_validate → PASS → 完成
每完成一个关键步骤就验证一次。不要等全部写完再跑测试——等到那时候,如果第 2 个函数有 bug,第 3、4 个可能都建立在错误基础上了。
复杂任务上,一次好的 planning 值十轮 retry。让 Agent 的脑子和手分开用——先用脑子想清楚,再用手去执行。
更多推荐


所有评论(0)