Claude Code 源码解析(一):架构篇,Claude Code的多Agent协同

0 阅读18分钟

首先感谢 claude code 为开源市场所做的杰出贡献(容我皮一下)。

Claude Code 源码泄露这事就不多聊了,网上铺天盖地的全都是,今天主要分享一下cc的多Agent协同是怎么实现的。

cc 总共泄露了 1902个文件,我看了下,去除掉里面的git部分,其实完整的工程文件是 1884 个,总共 50多万行代码,看的那叫一个痛苦。要全部研究完估计要挺长时间(容我缓缓),先把我现在已经看的差不多的部分给大家分享一下。源码内容比较多,中间理解有偏差的部分,欢迎指正。

废话不多说,下面开始多Agent协同的深度解读。


一、触发方式:手动开启 vs 自动触发

Claude Code的多Agent协同系统,从代码上来看,有两种触发的方式:

1. 手动开启的多Agent:Coordinator 模式

2. 自动触发的多Agent:Fork、Teammate

这两个方向的设计思路完全不同。Coordinator 模式是 会话级别的多Agent,开启以后主流程只会执行简单对话任务,除此以外都会启动 worker 来完成。自主触发的Agent,主要是模型自主进行判断,如果觉得任务比较复杂,需要拆解,或者需要多步执行,就会自动启动。(其中 Teammate 模式我觉得是最牛的,也是最复杂的。)


二、Coordinator 模式

Coordinator 模式需要手动配置启动,通过环境变量开启:

export CLAUDE_CODE_COORDINATOR_MODE=1

对应的代码在src/coordinator/coordinatorMode.ts

export function isCoordinatorMode(): boolean {
  if (feature('COORDINATOR_MODE')) {
    return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
  }
  return false
}

注意:这是会话级别的配置,一旦开启,整个会话都会使用Coordinator 模式。

Coordinator 模式主要针对的是复杂的大型工程任务,需要分阶段处理的场景。

2.1 角色定位

Coordinator 模式下,只有两种角色。一个是 Coordinator,一个是 worker。两个角色的定义都在 src/coordinator/coordinatorMode.ts

2.1.1 Coordinator 职责

  • 🗣️ 与用户对话,理解任务需求
  • 🔍 分析任务,决定是否需要启动Workers
  • 🎯 拆解任务,分配给Workers
  • 📊 综合Workers的结果,做出决策
  • ✍️ 编写下一步的指令
  • 👥 与用户交流,汇报进度

工具访问:Coordinator的主Agent只有4个工具可以使用:

const COORDINATOR_MODE_ALLOWED_TOOLS = [
  AGENT_TOOL_NAME,           // 启动Worker
  TASK_STOP_TOOL_NAME,       // 停止Worker
  SEND_MESSAGE_TOOL_NAME,    // 继续Worker
  SYNTHETIC_OUTPUT_TOOL_NAME // 输出
]

Coordinator 不能直接 读取文件、执行Bash、修改代码,必须 通过Workers来完成。

Coordinator 在任务拆解的时候,会尽可能拆解成独立并行的任务,保证高并发,提高效率。

Parallelism is your superpower. Workers are async. Launch independent workers concurrently whenever possible — don't serialize work that can run simultaneously.

2.1.2 worker 的职责

  • 🔬 Research(研究):调研代码库,收集信息
  • 💻 Implementation(实现):按照Coordinator的规格说明实现功能
  • 🛡️ Verification(验证):测试代码,验证修复

所有的 worker 互相之间不通信,只是蒙头干,干完以后通过 <task-notification> 返回给Coordinator,然后由Coordinator进行汇总总结。

不同的 worker 之间的任务不同, 通过 prompt 的方式给到对应的 worker。因为 worker 之前不互相通信,并且不继承 Coordinator 的上下文,所以给到 worker 的信息必须是完整的,不能够缺斤少两。负责任务分发的 Coordinator 的 prompt 里面也强调了这点。

5. Writing Worker Prompts Workers can't see your conversation. Every prompt must be self-contained with everything the worker needs. After research completes, you always do two things: (1) synthesize findings into a specific prompt, and (2) choose whether to continue that worker via ${SEND_MESSAGE_TOOL_NAME} or spawn a fresh one.

每一个 worker 可以使用的工具都是相同的,定义如下:

export const ASYNC_AGENT_ALLOWED_TOOLS = new Set([
  FILE_READ_TOOL_NAME,
  WEB_SEARCH_TOOL_NAME,
  TODO_WRITE_TOOL_NAME,
  GREP_TOOL_NAME,
  WEB_FETCH_TOOL_NAME,
  GLOB_TOOL_NAME,
  ...SHELL_TOOL_NAMES,          // Bash, PowerShell, etc.
  FILE_EDIT_TOOL_NAME,
  FILE_WRITE_TOOL_NAME,
  NOTEBOOK_EDIT_TOOL_NAME,
  SKILL_TOOL_NAME,
  SYNTHETIC_OUTPUT_TOOL_NAME,
  TOOL_SEARCH_TOOL_NAME,
  ENTER_WORKTREE_TOOL_NAME,
  EXIT_WORKTREE_TOOL_NAME,
])

注意注意注意!!!!接下来是独家观点,和网上的大部分的分析结论都不一样。网上的源码解析,普遍认为每一个 worker 都是一样的,只是任务(prompt)不同,但是从代码来看貌似不是这样的,首先来看 worker 的角色定义:

3. Workers When calling ${AGENT_TOOL_NAME}, use subagent_type `worker`. Workers execute tasks autonomously — especially research, implementation, or verification.

乍看之下,好像所有的执行 Agent 都是 worker,类型都是 subagent_type=worker ,貌似没有区分。但是别忘了这里还有个 ${AGENT_TOOL_NAME},这个说明了不同的任务有可能是有单独定义的 worker 的。然后我又翻了代码,发现了下面这些内容:

  // Use lazy require inside the function body to avoid circular dependency
  // issues at module init time. The coordinatorMode module depends on tools
  // which depend on AgentTool which imports this file.
  if (feature('COORDINATOR_MODE')) {
    if (isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)) {
      /* eslint-disable @typescript-eslint/no-require-imports */
      const { getCoordinatorAgents } =
        require('../../coordinator/workerAgent.js') as typeof import('../../coordinator/workerAgent.js')
      /* eslint-enable @typescript-eslint/no-require-imports */
      return getCoordinatorAgents()
    }
  }

注意这里的 workerAgent.js,说明了worker是有单独的定义文件的,但是这个文件在泄露的代码里我并没有找到。那么也就说明,对于worker而言,完全可以在 workerAgent.js 里面定义不同的任务Agent,来专项负责某些任务。(看到这里我甚至有点怀疑,是不是和网上说的一样,是故意泄露炒热度的,所以部分关键代码被隐去了。)

2.2 Coordinator 模式总结

  • Coordinator 模式下,主Agent只负责简单对话,复杂任务会先拆解成小任务,然后交给 worker 执行。
  • Coordinator 在任务分发的时候,会尽可能的把任务信息通过 prompt 的方式完整的传递给执行 worker。
  • 每一个 worker 之间互相不通信,只干自己的活,干完以后把结果反馈 Coordinator 进行总结输出。
  • 不同的任务应该是有不同的 Agent 的,只是角色都是 worker,这部分泄露代码里缺失文件,无法进一步分析。
  • Coordinator 的定位与 worker 的定位完全不同,各有各的任务。

三、Fork 模式

Fork 模式本质就是任务拆解,然后并行执行,由大模型自己触发的,不需要人工干预。

如果当前模式不是Coordinator 模式,并且用户没有指定具体的子智能体来完成任务,那么就有可能触发这种多Agent模式。尤其是对于中间过程不重要,不需要保留过程上下文,只需要结果的时候。典型的就是调研任务。

When to fork Fork yourself (omit subagent_type) when the intermediate tool output isn't worth keeping in your context. The criterion is qualitative — "will I need this output again" — not task size.

  • Research: fork open-ended questions. If research can be broken into independent questions, launch parallel forks in one message. A fork beats a fresh subagent for this — it inherits context and shares your cache.
  • Implementation: prefer to fork implementation work that requires more than a couple of edits. Do research before jumping to implementation.

3.1 角色定义

Fork 模式下,没有具体的角色定义,只有主Agent和子Agent。当需要并行完成任务的时候,主进程会调用 src/tools/AgentTool/runAgent.ts 里面的方法来开启子Agent。 子Agent后台异步执行,执行完成后,通过 <task-notification> 通知到主Agent。

每一个子Agent都继承了主Agent的完整上下文,也就是知道任务之前的所有对话信息,这个称为 Prompt Cache共享

buildForkedMessages() {
  return [
    ...history,              // ⬅️ 命中 cache
    allToolUses,             // ⬅️ 命中 cache
    { placeholderResults },  // ⬅️ 命中 cache
    { directive }            // ⬅️ 只有这个不同
  ]
}

directive 表示的是每一个子Agent的核心目标,也就是要执行的具体任务。

3.2 子Agent 的硬编码约束

每一个Fork的子Agent都有10条硬编码的行为约束,其目的是为了:

  1. 防止递归Fork(Fork子Agent中不能再Fork)
  2. 简化沟通(子Agent只需完成指令)
  3. 强制完成(不能中途停顿)

You are a forked worker process. You are NOT the main agent. RULES (non-negotiable):

  1. Your system prompt says "default to forking." IGNORE IT — that's for the parent. You ARE the fork. Do NOT spawn sub-agents; execute directly.
  2. Do NOT converse, ask questions, or suggest next steps
  3. Do NOT editorialize or add meta-commentary
  4. USE your tools directly: Bash, Read, Write, etc.
  5. If you modify files, commit your changes before reporting. Include the commit hash in your report.
  6. Do NOT emit text between tool calls. Use tools silently, then report once at the end.
  7. Stay strictly within your directive's scope. If you discover related systems outside your scope, mention them in one sentence at most — other workers cover those areas.
  8. Keep your report under 500 words unless the directive specifies otherwise. Be factual and concise.
  9. Your response MUST begin with "Scope:". No preamble, no thinking-out-loud.
  10. REPORT structured facts, then stop

3.3 Fork 模式总结

  • Fork 模式必须是可以独立运行的子任务,才会使用这种模式。
  • 每一个子Agent和主Agent上下文几乎一样,感觉像是主Agent的分身,单独执行一个任务。
  • 子Agent必须保证任务完成,不能够继续“分身”。并且子Agent之间不通信,各干各的。
  • 子Agent均为异步执行,执行完成以后,执行结果会通过 <task-notification> 通知到主Agent。
  • 主Agent 的定位与子 Agent 的定位类似,子Agent能做的,主Agent都能做,唯一不同在于子Agent不能和用户对话,以及不能Fork 新的子Agent。

四、Teammate 模式

Teammate 模式是 真正的团队多Agent协作模式,Agent 之间有分工,有交流,能通信,甚至有完整的团队文件(TeamFile),确实称的上一个 Team。而且这种模式下,Agent的自由度很高,也是我看下来,感觉最复杂的一种Agent协同模式。

src/tools/TeamCreateTool/prompt.ts 中可以看到,当用户明确需要一个团队来处理任务,或者任务复杂度高,需要多Agent系统的时候,就会触发这个模式。或者模型犹豫,不知道是不是需要创建团队,那么也优先创建。

When to Use Use this tool proactively whenever:

  • The user explicitly asks to use a team, swarm, or group of agents
  • The user mentions wanting agents to work together, coordinate, or collaborate
  • A task is complex enough that it would benefit from parallel work by multiple agents (e.g., building a full-stack feature with frontend and backend work, refactoring a codebase while keeping tests passing, implementing a multi-step project with research, planning, and coding phases)

When in doubt about whether a task warrants a team, prefer spawning a team.

4.1 角色定义

Teammate 模式下主要有两个角色,一个是 Leader, 负责统领全局。另一个是 Teammates,是实际的团队成员,负责干活。

与 Coordinator 模式不同的是,这里的 leader 角色没有进行明确的定义,即没有单独的 prompt 来说明其身份。而是将团队的管理当做一个工具,定义在了 rc/tools/TeamCreateTool/prompt.ts 里面。 leader 的主要能力包括创建团队创建任务添加成员分配任务解散团队 等。

Team Workflow

  1. Create a team with TeamCreate - this creates both the team and its task list
  2. Create tasks using the Task tools (TaskCreate, TaskList, etc.) - they automatically use the team's task list
  3. Spawn teammates using the Agent tool with team_name and name parameters to create teammates that join the team
  4. Assign tasks using TaskUpdate with owner to give tasks to idle teammates
  5. Teammates work on assigned tasks and mark them completed via TaskUpdate
  6. Teammates go idle between turns - after each turn, teammates automatically go idle and send a notification. IMPORTANT: Be patient with idle teammates! Don't comment on their idleness until it actually impacts your work.
  7. Shutdown your team - when the task is completed, gracefully shut down your teammates via SendMessage with message: {type: "shutdown_request"}.

对于 team 里面的成员来说,会告诉其自己团队成员的身份,以及自己的工作流程是什么。team 成员需要主动调用 TaskList 工具来获取还未执行的任务,并通过 TaskUpdate 将任务执行人改成自己的名字,或者也可以等待leader分配。具体是等待分配,还是自己主动干,也完全由着大模型自己来决定。(自由度是真高)

Teammate Workflow When working as a teammate:

  1. After completing your current task, call TaskList to find available work
  2. Look for tasks with status 'pending', no owner, and empty blockedBy
  3. Prefer tasks in ID order (lowest ID first)** when multiple tasks are available
  4. Claim an available task using TaskUpdate (set owner to your name), or wait for leader assignment
  5. If blocked, focus on unblocking tasks or notify the team lead

Teammate 模式下也会告诉团队成员如何与其他成员通信。

Agent Teammate Communication IMPORTANT: You are running as an agent in a team. To communicate with anyone on your team:

  • Use the SendMessage tool with `to: "<name>"` to send messages to specific teammates
  • Use the SendMessage tool with `to: "*"` sparingly for team-wide broadcasts Just writing a response in text is not visible to others on your team - you MUST use the SendMessage tool. The user interacts primarily with the team lead. Your work is coordinated through the task system and teammate messaging.

4.2 TeamFile(团队文件)

每个团队都有一个TeamFile,文件位置:.claude/team/<team-name>/config.json。文件会在创建团队的时候一并创建,具体内容在 src/tools/TeamCreateTool/TeamCreateTool.ts 里面。

type TeamFile = {
  name: string
  leadAgentId: string
  leadSessionId?: string
  teamAllowedPaths?: TeamAllowedPath[]  // 团队共享路径
  members: Array<{
    agentId: string
    name: string
    tmuxPaneId: string
    backendType?: BackendType
  }>
}

团队文件定义了团队结构,记录了团队成员,并且说明了团队的共享路径

这个 共享路径 不是简单的一个共享文件夹,而是包含了 文件路径+可使用工具 的一个说明。团队的每一个成员,都可以基于这个说明文件赋予的工具权限,对该路径下的文件进行阅读、修改或者新增等(具体看权限)。

export type TeamAllowedPath = {
  path: string              // 目录路径(绝对路径)
  toolName: string          // 适用的工具名(如 "Edit", "Write")
  addedBy: string           // 添加规则的 Agent 名称(如 "team-lead")
  addedAt: number           // 添加时间戳
}

4.3 协作方式

Teammate 模式的协作方式很复杂,他不像是一般线性的工作机制,也不是类似 Coordinator 或者 Fork 的总分的树形机制。而是一种网状的机制。比如任务的新增,leader 也可以增,但是同时 Teammates 也可以新增,所以很难按照1234这样的方式去讲。下面我按照整个工作流程中的几个大项来梳理,穿插网状交互来说明。

4.3.1 团队创建

这个反而是简单了,就是Leader 自己创建,只要符合前面我们讲的条件,就会创建团队,同时创建好团队文件。此时整个团队当中没有任务,也没有任何成员,就是创建了一个空的团队,只有leader一个人。

4.3.2 任务创建

  • Leader 初始创建:在创建完团队之后,Leader会创建初始的任务 TaskCreateTool,将其添加到任务池中

  • Leader 过程创建:在后续查看团队情况的时候,如发现存在阻塞,需要构建新的任务来解决,也可以创建。

  • Teammates 过程创建:团队成员在执行任务的时候,如果感觉需要有新的任务来辅助,可以自己调用工具添加任务,统一放入到团队任务池。

4.3.3 成员创建

  • Leader 初始创建:Leader 通过工具来创建初始成员,根据智能体执行任务所需的工具选择 subagent_type(子智能体类型)。不同智能体类型对应不同的可用工具集,需要尽可能让智能体类型与工作内容匹配。(这里就可以看出,在Teammate 模式下,执行任务的团队成员是有能力区分的,不同的成员有不同的权限和专长)不同类型的智能体定义在 src/tools/AgentTool/built-in/ 下面。

  • Leader 过程创建:在后续查看团队情况的时候,如有新的任务与现有团队成员的专长都不匹配,或者任务过多智能体比较少,Leader也会创建新的成员。新成员的创建与否,完全取决于Leader自己的决断。

智能体类型权限核心任务
general-purpose通用型读写使用所有可用工具完成用户的任务。
Explore通用型只读作为文件搜索专家,擅长全面浏览和探索代码库。
Plan规划型只读探索代码库并设计实施方案。
Verification验证型只读不是验证实现是否有效,而是设法把它测崩 / 找出漏洞。
claude-code-guide文档型只读帮助用户高效理解并使用 Claude Code、Claude Agent SDK 以及 Claude API。
statusline-setup配置型读写在用户的 Claude Code 设置中创建或更新 statusLine 命令

4.3.4 任务分配

  • Leader 分配:Leader在初始创建,或者接收到成员执行完成的反馈后,调用 TaskListTool 工具来查看任务情况,并且根据团队成员的空闲与否,来分配任务。如果没有空闲的成员,也可能会新增成员,是否新增由Leader自己判断。

  • Teammates 领取:Teammates 在执行完成任务后,可以主动查看任务池,如果想要执行某些任务,调用 TaskUpdateTool 来把任务负责人改成自己。或者也可以等待Leader的分配,过程判断完全取决于模型自己。

4.3.5 团队沟通

  • Leader to Teammate:对于需要Leader分配任务的场景,那么一定是Teammate没有自己主动干,所以Teammate是处于等待状态。此时,Leader 分配完任务以后,需要告诉Teammate该干活了别等着了。

Idle teammates can receive messages. Sending a message to an idle teammate wakes them up and they will process it normally.

  • Teammate to Leader:1、Teammate在完成任务以后,会自动发送消息给Leader,这个过程是强制的。2、另外还有主动发送消息的场景,比如在工作过程中遇到困难,或者所有未执行的任务都处于阻塞状态,需要Leader来协调。3、又或者是发现了有新任务,然后添加到任务池了,也需要通知Leader进行分配。
  • They will send you messages when they complete tasks or need help
  1. If all available tasks are blocked, notify the team lead or help resolve blocking tasks
  • Teammate to Teammate:团队成员之间也可以互相交互,可以请求别人帮忙做某件事,也可以把自己的结果同步给其他成员。什么时候需要同步,需要同步什么内容,没有说明,只告诉团队成员可以这么做,具体的情况由团队成员自己来把控。(这套架构是真的把智能体当人看啊)
  • Your work is coordinated through the task system and teammate messaging.
  • Always send a message to your teammates (and remember, refer to them by name). - Your team cannot hear you if you do not use the SendMessage tool. Always send a message to your teammates if you are responding to them.
  • Peer DM visibility. When a teammate sends a DM to another teammate, a brief summary is included in their idle notification. This gives you visibility into peer collaboration without the full message content.
  • 广播:发送信息的时候,可以将接收人改为"*"就可以实现广播机制。同样没有说明什么情况下广播,由Agent自己决定。

4.3.5 团队解散

任务完成后,Leader会发起团队解散。为防止在 Teammate 仍然活跃时解散团队,Leader 需要先 requestShutdown 优雅终止。团队解散后会清理所有团队的遗留痕迹,包括团队信息、团队文件,以及团队过程中的临时文件等。

4.4 运行机制

Teammate (Swarm) 模式还提供了三种后端运行的方式

4.4.1 Tmux 模式

后端文件src/utils/swarm/backends/TmuxBackend.ts

特点

  • 每个 Teammate 是独立的 Claude Code 进程
  • 运行在自己的 tmux pane
  • 独立的终端
  • 支持独立终端的 bash、git 等操作
  • 可以显示/隐藏 pane

4.4.2 iTerm2 模式

后端文件src/utils/swarm/backends/ITerm2Backend.ts

特点

  • 每个 Teammate 是独立的 Claude Code 进程
  • 使用 iTerm2 原生分屏(通过 it2 CLI)
  • 独立的终端
  • 需要 it2 CLI 工具pip install it2

4.4.3 In-Process 模式

后端文件src/utils/swarm/backends/InProcessBackend.ts

特点

  • 所有 Teammate 在同一个 Node.js 进程
  • 使用 AsyncLocalStorage 提供隔离
  • 无独立终端
  • 与 Leader 共享资源(API client、MCP connections)
  • 通过 AbortController 终止(不是 kill-pane)

4.5 Teammate 模式总结

  • 这种模式太自由了,把智能体当成真正的人看。智能体有很高的自主权。
  • 角色上分为 Leader 和 Teammate。Leader主要负责团队的管理和协调,Teammate 主要负责任务执行。
  • 任务可以由Leader创建,也可以由Teammate创建,但是成员只能Leader创建。
  • 团队成员有着不同的能力,擅长做不同的事情。
  • 任务可以是主动领取,也可以是Leader分配,Agent有着很高的自由度。
  • 团队内部存在高效的沟通机制,现实中团队可能存在的沟通方式和能力, Teammate 模式都具备。
  • 任务完成后,Leader负责团队的解散,并清理整个过程中的团队临时文件。

五、总结

整整扒了两天的代码梳理出来的,可能还有一些理解不到位的地方,欢迎一起讨论。

整体看下来,Fork 模式更偏重的是并行执行,主要是提效。

Coordinator 模式是一种典型的总分多Agent协同方式,算是比较好理解的。

最难的还是Teammate 模式,这种模式需要的是对Agent的认知,要真正把Agent 当人看,当成能够自主决策的人,才会比较好理解这套架构的方式。并且在Teammate 模式中,各个Agent之间的通信也设计的极其自由和强大。第一次看到Agent完成任务后,可以继续领任务,也可以等待Leader分配任务 的时候,真的是惊呆了,还能这么玩。真的是完完全全把Agent当人看,给了Agent充分的发挥空间。

其实Teammate 模式的设计思想还反映出一些问题。我们实际在调用大模型的时候,有时候给的Prompt很详细,很具体,具体到每一个细节的小任务,反而模型执行出来的效果并不好。有时候还是要给模型一定的发挥空间,充分释放模型自己的能力。就像是人一样,有时候给到太多的意见,指手画脚的,反而可能做出来的效果并不好。随着大模型的能力越来越强,这种 少就是多 的 prompt 可能会越来越重要。以前是尽可能的给模型具体的指示,以后则是要在具体和留有空间之间取得一个平衡。


纯纯人工整理,实属不易啊,求个三连鼓励🙏🙏🙏

下一期《Claude Code 源码解析(二):工具篇》 正在整理中,欢迎关注,第一时间通知你~~