TodoWrite - Agent 的规划能力
L03 要回答一个问题:如何在 L02 的基础上让 Agent 保持规划能力,而不偏航?
L02 给了我们骨架:Dispatch Map + Tool Schema + safe_path。但真实 Agent 执行复杂任务时会”走哪算哪”——被局部问题卡住,忘记整体规划。怎么做?
本章讲三个机制:
| 机制 | 解决什么问题 | 核心代码 |
|---|---|---|
| TodoManager | 任务列表管理 | update() + render() |
| 单线程约束 | 保持单一焦点 | in_progress_count > 1 报错 |
| nag_reminder | 软性问责提醒 | 3轮不更新就注入 <reminder> |
这三个机制叠加在 L02 的 Dispatch Map 上,循环本身不变。理解 L03,就理解了 Agent 如何”保持规划不偏航”。
一、TodoManager:任务列表管理器
“没有计划的 agent 走哪算哪。”
这句话概括了 TodoManager 的设计动机。先列步骤再动手,完成率翻倍。看代码:
class TodoManager: def __init__(self): self.items = [] self.rounds_since_todo = 0
def update(self, items: list) -> str: validated, in_progress_count = [], 0 for item in items: status = item.get("status", "pending") if status == "in_progress": in_progress_count += 1 # 单线程约束检查 validated.append({"id": item["id"], "text": item["text"], "status": status}) if in_progress_count > 1: raise ValueError("Only one task can be in_progress") self.items = validated self.rounds_since_todo = 0 # 重置计数器 return self.render()
def render(self) -> str: lines = [] for item in self.items: if item["status"] == "pending": lines.append(f"[ ] {item['text']}") elif item["status"] == "in_progress": lines.append(f"[>] {item['text']}") elif item["status"] == "completed": lines.append(f"[x] {item['text']}") return "\n".join(lines)update() 验证任务列表,render() 输出可视化格式。两者配合,让模型和人类都能看懂进度。
todo 是内省工具
TodoManager 和其他工具有本质区别:
| 工具 | 操作什么 | 目的 | 类型 |
|---|---|---|---|
| read_file | 文件系统 | 获取信息 | 外向工具 |
| write_file | 文件系统 | 改变外部世界 | 外向工具 |
| edit_file | 文件系统 | 改变外部世界 | 外向工具 |
| bash | 外部系统 | 执行命令 | 外向工具 |
| todo | Agent 自身状态 | 追踪认知进度 | 内省工具 |
内省工具:操作 Agent 自身状态,不操作外部世界。todo 是 Agent 自我意识的雏形——感知、思考、追踪自己。
s02 vs s03:对比理解
| 组件 | s02(L02) | s03(L03) | 变化 |
|---|---|---|---|
| Tools | 4 | 5 | + todo |
| TodoManager | 无 | 有 | 新增 |
| nag reminder | 无 | 有(3轮后) | 新增 |
| 循环 | - | 不变 | 核心 |
s02 没有 todo,模型每轮只能靠 messages 回忆进度。s03 有 TodoManager,模型可以显式追踪——这是”外化记忆”。
验证你的理解
Q1:为什么 todo 是”内省工具”?
:::details 答案 todo 操作的是 Agent 自身状态(任务进度),不操作外部世界(文件、系统)。
read/write/edit 操作外部世界,是”外向工具”。todo 是 Agent 自我意识的雏形——感知、思考、追踪自己。 :::
二、单线程约束:认知设计
TodoManager 有一个硬性规则:同时只能有一个 in_progress。
if in_progress_count > 1: raise ValueError("Only one task can be in_progress")为什么?不是技术限制,是认知设计约束。
技术上可以并行,但认知上不能
| 角度 | 问题 | 答案 |
|---|---|---|
| 技术 | LLM 一次响应能包含多个 tool_use 吗? | 能——技术上并行执行没问题 |
| 认知 | LLM 能同时关注多个任务吗? | 不能——注意力会分散 |
单线程约束的本质:强制模型保持单一焦点心智模型,避免注意力分散。
如果允许 5 个 in_progress,会发生什么?
[ ] 步骤1:分析代码[>] 步骤2:写实现[>] 步骤3:写测试[>] 步骤4:写文档[>] 步骤5:提交PR[ ] 步骤6:检查CI模型失去焦点概念——“当前在做什么”模糊了。todo.render() 输出混乱,人类无法追踪进度。
验证你的理解
Q2:如果取消单线程约束,模型同时标记 5 个 in_progress,会怎样?
:::details 答案 模型失去焦点概念,todo.render() 输出混乱:
- 人类无法追踪进度——5 个
[>]标记,不知道哪个是真正在做的- 模型注意力分散——“当前任务”概念失效,可能同时推进多个半成品
- 违反认知设计原则——todo 的设计目的是”单一焦点”,多焦点破坏这个目的 :::
三、nag_reminder:软性提醒
单线程约束是硬约束——直接报错。nag_reminder 是软约束——提醒但不强制。
# nag reminder: 3轮不更新就提醒if rounds_since_todo >= 3 and messages: last = messages[-1] if last["role"] == "user" and isinstance(last.get("content"), list): last["content"].insert(0, { "type": "text", "text": "<reminder>Update your todos.</reminder>", })
# 计数器累加(每轮结束时)if not called_todo_this_round: TodoManager.rounds_since_todo += 13轮不更新 todo,就注入 <reminder> 到 tool_result。模型看到提醒,可以选择响应或忽略。
软约束 vs 硬约束
| 约束类型 | 实现 | 模型能忽略吗 | 代价 | 收益 |
|---|---|---|---|---|
| 软约束 | nag reminder | 能 | 可能失效 | 保持模型自主性 |
| 硬约束 | 单线程约束 | 不能 | 违反信任模型原则 | 100%执行 |
信任模型原则:Agent 的决策主体是模型(神经网络),不是代码(if-else)。硬约束让 Harness 替模型做决策,违反这个原则。
nag 的副作用
如果每轮都注入 nag,会发生什么?
| 副作用 | 描述 |
|---|---|
| 打断思考流程 | 模型每轮被迫处理 <reminder>,无法连续思考 |
| 上下文污染 | tool_result 充斥 <reminder> 标签,有效信息被稀释 |
| 模型产生”忽略习惯” | 过频提醒会失效——像闹钟一样,响多了就不响了 |
设计权衡:3轮一次是平衡点——足够提醒,不至于打扰。
验证你的理解
Q3:nag reminder 为什么是软约束,不是硬约束?
:::details 答案 如果是硬约束(强制执行),违反信任模型原则:
- Harness 替模型做决策——“必须更新 todo”
- 模型失去自主性——从”决策者”降级为”执行者”
- Agent 范式失效——决策主体应该是模型,不是代码
软约束代价是可能失效,但收益是保持模型自主性。Agent 用软约束,因为决策主体是模型本身。 :::
四、回顾:L03 在循环上叠加了什么
回到开头的问题:如何在 L02 的基础上让 Agent 保持规划能力,而不偏航?
答案:
| 叠加机制 | 代码位置 | 循环的变化 |
|---|---|---|
| TodoManager | TOOL_HANDLERS["todo"] = TodoManager.update | 不变——只是多一个 handler |
| 单线程约束 | TodoManager.update() 内部 | 不变——护栏在 handler 层 |
| nag_reminder | 循环末尾注入 <reminder> | 不变——只是 tool_result 多一项 |
循环本身仍是 L01 的 while True + stop_reason + messages[]。L03 只是在”认知层”叠加了规划和提醒机制。
与后续课程的关系
理解 L03 后,你会发现后续课程都在 todo 机制上叠加——循环本身始终不变:
| 课程 | 叠加什么 | 变化在哪 |
|---|---|---|
| L04 Subagent | 子任务继承 todo | 子循环有自己的 TodoManager |
| L05 Skill Loading | todo 追踪 skill 加载 | todo 状态包含 skill 信息 |
| L06 Context Compact | todo 状态压缩保留 | messages 管理策略 |
| L07 Task System | todo 持久化 + DAG | 从内存到文件 |
| L09 Agent Teams | 多 agent todo 协调 | 跨 agent 任务分配 |
核心洞察:无论机制多复杂,骨架始终是 L01 的循环 + L02 的 Dispatch Map + L03 的 TodoManager。理解 L01、L02、L03,就理解了 Agent 系统的认知框架。
五、心智模型升级:从 L0 到 L2
读完这篇文章,你的理解经历了怎样的升级?
L03 核心概念的理解升级路径
| 概念 | L0(表面) | L1(关联) | L2(深层) |
|---|---|---|---|
| TodoManager | ”任务列表管理器" | "todo handler 注册进 Dispatch Map,操作自身状态" | "内省工具——Agent 自我意识雏形。操作认知状态,不操作外部世界。共享认知界面:模型和人类都能看懂” |
| 单线程约束 | ”只能做一个任务" | "技术上可并行,但 todo 强制单一焦点" | "认知设计约束——强制模型保持单一焦点心智模型。避免注意力分散,不是技术限制。类似’一心不可二用’的设计哲学” |
| nag_reminder | ”提醒更新 todo" | "3轮不更新就注入 <reminder>,可忽略" | "软约束原则——提醒不强制。代价是可能失效,收益是保持模型自主性。硬约束违反信任模型原则:决策主体是模型,不是代码” |
如何检验你达到了 L2?
用这个问题自测:
如果模型连续 10 轮忽略 nag,你会怎么设计?加强 nag 还是接受失效?
:::details L2 层级回答 接受失效,不加强 nag。
原因:
- 信任模型原则:决策主体是模型,Harness 只能提醒不能强制
- 加强 nag 的副作用:每轮注入 → 打断思考 → 上下文污染 → 模型产生忽略习惯
- 软约束设计权衡:代价是可能失效,收益是模型自主性。Agent 用软约束
如果真的需要 100%执行,应该改提示词(让模型理解 todo 重要性),不是改 Harness(强制执行)。 :::
本章目标:让每个核心概念都从 L0 升级到 L2。理解 L03,不只是记住代码,而是掌握”如何在循环上叠加规划能力”的设计原则。
六、复习可视化
TodoManager 流程
L02 vs L03 对比
单线程约束示意
软约束 vs 硬约束
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!