03:待办写入
⭐ 核心路径 · 上一课:[[02-tool-use/02-工具使用|02 工具使用]] · 下一课:[[04-subagent/04-子智能体|04 子智能体]]
"没有计划的 agent 走哪算哪" —— 先列步骤再动手,完成率翻倍。
Harness 层:规划 —— 让模型不偏航,但不替它画航线。
学习目标
- 理解模型在多步任务中丢失进度的根本原因
- 掌握
TodoManager的设计与实现 - 理解 "nag reminder" 机制及其触发条件
- 体会
in_progress独占约束对顺序聚焦的作用 - 能够独立扩展 tool dispatch map 加入新工具
问题:模型会丢进度
[[01-agent-loop/01-智能体循环|01 智能体循环]] 建立了核心循环,[[02-tool-use/02-工具使用|02 工具使用]] 将工具数从 1 扩展到 4。但多步任务中有一个微妙的问题:
模型会丢失进度。
一个 10 步重构任务,模型做完 1-3 步后就开始「即兴发挥」——跳步、重复、跑偏。这不是模型笨,而是架构问题:
- 上下文稀释:工具结果不断填充上下文,系统提示的影响力逐渐被稀释
- 缺少外部记忆:模型只能依赖内部注意力来记住"做到哪了",注意力不会专门记住进度
- 没有问责压力:模型跑偏了也没人提醒它
关键洞察
模型需要 一个自己能读写的进度板,而 Harness 需要 一个提醒机制。进度板给模型方向感,提醒机制制造问责压力。
核心概念
1. TodoManager:结构化的任务状态
class TodoManager:
def __init__(self):
self.items = []
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
return self.render()关键设计决策:
in_progress独占:同一时间只允许一个任务in_progress,强制模型顺序聚焦,避免并行贪心- 三个状态:
pending→in_progress→completed,简单够用 - 校验优先:所有输入先校验再存储,
text必填、status必须在枚举范围、最多 20 项
渲染输出格式:
[ ] #1: 添加类型提示
[>] #2: 添加文档字符串
[x] #3: 添加 main guard
(1/3 completed)这种格式模型一看就懂,而且能通过 todo 工具随时更新。
2. 注册 todo 工具
[[02-tool-use/02-工具使用|02 课]] 建立了 TOOL_HANDLERS 分发模式。加入 todo 只需要 加一行:
TOOL_HANDLERS = {
"bash": lambda **kw: run_bash(kw["command"]),
"read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
"edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
"todo": lambda **kw: TODO.update(kw["items"]), # ← 新的
}同时加入工具 schema:
TOOLS = [
# ... 之前的工具 schema ...
{
"name": "todo",
"description": "Update task list. Track progress on multi-step tasks.",
"input_schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "string"},
"text": {"type": "string"},
"status": {
"type": "string",
"enum": ["pending", "in_progress", "completed"]
}
},
"required": ["id", "text", "status"]
}
}
},
"required": ["items"]
}
}
]循环体 不变——这是 [[02-tool-use/02-工具使用|02 课]] 的核心原则:加工具不用改循环。
3. Nag Reminder:问责压力
引入一个计数器 rounds_since_todo。每一轮:
- 检查本轮是否调用了
todo工具 - 如果调用了,计数器归零
- 如果没调用,计数器 +1
- 当计数器 >= 3 时,在下一轮的
tool_result中注入<reminder>
rounds_since_todo = 0 if used_todo else rounds_since_todo + 1
if rounds_since_todo >= 3:
results.insert(0, {
"type": "text",
"text": "<reminder>Update your todos.</reminder>"
})为什么是 3 轮? 1 轮太激进(模型可能正在做一件事的中途),2 轮稍紧,3 轮是经验值——给模型一定的自由度,又不会让它完全放飞。
系统提示语
SYSTEM = f"""You are a coding agent at {WORKDIR}.
Use the todo tool to plan multi-step tasks. Mark in_progress before starting,
completed when done.
Prefer tools over prose."""三句话对应三个信息:身份、行为期望、偏好。
实验:运行 s03
cd learn-claude-code
python agents/s03_todo_write.py试试这些 prompt(英文 prompt 对 LLM 效果更好,也可以用中文):
Refactor the file hello.py: add type hints, docstrings, and a main guardCreate a Python package with __init__.py, utils.py, and tests/test_utils.pyReview all Python files and fix any style issues
观察模型行为:是否在开始时创建了 todo 列表?是否在每完成一个子任务后更新状态?如果连续多轮不更新,是否看到了 <reminder> 提醒?
与 s02 的对比
| 组件 | s02(之前) | s03(之后) |
|---|---|---|
| 工具数量 | 4 | 5(+todo) |
| 规划能力 | 无 | 带状态的 TodoManager |
| Nag 注入 | 无 | 3 轮后注入 <reminder> |
| Agent loop | 简单分发 | + rounds_since_todo 计数器 |
架构图
+--------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tools |
| prompt | | | | + todo |
+--------+ +---+---+ +----+----+
^ |
| tool_result |
+----------------+
|
+-----------+-----------+
| TodoManager state |
| [ ] task A |
| [>] task B <- doing |
| [x] task C |
+-----------------------+
|
if rounds_since_todo >= 3:
inject <reminder> into tool_result关键原则
- 进度是外部状态,不是内部记忆:不要把进度放在模型注意力中,要放在模型能读写的外部结构中
- 独占约束强制顺序:
in_progress独占让模型必须线性执行,避免多任务切换的认知开销 - Nag 制造问责压力:模型不更新计划,系统就追着问——这是 Harness 对模型行为最直接的约束
- 加工具不改循环:继续验证 [[02-tool-use/02-工具使用|02 课]] 的核心原则
预告:下一课
规划有了(s03),工具多了(s02),循环跑起来了(s01)。但如果任务太大,一个 agent 的上下文不够怎么办?
[[04-subagent/04-子智能体|04 子智能体]]:大任务拆小,每个子任务有自己独立的上下文。
graph LR
s01[智能体循环] --> s02[工具使用]
s02 --> s03[待办写入]
s03 --> s04[子智能体]
s03 -.-> s05[技能加载]
s03 -.-> s06[上下文压缩]
s04 --> s07[任务系统]
style s03 fill:#ffd700,stroke:#333,stroke-width:3px参考
- Learn Claude Code 项目
- 练习文件:
agents/s03_todo_write.py
[[00-课程概览/教学大纲|📋 返回教学大纲]]