Skip to content

规划与任务分解

规划(planning)回答的问题是:模型在执行之前(或之中),要不要、以什么形式,把"接下来要做什么"显式地写下来?

在 harness 的语境里,规划不是模型的抽象认知能力,而是一个具体的系统设计决策:把计划放在哪里(上下文里的文本、结构化工具调用、还是代码里的固定流程)、谁生成它(模型、另一个模型、还是人)、多久更新一次、失效了怎么办。这些决策直接决定 agent 在长任务上的表现、成本和可控性。

Agent 需要显式规划吗

这是过去三年 agent 工程里最有争议的的问题之一,两派观点都有重量级选手:

反方:规划是多余的脚手架。 纯反应式(reactive)的 agent——收到观察、直接决定下一步动作——结构最简单,每一步都基于最新的环境状态做决策,不会被一份事先写好的、可能早已过时的计划绑架。AutoGPT(2023 年 3 月发布)时代的惨痛经验支持这一派:模型会一本正经地制定宏伟计划,然后在一个失败步骤上死磕半小时,计划本身成了沉没成本。

正方:没有显式计划,长任务必然漂移。 LLM 的注意力和工作记忆都在上下文窗口里,几十轮工具调用之后,"最初的目标是什么"会被淹没在大量工具输出中。显式计划的本质是把目标外化(externalize)——写到一份模型每次都能重新读到的清单里,对抗上下文漂移。Claude Code 在系统提示中直接写明:不使用 TodoWrite 规划任务"可能会忘记做重要任务——这是不可接受的"(据对其提示词的逆向分析)。

这场争论的一个注脚

有趣的是,Claude Code 官方文档只列出了 TodoWrite 这个工具的存在,却没有文档化它"主动规划"的核心行为,以至于有用户专门开了 issue #6968 抱怨文档与行为的脱节。这从侧面说明:显式规划在这些产品里不是一个可选的"功能",而是 agent 运转方式的一部分。

本文的立场(也是当前主流产品的实践结论):争论的重点不该是"要不要规划",而是"计划以什么粒度、什么形式、在什么时机被写入和修订"。下面用三种模式把这个设计空间摊开。

三种规划模式

模式一:无规划(纯反应式)

最原始的 agent loop:观察 → 行动 → 观察 → 行动……没有独立的规划阶段,每一步都是模型基于当前上下文直接选动作。

text
┌─────────────────────────────────────────────┐
│              纯反应式(ReAct 风格)           │
│                                             │
│   观察 ──> 想一步 ──> 做一步 ──> 观察 ──> …  │
│                                             │
│   计划只存在于模型的"当下思考"里,             │
│   没有任何持久化的计划状态                    │
└─────────────────────────────────────────────┘

代表是 ReAct(Reasoning + Acting,Yao 等人 2022 年发表、ICLR 2023 收录的论文):模型交替输出"思考轨迹"(reasoning trace)和"动作"(action)。思考就是规划,但它是一次性的、随生成随丢弃的。

  • 优点:结构极简;每步决策都基于最新状态,天然适应环境变化;没有"计划过时"的问题。
  • 缺点:缺乏全局视野。模型容易陷入局部循环(反复尝试同一个失败动作),在长任务上逐渐忘记原始目标;思考和行动耦合在一起,没法单独审查"它打算干什么"。

SWE-agent、OpenHands 的基础循环本质上都是这个模式,只是用工具和提示工程做了大量加固。

模式二:Plan-and-Execute(先规划后执行)

把规划和执行拆成两个模块、两次 LLM 调用:Planner 先生成一份完整的多步计划,Executor 逐步执行,可选的 Replanner 在失败时介入。

text
┌──────────────────────────────────────────────────┐
│              Plan-and-Execute                    │
│                                                  │
│  任务 ──> ┌─────────┐  完整计划  ┌──────────┐    │
│           │ Planner │ ────────> │ Executor │    │
│           │ (大模型) │  step 1..N │ (可小模型)│   │
│           └─────────┘           └────┬─────┘    │
│                 ▲                    │          │
│                 │  失败/偏离时重新规划  │          │
│           ┌─────────┐                ▼          │
│           │Replanner│ <───────  执行结果         │
│           └─────────┘                           │
└──────────────────────────────────────────────────┘

谱系上可以追溯到 Plan-and-Solve 提示法(Wang 等人 2023 年的论文,先让模型制定计划再逐步求解)和 BabyAGI(任务队列 + 任务创建 agent 的循环架构)。LangChain 在 2024 年 2 月的官方博客中系统总结了 plan-and-execute agent 的实现方式。

  • 优点:计划在执行前整体可见、可审计(可以给人审批);规划阶段不随每步动作重复调用,省 token——有工程实践报告称在多工具任务上可节约 30–60% 的 token(参考,注意这是个案经验而非严谨基准);可以做模型分层:贵的大模型做规划,便宜的小模型做执行。
  • 缺点计划脆弱性是致命伤。计划在 T0 时刻基于不完整信息生成,执行到第 3 步时世界已经变了,但计划不知道。频繁触发 replanning 又会抵消掉省下的成本。计划写得越细,碎得越快。

模式三:规划-执行交织(Interleaved)

规划不是一次性事件,而是贯穿整个会话的持续活动:agent 有一份持久化的计划工件(通常是一份 todo list),执行过程中不断增删改——每完成一步就更新状态,发现新信息就插入新任务,方向错了就划掉重写。

text
┌──────────────────────────────────────────────┐
│            交织模式(Claude Code 风格)        │
│                                              │
│   ┌───────────────┐                          │
│   │  Todo List     │ <── 每次执行后回写状态    │
│   │  (显式工作记忆) │                          │
│   └──────┬────────┘                          │
│          │ 每步决策前可见                      │
│          ▼                                    │
│   观察 ──> 思考 ──> 行动 ──> 观察 ──> …       │
│                                              │
│   规划与执行共享同一个 agent 循环,             │
│   计划是"活的",随环境演化                     │
└──────────────────────────────────────────────┘

Claude Code 的 TodoWrite + 执行循环是这种模式的标杆实现;它的 plan mode(规划模式下用 ExitPlanMode 工具提交计划给用户审批后再动手)则是在交织模式上叠加了一个"关键节点人工审批"的闸口。

  • 优点:兼具全局视野和适应性——计划始终在上下文里(对抗目标漂移),又随时可以改(对抗计划脆弱性);用户能实时看到进度,干预成本低。
  • 缺点:工具调用开销多了一层(每次状态更新都是一次工具调用);依赖模型自觉维护清单,模型可能忘记更新状态(下面会展开);实现上比纯反应式复杂。

三种模式对比

维度纯反应式Plan-and-Execute规划-执行交织
计划存在形式临时的思考轨迹一次性生成的完整计划持久、持续修订的任务清单
计划生成时机每步隐式执行前一次会话全程动态
全局视野强(但会过时)强(且保鲜)
环境适应性最强弱(依赖 replanner)
可审计/可审批最好(整体可见)好(实时可见)
token 开销低(不复用 planner 时)中高(每步带清单)
典型代表ReAct、SWE-agent 基础循环BabyAGI、LangChain plan-and-executeClaude Code(TodoWrite)

一个判断框架

任务越短、环境越动态,越适合反应式;任务步骤越确定、越需要事前审批,越适合 plan-and-execute;长任务 + 不确定环境 + 需要人类随时介入——交织模式是目前工程上的最优折中,这也是为什么主流 coding agent 都收敛到了它。

Todo List:显式工作记忆的机制

Todo list 值得单独拆解,因为它看起来 trivial,实际上是交织模式里最精巧的设计:把一个"规划问题"降维成了一个"工具调用问题"

Claude Code 的 TodoWrite 是怎么工作的

根据对 Claude Code 提示词与工具定义的逆向分析,TodoWrite 的机制可以分解为四层:

1. 数据结构极简。 工具的输入就是整个 todo 数组,每项只有三个字段:

json
{
  "todos": [
    {
      "id": "1",
      "content": "Run the build",
      "status": "in_progress"
    },
    {
      "id": "2",
      "content": "Fix any type errors",
      "status": "pending"
    }
  ]
}

状态机只有三态:pending(未开始)、in_progress(进行中)、completed(已完成)。没有优先级、没有依赖关系、没有截止时间——所有花哨的字段都会变成模型的维护负担。

2. 全量覆写语义。 每次调用 TodoWrite 都是提交完整的新清单(工具返回 oldTodos 和 newTodos 的对比),而不是增量 patch。这避免了 diff 应用失败这类状态同步 bug,代价是每次调用多花一点 token。

3. 系统提示规定纪律。 模型被要求"VERY frequently"使用这个工具,并遵守一组硬规则:

  • 任务有 3 个以上步骤、非平凡、或用户给了多项要求时,必须建清单;
  • 任意时刻只能有一个 in_progress
  • 完成一项立即标记 completed,不许攒着批量标记;
  • 遇到阻塞不许标完成,而是新增一个任务描述需要解决什么;
  • 测试没过、实现不完整、有未解决的错误,都不许标 completed
  • 清单里不再相关的任务要整个删掉,保持列表干净。

4. 单一信息源。 这份清单位于对话上下文中,模型每轮生成时都能看到它——这就是它对抗目标漂移的原理:不管中间插入了多少工具输出,"我在哪、还剩什么"始终在最近可见的位置。

为什么"只能有一个 in_progress"是重要的工程决策

这个约束表面上是为了让用户界面清晰,深层作用是给模型强加串行焦点。LLM 在长上下文里容易"多头并进":改着 A 文件突然想起 B 问题,跳过去改 B,回来时 A 的修改上下文已经模糊。强制单焦点把 agent 的行为约束成一条可追踪的轨迹,事后调试(看 observability 日志)时也能精确回答"它在哪一步走偏的"。

它为什么有效:认知卸载,而非智能提升

注意 TodoWrite 没有任何运行时逻辑——harness 不会检查清单、不会强制执行、不会因为模型忘了更新而报警(这正是一些开源复刻项目暴露的问题:模型常常忘记更新状态,见 opencode issue #28961)。它生效的全部机制是认知卸载(cognitive offloading)

  1. 生成清单的动作本身强迫模型在动手前把任务结构化地想一遍;
  2. 清单驻留在上下文里,成为每轮决策的锚点;
  3. 状态更新动作把"回顾进度"变成了循环中的固定节律——每次标记 completed,模型都必须隐式回答"上一步真的做完了吗?下一步是什么?"。

理解这一点很重要:todo list 不是给用户的进度条(虽然它也起这个作用),它是模型写给自己的外部记忆。这也是为什么它和记忆系统有本质区别——它不持久化到会话外,不追求可检索性,只追求"当前会话内始终可见"。

任务分解的粒度问题

显式规划引入了一个新问题:任务要拆到多细?

拆得太粗("实现整个功能"一条任务):清单退化成装饰品,失去了跟踪和纠偏的价值,长任务中段的漂移照样发生。

拆得太细(每个函数、每次编辑都一条):清单本身吃掉大量上下文和工具调用;更糟的是,细粒度计划是在信息最不全的时候写下的,执行中必然大量作废,模型陷入"维护清单"而不是"完成任务"。

工程上的经验法则:

  • 以"可验证的检查点"为粒度。 一条好任务的标准是它有一个客观的完成判据——"跑通构建并修复类型错误"好,"改代码"差。这直接呼应 TodoWrite 的纪律:"测试在跑红就不许标 completed"。
  • 先粗后细,滚动细化(rolling decomposition)。 计划初稿只写阶段级任务(3–7 条);进入某阶段时再当场展开成子步骤。Claude Code 的示例就是这么做的:先建"调研代码库 → 设计 → 实现 → 导出功能"四条,调研完成后才把"修复 10 个类型错误"展开成 10 条。
  • 深度不超过两层。 实践中三层以上的任务嵌套几乎总是过度设计——如果真需要那么深的分解,说明该拆给子 agent 而不是拆给 todo list。

何时重规划(Replanning)

计划会过时,问题是 harness 以什么机制发现和响应。常见的触发信号:

触发信号例子合适的响应
同一动作反复失败同一个测试连修 3 次还红停下执行,重新审视当前任务的假设
观察与计划预期矛盾计划假设有 ORM,实际代码是裸 SQL修订当前及后续任务
执行中发现新子任务改 API 时发现还要迁移数据向清单插入新任务(微观重规划)
环境/需求外部变化用户中途改需求废弃相关任务,重建清单尾部

两个反面教训同样重要:

  • 不要每步都重规划。 BabyAGI 式的"每执行一步就重新生成任务队列"会让计划在每一步之间震荡,agent 永远在调整计划而不是干活,还烧掉大量 token。重规划应该是事件驱动的(失败、矛盾、新信息),不是时间驱动的。
  • 微观修订优于整体重规划。 划掉一条、插入两条、改一条措辞——交织模式下绝大多数"重规划"都是这种廉价操作。只有当目标本身变化或计划的整体假设崩塌时,才值得推翻重写。

一个隐蔽的失败模式

重规划最危险的情形是模型悄悄放弃原计划却不更新清单:清单上还挂着"修复类型错误",模型已经在做别的了。用户看到的是一份与实际行为脱节的计划,这比没有计划更糟——它提供了虚假的可审计性。这也是为什么"完成立即标记、受阻立即新增任务"这类纪律要写进系统提示里反复强调。

推理模型时代:显式规划还必要吗

2024 年 9 月 12 日 OpenAI 发布 o1-preview(正式版 o1 于 2024 年 12 月 5 日发布),2025 年 1 月 20 日 DeepSeek 发布并开源 R1。这类推理模型在生成回答前会产出长链思维(long chain-of-thought),内部完成分解、自我检查、回溯。于是一个自然的质疑出现了:模型自己就能"想",harness 还要显式规划干什么?

有必要区分两种"规划":

推理模型内化的是"解题推理"(reasoning)——这一段的逻辑怎么推、这个 bug 怎么修。它发生在单次生成的思维链里,随输出结束而消散,对环境状态一无所知。

Harness 外化的是"任务状态"(state)——整个任务清单里哪些做完了、哪些没做、当前卡在哪、中途发现了什么新情况。它必须跨越多次生成、多次工具调用持续存在,而单次思维链无论多长都做不到这一点。

由此可以给出一个有依据的判断:推理模型让 ReAct 式的"逐步思考"变得多余,却让 TodoWrite 式的"外化状态"更加必要。 理由有四:

  1. 思维链没有持久性。 模型第 30 轮调用时看不到第 2 轮的思维链(即使看到也会被截断),但 todo list 作为普通对话内容一直可见。跨轮的状态连续性只能靠外化。
  2. 思维链不可审计、不可干预。 用户无法在模型"想"的中途打断纠正;todo list 和 plan mode 的审批闸口提供了人机协作的实体把手。Anthropic 在《Building effective agents》中也强调:agent 系统用延迟和成本换任务表现,因此更需要在关键节点保留可检查、可干预的结构。
  3. 思维链管不了环境。 计划失效的主因是环境反馈与预期不符,而不是推理不够深。推理再强的模型,不重新观察环境也不知道世界变了。
  4. 实测证据。 Claude Code、OpenHands 等系统在接入更强推理模型后并没有移除 TodoWrite / 计划工具,反而持续强化其纪律——这是最有说服力的行业投票。

一个合理的预测是:随着模型变强,规划会进一步向"轻量、结构化、可审批"收敛——更少的预设流程(workflow),更多的模型自主决策(agent),但外化的计划工件作为人机接口和状态锚点会长期存在。

实践建议:什么任务强制规划,什么任务自由发挥

如果你在设计 harness 或编写 agent 的使用规约,下面是一份可直接用的决策清单。

强制显式规划(建 todo list / 先进 plan mode):

  • 任务包含 3 个以上可识别步骤(Claude Code 的官方阈值,实践证明是合理的经验值);
  • 用户一次给了多个独立需求;
  • 任务会跨多个文件/模块/系统,中途容易产生遗漏;
  • 有不可逆操作(数据库迁移、删除、部署)——计划必须先给人审批;
  • 长会话任务,预计超过十几轮工具调用;
  • 需要多人/多 agent 交接的场景——清单就是交接文档。

让它自由发挥(纯反应式):

  • 单步或琐碎任务("跑一下测试"、"这个函数什么意思");
  • 探索性调研——目标是"搞清楚怎么回事",连分解的依据都还没有,此时写计划纯属仪式;
  • 高度不确定、每一步都依赖上一步观察的任务(如交互式调试、渗透测试),预设计划的半衰期以分钟计;
  • 纯对话/信息查询。

一条元规则

判断标准不是"任务大不大",而是**"计划的有效期有多长"**。计划有效期长(步骤确定、环境稳定)→ 值得显式规划;有效期短(探索、调试、对抗性环境)→ 反应式 + 滚动细化。拿不准时的默认值:建一份粗粒度清单(3–7 条),边做边细化。

延伸阅读

  • Agent 循环——规划机制寄生于其中的主循环结构
  • 上下文工程——todo list 为什么有效:它是对上下文内容的主动管理
  • 记忆系统——外化计划与持久化记忆的分工
  • 工具——TodoWrite 展示了"零运行时逻辑的纯提示工具"这一特殊工具类型
  • 子 Agent——当任务分解深度超过 todo list 的承载能力时的下一步
  • 可观测性——计划与执行的偏差是最重要的调试信号之一
  • Claude Code 案例——TodoWrite 与 plan mode 的完整产品设计剖析
  • SWE-agent 案例——纯反应式路线如何靠工具设计弥补无规划的缺陷
  • 核心论文——ReAct、Plan-and-Solve 等原始文献导读

参考资料