所属分卷:卷五「Anthropic Agent 设计研究」
建议前读:11. 子代理与任务系统
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 恢复执行。
- 主入口与前后台分支:src/tools/AgentTool/AgentTool.tsx
- 实际 agent 执行:src/tools/AgentTool/runAgent.ts
- 恢复后台 agent:src/tools/AgentTool/resumeAgent.ts
- 后台任务状态:src/tasks/LocalAgentTask/LocalAgentTask.tsx
- 通用后台生命周期:src/tools/AgentTool/agentToolUtils.ts
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
如果 agent 只能同步执行,那么 Claude Code 在复杂工程任务里会有两个严重问题:
- 主线程会被长任务阻塞
- agent 完成后很难被继续利用其已有上下文
Anthropic 选择把 agent 建成“可持续存在的任务”,这样主线程可以继续工作,而 agent 则以 transcript 和 task state 的形式留在系统里。
src/tools/AgentTool/AgentTool.tsx 有一个容易被忽略的点:前台 agent 也会在运行够久后走 registerAgentForeground()。
原因是系统需要给用户两个能力:
- 如果任务跑久了,可以切到后台
- 如果开启 auto-background,也可以自动转后台
也就是说,foreground 不是和任务系统分离的另一条实现,而是后台生命周期的一个前置阶段。
同一个文件里有 PROGRESS_THRESHOLD_MS = 2000 和 BackgroundHint UI。这个提示并不是装饰,它意味着:
- agent 已经被视为潜在可后台化对象
- 系统在等待一个“是否继续占用前台”的决策点
这也解释了为什么 Claude Code 的任务系统和 REPL UI 是紧耦合的。
从 outputSchema 和 registerAsyncAgent() 能看到后台 agent 的最小公共面:
agentIddescriptionpromptoutputFile- 任务状态与 summary
这说明 Claude Code 对后台 agent 的抽象不是“返回 future”,而是“创建一个有 identity 的任务对象”。
runAsyncAgentLifecycle() 是一个很关键的中枢。它负责:
- 驱动 agent stream 到结束
- 更新进度
- 失败/终止/完成时写状态
- 发出
task-notification - 在合适时机触发 summarization
这让 agent 的长期运行具备统一协议,而不是每条 spawn 路径自己处理完成通知。
src/tools/AgentTool/resumeAgent.ts 表明 resume 有一套专门的恢复流程:
- 读 transcript 和 metadata。
- 过滤空白 assistant message、孤儿 thinking、未解决 tool uses。
- 重建 content replacement state。
- 尝试恢复 worktree cwd。
- 对 fork agent 还要恢复父 system prompt。
- 以新的 user prompt 继续跑
runAsyncAgentLifecycle()。
这不是“重新起一个长得像旧 agent 的新 agent”,而是 continuation。
resumeAgentBackground() 对 fork resume 的处理最说明 Anthropic 的细致程度:
- 它必须恢复父线程的 rendered system prompt
- 不能简单重新调用
getSystemPrompt() - 因为那会造成 prompt bytes 漂移,破坏 fork 的 cache-identical prefix
这说明 Anthropic 把恢复协议设计到了 prompt cache 这一层,不只是把 transcript 读出来。
源码里的设计不是让主线程持续订阅 agent stream,而是:
- 日常状态主要看任务列表
- 必要时可以读
outputFile - 结果完成后自动生成
<task-notification>
这是一种很工程化的折中:保留调试入口,但默认不让主线程持续被 agent 的工具噪声污染。
这一章最重要的研究意义在于:Anthropic 没有把 agent 当作一个“调用模型的封装函数”,而是当作一个带身份、状态、可恢复上下文和通知协议的长期工作单元。
这更像任务运行时,而不是普通 function calling。
Claude Code 的 agent 生命周期设计说明,Anthropic 真正在构建的是一个可以持续调度、暂停、恢复、汇报的 agent runtime。background 和 resume 不是附加功能,而是这套系统能处理真实工程任务的前提。