一、引言与背景
1.1 什么是多 Agent 协作?
在传统的 AI 编程助手中,一个 Agent(AI 助手)独立处理所有任务——读代码、写方案、改代码、跑测试,全部串行执行。当任务复杂度上升(如大型代码审查、多模块重构),单 Agent 模式面临两个核心瓶颈:
- 上下文窗口有限:单个 Agent 的对话历史长度受限,无法同时持有整个大型项目的全部信息
- 串行效率低下:安全审查、性能分析、测试编写等任务相互独立,串行处理浪费时间
多 Agent 协作的核心思路:将一个复杂任务分解为多个子任务,由多个专职 Agent 并行处理,通过协调机制整合结果。
1.2 Agent Teams 在 Claude Code 中的定位
Agent Teams 是 Claude Code 中实现多 Agent 协作的核心系统,它让一个 “Leader”(主 Agent)可以创建并管理一组 “Teammates”(子 Agent),协同完成复杂任务。
核心价值:
- 并行化:多个 Teammate 同时工作,缩短总耗时
- 专业化:每个 Teammate 可以有不同角色(安全审查员、测试工程师等)
- 隔离性:每个 Teammate 至少拥有独立执行上下文;在 pane backend 模式下还能获得更强的终端/进程隔离。当前源码对
worktreePath有 schema 与清理逻辑兼容,但未看到它作为 Teammate 标准派生参数公开暴露
1.3 Agent Teams 开启条件
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 claude
二、示例场景与时间线
创建一个3人代码评审团队来 review 这个 PR: https://github.com/cloudwego/eino/pull/xxx
- 一个专门找安全漏洞
- 一个关注性能影响
- 一个检查测试覆盖率
2.1 TeamLead 完整时间线
下面这条时间线更适合理解为一次可能的运行轨迹,而不是源码保证的固定顺序。实际运行中,Team 创建、任务规划分配都依赖于模型的主张规划能力。
用户: "创建一个3人代码评审团队来 review 这个 PR"
│
▼
┌─ Step 1~3 ── 理解 PR────────────────────────────────────────────────────┐
│ Bash("gh pr view 852 --json ...") → 获取 PR 元信息 │
│ Bash("gh pr diff 852") → 获取完整 diff (60.9KB) │
│ Read(agentsmd.go) ┐ │
│ Read(loader.go) ├── 并行读取 3 个源文件 │
│ Read(test.go) ┘ (test.go 52.3KB 自动截断到 persisted-output) │
└──────────────────────────────────────────────────────────────────────────┘
│
▼ "Now I have all the code. Let me create the review team."
┌─ Step 4 ─────────────────────────────────────────────────────────────────┐
│ TeamCreate("pr-852-review") │
│ → 名称未冲突时直接使用: "pr-852-review" │
│ → lead_agent_id: "team-lead@pr-852-review" │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 5 ─────────────────────────────────────────────────────────────────┐
│ TaskCreate("安全漏洞审查") ──→ 创建任务 │
│ TaskCreate("性能影响分析") ──→ 创建任务 ← 3 个任务可并行创建 │
│ TaskCreate("测试覆盖率检查") ──→ 创建任务 │
│ 注:任务 ID 由运行时分配,源码不保证固定编号 │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 6 ─────────────────────────────────────────────────────────────────┐
│ Agent(name="security-reviewer", team_name="pr-852-review", ┐ │
│ prompt="审查安全风险...", model="sonnet") ├─ 并行 │
│ Agent(name="perf-reviewer", team_name="pr-852-review", │ │
│ prompt="分析性能影响...", model="sonnet") │ │
│ Agent(name="test-reviewer", team_name="pr-852-review", ┘ │
│ prompt="检查测试覆盖率...", model="sonnet") │
│ 每个 Agent 附带完整任务 Prompt: 角色定义+文件清单+审查维度+报告模板 │
│ │
│ 输出: "三个审查员正在同时工作,完成后我会汇总报告给你。" │
└──────────────────────────────────────────────────────────────────────────┘
│
│ ════════════ Leader 进入被动等状态 ════════════
│
▼
┌─ Step 7 ─────────────────────────────────────────────────────────────────┐
│ ◀── <teammate-message perf-reviewer> 性能审查报告(某个 teammate 先完成)│
│ │
│ 输出: "安全和性能审查已完成,等待测试覆盖率审查员完成报告。" │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 8 ─────────────────────────────────────────────────────────────────┐
│ ◀── <teammate-message security-reviewer> idle_notification(常见为 1 次;某些 Stop/生命周期路径可能额外出现) │
│ │
│ 输出: "安全和性能审查员已完成工作进入空闲状态" │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 9 ─────────────────────────────────────────────────────────────────┐
│ ◀── <teammate-message test-reviewer> 测试覆盖率报告(另一个 teammate 完成)│
└──────────────────────────────────────────────────────────────────────────┘
│
▼ "三份报告全部到齐。先关闭团队,然后汇总。"
┌─ Step 10 ────────────────────────────────────────────────────────────────┐
│ SendMessage(shutdown_request → security-reviewer) ┐ │
│ SendMessage(shutdown_request → perf-reviewer) ├── 并行关闭 │
│ SendMessage(shutdown_request → test-reviewer) ┘ │
│ │
│ 输出: 📋 综合评审报告 │
│ ├── 🔴 高风险 × 4 (提示注入/路径穿越/缓存key/缓存未测试) │
│ ├── 🟡 中风险 × 4 (maxBytes/I-O/Builder/nil防御) │
│ └── 🟢 亮点 × 3 (并发安全/ReDoS安全/95.3%覆盖率) │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 11 ────────────────────────────────────────────────────────────────┐
│ ◀── shutdown_approved ✓ (security-reviewer / perf-reviewer / │
│ test-reviewer 返回关闭决策) │
│ Leader 处理批准结果后,UI/AppState 侧还可能生成 `teammate_terminated` │
│ 之类的系统通知用于展示,但这不是主关闭协议本身 │
└──────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ Step 12 ────────────────────────────────────────────────────────────────┐
│ TeamDelete()(前提:team file 中已无 `isActive !== false` 的活跃成员) │
│ → 成功后返回类似 "Cleaned up directories and worktrees for team ..." │
│ 但 worktree 清理仅在成员实际带有 `worktreePath` 时才会发生 │
└──────────────────────────────────────────────────────────────────────────┘
2.2 Teammate 完整时间线
三个 Teammate 同时启动、并行工作、各自独立。以下只列一个 Teammate 的一次可能运行轨迹;另外两个的具体任务与顺序也会随运行时调度变化。
收到任务 Prompt: "你是一个专业的安全审计员..."
│
▼
┌─ Step 1 读取与探索源码 ───────────────────────────────┐
│ Read(agentsmd.go, loader.go, agentsmd_test.go) │
│ ↳ test 文件 52.3KB 截断 → 多次重试(Read/Bash/head) │
│ Glob("adk/filesystem/*.go", "adk/internal/*.go") │
│ Read(filesystem/backend.go, internal/config.go) │
└─────────────────────────────────────────────────────────┘
│
▼ 分析完毕,整理报告
┌─ Step 2 ───────────────────────────────────────────────┐
│ TaskUpdate(当前已认领任务, in_progress → completed) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─ Step 3 ───────────────────────────────────────────────┐
│ 📤 SendMessage(report → team-lead) │
│ → 安全审查报告: 2 高危 + 2 中危 + 安全亮点 │
└─────────────────────────────────────────────────────────┘
│
▼ idle_notification(通常 1 次;部分生命周期路径可能额外出现)
→ 收到 shutdown_request → shutdown_approved ✓
2.3 功能分析
- 模型驱动(WithTool)
- 团队创建
- 任务规划
- 生成 Teammate
- 指派/认领任务
- Agent 之间通信
- 汇总与关闭
- 程序驱动
- 显式
TaskUpdate(owner=...),自动投递消息到 owner 邮箱 - task_reminder 任务提醒
- Teammate 空闲后,程序自动投递 idle_notification 消息到 TeamLead 邮箱
- shutdown_approved,自动退出 Teammate run loop
- 显式
三、架构全景

3.1 文件系统
Agent Teams 的本地协作主链路(team / task / inbox 等关键协作状态)不依赖中央数据库或额外队列基础设施,而是直接落到本地文件系统;
~/.claude/
├── teams/{team-name}/
│ ├── config.json # 团队配置(成员列表、模型、颜色等)
│ └── inboxes/ # 消息收件箱
│ ├── team-lead.json # Leader 的收件箱
│ ├── {agent-name}.json # 各 Teammate 的收件箱
│ └── *.json.lock # 并发写入锁
├── tasks/{team-name}/
│ ├── *.json # 任务文件
│ └── .lock # 任务并发锁
└── projects/{project}/{session}/subagents/
├── agent-*.jsonl # Agent 对话记录
└── agent-*.meta.json # Agent 元数据
设计优势:
- 本地协作链路零额外基础设施依赖:team / task / inbox 主链路不需要 Redis/MQ/DB,开箱即用
- 天然持久化:进程崩溃后文件仍在,支持恢复
- 可调试性强:直接查看 JSON 文件即可排查问题
3.2 三种运行后端
Agent Teams 运行时一共有三类执行后端:In-Process、tmux、iTerm2 pane。
| 后端 | 原理 | 通信方式 | 启动速度 | 隔离级别 | 适用场景 |
|---|---|---|---|---|---|
| In-Process | Teammate 在 Leader 同一进程内运行,用 AsyncLocalStorage 隔离上下文 | 初始 prompt 直传;后续消息以内存队列 + 文件信箱为主 | 毫秒级 | 逻辑隔离 | teammateMode=in-process,或 auto 模式下无 pane backend 时;非交互会话也会强制走此模式 |
| Tmux | 通过 tmux 后端运行;可表现为 split pane,也可在 use_splitpane=false 时进入独立 tmux window |
仅文件信箱 | 秒级 | 进程隔离 | 需要完全隔离 |
| iTerm2 | 使用 iTerm2 原生 split pane | 仅文件信箱 | 秒级 | 进程隔离 | auto / tmux 进入 pane backend 后,在 macOS + iTerm2 环境下可能被检测为该后端 |
实际解析逻辑:
teammateMode = in-process
→ 总是使用 In-Process
teammateMode = tmux
→ 优先走 pane backend(tmux / iTerm2 检测与启动逻辑)
→ 若 pane backend 检测失败,会直接报错,不会静默回退到 In-Process
teammateMode = auto(默认)
→ 在 tmux / iTerm2 环境中优先尝试 pane backend
→ 不在 pane 环境时直接使用 In-Process
→ 若处于 auto 且 pane backend 检测失败,则回退到 In-Process
non-interactive session(如 `-p`)
→ 无论配置为何,都会强制走 In-Process
四、Teammate 派生与执行机制
4.1 spawnMultiAgent —— Teammate 派生路由
spawnMultiAgent 是整个 Agent Teams 的 Teammate 工厂模块,被 AgentTool 调用;当 AgentTool 能解析出 teamName,且同时提供了 name 参数,并且当前调用者不是 teammate 时,不走普通 SubAgent 路径,而是调用 spawnTeammate() 进入多 Agent 派生流程;
AgentTool 的主分叉:
AgentTool 接收到调用
│
├─ 能解析出 teamName 且提供了 name,且当前不是 teammate
│ → spawnTeammate()(spawnMultiAgent.ts)
│ → 创建长期运行的 Teammate,fire-and-forget
│ → 返回 { status: "teammate_spawned", agent_id, color, ... }
│
├─ 当前已经是 teammate,且再次命中 team 上下文并提供了 name
│ → 直接报错,禁止 teammate 再派生 teammate
│
└─ 其余情况
→ 继续走普通 AgentTool 分支
→ 可能进入同步 runAgent()、fork 路径,或 run_in_background 异步路径
每次派生 Teammate 的核心步骤(Pane / In-Process 两条路径顺序并不完全相同):
| 步骤 | 操作 | 关键函数/逻辑 |
|---|---|---|
| ① | 名称去重 | generateUniqueTeammateName() — 重复则追加 -2, -3 |
| ② | 清洗名称并生成 Agent ID | 先 sanitizeAgentName(),再按 {name}@{teamName} 生成 |
| ③ | 选择执行路径 | Pane 路径会先做 backend 检测;In-Process 路径则直接进入 spawnInProcessTeammate() |
| ④ | 分配颜色 | assignTeammateColor() — 8 色轮转 |
| ⑤ | 注册运行态 | Pane 路径会先更新 AppState、注册后台任务,再写团队 config.json;In-Process 路径在 spawn 阶段就会先注册 task 到 AppState,启动后再补写 team context / config.json |
| ⑥ | 投递初始 Prompt | Pane 后端通过 mailbox 文件信箱投递;In-Process 仅初始 prompt 不走 mailbox,而是通过 startInProcessTeammate() 直接传入,避免重复消息;后续协作消息仍会走 mailbox / 内存队列 |
4.2 InProcessRunner —— Teammate 执行主循环
inProcessRunner 是 In-Process 模式下 Teammate 的执行引擎。SpawnMultiAgent 负责"创建 Teammate",而 InProcessRunner 负责 Teammate 创建之后的整个生命周期运行。
主循环的完整流程:
while (!aborted && !shouldExit) {
│
├─ ① Token 检查 — 超过 autoCompactThreshold → compactConversation() 压缩历史
│
├─ ② runAgent() — 调用与 AgentTool/SubAgent 相同的 API 推理循环
│ ├── LLM 推理 → 工具调用 → 结果返回 → 循环
│ ├── 每条消息实时更新 AppState(进度、transcript)
│ └── 支持 Escape 中断当前 Turn(不杀死整个 Teammate)
│
├─ ③ Turn 结束 → 标记 isIdle = true
│
├─ ④ sendIdleNotification() → 写入 Leader 的文件信箱
│ idleReason: available | interrupted | failed
│
└─ ⑤ waitForNextPromptOrShutdown() — 阻塞等待
每 500ms 轮询,按优先级检查:
├─ pendingUserMessages[](内存队列,来自 UI 直接注入)
├─ shutdown_request(最高优先级,优先于普通消息)
├─ team-lead 消息(Leader 消息优先于 Peer 消息)
├─ 其他 Teammate 消息(FIFO 顺序)
└─ tryClaimNextTask()(自动认领未分配的 pending 任务)
│
├─ shutdown_request → 传给模型决策(批准/拒绝)
├─ new_message → 唤醒,回到循环顶部开始新 Turn
└─ aborted → 退出循环
}
关键设计点:
| 设计 | 说明 |
|---|---|
| 消息优先级 | pendingUserMessages > shutdown > Leader 消息 > Peer 消息 > 任务认领,避免用户/Leader 指令被对等消息淹没 |
| 权限桥接 | createInProcessCanUseTool() — 当 Teammate 需要执行危险操作时,优先通过 Leader 的 UI confirm queue 弹确认框(带 Teammate 颜色标识);若这条路径不可用,则回退到 mailbox 的 permission_request / permission_response 协议 |
| 自动任务认领(仅 In-Process) | 不仅 Idle 等待期间会调用 tryClaimNextTask() 自动认领任务 |
| 独立 contentReplacementState | 每个 Teammate 维护独立的内容替换状态,compact 后重置,保证缓存前缀一致性 |
五、任务协调机制
5.1 任务模型设计
任务系统支持通过 blocks / blockedBy 表达有向依赖关系;当前源码中可以建立任务依赖边,但没有看到显式的“无环校验”,因此更准确地说是“支持依赖关系建模”,而不是严格保证 DAG。每个任务包含:
Task {
id: "1"
subject: "OAuth 安全审查" # 简要标题
description: "审查所有 OAuth 相关..." # 详细描述
status: "pending | in_progress | completed"
owner: "sec-reviewer" # 当前负责人(实现里可能是 agentName,也可能是 agentId)
blocks: ["2"] # 此任务完成后解锁任务 #2
blockedBy: ["3"] # 此任务被任务 #3 阻塞
}
依赖关系示例:
Task #1 (OAuth 审查) [pending]
↓ blocks
Task #2 (登录功能) [pending] ← blockedBy: ["1"]
↓ blocks
Task #3 (集成测试) [pending] ← blockedBy: ["2"]
Team 模式下的额外自动化:
| 能力 | 说明 |
|---|---|
| owner 写入时机 | TaskCreate 默认 owner 为空;后续可能在三种情况下写入:显式 TaskUpdate(owner=...)、In-Process 自动认领 pending 任务,或 Teammate 将任务标记为 in_progress 且原任务还没有 owner 时由 TaskUpdateTool 自动补写当前 agentName |
| 任务分配通知 | 显式 TaskUpdate(owner=...) 分配任务时,会尝试按 owner 这个键写入 task_assignment 通知; |
| 完成验证 | 在任务被真正标记为 completed 之前执行 Hook;Hook 可阻止不合格的完成 |
| 完成后引导 | 提示 Teammate 调用 TaskList 查找下一个任务 |
5.2 任务退回机制
当前源码里,部分 Teammate 关闭/移除路径会调用任务退回逻辑;但不宜把它概括成“所有退出/故障场景都会统一自动退回”,也不宜把它理解成“所有已认领任务都会被稳定覆盖”:
命中已接入 `unassignTeammateTasks()` 的关闭/移除路径
↓
在当前这条 `teamName` 对应的任务列表里,查找该 Teammate 名下所有未完成任务
↓
将每个任务的 owner 清空,status 重置为 "pending"
↓
命中并成功退回的任务会回到"待领取"状态,可被其他 Teammate 认领
因此更准确地说:系统具备任务退回能力,但这是若干生命周期清理路径提供的能力,而不是对所有故障场景的绝对保证。并且当前实现里 taskListId 仍存在分叉(尤其 In-Process 自动认领显式使用 parentSessionId),所以也不能把“所有已认领任务都会被这条退回路径覆盖”视为强保证。
六、通信系统设计
6.1 异步消息投递
核心设计:异步消息投递(包含部分异步请求/响应协议)
SendMessage 整体上采用异步消息投递:发送方不会同步等待对方业务回复,但各路由仍会等待本次投递动作本身完成(如写 mailbox、发 UDS/bridge、恢复本地 agent)。同时,shutdown / permission / plan approval 等场景又在协议层体现为异步 request/response,只是它们并不是同步 RPC。这个设计避免了:
- 死锁:A 等 B 回复,B 等 A 回复
- 阻塞:发送慢消息拖慢整个系统
6.2 消息类型概览
系统内部会识别多类消息/协议;但要注意,SendMessageTool.message 本身只接受字符串,以及 shutdown_request / shutdown_response / plan_approval_response 三种结构化输入。下面的表格按“工具输入语义 / mailbox 内部协议”来汇总,避免把两者混为一谈:
| 类别 | 消息类型 | 说明 |
|---|---|---|
| 协作消息 | message |
普通字符串直发语义,不是固定的 JSON type |
broadcast |
普通字符串广播语义,不是固定的 JSON type |
|
| 生命周期 | shutdown_request |
Leader → Teammate 关闭请求 |
shutdown_response |
SendMessageTool 的结构化输入类型;工具内部会进一步落到 mailbox 协议消息 |
|
shutdown_approved / shutdown_rejected |
Teammate → Leader 的实际 mailbox 生命周期消息 | |
idle_notification |
Teammate → Leader 空闲状态通知 | |
| 任务相关 | task_assignment |
当前主要出现在 TaskUpdate(owner=...) 这类显式 owner 变更通知路径;不应概括为所有任务分配/认领都会自动发送 |
| 审批流程 | plan_approval_request |
Teammate → Leader 计划审批请求 |
plan_approval_response |
Leader → Teammate 批准/拒绝计划 | |
| 权限管理 | permission_request |
Teammate → Leader 权限申请 |
permission_response |
Leader → Teammate 权限决策 | |
sandbox_permission_request |
Teammate → Leader 的沙箱权限申请 | |
sandbox_permission_response |
Leader → Teammate 的沙箱权限决策 | |
mode_set_request |
运行时模式变更 | |
team_permission_update |
团队级权限同步 |
6.3 Idle/Wakeup 机制
In-Process Teammate 可抽象为 "工作-空闲-唤醒"循环;而 pane / external teammate 在源码中更多通过 inbox poller + 生命周期 hook 实现近似语义:
Teammate 主循环:
while (未终止) {
① 处理消息 / 执行任务(一个 Turn)
② Turn 结束 → 进入 Idle 状态 → 发送 idle_notification 给 Leader
③ 等待新消息(阻塞在 `waitForNextPromptOrShutdown()`)
④ 新消息到达 → 唤醒 → 回到 ①
⑤ 收到 `shutdown_request` → 交给模型决定批准/拒绝;批准后才进入退出流程
}
消息送达时机取决于 Teammate 状态:
| Teammate 状态 | 消息送达方式 | 何时处理 |
|---|---|---|
| Idle(等待中) | 写入 inbox;随后在下一次 wait / poll 周期发现 | 在下一次检查周期开启新 Turn |
| 执行中(In-Process) | 普通 SendMessage 仍写入文件 inbox;少数来自 UI/转录视图的直接注入会进入 pendingUserMessages 内存队列 |
文件 inbox 在下一次 wait/poll 时消费;内存队列在下一次安全边界消费 |
| 执行中(External) | 写入磁盘信箱 | 下一个 Turn 开始时注入 |
关键设计点:消息不会中断正在执行的 Turn,只在安全边界(Turn 间或工具调用间)被消费,保证了执行的原子性。
6.4 Leader 的消息感知
Leader 主要通过两种机制感知消息;但这个总结默认针对交互式/UI 场景:
- Inbox Poller(交互式主路径):UI 层每 1 秒轮询一次 inbox;空闲时直接把消息提交成新 turn,忙时先暂存到
AppState.inbox - Attachment 注入(补充路径 / 非交互补位路径):
teammate_mailbox会把 mailbox 中未读、且未被isStructuredProtocolMessage()识别为需交给useInboxPoller专门处理的消息作为附件注入上下文;这不是简单等同于“所有非协议消息”。在 Leader 视角下它还会合并AppState.inbox中待处理消息。需要注意:在-p等非交互模式下,useInboxPoller不运行,因此 attachment 路径还会额外承担部分原本由 poller 处理的收尾逻辑(例如处理shutdown_approved)
七、其他机制
- 动态 Attachment 系统 ,Agent Teams 在每个 Turn 的 API 调用前,会并行计算一组动态 Attachment(附件);具体数量取决于当前会话形态、工具集、feature flag 与用户输入等条件。
- task_reminder,提醒使用任务工具(默认阈值为连续 10 个 assistant turns 未进行任务管理,且还受 Todo V2、
TaskUpdateTool可用性、Brief 场景、用户类型等条件约束)
- task_reminder,提醒使用任务工具(默认阈值为连续 10 个 assistant turns 未进行任务管理,且还受 Todo V2、
- 隔离机制
- 支持不同的 Teammate 使用 Worktree 隔离文件系统
八、Eino 实现

九、总结
Agent Teams 实现了一套较完整的多 Agent 协作系统,核心设计理念可以概括为:
- 扁平架构,简洁可控:Leader/Teammate 两层结构,避免层级递归的复杂性
- 文件系统即基础设施:零外部依赖,用 JSON 文件实现消息队列、配置中心和持久化
- 异步消息投递:不走同步阻塞 RPC;同时部分场景会通过异步 request/response 协议协作,状态变化主要靠轮询和附件机制感知
- 多层隔离:当前稳定可见的是 AsyncLocalStorage(上下文级)+ 进程隔离(Tmux/iTerm2);另外源码对
worktreePath有兼容与清理逻辑,但不宜直接概括为 Teammate 的公开标准能力 - 自愈容错:提供任务退回(部分路径)、自动清理、后端回退等机制,提升系统韧性
- 成本优化:启用相关 feature 时的缓存分界、Fork 上下文继承、动态 Attachment 按需注入
在 Agent 协作这个方向上,Agent Teams 的"用最简单的技术解决最复杂的协调问题"的哲学,值得在我们自己的多 Agent 系统设计中借鉴。