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 分钟),做两件事:

  1. 确认/修正选型:“数据结构选型合理,但 execute 需要加超时控制,建议用 concurrent.futures”
  2. 补充 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 分离还有一个容易被忽视的好处:人审计代码的效率大幅提升

没有计划时,审计者需要:

  1. 读完全部代码(可能 500+ 行)
  2. 在脑子里逆向推导"Agent 的设计意图是什么"
  3. 判断设计是否合理
  4. 判断实现是否符合设计

有了计划后,审计者只需要:

  1. 看计划(5 分钟,200 字)→ 判断设计是否合理
  2. 对照计划看代码 → 只需要检查"实现是否符合计划"

审计效率从"读懂 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 的脑子和手分开用——先用脑子想清楚,再用手去执行。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐