TodoWrite
给 Agent 一个写计划、跟计划的能力。
不替它画航线,但给它一支笔和一张纸。
问题:多步任务中,Agent 会"迷路"
想象你让 Agent 做一个 5 步任务(比如重构一个 Python 文件:加类型标注、加 docstring、加 main guard、跑测试、修复 lint)。在没有计划工具的情况下,典型的执行过程是这样的:
❌ 没有 Todo 时的执行轨迹
✅ 有 Todo 时的执行轨迹
解决方案:TodoManager + Nag Reminder
s03 加了两样东西,一个给模型用,一个管着模型:
① TodoManager — 给模型一个写计划的地方
就是一个 todo 工具。模型调用它,传入一个 todo 列表,每个 todo 有 id(编号)、text(描述)、status(状态:pending / in_progress / completed)。
TodoManager 负责验证(最多 20 项、状态只能是三种之一、同时只能有一个 in_progress)并渲染成可读格式。
② Nag Reminder — 模型忘更新时提醒它
如果模型连续 3 轮(调用 LLM + 执行工具 = 1 轮)没有碰 todo 工具,harness 会自动在下一轮的工具结果里插入一条 <reminder>Update your todos.</reminder>。
这不是"系统发出了警告"——它是悄悄地出现在 user 消息里的,模型会把它当成对话的一部分,自然地响应它。
两个机制合在一起的效果:模型主动写计划 → 按计划执行 → 忘了就有人催。
案例:用同一个任务对比"无 Todo"和"有 Todo"
任务:"重构 hello.py:加类型标注、加 docstring、加 main guard"。我们跟踪 Agent 在有无 Todo 工具时的实际行为差异。
场景 A:没有 Todo(s02 的行为)
read_file hello.py。没计划。edit_file 加类型标注。场景 B:有 Todo(s03 的行为)
todo,列 3 项:[>] #1: 加类型标注 [ ] #2: 加 docstring [ ] #3: 加 main guard
edit_file 加类型标注 → todo 把 #1 标 completed,#2 标 in_progress。edit_file 加 docstring → todo 把 #2 标 completed,#3 标 in_progress。edit_file 加 main guard → todo 全标 completed。TodoManager 内部:它不是一个文件,是一个内存对象
s03 的 TodoManager 不是写到磁盘的——它就是一个 Python 对象,存在内存里。这意味着:每次运行 s03,todo 列表都是空的。(后面 s07 才会把任务持久化到磁盘。)
TodoManager 做了三件事
① 验证:每次模型调 todo,TodoManager 检查数据是否合法——最多 20 项、每项必须有 text、status 只能是 pending/in_progress/completed、同时只能有一个 in_progress。如果模型传了不合法的数据(比如两个 in_progress),直接报错。
② 存储:把验证通过的 todo 列表存到 self.items。注意每次 update 是全量替换,不是增量追加。模型每次调 todo 都要传完整的列表。
③ 渲染:把 todo 列表变成人类可读的文本格式,作为 tool_result 返回给 LLM:
[ ] #2: 添加 docstring
[ ] #3: 添加 main guard
(0/3 completed)
为什么强制"同一时间只有一个 in_progress"?
这是 s03 最精妙的设计决策之一。如果允许多个 in_progress,模型会"同时做 5 件事",每件事都做一半。强制顺序聚焦 = 做完一个,再做下一个。这是用数据结构约束行为。
另外:TodoManager 只有 40 行代码。一个类 + 两个方法(update + render)。不需要数据库,不需要队列——就是内存里存一个列表。大道至简。
Nag Reminder:不更新计划就催你
模型有时候会"忘记"更新 todo。比如连续 4 轮都在做实际工作,没调 todo 工具。这时 harness 会介入:
Nag 的工作流程
Agent Loop 里有一个 rounds_since_todo 计数器:
① 每轮循环结束前,检查这轮模型有没有调 todo 工具。
② 调了 → 计数器归零。
③ 没调 → 计数器 +1。
④ 计数器 ≥ 3 → 在 tool_result 消息里注入一条 reminder 文本。
关键设计:reminder 不是"系统警告",它是悄悄塞进对话的
注意注入的 reminder 是 {"type": "text"},被塞在 results 列表里,和其他 tool_result 一起以 role: "user" 进入 messages。模型看到的是:
{"type": "tool_result", ...},
{"type": "text", "text": "<reminder>Update your todos.</reminder>"},
]}
模型把它当成对话的自然一部分来响应,通常会紧接着调一次 todo 来更新状态。这个设计比"系统发警告"自然得多——不会打断模型的思路,只是轻轻拍一下肩膀。
代码拆解:s03 的三个新东西
TodoManager 类
一个 Python 类,40 行。接收模型传来的 items 列表,验证、存储、渲染。和 s02 的 dispatch map 一样简单——加一个工具,加一个 handler 函数。
dispatch map 加一行(又是这个模式)
todo 工具和 bash、read_file 一样注册。handler 就是一个 lambda 调用 TODO.update()。
Agent Loop 里的两个改动
Agent Loop 本体多了两样:① 追踪这轮有没有用 todo(used_todo 标记);② 连续3轮没用就注入 nag。
s02 vs s03:什么变了
| 组件 | s02 | s03 |
|---|---|---|
| 工具数量 | 4 | 5 (+todo) |
| 规划能力 | 无 | TodoManager 带状态追踪 |
| Nag 注入 | 无 | 3 轮后注入 reminder |
| Agent Loop | 简单分发 | + rounds_since_todo + nag |
| Dispatch Map | 加一行,模式不变 | |
| 外层 REPL | 完全不变 | |
从 s03 带走的三个核心认识
外部记忆 > 依赖模型记性
模型不是不努力,是记不住。Todo 列表作为外部记忆,始终在对话历史中可见,不会被后来的工具结果冲掉。
用数据结构约束行为
"同一时间只能一个 in_progress"不是建议,是硬约束。好的 harness 设计用数据验证来引导模型的执行模式,而不是靠提示词"建议"。
被动注入 > 主动打断
Nag reminder 不是弹窗警告,而是悄悄塞进对话的文本。模型把它当成对话的自然部分来响应。Harness 的干预应该是"不显眼"的。
落地案例:TodoWrite 在真实 AI 产品中的应用
学完 TodoWrite 之后,你会发现几乎所有处理复杂任务的 AI Agent 都有某种"计划追踪"机制。以下是三个真实案例。
📋 场景一:Claude Code 的 TodoWrite 工具(你天天在用的)
你平时用 Claude Code 时看到的那个任务列表——[ ] pending、[>] in_progress、[x] completed——就是 TodoWrite。它就是 s03 的代码跑在 Claude Code 里的样子。
[x] #2: 实现表单验证逻辑
[>] #3: 对接后端认证 API
[ ] #4: 添加错误处理和提示
[ ] #5: 写单元测试
(2/5 completed)
下次 Claude Code 列出 Todo 时,你就能"看到背后":它调了 todo 工具 → TodoManager.update() 验证了数据 → 渲染成这个格式 → 作为 tool_result 注入了对话。如果 Claude 连着几轮没更新进度,你可能会在对话里看到它突然"自己提醒自己"更新 todo——那就是 nag reminder 在工作。
🤖 场景二:SWE-agent — 学术界的编码 Agent
SWE-agent 是普林斯顿大学开源的编码 Agent,在 SWE-bench 基准测试中名列前茅。它的核心设计之一就是规划:
开局:Agent 先查看 issue 描述和代码库结构,生成一个"plan"——列出需要修改的文件和修改思路。
执行:Agent 按 plan 逐项执行,每完成一步就更新进度。
反思:如果某一步失败了,Agent 回到 plan 重新评估。
SWE-agent 的论文里有一个关键数据:带了 plan 的 Agent 在复杂任务上的成功率比无 plan 的高出 30%+。这和 s03 的设计直觉完全吻合——没有计划的 Agent 走哪算哪。
🧠 场景三:AutoGPT / BabyAGI — 任务分解 Agent
AutoGPT 和 BabyAGI 是 2023 年引爆 AI Agent 热潮的两个开源项目。它们的核心机制就是 TodoWrite 的强化版:
用户给一个大目标("帮我研究新能源汽车市场并写一份报告")。
Agent 自动拆成子任务:搜索资料 → 阅读关键文章 → 提取数据 → 写大纲 → 写正文 → 排版。
每个子任务就是一个 todo(pending → in_progress → completed)。
优先级排序:Agent 不仅追踪进度,还会动态调整优先级——如果某个子任务发现新线索,会插入新的 todo。
AutoGPT 和 BabyAGI 都因为"上下文管理"问题而难以稳定工作——这正是 s04-s06 要解决的问题。你学到的 TodoWrite 是它们任务管理机制的最小原型。