首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Claude Code 的任务列表是怎么实现的

Claude Code 的任务列表是怎么实现的

作者头像
CandyTong
发布2026-05-09 14:00:56
发布2026-05-09 14:00:56
180
举报

1. CLI 看到的任务列表是怎么样的

Claude Code CLI 里的任务列表,是当前请求的进度视图。它通常由多条任务项组成,每条任务项对应一个可以被跟踪的步骤。

一个典型任务列表可以理解成这样:

代码语言:javascript
复制
□ 修改登录错误提示
□ 补充登录错误提示测试

当模型开始处理第一项时,状态会变化:

代码语言:javascript
复制
◐ 修改登录错误提示
□ 补充登录错误提示测试

第一项完成后,又会变成:

代码语言:javascript
复制
✓ 修改登录错误提示
□ 补充登录错误提示测试

所以在单 Agent CLI 会话里,用户看到的不是一个后台任务调度队列,而是一份当前请求的结构化进度记录。它表达三个核心信息:

信息

含义

任务标题

当前请求被拆成了哪些步骤

任务状态

每一步是待处理、进行中还是已完成

当前进度

模型现在推进到哪一步

任务状态一般可以抽象成三类:

代码语言:javascript
复制
pending       待处理
in_progress   进行中
completed     已完成

这份任务列表的作用是让复杂请求变得可跟踪。它不直接修改代码、不运行命令,也不自动推进下一步;它只是保存“模型认为当前工作应该如何完成”的进度状态。

这篇文章只讨论单个 Claude Code CLI 会话里,模型如何通过任务工具维护用户看到的任务列表。团队协作、swarm、external tasks mode 里的共享任务队列、任务认领和跨进程调度不在本文范围内;那些场景会复用同一套任务数据结构,但职责比这里讲的单 Agent 进度视图更宽。

2. 任务列表是如何生成和更新的

任务列表由模型通过任务工具维护。模型不是在脑子里默默记住进度,而是通过工具把进度写成结构化状态。

这里的任务工具不是用户在终端里手动输入的 CLI 命令,而是模型在一次请求执行过程中可以调用的内部工具。CLI 里看到的列表,是这些工具读写后的任务状态再被界面渲染出来的结果。

这套工具可以分成四类。

工具

作用

TaskCreate

创建一个新的任务项,写入标题、描述、状态等字段

TaskUpdate

更新任务项,例如标记进行中、标记完成、修改描述、设置依赖

TaskList

查看当前任务列表摘要

TaskGet

查看某个任务的完整详情

一个复杂请求进来后,模型会先判断是否需要拆分。如果需要,它会调用 TaskCreate 创建任务项。创建出来的任务默认是待处理状态。

例如用户说:

代码语言:javascript
复制
帮我修改登录错误提示,并补一个测试。

模型可能创建两个任务:

代码语言:javascript
复制
#1 修改登录错误提示
#2 补充登录错误提示测试

接下来模型开始处理第一个任务,就调用 TaskUpdate 把它标记为 in_progress。修改完成并确认结果后,再调用 TaskUpdate 把它标记为 completed

如果模型需要重新确认当前还有哪些任务,可以调用 TaskList。如果某个任务的摘要不够,需要读取完整描述,可以调用 TaskGet

这几个工具共同完成一件事:让模型能够生成、读取和更新任务列表状态。

但这些工具本身不负责判断“代码是否真的改好了”,也不负责判断“现在是否该继续下一项”。这些判断仍然由模型根据工具返回结果来完成。

更细一点看,TaskCreate 会把新任务写成 pending 状态,并保存 subjectdescriptionactiveFormmetadata 等字段;TaskUpdate 会按任务 ID 读取现有任务,再合并要修改的字段,比如 statusownerblocksblockedByTaskList 只返回列表页需要的摘要信息;TaskGet 才返回单个任务的完整描述和依赖关系。

任务列表的维护过程可以概括成:

代码语言:javascript
复制
创建任务项
  -> 标记当前任务进行中
  -> 执行实际工作
  -> 根据结果更新任务状态
  -> 必要时读取任务列表决定下一步

2.1 任务状态如何持久化

在实现层,任务列表不是只存在于模型上下文里的自然语言文本,而是落到本地 JSON 文件里的结构化状态。

任务会写到配置目录下的 tasks 子目录中,路径为:

代码语言:javascript
复制
<claude-config-home>/tasks/<taskListId>/<taskId>.json

每个任务对应一个 JSON 文件。一个任务对象大致可以理解成这样:

代码语言:javascript
复制
{
  "id": "1",
  "subject": "修改登录错误提示",
  "description": "把登录失败时的提示改得更清楚",
  "activeForm": "修改登录错误提示",
  "status": "in_progress",
  "blocks": [],
  "blockedBy": [],
  "metadata": {}
}

创建任务时,系统会在任务列表级别加锁,读取已有任务文件和 .highwatermark,计算下一个任务 ID,然后写入新的 <taskId>.json.highwatermark 用来记住曾经分配过的最大 ID,避免任务被删除或列表 reset 后复用旧 ID。

更新任务时,系统会先确认任务文件存在,再对单个任务文件加锁,读取 JSON、合并字段、重新写回。这样做的目的不是让任务工具替模型决策,而是让任务状态成为一份模型可以反复读写、CLI 可以稳定渲染、长流程中也可以重新取回的外部进度记录。

3. 从用户输入到任务完成,发生了什么

用一个具体例子来看完整流程。

用户输入:

代码语言:javascript
复制
帮我修改登录错误提示,并补一个测试。

模型会把这个请求拆成两个任务:

代码语言:javascript
复制
#1 修改登录错误提示
#2 补充登录错误提示测试

然后模型按顺序推进:先修改实现,再补测试,最后汇总结果。

任务列表Claude Code CLI用户任务列表Claude Code CLI用户输入:修改登录错误提示,并补一个测试1模型思考:拆成两个可跟踪任务2TaskCreate:新增待处理任务 1:修改登录错误提示3TaskCreate:新增待处理任务 2:补充登录错误提示测试4TaskUpdate 任务1设为进行中5内部调用读文件/编辑工具,完成提示文案修改6TaskUpdate 任务1设为已完成7TaskUpdate 任务2设为进行中8内部调用编辑/命令工具,补测试并验证9TaskUpdate 任务2设为已完成10返回最终结果和验证说明11

这张图里有几个关键点:

  1. 1. 模型先理解请求,再决定是否拆成任务;不是所有请求都会生成任务列表。
  2. 2. TaskCreate 只负责新增单个任务,所以两个任务对应两次创建。
  3. 3. TaskUpdate 负责推进状态:设为进行中、设为已完成。

3.1 任务列表和 Agent loop 的关系

任务列表经常让人误以为:只要里面还有未完成任务,Claude Code 就会自动继续执行。

要解释这个误解,先要看 Agent loop 是什么。一次请求不会只经历“模型回答一次”这么简单;更接近真实情况的是:模型先根据当前上下文决定下一步,如果它需要动作,就发出工具调用;CLI 执行工具,把结果收集回来,再和已有上下文一起交给模型,让模型继续判断下一步。

在这个循环里,任务列表只是可被模型调用和读取的一类工具状态,和读文件、改文件、运行命令一样,都属于模型可以借助的外部能力。它能影响模型判断,但不是 Agent loop 的驱动器。

Agent loop 的基本关系可以理解为:

代码语言:javascript
复制
模型决定下一步
  -> 如果需要动作,就发出工具调用
  -> Claude Code CLI 执行对应工具
  -> 工具结果被收集起来
  -> 组装成下一轮模型上下文
  -> 模型再次决定下一步

对应到源码里的核心分支,可以抽象成下面这段伪代码:

代码语言:javascript
复制
while (true) {
  const response = await llm.chat(messages)  // 想:让模型决定下一步

  if (response.toolCalls.length === 0) {
    break  // 模型认为任务完成了,没有工具要调
  }

  for (const toolCall of response.toolCalls) {
    const result = await executeTool(toolCall)  // 做:执行工具
    messages.push(result)                        // 看:把结果加入上下文
  }
}

这段伪代码省略了错误恢复、上下文压缩、stop hook 等旁支,只保留和任务列表最相关的主线。

在这个循环里,任务列表工具只负责一件事:维护进度状态。

所以,在单 Agent 主循环里,任务列表不是 Agent loop 的驱动器,也不是后台调度器。它不会因为还有 pending 任务就自动触发下一步。

真正决定 Agent loop 是否继续的是:模型下一次输出的是工具调用,还是最终回复

如果模型看到任务列表里还有未完成项,并决定继续处理,它会继续发出工具调用;如果模型认为事情已经完成,就会输出最终回复,Agent loop 也就结束。

3.2 如何判断是否有未完成的任务

任务列表里是否还有未完成任务,可以通过任务状态判断。

只要还有 pendingin_progress,就说明任务列表里仍然存在未完成项:

代码语言:javascript
复制
#1 completed
#2 in_progress

这表示第一项完成了,第二项还在进行。

但这个判断本身有两种用途:

  1. 1. 用于展示层。CLI 可以根据任务状态决定任务列表是否还需要展示、是否还需要继续监听变化、是否可以在全部完成后隐藏。
  2. 2. 间接辅助模型决策

需要强调的是,执行层并不是这样工作的:

代码语言:javascript
复制
if 还有未完成任务:
    自动继续 Agent loop

真实情况并不是这样。更准确地说,任务列表状态会进入模型上下文,影响模型下一步输出;但 Agent loop 真正检查的是模型下一次响应里有没有工具调用。

也就是说,“是否继续”不是任务列表自己决定的。如果模型看完上下文后继续输出工具调用,Agent loop 就继续;如果模型没有输出工具调用,而是输出最终回复,Agent loop 就结束。

长上下文下模型忘记进度或产生幻觉怎么办

极端情况下,上下文很长,模型可能不再清楚当前任务列表的最新状态。比如它创建过两个任务,完成了第一个,但后续经过很多轮工具调用后,模型可能误以为第二个也已经处理完,或者忘记把第二个任务标记为进行中。

Claude Code 对这种情况有一个补偿机制:任务状态提醒。它的目的不是判断上下文够不够长,而是在长流程里定期把结构化任务状态重新放回模型视野,降低模型凭记忆猜进度的概率。

当系统发现模型已经有一段时间没有维护任务列表时,会把当前任务列表重新放回上下文,提醒模型检查进度。这里的“一段时间”不是按现实时间计算,也不是按上下文长度直接触发,而是按模型响应轮次计算:如果距离上次 TaskCreate / TaskUpdate 已经经过足够多轮,同时距离上次提醒也经过足够多轮,就可以生成一次 task_reminder

这个补偿发生在 Agent loop 的中间位置:工具执行完成之后、下一轮模型调用之前

更具体地说,一轮模型输出工具调用后,Claude Code 会先执行这些工具,并把工具结果收集起来。随后系统会检查是否需要补充上下文附件;如果满足任务状态提醒条件,就生成一条任务提醒附件,把当前任务列表放进去。最后,Claude Code 再把工具结果和这些附件一起组装进下一轮上下文,发起下一次模型调用。

继续用上一节的伪代码看,它不是在任务刚创建时立即提醒,也不是在最终回复之后提醒,而是在这里补偿:

代码语言:javascript
复制
while (true) {
  const response = await llm.chat(messages)  // 想:让模型决定下一步

  if (response.toolCalls.length === 0) {
    break  // 模型认为任务完成了,没有工具要调
  }

  for (const toolCall of response.toolCalls) {
    const result = await executeTool(toolCall)  // 做:执行工具
    messages.push(result)                        // 看:把结果加入上下文
  }

  // 补偿点:工具调用完成后、下一轮模型调用前。
  // 系统会在这里检查是否需要补充 task_reminder,
  // 再把提醒一起放进下一轮上下文。
}

这个提醒大致起到这样的作用:

代码语言:javascript
复制
当前还有任务列表状态。
如果这些任务仍然和当前工作相关,请考虑更新它们。

它不会替模型完成判断,也不会强制模型继续执行。它只是把任务进度重新放回模型视野,让模型少依赖模糊记忆,多依赖刚刚注入上下文的结构化状态,从而降低长上下文中忘记进度或凭空补全进度的概率。

所以长上下文下的处理方式可以概括为:

代码语言:javascript
复制
任务列表保存状态
工具执行完成后,系统在进入下一轮模型调用前重新提醒
模型重新看到状态后决定是否继续或更新

4. 总结

Claude Code 的任务列表实现,关键不在于“列出几个待办”,而在于把任务进度从模型脑海里的临时判断,变成一份可以被创建、更新、读取的结构化状态。

这样做有三个直接结果。

第一,任务进度变得可见。CLI 能把当前有哪些任务、做到哪一步、哪一项正在进行中,稳定地展示给用户,而不是完全依赖模型临时生成一段自然语言说明。

第二,任务进度变得可操作。模型不是只能“记得自己做到哪”,而是可以通过 TaskCreateTaskUpdateTaskListTaskGet 明确地创建任务、推进状态、重新读取现状。这样任务列表就不只是显示层,而是 Agent loop 里可被模型反复使用的一份外部状态。

第三,任务进度变得可恢复。随着上下文变长,模型未必始终清楚当前还剩哪些任务;但因为任务列表已经被工具写成了结构化记录,Claude Code 就可以在合适的时候把它重新注入上下文,提醒模型恢复对当前进度的视野。

因此,这套机制的分工其实很清楚:

  • 任务列表:保存结构化进度
  • 任务工具:负责创建、更新、读取这份进度
  • 模型:基于当前上下文的进度决定下一步
  • Agent loop:负责执行工具调用,并把结果推进到下一轮上下文

用一句话收束就是:

代码语言:javascript
复制
任务列表把进度状态显式保存起来;长流程中多轮未维护任务工具时,系统还会把当前任务列表重新注入上下文;模型结合整体上下文决定下一步,Agent loop 负责执行模型产出的工具调用。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Candy 的修仙秘籍 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. CLI 看到的任务列表是怎么样的
  • 2. 任务列表是如何生成和更新的
    • 2.1 任务状态如何持久化
  • 3. 从用户输入到任务完成,发生了什么
  • 3.1 任务列表和 Agent loop 的关系
  • 3.2 如何判断是否有未完成的任务
    • 长上下文下模型忘记进度或产生幻觉怎么办
  • 4. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档