逆向解析 Claude Code Agent Teams:架构与协议

机器翻译 原文.

Banner for 逆向解析 Claude Code Agent Teams:架构与协议

内容

引言

Claude Code(v2.1.47)提供了一项名为 Agent Teams 的实验性功能:多个 Claude Code 会话通过由负责人和队友组成的拓扑结构,协同处理共享工作。我一直在开发 Hive,这是一个多智能体编程编排器,目标相似,架构却截然不同,因此我想了解 Anthropic 的方案在底层是如何运作的。

本文记录了我通过以下方式得到的发现:

  1. 阅读官方文档
  2. 检查先前团队会话在磁盘上留下的实际文件
  3. 让 Claude 分析 Claude Code 二进制文件(v2.1.47),了解实现细节(哈!)

目录

1. 架构概览

一个智能体团队由四个部分组成:

组件 作用
团队负责人 创建团队并启动队友的主 Claude Code 会话
队友 独立的 Claude Code 实例,各自拥有独立的上下文窗口
任务列表 以独立 JSON 文件形式存储在磁盘上的共享工作项
邮箱 每个智能体各自用于接收消息的收件箱文件

整个协调层都基于文件。位于 ~/.claude/ 的文件系统是唯一的协调基础:

~/.claude/
├── teams/{team-name}/
│   ├── config.json                  # team membership registry
│   └── inboxes/{agent-name}.json    # per-agent mailbox
└── tasks/{team-name}/
    ├── .lock                        # flock() for concurrent task claiming
    ├── .highwatermark               # auto-increment counter
    ├── 1.json                       # individual task files
    ├── 2.json
    └── ...

这是一种从根本上去中心化的设计。负责人只是另一个拥有额外工具(TeamCreate、TeamDelete、SendMessage)的 Claude 会话。没有后台进程。协调通过共享文件访问自然实现。

在一个正在进行的会话中,如果你让 Claude 启动一个团队来执行某类任务,然后在另一个窗口运行以下命令,就能实时观察文件系统的更新。

watch -n 0.5 'tree ~/.claude/teams/ 2>/dev/null; echo "---"; tree ~/.claude/tasks/ 2>/dev/null'

例如,使用以下提示词:

can you spawn an agent team to examine this codebase?
  - have one look for bugs
  - have one look for complexity
  - have one look for good things to call out and play devil's advocate against the other two agents

我观察到了如下结果:

teams
└── code-review
    ├── config.json
    └── inboxes
        ├── bug-hunter.json
        ├── complexity-analyst.json
        ├── devils-advocate.json
        └── team-lead.json

团队配置

位于 ~/.claude/teams/{team-name}/config.json 的团队配置包含一个 members 数组,队友通过读取它来发现彼此:

{
  "members": [
    { "name": "team-lead", "agentId": "abc-123", "agentType": "leader" },
    {
      "name": "researcher",
      "agentId": "def-456",
      "agentType": "general-purpose"
    }
  ]
}

名称是主要的寻址方式(虽然存在 UUID,但并不用于路由)。所有消息传递和任务分配都使用 name 字段。

2. 共享任务列表

文件格式

每个任务都以独立 JSON 文件的形式存储在 ~/.claude/tasks/{team-name}/ 中。下面是先前一次会话中的真实示例:

{
  "id": "1",
  "subject": "Hunt for bugs across the codebase",
  "description": "...",
  "activeForm": "Hunting for bugs",
  "owner": "bug-hunter",
  "status": "completed",
  "blocks": [],
  "blockedBy": []
}

任务结构:

字段 类型 说明
id string 数字 ID,通过 .highwatermark 自动递增
subject string 祈使句形式的标题(例如“运行测试”)
description string 详细需求和验收标准
activeForm string 现在进行时形式,用于加载指示器显示(“正在运行测试”)
status string pending → in_progress → completed(或 deleted)
blocks string[] 被此任务阻塞的任务 ID
blockedBy string[] 此任务开始前必须完成的任务 ID

并发控制

两个特殊文件负责协调:

  • .lock:一个 0 字节文件,用于文件系统级互斥(flock())。在我机器上观察到的全部 42 个任务目录中都存在。
  • .highwatermark:包含一个整数(例如 "3"、"13"),表示下一个可用的自增任务 ID。

任务认领

任务认领使用文件锁来避免竞态条件。队友倾向于优先选择 ID 最小的任务。如果某个任务的 blockedBy 数组非空,就必须等所有阻塞它的任务都进入终态后,才能认领该任务。

观察结果:大多数任务目录为空

在我机器上的 42 个任务目录中,只有 5 个包含实际的任务 JSON 文件。其余 37 个只有 .lock 和 .highwatermark。这可能意味着任务完成后会被清理,也可能是这些会话中的 Claude 使用了内部任务列表(自任务列表功能推出以来就已提供),但没有将任务拆分为子任务文件。

3. 智能体间通信

邮箱模式

每个智能体都有一个位于 ~/.claude/teams/{team-name}/inboxes/{agent-name}.json 的 JSON 数组文件。下面是先前一次会话中的真实收件箱,当时团队负责人向一个 controlplane-agent 分派了工作:

[
  {
    "from": "team-lead",
    "text": "{\"type\":\"task_assignment\",\"taskId\":\"1\",\"subject\":\"Phase 2: Control-plane - remove participants/presence\",\"description\":\"Remove multiplayer code from the control-plane package...\",\"assignedBy\":\"team-lead\",\"timestamp\":\"2026-02-18T02:37:16.890Z\"}",
    "timestamp": "2026-02-18T02:37:16.890Z",
    "read": false
  }
]

注意这种 JSON 内嵌 JSON 的编码方式:text 字段是一个 JSON 字符串,其中包含序列化后的消息对象。外层信封包含 from、text、timestamp 和 read 字段。

消息类型

type 字段位于 text 载荷中,支持以下类型:

类型 方向 用途
task_assignment 负责人 → 队友 分配任务并提供完整细节
message 任意成员 → 任意成员 向单个接收者发送私信
broadcast 负责人 → 全体队友 向每位队友发送相同消息
shutdown_request 负责人 → 队友 请求正常关闭
shutdown_response 队友 → 负责人 同意或拒绝关闭
plan_approval_request 队友 → 负责人 提交计划以供审核
plan_approval_response 负责人 → 队友 批准或拒绝,并提供反馈
idle_notification 队友 → 负责人 队友的回合结束时自动发送

投递机制

写入路径:发送者向接收者的收件箱 JSON 数组文件追加一条新记录。

读取路径:接收者轮询自己的收件箱文件。新消息会作为合成的对话回合注入(看起来就像用户发送的消息)。

广播:确实就是将同一条消息写入每位队友的收件箱文件。Token 成本随团队规模线性增长。

通信就是文件追加加上文件读取。发送与接收之间的延迟取决于接收者的轮询间隔。

队友间私信的可见性

当一位队友向另一位队友发送私信时,发给负责人的空闲通知中会包含一段简短摘要。这样,负责人就能了解队友之间的协作情况,而无需接收完整的消息内容。

4. 智能体的创建与生命周期

队友的创建方式

每位队友都是一个独立的 claude CLI 进程。负责人通过 Task 工具,使用 team_name 和 name 参数来启动它们。启动的进程会设置以下环境变量:

  • CLAUDE_CODE_TEAM_NAME:在启动的队友进程中自动设置
  • CLAUDE_CODE_PLAN_MODE_REQUIRED:如果需要审批计划,则设置为 true

上下文初始化

队友会像任何新会话一样,加载相同的项目上下文:

  • 工作目录中的 CLAUDE.md 文件
  • MCP 服务器
  • 技能
  • 负责人提供的启动提示词

负责人的对话历史不会传递给队友。 每位队友都从头开始,仅以启动提示词作为上下文。

内部实现

根据对 Claude Code v2.1.47 的二进制分析,队友上下文通过 AsyncLocalStorage 管理,包含以下字段:

  • agentId、agentName、teamName
  • parentSessionId、color
  • planModeRequired

关键内部函数:

  • isTeammate() / isTeamLead():角色检测
  • waitForTeammatesToBecomeIdle():供负责人使用的同步原语
  • getTeammateContext() / setDynamicTeamContext():运行时上下文管理

空闲检测

每个 LLM 回合结束后,队友都会自动进入空闲状态,并向负责人发送一条 idle_notification。这是正常的等待状态,并不表示出错或状态失效。向空闲队友发送消息会唤醒它(下一个轮询周期会读取到收件箱中的消息)。

关闭协议

  1. 负责人向队友发送 shutdown_request
  2. 队友可以同意(正常退出)或拒绝(说明原因并继续工作)
  3. 通过 TeamDelete 清理团队,会删除 ~/.claude/teams/{team-name}/ 和 ~/.claude/tasks/{team-name}/
  4. 如果仍有任何队友处于活动状态,清理就会失败;必须先关闭它们

权限继承

队友在启动时继承负责人的权限模式。如果负责人使用 --dangerously-skip-permissions 运行,所有队友也会如此。启动后可以修改各个队友的模式,但无法在启动时为每位队友单独配置。

5. 质量门禁与钩子

Agent Teams 与 Claude Code 的钩子系统集成,以强制执行质量要求:

TeammateIdle 钩子

在队友即将进入空闲状态时触发。退出码 2 会将 stderr 作为反馈发送,并阻止其进入空闲状态,让队友继续工作。

{
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project"
}

TaskCompleted 钩子

在任务被标记为完成时触发。退出码 2 会阻止完成操作,并将 stderr 作为反馈返回。

{
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}

它会在两种情况下触发:(1)任何智能体通过 TaskUpdate 显式将任务标记为完成时,或(2)智能体团队中的队友结束回合时,仍有进行中的任务。

钩子处理器类型

类型 说明
command Shell 脚本。通过 stdin 接收 JSON,通过退出码表达决定。
prompt 单回合 LLM 评估。返回 {ok, reason}。
agent 配有读取工具的多回合子智能体。最多 50 个回合。

6. Token 成本

当队友在计划模式下运行时,智能体团队消耗的 token 约为标准会话的 7 倍。每位队友都是独立的 Claude 实例,各自维护完整的上下文窗口。

基准参考

  • Claude Code 平均用量:约 6 美元/开发者/天
  • 智能体团队:在基准用量之上,大致随团队规模成比例增加

7. 架构总结

维度 Claude Code Agent Teams
协调基础 平面文件(~/.claude/tasks/、~/.claude/teams/)
任务格式 每个任务一个 JSON 文件 + 用于认领的 .lock
消息传递 JSON 收件箱文件(追加 + 轮询)
智能体生命周期 自行管理的 CLI 进程
工作隔离 共享工作目录
合并策略 无(智能体直接编辑文件)
重试/升级处理 手动(由负责人决定,或由用户介入)
拓扑结构 负责人 + 扁平的队友结构,支持点对点消息传递
调度 自主认领(队友领取下一个任务)
状态持久性 仅依靠文件;不支持在进程内恢复队友
质量门禁 Shell 钩子(TeammateIdle、TaskCompleted)
Token 跟踪 仅按会话跟踪,不跨智能体汇总
停滞检测 手动(由用户发现队友已停止工作)
并发控制 隐式(团队规模 = 队友数量)
依赖模型 任务文件中的 blocks/blockedBy

资料来源

官方文档

磁盘上的文件(Claude Code v2.1.47)

在 /Users/tau/.claude/ 观察到:

  • 包含 config.json 和 inboxes/{agent-name}.json 文件的团队目录
  • 包含 .lock、.highwatermark 和独立任务 JSON 文件的任务目录
  • 从 team-lead 发给 cp-agent 的任务分配消息示例,时间戳为 2026-02-18T02:37:16.890Z

二进制分析

Claude Code v2.1.47 二进制文件。通过字符串分析识别出的内部函数:getTeamName、getAgentName、getAgentId、isTeammate、isTeamLead、waitForTeammatesToBecomeIdle、getTeammateContext、setDynamicTeamContext、createTeammateContext。AsyncLocalStorage 上下文字段:agentId、agentName、teamName、parentSessionId、color、planModeRequired。

Hive 代码库