深度解析 Claude Code 六个 Task 系列工具,涵盖任务列表数据模型、多任务追踪、跨 Agent 状态共享等场景,是官方最系统的任务管理文档。
这是我的 Claude Code 工具系列第十篇文章。前九篇涵盖了:
交互原语三件套——AskUserQuestion、EnterPlanMode 和 ExitPlanMode。
执行原语链——Grep + Glob → Read → Edit / Write。
通用后备方案:Bash。
元工具:Agent。
前九个工具都是关于 Claude 执行当下正在发生的事。每次工具调用都执行一个即时动作。真实项目还需要 Claude 记住需要做什么、跟踪进度、将大任务分解、以及在多个 Claude 之间共享一份检查清单。
这需要一个任务管理系统。Claude Code 的答案是 Task 工具家族——六个组成待办系统的工具。
本系列以一篇讲解工具是什么以及 Claude 如何使用它们的前置文章开头。本文与其他文章一样,遵循该文中介绍的的四层框架。
Task 工具家族:TaskCreate / TaskList / TaskGet / TaskUpdate / TaskStop / TaskOutput
这是本系列首次在一篇文章中同时审视六个工具。为什么要将它们归为一组?因为它们共享同一个数据模型——任务列表——并且在语义上紧密耦合。单独讨论其中一个会将注意力从系统转向单一操作。这就像解释如何创建一个 Jira 工单而不解释周围的 Jira 系统。
这个工具家族实际上包含两组:
前四个是对概念性待办任务的 CRUD 操作——即 Claude 记住需要去做的事。
后两个控制运行时任务——当前正在运行的真实 Bash 进程或子智能体。
它们都叫 Task,但操作的是不同的事物。这是该家族最令人困惑的设计选择,将贯穿整篇文章。
Task 工具家族——尤其是四个待办工具——解决了 Claude 如何在多次工具调用和跨时间维度上管理多步骤工作:
使分解可见:复杂需求变成条目,用户可以看到其进度。
跟踪进度:每个任务都有 pending、in_progress 或 completed 状态。
建模依赖关系:"A 阻塞 B"变得明确并强制执行顺序。
协调多个 Claude:主 Claude 分解工作,子智能体认领所有者,大家共享同一份列表。
压缩上下文:一个简短的主题可以代表一整块工作,减少主 Claude 必须在脑海中记住的内容。
与早期工具的关键区别在于:Task 是唯一具有持久状态的工具家族。Read、Edit 和 Bash 在一次工具调用中返回一次结果。而 TaskCreate 创建的条目保留在运行时中,并在后续的 TaskList 调用中持续出现,直到被完成或删除。
场景:用户说,"添加一个用户资料页面。它需要后端 API、前端组件、数据库 schema、测试和权限检查。"
这是一个多任务需求。
没有任务,Claude 只能:
在工作期间将计划保持在短期记忆中。
在聊天中宣布每一个下一步作为文本进度日志。
当对话变长时,最终遗漏一个需求——也许是测试。
每当用户问"进展到哪里了"时,重建整个对话。
核心问题在于检查清单只存在于 Claude 的短期上下文中。上下文压缩、子智能体交接或会话恢复都可能使其消失。
步骤 1:立即创建任务
TaskCreate(subject: "Design database schema", description: "Add profile fields to users or create a profiles table")
TaskCreate(subject: "Write migration", description: "Generate the Knex migration")
TaskCreate(subject: "Implement backend API", description: "GET/PATCH /api/profile through the auth middleware")
TaskCreate(subject: "Build ProfilePage", description: "Add the /profile route, form, and API submission")
TaskCreate(subject: "Add tests", description: "API tests plus frontend component tests")
每次调用返回一个 ID,如 task_001 到 task_005。
步骤 2:添加依赖关系
schema 必须在 API 之前存在,API 必须在前端之前存在。TaskUpdate 通过 blockedBy 表达这些关系:
TaskUpdate(taskId: "task_002", addBlockedBy: ["task_001"])
TaskUpdate(taskId: "task_003", addBlockedBy: ["task_002"])
TaskUpdate(taskId: "task_004", addBlockedBy: ["task_003"])
TaskUpdate(taskId: "task_005", addBlockedBy: ["task_003"])
现在列表形成了一个依赖图:schema → migration → API → (frontend + tests)。
步骤 3:找到下一个可用任务
task_001 · pending · Design database schema · blockedBy: []
task_002 · pending · Write migration · blockedBy: [task_001]
task_003 · pending · Implement backend API · blockedBy: [task_002]
task_004 · pending · Build ProfilePage · blockedBy: [task_003]
task_005 · pending · Add tests · blockedBy: [task_003]
只有 task_001 是 pending 且未被阻塞的,所以它是下一个任务。
步骤 4:认领、执行并完成
TaskUpdate(taskId: "task_001", status: "in_progress")
# Claude designs the schema and records the decision
TaskUpdate(taskId: "task_001", status: "completed")
一旦 task_001 完成,migration 任务就被解除阻塞。
步骤 5:将任务委托给子智能体
前端任务可以被委托:
Agent(
description: "Build ProfilePage",
prompt: "Task task_004: build the ProfilePage at /profile. Use TaskGet for the full details."
)
子智能体可以用任务 ID 调用 TaskGet,用 TaskUpdate 认领任务,并将其标记为完成。主 Claude 和子智能体通过共享任务系统协调,而不是发送临时消息。
步骤 6:报告进度
每当用户要求更新时,TaskList 就足够了:
✅ task_001 · completed · Design database schema
✅ task_002 · completed · Write migration
🔄 task_003 · in_progress · Implement backend API (Claude)
⏸️ task_004 · pending · Build ProfilePage (blocked by 003)
⏸️ task_005 · pending · Add tests (blocked by 003)
一个列表使状态一目了然。
早期工具是关于执行的。Task 工具家族是关于记忆的。它将 Claude 的短期计划移入运行时存储。
这产生了两个主要效果:
跨上下文持久化:任务在上下文压缩、切换和恢复中存活。
跨 Claude 共享:主 Claude 和子智能体通过 Task 系统同步,而不是手动互发消息。
这类似于人类工程团队将工作写入 Jira。这不是对个人记忆的否定;记忆是个人的,而任务是共享的。将它们写下来才能实现协作、跟踪和完整性。
该家族的提示提供了相当严格的指导:
三步或更多步的复杂工作。一步任务不需要任务条目。
nontrivial 的多操作工作。规划和跟踪在这里有价值。
用户明确要求待办列表。
一条指令中的多个需求。一起创建它们。
计划模式。跟踪计划的步骤。
开始工作时。在行动之前认领任务并将其标记为 in_progress。
完成工作时。立即将其标记为 completed 并检查新解除阻塞的任务。
不要将 Task 工具用于:
tracking 创建的噪音多于价值的微不足道的任务
少于三步的简单工作
纯对话或信息类回答
核心判断是:Task 工具家族适用于有重要意义规模的工作。如果一次工具调用就完成了任务,那么 Task 条目就是噪音。如果工作有分解、依赖关系或值得跟踪的进度,而不创建一个任务就是流程上的失败。
TaskCreate / TaskList / TaskGet / TaskUpdate / TaskStop / TaskOutput
共享的 Task 前缀取代了 Todo、Ticket 或 Job 等替代方案。"Task" 意味着一个明确的执行所有者;todo 只能意味着"某天看看这个"。这个名字本身暗示存在一个 owner 字段。
CRUD 后缀——Create、List、Get、Update——是标准的类数据库动词:创建一个、列出所有、检索一个、更新一个。这四个名称立即建立了一个可枚举、可寻址、可变的实体集合的心智模型。
特意没有设计 TaskDelete。硬删除通过 TaskUpdate(status: "deleted") 表示。删除被视为状态机中的终止状态,而不是单独的操作,将所有状态转换集中到 TaskUpdate 中,减少决策负担。
TaskStop 和 TaskOutput 引入了语义漂移。它们复用了 Task 命名空间,但操作的是运行中的后台进程——Bash 或子智能体——而不是概念性的待办任务。设计者选择了一个命名空间而非单独的运行时任务家族,但这也是该家族最明显的困惑来源。
activeForm 是该家族中最具野心的字段名。它不叫 presentContinuous、verbForm 或 spinnerLabel;activeForm 听起来很语法化。当 Claude 写入它时,这个名称会推动 Claude 将动作转换为现在进行时,而不是输入一个通用的 UI 标签。
在以下场景中主动使用此工具:复杂多步骤任务——当某个任务需要三个或更多独立步骤或操作时。
"三个或更多"是一个明确的阈值。它训练 Claude 不要为每个小操作都创建任务,用一个可衡量的规则取代了模糊的"复杂"一词。
收到新指令后——立即将需求捕获为任务。开始工作时——在开始之前将任务标记为 in_progress。完成之后——标记为 completed 并添加后续任务。
节奏是精确的:接收 → 创建;开始 → 进行中;完成 → 已完成。它包裹每一个工作段落,防止工作悄无声息地开始或结束。
只有在任务完全完成时才将其标记为 completed。如果存在错误、阻塞或未完成的工作,保持 in_progress 状态。切勿在测试失败、实现部分完成或存在未解决错误时标记为已完成。
这阻止了"虚假完成"——那种因为大方向看起来对就标记为完成、却留下半成品工作的倾向。
当有多个任务可工作时,按 ID 顺序(从小到大)处理。
较早的任务往往是后续任务的前置条件,因此 ID 顺序使默认调度与创建顺序一致。
在更新任务之前,先使用 TaskGet 读取任务的最新状态。
另一个 Agent 可能已经改变了该任务,特别是在多 Claude 工作流中。在写入之前获取最新状态是一种简单的乐观并发控制形式:先读后写,绝不盲目覆盖过期状态。
弃用说明:后台任务在工具结果和完成通知中返回输出文件路径。对于 Bash 任务,推荐在该输出路径上使用 Read。
工具描述直接说明不要使用它并给出了替代方案。这反映了一个更广泛的设计原则:如果现有原语可以覆盖某个能力,就不要为它维护一个独立的工具。更少的工具意味着更小的 API 表面积和更少的决策负担。
如果 Claude 很长一段时间没有使用任务工具,harness 可以插入一条系统提醒:
任务工具最近没有被使用。如果你的工作需要进度跟踪,请使用 TaskCreate 和 TaskUpdate。
这促使 Claude 进行进度跟踪,而不是强制要求。提醒以"仅在相关时使用"的等价表述结尾。早期工具不需要这个钩子,因为它们的价值是立竿见影的;Task 工具需要一个跨时间的推动。
id:系统生成唯一标识符subject:简短的祈使句标题,如"Run tests"description:详细说明activeForm:现在进行时形式,如"Running tests",用于 spinnerstatus:pending、in_progress、completed 或 deletedowner:执行工作的 Agent;空表示无人认领blocks:被此任务阻塞的任务blockedBy:阻塞此任务的任务metadata:任意键值数据同一任务以三种形式出现:
它们映射到不同的 UI 位置:
强制使用进行时形式不仅仅是外观上的。Claude 必须同时提供"要做什么"和"现在正在发生什么",编码了意图开始与已经开始之间的区别。
它们是同一关系的两个视图:
A blocks B ⇔ B is blockedBy A
运行时保持两个方向一致。Claude 可以用 addBlocks 或 addBlockedBy 添加一侧,另一侧自动同步。
这是为了可读调度语义的冗余:"我阻塞了什么"和"什么阻塞了我"对 Claude 来说是不同的问题,即使它们描述的是同一条边。
TaskUpdate 接受 addBlocks 和 addBlockedBy,而不是替换式的 blocks: [...]。因此添加一个依赖不可能意外擦除现有依赖。增量更新更安全,且天然具有幂等性。
pending → in_progress → completed
已完成的任务不能退回为 in_progress;如果需要重做工作,创建一个新任务。这防止了不可预测的状态振荡。
deleted 是错误任务的终端清理状态。已删除任务从正常列表中消失,但其 ID 保持保留以防止重用。因此删除是状态机的一部分,而不是从数据库中消失。
blockedBy 也约束状态转换。具有未完成依赖的任务不能被认领为 in_progress。Status 不是孤立字段;它是由当前依赖图控制的多字段转换。
owner 标识当前拥有任务的 Agent:
这是分布式工作队列的基本模式,Claude 实例作为消费者。
metadata 是一个自由格式的键值逃生舱,用于文件路径、参考链接、子 Agent 上下文或临时笔记。owner 是核心契约;metadata 留下了扩展空间。
该家族在 schema 层面静态检查与运行时状态机检查之间取得了平衡:
静态约束属于 schema;动态约束如依赖、并发和合法状态转换属于运行时。Task 家族比 Read 或 Edit 更加平衡:schema 保护输入,运行时保护转换。
TaskOutput 的弃用也说明了透明的回退。输出检索不是被一个新的专门工具取代;而是降级为对现有输出路径的 Read。现有的原语能够覆盖的能力不需要另一个工具。
Task 家族与 Agent 耦合最紧密:
Task 和 Bash 也有一个有用的类比。Bash 的 run_in_background 将命令放到后台;TaskCreate 将待办事项放入持久化存储。两者都防止主循环阻塞,但它们解决不同的问题:Bash 是异步机器 I/O,而 Task 是异步人机协作。
因此该家族在生态系统中的位置是独特的。前九个工具每次调用执行一个即时操作。Task 是一个元原语,存储了跨时间应该发生的事情。
Task 家族的优雅不在于存在一个待办事项列表。 而在于它的信号跨越四层并形成了一个完整的二元系统:
命名层面:六个工具——四个 CRUD 操作加 Stop 和 Output。activeForm 在字段名中编码了语法;以 status: "deleted" 代替省略 TaskDelete;输出检索回退到 Read。
工具级描述:每个工具有独立的 prompt,但它们相互引用并编码了协作契约——三步阈值、计时协议、禁止虚假完成、过期警告、弃用通知和提醒钩子。
字段级描述:subject、description 和 activeForm 映射到三个 UI 上下文;blocks 和 blockedBy 暴露了依赖的两个方向;带 add 前缀的字段防止破坏性替换;owner 是硬协作字段而 metadata 是灵活的逃生舱。
Schema 验证:静态约束如必需的 activeForm 和 status 枚举存在于 schema 中;动态约束如状态转换、依赖锁和过期更新存在于运行时。
Task 将 Claude Code 从现在时延伸到将来时。前九个工具现在做某事;Task 将必须发生的事情存储在跨工具调用、时间和 Claude 实例的运行时存储中。结果是从依赖心智努力对抗遗忘转向使用系统对抗遗忘。遗忘不再具有灾难性,因为列表仍然存在。
更深入的理解是,dual tool family 需要同时具备完整的生命周期和退出路径。CRUD 不是"只增不删":completed 终结正常生命周期,deleted 清理误建的任务,而 dependency updates 则释放被阻塞的工作。每个 task 都有一种明确的结束方式。
forced progressive activeForm 是这个工具家族最大胆的字段设计。它把"填写一个 UI 标签"变成了一种语法转换,训练 Claude 将工作视为正在发生,而非仅仅计划要做。这种区别就是"已启动"和"准备启动"之间的差异——也就是静态待办列表与实时工作节奏之间的差异。
下一篇文章将探讨 WebFetch + WebSearch,这对姊妹工具将把 Claude 的能力从文件系统一步异步任务扩展到外部网络:一个是"带 AI 的 curl",另一个是"带过滤器的搜索"。