Claude Code 的 Todo 和 Task 我的学习记录

发布时间:2026/8/11 4:56:47
Claude Code 的 Todo 和 Task 我的学习记录 背景我在仿写 Claude CodeCC的过程中读到 s12 课程里对 CC 任务系统的分析冒出一串疑问CC 的 Todo 和 Task 是什么关系是会话内二选一吗谁来决定用哪个决定后还能改吗……把这次自己的这些疑问和学习整理成文作为问题记录和学习记录。先给结论CC 中 Task System 和 TodoWrite 是两套独立的系统同时存在。一个会话只启用其中一套靠isTodoV2Enabled()这个开关切换。这个选择由 harness 层CC 运行环境在会话启动时决定LLM 没有决策权会话内不会中途切换。说明以上结论基于课程 README 对 CC 源码utils/tasks.ts、Task 系列工具、hooks/useTaskListWatcher.ts的分析非官方文档直接结论可能随版本变化。如需权威依据建议直接核对源码。问题一Todo 和 Task 是会话内二选一吗是的。两套系统独立存在一个会话只启用一套。但它们不是「一套的升级版」而是并行两套用开关切换。TodoWriteTask System定位当前任务的轻量执行清单可恢复的任务系统存储进程内内存~/.claude/tasks/{taskListId}/{id}.json每任务一个文件依赖无blockedBy/blocks依赖图生命周期当前会话跨会话持久化并发无保护文件锁、owner 认领问题二什么时候决定用哪个谁来决定会话启动时就确定了跟对话内容无关是确定性规则交互式会话终端里跑claude实时对话→ 默认Task (V2)非交互式 / SDK 调用 → 默认TodoWrite (V1)环境变量CLAUDE_CODE_ENABLE_TASKS→ 强制启用 Task不是大模型决定的。LLM 只是被动使用 harness 暴露给它的工具交互式会话里它只能看到TaskCreate/TaskGet/TaskUpdate/TaskList非交互式里只能看到TodoWrite/TodoRead。「用 todo 还是 task」这个问题对模型来说根本不存在它没有选择权。决定后会话内不会更改想换模式只能重开会话或用环境变量控制。问题三交互式会话和非交互式会话指的是什么不是「新版 vs 老版」是调用 CC 的方式不同交互式人在终端前敲claude进入持续对话界面有 spinner、权限确认弹窗、任务面板会话可以挂很久。CC 桌面客户端、IDE 插件都属于这一类。非交互式一次性调用没有持续对话界面没有人可点弹窗。典型场景claude -p ...print 模式跑完即退出、管道喂输入、CI/CD 流水线自动化调用、用 Agent SDK 在代码里驱动 CC 引擎。区分的关键是人在终端前实时对话 vs 脚本/流水线/代码自动调用。问题四保留 TodoWrite 只是为了兼容老场景吗一半是兼容一半是非交互场景根本用不上 Task 的重型能力。Task 比 TodoWrite 强在文件锁并发保护、owner 认领、fs.watch响应式监听、activeForm在 spinner 显示、生命周期 hooks。这些面向的是交互式 UI 和多 agent 协作。但在脚本 / CI 场景里没有界面需要fs.watch实时刷新没有 spinner 需要显示 activeForm没有多 agent 并发不需要 owner 和锁最要命的是Task 持久化到磁盘每次跑完会在~/.claude/tasks/留下一堆跨会话残留文件。TodoWrite 在内存里跑完即散零副作用。所以非交互场景用 TodoWrite不是因为「任务简单」而是因为「环境不需要那些重能力且轻量无副作用」。问题五为什么不给大模型自主权harness 层限定死这是最核心的问题。我的理解有三层模型对自己的运行环境一无所知。「用 todo 还是 task」取决于运行时环境——是不是 TUI、有没有fs.watch、任务要不要跨会话存活。这些只有 harness 知道让模型选等于让它猜。交互式场景恰恰需要 Task 的重能力不是「放弃了轻量的」。交互式会话能挂几天、有常驻任务面板、可能多 agent 协作。TodoWrite 的「轻」在这种场景下是不可用不是优点。给自主权的代价是全局不可预测性。模型有时用 todo、有时用 task进度散在两套系统里崩溃恢复直接失效行为不统一测试、hook、agent 都没法稳定依赖两套工具都暴露prompt 膨胀、缓存命中率下降。模型的自主权应该体现在「给定工具集里怎么编排」——规划顺序、决定要不要建任务、怎么拆解这些是完全自由的。但它不能决定「有哪些工具」那是环境层的事。问题六Task 功能完全覆盖 Todo为什么还要两套如果按「简单任务 vs 复杂任务」来分那确实冗余——交互式会话里 Task 完全能列简单的平铺清单。但分割线是运行环境不是任务复杂度。非交互场景直接用 Task 是纯开销 副作用见问题四。那为什么不做「一套系统 持久化开关」我判断是三个原因机制差异太大TodoWrite 一个调用、数组进出、无状态Task 是文件 锁 watcher hooks 的整套生态硬合并是把简单事做成 800 行。向后兼容SDK 老用户、hook、自建 agent 都引用TodoWrite这个名字直接摘掉会破坏现有生态。工具名本身就是环境提示暴露 Task 暗示「这里是持久任务系统」暴露 TodoWrite 暗示「这里是轻量即跑即弃」模型对所处环境的感知更准。总结Todo 和 Task 是两套独立系统会话内二选一由 harness 层按运行环境交互 / 非交互决定LLM 无决策权。交互式 Task非交互式 TodoWriteCLAUDE_CODE_ENABLE_TASKS可强制启用 Task。保留 TodoWrite 既有兼容成分也有「非交互场景用 Task 是纯开销」的机制合理性。不给模型自主权是因为「用哪套」是环境事实而非策略选项。对我自己开发的智能体简单来说没有非交互场景直接锁死用 Task 即可不必纠结两套并存也可以尝试把todo工具和task工具的区分在systemprompt中向大模型说明清楚让大模型自己根据简单或复杂场景进行灵活调用。如果你也想弄懂 Claude Code 这类 Coding Agent 到底是怎么工作的这个仓库也许能帮你少走一些弯路。目前我的github的项目OpenAI SDK 版已经更新到第十二课了我学习的课程为learn-claude-code的v2版本课程大家一起学习共同进步。如果对你有帮助请为我点一个Star谢谢。项目地址https://github.com/peijiping/learn-claude-code-langchain