C# 实现简版 Claude Code | 用 Todo 对抗遗忘(3)
❝该系列文章基于
github.com/shareAI-lab/learn-claude-code写就,该仓库以大道至简的风格剖析了Claude Code的核心原理,值得大家学习。由于该仓库是基于Python语言,为方便.NET开发者学习,我已经将代码基于.NET 10的dotnet file重写,源码已上传至github,源码地址见文末。

v2: 用 Todo 实现自我约束
❝本文是 Learn Claude Code (C# 版) 系列的第三篇,对应代码文件
v2_todo_agent.cs。
问题:上下文衰减
v1 对简单任务很好用。但试试这个:
You: 重构 auth 模块、添加单元测试、更新 API 文档观察模型的行为:
开始重构 auth...
突然想起要写测试,切换过去...
写了一半测试,想起还没看完 auth 代码...
10 次工具调用后:"等等,我在干什么来着?"
这就是上下文衰减——计划只存在于模型的"脑海"中,随着对话变长逐渐模糊。
解决方案:让计划显式化
v2 添加一个新工具 TodoWrite,从根本上改变工作方式:
Before (v1):
模型内部:"我先做 A,然后 B,然后 C" (不可见)
10 次工具调用后:计划已模糊After (v2):
[ ] 重构 auth 模块
[>] 添加单元测试 <- 当前正在做这个
[ ] 更新 API 文档
(1/3 completed)现在你和模型都能看到计划。模型可以:
随时查看进度
一次只做一件事
完成后更新状态
TodoManager 设计
核心数据结构
record TodoItem(string Content, string Status, string ActiveForm);
class TodoManager
{
private List<TodoItem> _items = [];
// Content: 任务描述,如 "添加单元测试"
// Status: pending | in_progress | completed
// ActiveForm: 现在进行时,如 "正在编写测试用例..."
}ActiveForm 是个精妙的设计——当任务在进行中时,显示模型正在做什么:
[>] 添加单元测试 <- 正在编写 AuthService 的测试用例...约束即护栏
public string Update(JsonElement itemsJson)
{
// ...验证逻辑...
if (validated.Count > 20)
throw new InvalidOperationException("Max 20 todos allowed");
if (inProgressCount > 1)
throw new InvalidOperationException("Only one task can be in_progress");
}约束 | 原因 |
|---|---|
最多 20 项 | 防止无限任务列表 |
一个 in_progress | 强制一次只做一件事 |
必填 activeForm | 确保可见性 |
这些不是限制,是护栏——它们让复杂任务成为可能。
渲染输出
public string Render()
{
// [x] Completed task
// [>] In progress task <- Doing something...
// [ ] Pending task
//
// (2/3 completed)
}模型看到这个渲染结果,就能理解当前状态。
TodoWrite 工具定义
new Tool
{
Name = "TodoWrite",
Description = "更新任务列表。用于规划和追踪进度。",
InputSchema = new InputSchema
{
Type = "object",
Properties = new Dictionary<string, JsonElement>
{
["items"] = JsonDocument.Parse("""
{
"type": "array",
"items": {
"type": "object",
"properties": {
"content": {"type": "string"},
"status": {"type": "string", "enum": ["pending", "in_progress", "completed"]},
"activeForm": {"type": "string"}
},
"required": ["content", "status", "activeForm"]
}
}
""").RootElement
},
Required = ["items"]
}
}注意:模型每次发送完整列表,不是增量更新。这简化了状态管理。
软提示机制
强制要求模型用 Todo 会很烦人。v2 用"软提示"来引导:
初始提醒
const string INITIAL_REMINDER = "<reminder>对于多步骤任务请使用 TodoWrite。</reminder>";
// 在第一条用户消息前注入
if (firstMessage)
{
content.Add(ContentBlock.CreateText(INITIAL_REMINDER));
firstMessage = false;
}持续提醒
int roundsWithoutTodo = 0;
const string NAG_REMINDER = "<reminder>10+ 轮未更新 todo。请更新 todos。</reminder>";
// 在 Agent 循环中
if (usedTodo)
roundsWithoutTodo = 0;
else
roundsWithoutTodo++;
if (roundsWithoutTodo > 10)
results.Insert(0, ContentBlock.CreateText(NAG_REMINDER));这是"软约束"——不强制,但引导。模型仍然可以选择不用 Todo,但会收到温和的提醒。
实际工作流程
You: 重构 auth 模块、添加测试、更新文档
<reminder>对于多步骤任务请使用 TodoWrite。</reminder>
好的,我来规划这个多步骤任务。
> TodoWrite
[ ] 重构 auth 模块
[ ] 添加单元测试
[ ] 更新 API 文档
(0/3 completed)
首先标记第一个任务为进行中:
> TodoWrite
[>] 重构 auth 模块 <- 正在分析现有代码结构...
[ ] 添加单元测试
[ ] 更新 API 文档
(0/3 completed)
让我先看看 auth 模块的代码:
> read_file: {"path":"src/Auth/AuthService.cs"}
...
[分析后进行重构]
> edit_file: {...}
Edited src/Auth/AuthService.cs
重构完成,更新 todo:
> TodoWrite
[x] 重构 auth 模块
[>] 添加单元测试 <- 正在为 AuthService 编写测试...
[ ] 更新 API 文档
(1/3 completed)
...继续下一个任务...深层洞察
约束既限制又赋能
这个模式在 Agent 设计中反复出现:
约束 | 赋能 |
|---|---|
max_tokens 限制 | 可管理的响应 |
工具 schema 限制 | 结构化调用 |
Todo 约束 | 复杂任务追踪 |
好的约束不是限制,是脚手架。
外部化记忆
TodoManager 本质上是把模型的"工作记忆"外部化:
内部记忆:模糊、会衰减
外部记忆(Todo):精确、持久
这是一种认知卸载——让模型专注于当前任务,不用担心忘记整体计划。
状态机视角
每个 TodoItem 是一个简单状态机:
pending -> in_progress -> completed"只能一个 in_progress" 的约束确保了线性执行——这对于 LLM 来说是最容易处理的模式。
运行示例
Mini Claude Code v2 (with Todos) - /path/to/project
输入 'exit' 退出。
You: 创建一个 REST API 项目:添加用户管理、产品管理、订单管理
好的,这是一个多步骤任务,我来规划:
> TodoWrite
[ ] 创建项目结构
[ ] 实现用户管理 API
[ ] 实现产品管理 API
[ ] 实现订单管理 API
(0/4 completed)
开始第一个任务:
> TodoWrite
[>] 创建项目结构 <- 正在初始化 ASP.NET Core 项目...
[ ] 实现用户管理 API
[ ] 实现产品管理 API
[ ] 实现订单管理 API
(0/4 completed)
> bash: {"command":"dotnet new webapi -n MyApi"}
The template "ASP.NET Core Web API" was created successfully.
...从 v2 到 v3
v2 解决了单个 Agent 的规划问题。但对于大型任务:
You: 探索整个代码库,然后重构 auth 模块问题:
探索阶段产生大量输出(
cat20 个文件)这些细节填满了上下文
真正重构时,关键信息已经"被挤出去"了
这就是上下文污染。v3 将引入子代理——通过上下文隔离来解决这个问题。
总结
v2 的哲学:
❝让计划显式化。约束赋能复杂性。
一个简单的 TodoManager + 一个 TodoWrite 工具,就让 Agent 能够处理多步骤任务。这是"外部化认知"的典型应用。
点击阅读原文,获取仓库地址:👇👇👇

更多推荐



所有评论(0)