# 三行速读

  1. 这章给 Agent 加的是 “会话内导航”,不是持久任务系统。
  2. 关键设计是 “只有一个 in_progress ”,强制执行焦点收敛。
  3. reminder 机制让计划持续活跃,防止任务做到一半跑偏。

# 先修知识

  • 理解 s02 的工具调用模式(todo 也是工具)。
  • 知道状态机里 “约束” 比 “自由文本” 更重要。

# 读完后你应该能做到(可检验清单)

# 本篇要解决什么

当任务从 “一步到位” 变成 “多步协作” 时,模型很容易偏航。s03 引入会话级计划,让系统在当前会话里始终知道:

  • 目标被拆成了哪些步骤;
  • 现在正在执行哪一步;
  • 已完成了多少。

注意:这还不是持久任务系统,是会话内导航。

# 用一个类比先理解

把它想成白板看板:

  • pending 是待办列;
  • in_progress 是进行中列;
  • completed 是已完成列。

看板不保证你一定完成任务,但能显著降低 “忘了自己在做什么” 的概率。

# 为什么这一步必须现在做

如果没有计划状态,模型容易在工具调用中迷失:读了很多文件,但不推进主目标。s03 相当于先补 “节奏管理”,再进入后续更复杂的任务图系统(s12)。

# 关键代码怎么读

# 1) 计划状态结构

1
2
3
4
5
6
7
8
9
10
@dataclass
class PlanItem:
content: str
status: str = "pending"
active_form: str = ""

@dataclass
class PlanningState:
items: list[PlanItem] = field(default_factory=list)
rounds_since_update: int = 0

active_form 是个小细节,但对可读性很有帮助:可以标注 “正在修改测试” 这类进行态。

# 2) update 里的约束很关键

1
2
3
4
5
6
def update(self, items: list) -> str:
if len(items) > 12:
raise ValueError("Keep the session plan short (max 12 items)")
...
if in_progress_count > 1:
raise ValueError("Only one plan item can be in_progress")

这里在强制 “计划可执行”。限制项数和并行进行数,是为了避免计划失控。

# 3) 计划失活提醒

1
2
3
4
def reminder(self) -> str | None:
if self.state.rounds_since_update < PLAN_REMINDER_INTERVAL:
return None
return "<reminder>Refresh your current plan before continuing.</reminder>"

不是每轮都打断,而是超阈值后轻提醒,这种节奏更符合真实工作流。

# 4) 在 loop 中注入提醒

1
2
3
4
5
6
7
if used_todo:
TODO.state.rounds_since_update = 0
else:
TODO.note_round_without_update()
reminder = TODO.reminder()
if reminder:
results.insert(0, {"type": "text", "text": reminder})

提醒依然走统一消息通道,不额外开旁路。

# 你可以怎么复现

  1. 给模型一个 5 步以上任务。
  2. 强制要求先调用 todo 写计划。
  3. 观察 3-5 轮后是否仍围绕主目标推进。

# 常见误区

  • 误区 1:把 todo 当作持久任务系统。
  • 误区 2:列了计划但不更新状态。
  • 误区 3:允许多个 in_progress ,导致注意力分散。

# 一句话总结

s03 的价值是给会话装上 “方向盘”,让模型在复杂任务中持续对齐主目标。

# 补充解读:s03 里计划系统的 “硬约束” 价值

很多人看 s03 会觉得它只是加了个 todo。其实真正有价值的是它把 “计划管理” 做成了有约束的状态系统。

# A. TodoManager.update 不是保存文本,而是校验状态

1
2
3
4
5
6
7
8
if len(items) > 12:
raise ValueError("Keep the session plan short (max 12 items)")
...
if status not in {"pending", "in_progress", "completed"}:
raise ValueError(...)
...
if in_progress_count > 1:
raise ValueError("Only one plan item can be in_progress")

这三个检查分别限制了:

  • 计划规模(防膨胀);
  • 状态合法性(防脏数据);
  • 执行焦点(防并行分心)。

对新人来说,这里最关键的观念是:计划系统不是 “写下来就好”,而是 “要持续可执行”。

# B. render() 是把状态转成人能看懂的看板

1
2
marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}[item.status]
line = f"{marker} {item.content}"

这段看似 UI 小事,实际上影响很大。计划系统如果不可读,就不会被持续使用;不持续使用,就会退化成摆设。

# C. reminder 机制的节奏设计

1
2
3
if self.state.rounds_since_update < PLAN_REMINDER_INTERVAL:
return None
return "<reminder>Refresh your current plan before continuing.</reminder>"

它没有每轮都打断,而是 “延迟提醒”。这是一种很实用的工程折中:既避免唠叨,也避免计划失活。

# D. 主循环里 used_todo 分支

1
2
3
4
if used_todo:
TODO.state.rounds_since_update = 0
else:
TODO.note_round_without_update()

这段在维护 “计划活跃度”。计划只要几轮不更新,就会逐渐与实际执行脱节。

# 进阶练习

  1. PLAN_REMINDER_INTERVAL 从 3 改到 2,观察行为变化。
  2. PlanItem 增加 priority 字段,尝试不破坏现有约束逻辑。
  3. 设计一个 “自动完成检测” 小实验(例如某步骤对应文件出现即自动标 completed)。

# 读到这里你应掌握的点

  • Todo 不是注释,是状态系统。
  • 状态系统要有硬约束。
  • 会话内计划和持久任务图是两层不同东西(s12 才是后者)。

# 统一术语口径(本章)

  • PlanItem :单个会话计划项,包含内容与状态。
  • in_progress :当前唯一正在执行的步骤状态。
  • reminder :计划长期未更新时注入的提醒消息。

# 章节衔接(从易到难)

  • 本章解决 “会话内不跑偏”。
  • 下一章 s04 解决 “支线探索如何不污染主上下文”。