外观
规划与任务分解
规划(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-execute | Claude 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):
- 生成清单的动作本身强迫模型在动手前把任务结构化地想一遍;
- 清单驻留在上下文里,成为每轮决策的锚点;
- 状态更新动作把"回顾进度"变成了循环中的固定节律——每次标记 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 式的"外化状态"更加必要。 理由有四:
- 思维链没有持久性。 模型第 30 轮调用时看不到第 2 轮的思维链(即使看到也会被截断),但 todo list 作为普通对话内容一直可见。跨轮的状态连续性只能靠外化。
- 思维链不可审计、不可干预。 用户无法在模型"想"的中途打断纠正;todo list 和 plan mode 的审批闸口提供了人机协作的实体把手。Anthropic 在《Building effective agents》中也强调:agent 系统用延迟和成本换任务表现,因此更需要在关键节点保留可检查、可干预的结构。
- 思维链管不了环境。 计划失效的主因是环境反馈与预期不符,而不是推理不够深。推理再强的模型,不重新观察环境也不知道世界变了。
- 实测证据。 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 等原始文献导读
参考资料
- ReAct: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629)
- Plan-and-Solve Prompting (arXiv:2305.04091)
- LangChain 官方博客:Plan-and-Execute Agents
- A Brief Analysis of Claude Code's Execution and Prompts(Claude Code 提示词逆向分析)
- anthropics/claude-code issue #6968:TodoWrite 主动规划行为未文档化
- opencode issue #28961:模型不主动更新 todo list 的问题案例
- Anthropic: Building Effective Agents
- Blck Alpaca 知识库:Plan-and-Execute 架构(含 token 节约的工程经验数据)
- 宝玉的分享:对 DeepSeek R1 的科普(含 o1/R1 发布时间线)
- 数据学习:什么是推理大模型(o1/R1 发布日期)