不是每一次 agent 交互都是聊天。有时 agent 正在执行多步骤计划,而展示进度的最佳方式是一个实时更新的 todo list。Deep agent todo list pattern 会直接从 agent 的 state 读取 todos array,并在 agent 推进计划时,用每个 item 的当前 status 渲染它。这是基于你用于聊天的同一个 useStream hook 构建的进度 dashboard。它展示了 agent state 可以驱动任何 UI,而不仅是 message bubbles。

工作原理

Deep agents 包含内置 todos state,用于在 agent 推进计划时跟踪任务进度。随着 agent 执行,它会把每个 todo 的 status 从 "pending" 更新为 "in_progress",再更新为 "completed"useStream hook 通过 stream.values.todos 暴露该 state,你的 UI 会响应式地渲染它。 流程如下:
  1. User 提交 request
  2. Agent 创建 plan,并在其 state 中填充 todos
  3. Agent 开始执行,每个 todo 都会经历 pendingin_progresscompleted
  4. stream.values.todos 会随着 agent 推进实时更新
  5. 你的 UI 使用当前 status 重新渲染 todo list

设置 useStream

不需要特殊配置。将 useStream 指向你的 agent,并从 stream.values 读取 todos
The code examples use useStream<typeof myAgent> for type-safe stream state. See Type inference for Python or JavaScript backends.
import { useStream } from "@langchain/react";

const AGENT_URL = "http://localhost:2024";

export function TodoAgent() {
  const stream = useStream<typeof myAgent>({
    apiUrl: AGENT_URL,
    assistantId: "deep_agent_todo_list",
  });

  const todos = stream.values?.todos ?? [];

  return (
    <div>
      <TodoList todos={todos} />
      {stream.messages.map((msg) => (
        <Message key={msg.id} message={msg} />
      ))}
    </div>
  );
}

构建 TodoList component

Todo list 会使用 status icon、color coding 和反映当前 state 的视觉样式渲染每个 item:
function TodoList({ todos }: { todos: Todo[] }) {
  const completed = todos.filter((t) => t.status === "completed").length;
  const percentage = todos.length
    ? Math.round((completed / todos.length) * 100)
    : 0;

  return (
    <div className="rounded-lg border bg-white p-4 shadow-sm">
      <div className="mb-4 flex items-center justify-between">
        <h2 className="text-lg font-semibold">Agent Progress</h2>
        <span className="text-sm text-gray-500">
          {completed}/{todos.length} tasks
        </span>
      </div>

      <ProgressBar percentage={percentage} />

      <ul className="mt-4 space-y-2">
        {todos.map((todo, i) => (
          <TodoItem key={i} todo={todo} />
        ))}
      </ul>
    </div>
  );
}

Progress bar

可视化 progress bar 可以让 users 一眼看到总体完成情况:
function ProgressBar({ percentage }: { percentage: number }) {
  return (
    <div className="space-y-1">
      <div className="flex items-center justify-between text-xs text-gray-500">
        <span>Progress</span>
        <span>{percentage}%</span>
      </div>
      <div className="h-2 overflow-hidden rounded-full bg-gray-200">
        <div
          className="h-full rounded-full bg-green-500 transition-all duration-500"
          style={{ width: `${percentage}%` }}
        />
      </div>
    </div>
  );
}

单个 todo item

每个 item 都会有 status icon、按颜色区分的 text,以及用于 completed tasks 的删除线样式:
function TodoItem({ todo }: { todo: Todo }) {
  const config = {
    pending: {
      icon: "○",
      textClass: "text-gray-600",
      bgClass: "bg-gray-50",
      iconClass: "text-gray-400",
    },
    in_progress: {
      icon: "◉",
      textClass: "text-amber-800",
      bgClass: "bg-amber-50 border-amber-200",
      iconClass: "text-amber-500 animate-pulse",
    },
    completed: {
      icon: "✓",
      textClass: "text-green-800 line-through",
      bgClass: "bg-green-50 border-green-200",
      iconClass: "text-green-500",
    },
  };

  const style = config[todo.status];

  return (
    <li
      className={`flex items-start gap-3 rounded-md border px-3 py-2 ${style.bgClass}`}
    >
      <span className={`mt-0.5 text-lg leading-none ${style.iconClass}`}>
        {style.icon}
      </span>
      <span className={`text-sm ${style.textClass}`}>{todo.content}</span>
    </li>
  );
}
in_progress icon 使用 animate-pulse 来吸引 user 注意当前 active task。

计算进度

直接从 todos array 派生 progress metrics:
const todos = stream.values?.todos ?? [];

const completed = todos.filter((t) => t.status === "completed").length;
const inProgress = todos.filter((t) => t.status === "in_progress").length;
const pending = todos.filter((t) => t.status === "pending").length;
const percentage = todos.length
  ? Math.round((completed / todos.length) * 100)
  : 0;
这些 values 会随着 agent 修改 state 而响应式更新,让 progress bar 和 counters 保持同步。

与 chat messages 结合

Todo list 可以与常规 chat interface 并行使用。实用布局会把 todo list 显示为持久 sidebar 或 header panel,并在下方显示 chat messages:
function TodoAgentLayout() {
  const stream = useStream<typeof myAgent>({
    apiUrl: AGENT_URL,
    assistantId: "deep_agent_todo_list",
  });

  const todos = stream.values?.todos ?? [];

  return (
    <div className="flex h-screen flex-col">
      {todos.length > 0 && (
        <div className="border-b bg-gray-50 p-4">
          <TodoList todos={todos} />
        </div>
      )}

      <main className="flex-1 overflow-y-auto p-6">
        <div className="mx-auto max-w-2xl space-y-4">
          {stream.messages.map((msg) => (
            <Message key={msg.id} message={msg} />
          ))}
        </div>
      </main>

      <ChatInput
        onSubmit={(text) =>
          stream.submit({ messages: [{ type: "human", content: text }] })
        }
        isLoading={stream.isLoading}
      />
    </div>
  );
}
仅当 todos.length > 0 时显示 todo list。在 agent 创建 plan 前,没有内容可显示。显示空 component 会浪费空间。

Use cases

Todo list pattern 适合任何 agent 执行 structured plan 的场景:
  • Project planning:Agent 将 project 拆成 tasks,并按顺序完成
  • Research workflows:每个 research question 都会成为一个 todo,由 agent 调查并完成
  • Data processing:Ingestion、validation、transformation 和 export 等 steps 都有自己的 todo
  • Onboarding flows:Agent 逐步完成 setup steps,并在配置 services 时逐项勾选
  • Report generation:Report 的 sections 会成为 todos:gather data、analyze trends、write summary、format output

处理 empty 和 loading states

处理 agent 尚未创建 plan 时的 initial state:
function TodoList({ todos, isLoading }: { todos: Todo[]; isLoading: boolean }) {
  if (todos.length === 0 && !isLoading) {
    return null;
  }

  if (todos.length === 0 && isLoading) {
    return (
      <div className="rounded-lg border bg-white p-4 shadow-sm">
        <div className="flex items-center gap-2 text-sm text-gray-500">
          <span className="animate-spin"></span>
          Agent is creating a plan...
        </div>
      </div>
    );
  }

  return (
    <div className="rounded-lg border bg-white p-4 shadow-sm">
      {/* ... full todo list rendering */}
    </div>
  );
}

Best practices

  • 突出显示 todo list。它是 plan-based agents 的主要进度指标。不要把它埋在首屏以下。
  • 为 status transitions 添加动画。平滑 transitions 会让 agent 显得响应更及时。对 background color、text decoration 和 opacity 使用 CSS transitions。
  • 只突出一个 in_progress item。Agents 通常一次处理一个 task。如果多个 items 都显示为 in_progress,UI 会变得嘈杂。可以考虑只让第一个 item pulse。
  • 折叠或弱化 completed items。随着 list 增长,completed items 的相关性会降低。降低它们的视觉权重,让 users 聚焦仍在发生的事情。
  • 显示 progress percentage。像“67% complete”这样的单个数字很容易理解,即使隔着距离也能看懂。
  • 保持 todo list 同步。因为 stream.values 会响应式更新,todo list 会自动保持最新。不要添加手动 polling 或 refresh logic。