Typography

学习笔记

Claude Agent SDK学习20 TODO进度跟踪

发布于 # Claude Agent SDK

当前 Agent 的问题

第十九章之后,你的 agent 已经具备了强大的治理和观测能力,但从最终用户角度看,它仍然可能像一个黑盒。

用户会问:

内部有 trace 和日志不代表用户能理解任务进度。Todo 跟踪解决的是“对用户可见的执行进度”。

本章功能的作用

这一章会引入:

这一层解决的是“用户能不能看懂 agent 现在在干什么”。即使你的系统内部已经有日志和 trace,如果前端用户看不到任务正在推进哪些步骤,长任务体验依然会很差。

官方文档也给了一个比较明确的触发经验:Todo 通常更容易出现在需要 3 个以上明显步骤的复杂任务里,或者用户自己明确给出了一组待办项。因此它不是一个每轮都会出现的信号,而是“任务复杂度足够高时,Claude 主动提供的进度结构”。

具体使用方式

第一步:给 Claude 一个足够复杂的任务

TodoWrite 通常出现在多步骤任务里。要观察这项能力,prompt 最好不是一句简单问答,而是一个需要规划、拆分、推进的复杂任务。

这是一个非常实际的前提条件。Todo 不是每次都会出现,它通常只在 Claude 判断“有必要把任务拆成几个阶段持续跟踪”时才会产生。因此,示例任务本身必须足够复杂,才能让这项能力有发挥空间。

Todo 的生命周期也比很多人想的更明确:通常是先以 pending 创建,进入执行时变成 in_progress,完成后变成 completed,最后整组任务结束时可能会被移除。理解这条状态流后,你的前端才能正确映射“已完成多少、当前在做什么、还有没有剩余任务”。

第二步:在 assistant 消息中查找 tool_use

Todo 不是独立消息类型,而是 assistant 内容块中的工具调用。因此你需要遍历 message.message.content,寻找 type === "tool_use"name === "TodoWrite" 的块。

第三步:读取每个 todo 的状态和内容

一旦拿到 TodoWrite,就可以从 block.input.todos 中提取待办项列表。每一项至少包含状态和文本内容,这已经足够驱动一个基础进度面板。

第四步:把 todo 状态映射成用户可读 UI

最简单的做法是打印 pendingin_progresscompleted;更完整的做法可以映射成进度条、步骤列表或 IDE 侧边栏状态。

关键概念

1. Todo 不是额外 API,而是消息流的一部分

Claude 在适当场景下会通过 TodoWrite 工具块更新待办列表。

2. Todo 的典型生命周期

3. 为什么它很重要

对于复杂任务,Todo 能显著改善“用户是否知道系统还在工作”的体验。

可运行示例

把下面代码保存为 chapter-20-todos.ts

import { query } from "@anthropic-ai/claude-agent-sdk";

async function main() {
  for await (const message of query({
    prompt: "Plan and describe how to optimize a React application's performance, and track progress with todos.",
    options: {
      maxTurns: 15
    }
  })) {
    if (message.type === "assistant") {
      for (const block of message.message.content) {
        if (block.type === "tool_use" && block.name === "TodoWrite") {
          console.log("\nTodo update:");
          for (const todo of block.input.todos) {
            console.log(`- ${todo.status}: ${todo.content}`);
          }
        }
      }
    }

    if (message.type === "result") {
      console.log("\nFinal result:\n");
      console.log(message.result);
    }
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

运行:

npx tsx chapter-20-todos.ts

示例拆解

第一步:使用一个天然适合拆步骤的性能优化任务

示例 prompt 要求 Claude 规划并说明 React 性能优化方案,这类任务很容易触发待办拆分,因此适合教学演示。

第二步:把 maxTurns 拉高,给 Claude 足够推进空间

如果最大轮数太低,Claude 可能还没来得及建立或更新 todo 列表,任务就被截断了。示例中的 maxTurns: 15 就是在给它足够的执行空间。

第三步:在 assistant 消息里提取 TodoWrite

示例逐个遍历内容块,并在发现 TodoWrite 时立刻打印所有 todo。这里展示的是最直接的“消息流转 UI 状态”的路径。

第四步:把最终自然语言结果和中间 todo 更新分开显示

示例中 todo 更新会先出现在过程里,而最终结论在 result 中统一打印。这种分离方式非常适合真实产品中的“进度区 + 结果区”界面设计。

运行时你应该观察什么

易错点

本章结束后你应该掌握

本章小结

到这里,你的 agent 已经不只是“强大”,还开始变得“对用户透明”。这对长任务体验尤为重要。