Skip to content

Latest commit

 

History

History
162 lines (108 loc) · 6.65 KB

File metadata and controls

162 lines (108 loc) · 6.65 KB

40. Agent 运行时生命周期:Background 与 Resume

所属分卷:卷五「Anthropic Agent 设计研究」

建议前读:11. 子代理与任务系统

建议后读:41. Agent 隔离:Worktree、Remote 与 CWD Override

研究问题

Claude Code 的 agent 为什么不是“一次调用,一次返回”,而是被做成可 foreground、background、resume 的长期执行单元?

一句话结论

Anthropic 把 agent 设计成任务对象,而不是普通函数调用:它可以先前台运行,再转后台,再通过 transcript 与 metadata 继续恢复,这样多步工程工作才能持续存在于会话之外。

这篇讲什么

这一章解释 agent 的生命周期协议,包括:

  • 前台启动
  • 进入 background
  • 任务注册
  • 通知回流
  • transcript 持久化
  • resumeAgentBackground() 如何继续既有 agent

如果你不看源码,只看这一章,应该记住什么

  • Claude Code 里的 agent 有自己的任务身份,不只是一次 tool result。
  • background 化之后,主线程看到的是通知和 output file,而不是持续流式共享上下文。
  • resume 不是“重新跑一遍”,而是基于 transcript、replacement state、worktree metadata 恢复执行。

源码依据

Mermaid 图:agent 生命周期状态图

flowchart TD
    A["spawn agent"] --> B{"立即后台?"}
    B -- 是 --> C["registerAsyncAgent"]
    B -- 否 --> D["foreground running"]
    D --> E{"用户或系统转后台?"}
    E -- 否 --> F["同步完成并返回结果"]
    E -- 是 --> G["registerAgentForeground -> background signal"]
    G --> C
    C --> H["runAsyncAgentLifecycle"]
    H --> I["写 transcript / metadata / output file"]
    I --> J["task-notification 回流主线程"]
    J --> K{"是否继续"}
    K -- resume/send message --> L["resumeAgentBackground"]
    L --> H
Loading

Anthropic 在这里想解决什么

如果 agent 只能同步执行,那么 Claude Code 在复杂工程任务里会有两个严重问题:

  • 主线程会被长任务阻塞
  • agent 完成后很难被继续利用其已有上下文

Anthropic 选择把 agent 建成“可持续存在的任务”,这样主线程可以继续工作,而 agent 则以 transcript 和 task state 的形式留在系统里。

前台 agent 为什么也要先注册

src/tools/AgentTool/AgentTool.tsx 有一个容易被忽略的点:前台 agent 也会在运行够久后走 registerAgentForeground()

原因是系统需要给用户两个能力:

  • 如果任务跑久了,可以切到后台
  • 如果开启 auto-background,也可以自动转后台

也就是说,foreground 不是和任务系统分离的另一条实现,而是后台生命周期的一个前置阶段。

BackgroundHint 暴露了怎样的运行逻辑

同一个文件里有 PROGRESS_THRESHOLD_MS = 2000BackgroundHint UI。这个提示并不是装饰,它意味着:

  • agent 已经被视为潜在可后台化对象
  • 系统在等待一个“是否继续占用前台”的决策点

这也解释了为什么 Claude Code 的任务系统和 REPL UI 是紧耦合的。

async agent 的最小公共接口

outputSchemaregisterAsyncAgent() 能看到后台 agent 的最小公共面:

  • agentId
  • description
  • prompt
  • outputFile
  • 任务状态与 summary

这说明 Claude Code 对后台 agent 的抽象不是“返回 future”,而是“创建一个有 identity 的任务对象”。

runAsyncAgentLifecycle 真正承担什么

runAsyncAgentLifecycle() 是一个很关键的中枢。它负责:

  • 驱动 agent stream 到结束
  • 更新进度
  • 失败/终止/完成时写状态
  • 发出 task-notification
  • 在合适时机触发 summarization

这让 agent 的长期运行具备统一协议,而不是每条 spawn 路径自己处理完成通知。

resume 不是重新 spawn

src/tools/AgentTool/resumeAgent.ts 表明 resume 有一套专门的恢复流程:

  1. 读 transcript 和 metadata。
  2. 过滤空白 assistant message、孤儿 thinking、未解决 tool uses。
  3. 重建 content replacement state。
  4. 尝试恢复 worktree cwd。
  5. 对 fork agent 还要恢复父 system prompt。
  6. 以新的 user prompt 继续跑 runAsyncAgentLifecycle()

这不是“重新起一个长得像旧 agent 的新 agent”,而是 continuation。

为什么 fork resume 要特殊处理

resumeAgentBackground() 对 fork resume 的处理最说明 Anthropic 的细致程度:

  • 它必须恢复父线程的 rendered system prompt
  • 不能简单重新调用 getSystemPrompt()
  • 因为那会造成 prompt bytes 漂移,破坏 fork 的 cache-identical prefix

这说明 Anthropic 把恢复协议设计到了 prompt cache 这一层,不只是把 transcript 读出来。

output file 与 notification 的关系

源码里的设计不是让主线程持续订阅 agent stream,而是:

  • 日常状态主要看任务列表
  • 必要时可以读 outputFile
  • 结果完成后自动生成 <task-notification>

这是一种很工程化的折中:保留调试入口,但默认不让主线程持续被 agent 的工具噪声污染。

对 Anthropic agent 设计研究意味着什么

这一章最重要的研究意义在于:Anthropic 没有把 agent 当作一个“调用模型的封装函数”,而是当作一个带身份、状态、可恢复上下文和通知协议的长期工作单元。

这更像任务运行时,而不是普通 function calling。

这篇的工作结论

Claude Code 的 agent 生命周期设计说明,Anthropic 真正在构建的是一个可以持续调度、暂停、恢复、汇报的 agent runtime。background 和 resume 不是附加功能,而是这套系统能处理真实工程任务的前提。

延伸阅读