深入 Claude Code 源码(六):多智能体——Coordinator 与 AgentTool 深度解析

深入 Claude Code 源码(六):多智能体——Coordinator 与 AgentTool 深度解析
单个 AI Agent 的局限是明显的上下文窗口是固定的一次只能处理有限量的信息任务是串行的做完一件事才能开始下一件注意力是单一的无法同时从多个角度分析同一个问题。当任务足够复杂——比如同时重构多个模块、并行跑多条研究思路、让一个「策略层」和多个「执行层」分工合作——单个 Agent 就开始捉襟见肘了。Claude Code 对这个问题的答案是让多个 Claude 实例协同工作。具体有两种实现方式一种是工具级别的子代理AgentTool另一种是进程级别的协调者模式Coordinator Mode。本文带大家把这两套机制都搞清楚。一、两种多智能体模式的本质区别先把这两种模式的区别说清楚因为它们经常被混淆。AgentTool工具调用子代理主代理通过调用Agent工具工具名Agent来派发子任务。子代理在一个独立的queryLoop里运行有自己的消息历史和上下文完成后把结果返回给主代理主代理继续它的任务。这本质上是一种串行嵌套——主代理暂停等子代理完成再继续。Coordinator Mode协调者模式通过环境变量CLAUDE_CODE_COORDINATOR_MODE1激活。此时 Claude 变成一个「协调者」它不直接执行代码而是把任务分发给一组可以真正并行运行的 worker agents工人代理。Worker agents 有各自独立的进程可以同时执行协调者统筹全局。用一个建筑工程的比喻AgentTool 像一个包工头在现场盯着活干做完一道工序再做下一道Coordinator Mode 像一个项目经理把土建、水电、装修三队同时派出去自己负责协调进度和验收结果。二、AgentTool子代理的完整生命周期src/tools/AgentTool/AgentTool.tsx是子代理系统的核心。子代理的创建forkSubagent()当主代理调用Agent工具时forkSubagent()函数负责创建一个新的执行上下文从当前会话的工具列表里按照白名单过滤出子代理可用的工具集子代理默认不继承所有工具创建一个新的AbortController并绑定到主代理的 abort 信号——主代理被中断时所有子代理也会联动中断分配一个新的agentIdUUID用于标识这个子代理的 session、日志、文件产出在这个独立上下文下运行query()循环就像一个完整的会话只是上下文隔离子代理的工具限制子代理的工具集比主代理受限有两层控制通用限制防止子代理再创建子代理避免无限递归AgentTool在子代理的工具列表里默认被排除Coordinator Mode 限制如果当前是协调者模式worker agent 只能使用ASYNC_AGENT_ALLOWED_TOOLS白名单里的工具这个白名单来自coordinatorMode.ts专门设计来防止 worker「越权」子代理的进度可视化agentColorManager.ts为每个子代理分配一个颜色。终端 UI 里主代理的输出和每个子代理的输出会用不同颜色区分大家一眼就能看出「这条 Bash 命令是哪个代理执行的」。这在调试多代理任务时极大地降低了认知负担。子代理的 Resume子代理也有自己的 session ID产出的 transcript 文件存储在独立路径下。通过resumeAgent()可以在主进程重启后恢复一个中途中断的子代理让它继续未完成的任务。三、Coordinator Mode协调者的工作机制Coordinator Mode 是一个更复杂的多代理协作框架通过bun:bundle的条件编译开关COORDINATOR_MODE保护只在有对应 feature flag 的构建里存在。如何激活// src/coordinator/coordinatorMode.tsexportfunctionisCoordinatorMode():boolean{if(feature(COORDINATOR_MODE)){returnisEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)}returnfalse}设置环境变量CLAUDE_CODE_COORDINATOR_MODE1即可退出设置0或删除变量。协调者的 System Prompt 注入getCoordinatorUserContext()会在协调者的 user context 里注入特殊指令大意是你是一个协调者你的职责是分解任务、分配给 worker agents、汇总结果。不要自己直接执行代码或编辑文件把具体工作交给 workers。这段指令通过QueryEngine.submitMessage()里的userContext注入而不是放在systemPrompt里是为了避免影响工具列表的序列化格式。协调者与 Worker 的通信工具协调者的工具箱里有几个专用工具工具作用TeamCreateTool创建一组 worker agents启动并行执行TeamDeleteTool解散 worker 团队等待最终结果SendMessageTool向特定 worker 发送指令或接收其回复这三个工具在普通的非协调者会话里不可见只有isCoordinatorMode()为 true 时才会被注入到工具列表。Scratchpad 共享目录协调者和 worker agents 之间传递大型中间结果比如分析报告、代码片段通过一个共享的 scratchpad 目录完成。这个目录通过CLAUDE_CODE_SCRATCHPAD_DIR环境变量指定或者由 Statsig feature gatetengu_scratch控制是否启用。Worker 把结果写到 scratchpad协调者读取这样就不需要把大量内容都放进消息里避免撑爆各自的 context。四、完整的多智能体协作架构把两种模式放在一起整体架构如图所示AgentTool 子代理串行嵌套Worker Agent 2并行Worker Agent 1并行协调者进程TeamCreateTool SendMessageToolTeamCreateTool SendMessageToolAgentTool 调用写入 scratchpad写入 scratchpad读取 scratchpadSendMessageTool 回复SendMessageTool 回复Coordinator Claude分析任务、制定分工Worker Claude执行子任务 A独立进程独立 contextWorker Claude执行子任务 B独立进程独立 contextSub-agent Claude在 Worker 内部执行更细粒度子任务Scratchpad共享文件目录可以看到两种模式可以组合使用——Coordinator 用TeamCreateTool启动并行 WorkersWorkers 内部再用AgentTool派发更细粒度的子代理形成两级的层次结构。五、任务系统Task Tools独立于 AgentTool 和 Coordinator Mode 之外Claude Code 还有一套异步任务系统对应TaskCreateTool、TaskGetTool、TaskListTool、TaskStopTool、TaskUpdateTool、TaskOutputTool这组工具。任务系统的设计目标是让 Claude 能够创建后台长时间运行的任务任务有自己的进程产出 stdout/stderr 输出主代理可以随时查询任务状态或获取输出不需要一直等待。这就像 Linux 的后台运行——Claude 说「去跑这个测试套件跑完了我来看结果」然后继续做其他事情通过TaskGetTool定期查询结果Claude 调用 TaskCreateTool → 启动 npm test 后台任务 Claude 继续做其他工作修改其他文件、写文档等 Claude 调用 TaskGetTool → 查询任务状态「running已跑 47/200 个测试」 Claude 调用 TaskGetTool → 查询任务状态「completed所有测试通过」 Claude 汇总结果返回给用户任务系统和 AgentTool 的区别在于AgentTool 的子代理是另一个 Claude 实例有推理能力Task 是一个普通的 shell 进程只是执行命令。前者适合需要 AI 判断的复杂子任务后者适合确定性的脚本执行。六、自定义 Agent 定义src/tools/AgentTool/loadAgentsDir.ts负责从本地目录加载自定义 agent 定义这个目录通常是项目的.claude/agents/。大家可以在这里定义专属的 agent 角色比如test-engineer.json专注于写测试的 agent只有文件读写和 Bash 工具权限doc-writer.json专注于写文档的 agent只能读文件和写 markdown每个 agent 定义包含角色名称、系统提示词补充、工具白名单、颜色标识等。主代理在调用Agent工具时可以指定要派遣哪个预定义角色而不是每次都从零开始描述这个 agent 的职责。这类似于公司里的岗位 JD——不用每次招人都从头描述岗位职责直接套用预定义的岗位模板。七、Session 模式感知与 ResumeCoordinator Mode 有一个细节值得关注当用户用--resume恢复一个历史会话时系统需要确保恢复的模式和当时的模式一致——不能把协调者会话用普通模式恢复也不能把普通会话用协调者模式恢复。matchSessionMode()负责处理这个一致性exportfunctionmatchSessionMode(sessionMode:coordinator|normal|undefined,):string|undefined{constcurrentIsCoordinatorisCoordinatorMode()constsessionIsCoordinatorsessionModecoordinatorif(currentIsCoordinatorsessionIsCoordinator){returnundefined// 模式匹配无需处理}// 动态修改环境变量让 isCoordinatorMode() 返回正确的值if(sessionIsCoordinator){process.env.CLAUDE_CODE_COORDINATOR_MODE1}else{deleteprocess.env.CLAUDE_CODE_COORDINATOR_MODE}return已切换到匹配会话的模式}这种「动态修改环境变量」的做法看起来有点非常规但是有充分理由的isCoordinatorMode()每次调用都是读环境变量没有缓存所以修改环境变量之后下一次调用就立刻反映新的值不需要重启进程。在 Resume 场景里这是最干净的实现方式。到这里为止我们共同走完了 Claude Code 源码 Harness 工程全解析的六个篇章启动层守门员检查 并行预热 编译时功能开关 三条执行路径查询引擎AsyncGenerator 流式管道 queryLoop 状态机 错误恢复 session 持久化工具系统统一契约 三层权限防护 BashTool 的精密安全设计 结果大小管理上下文管理四层防御Budget → Snip → Microcompact → AutoCompact Token Budget 续写MCP 协议三种传输层 OAuth 鉴权 Elicitation 双向通信多智能体AgentTool 嵌套子代理 Coordinator Mode 并行协作 任务系统Claude Code 还在快速迭代很多实验性功能HISTORY_SNIP、CONTEXT_COLLAPSE、REACTIVE_COMPACT、COORDINATOR_MODE正在逐步走向正式发布。所以这里不是终点而是一个全新的开始——源码永远是最准确的文档有疑问的时候直接去读它比任何文章都更可靠。祝大家都拥有一段愉快而充实的 AI 工程探索之旅