一、引言:从单 Agent 到多 Agent 协作
在 前文 中,我们分析了 Claude Code 的 Sub-Agent 系统——父子关系、fork 缓存、内存快照等机制。但 Sub-Agent 本质上是层级式的:父 Agent 同步等待子 Agent 完成。当面对复杂的多步骤任务(如"前端、后端、测试并行开发")时,这种模式效率有限。
Agent Teams(内部代号 “Tengu”)是 Claude Code 的下一步进化——一个真正的多智能体协作系统。它的核心设计思想是:
一个 Team Lead 创建并协调多个 Teammates,每个 Teammate 是一个独立的 Agent 实例,通过基于文件的邮箱系统进行异步通信,共同完成复杂任务。
本文基于 2026 年 3 月 31 日的源码快照,从源码层面深入剖析 Agent Teams 的完整实现。
全文架构
| 章节 | 主题 | 核心文件 |
|---|---|---|
| 二 | 功能开关与团队创建 | agentSwarmsEnabled.ts, TeamCreateTool.ts |
| 三 | 队友产卵与三种后端 | spawnMultiAgent.ts, backends/types.ts |
| 四 | 邮箱通信协议 | teammateMailbox.ts, SendMessageTool.ts |
| 五 | 队友生命周期与主循环 | inProcessRunner.ts, teammateInit.ts |
| 六 | 权限同步模型 | permissionSync.ts, leaderPermissionBridge.ts |
| 七 | 状态持久化与上下文隔离 | teamHelpers.ts, teammateContext.ts |
| 八 | 任务列表协调模式 | tasks.ts, TaskCreateTool.ts |
| 九 | 计划审批模式 | ExitPlanModeTool.ts |
| 十 | 总结与设计哲学 | — |
二、功能开关与团队创建
2.1 功能开关:谁能使用 Agent Teams?
Agent Teams 功能由 isAgentSwarmsEnabled() 集中控制:
// utils/agentSwarmsEnabled.ts
export function isAgentSwarmsEnabled(): boolean {
// Anthropic 内部用户:始终启用
if (process.env.USER_TYPE === 'ant') {
return true
}
// 外部用户需要同时满足两个条件:
// 1. 环境变量 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=true 或 --agent-teams CLI 标志
if (!isEnvTruthy(process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS) && !isAgentTeamsFlagSet()) {
return false
}
// 2. GrowthBook 远程开关 tengu_amber_flint 启用(作为 killswitch)
if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true)) {
return false
}
return true
}
这个双重门控设计确保了:
- 内部用户(Anthropic 工程师)可以随时使用进行开发和测试
- 外部用户需要显式 opt-in,同时 Anthropic 保留远程 killswitch 能力
2.2 团队创建流程
TeamCreateTool 是创建团队的入口,其 call() 方法执行以下步骤:
// tools/TeamCreateTool/TeamCreateTool.ts
async call(input, context) {
// 1. 唯一性检查 — 每个 Leader 只能管理一个团队
if (existingTeam) {
throw new Error(`Already leading team "${existingTeam}".`)
}
// 2. 生成唯一团队名 — 冲突时使用 word slug
const finalTeamName = generateUniqueTeamName(team_name)
// 3. 创建确定性 Leader Agent ID
const leadAgentId = formatAgentId(TEAM_LEAD_NAME, finalTeamName)
// 结果: "team-lead@my-project"
// 4. 写入 TeamFile (config.json)
const teamFile: TeamFile = {
name: finalTeamName,
createdAt: Date.now(),
leadAgentId,
leadSessionId: getSessionId(),
members: [{ agentId: leadAgentId, name: TEAM_LEAD_NAME, ... }],
}
await writeTeamFileAsync(finalTeamName, teamFile)
// 5. 注册会话清理 — 确保退出时清理孤立目录
registerTeamForSessionCleanup(finalTeamName)
// 6. 创建对应的任务列表目录(Team = TaskList,1:1 对应)
await resetTaskList(taskListId)
await ensureTasksDir(taskListId)
// 7. 更新 AppState
setAppState(prev => ({
...prev,
teamContext: { teamName, teamFilePath, leadAgentId, teammates: {...} },
}))
}
关键设计决策:
- 确定性 Agent ID:格式为
agentName@teamName(如researcher@my-project),可重复、人类可读、可预测,无需查询即可计算出队友的 ID - Team = TaskList:每个团队自动创建一个同名的任务列表目录,实现 1:1 对应
- 会话清理注册:解决了 GitHub issue #32730 中团队目录永远残留在磁盘上的问题
2.3 TeamFile 数据结构
// utils/swarm/teamHelpers.ts
export type TeamFile = {
name: string
description?: string
createdAt: number
leadAgentId: string
leadSessionId?: string // Leader 的 Session UUID
hiddenPaneIds?: string[] // 隐藏的窗格 ID
teamAllowedPaths?: TeamAllowedPath[] // 团队级路径权限
members: Array<{
agentId: string // "researcher@my-team"
name: string // "researcher"
agentType?: string
model?: string
color?: string
planModeRequired?: boolean
joinedAt: number
tmuxPaneId: string
cwd: string
backendType?: BackendType // 'tmux' | 'iterm2' | 'in-process'
isActive?: boolean // false=idle, true/undefined=active
mode?: PermissionMode // 当前权限模式
}>
}
存储路径:~/.claude/teams/{team-name}/config.json
三、队友产卵与三种后端
3.1 三种执行后端
Claude Code 支持三种队友执行后端,每种适用于不同场景:
| 后端类型 | 进程模型 | 通信方式 | 适用场景 |
|---|---|---|---|
| in-process | 同一 Node.js 进程 | AsyncLocalStorage 隔离 | 默认模式,资源共享 |
| tmux | 独立 CLI 进程 | tmux 分屏 + 文件邮箱 | 终端用户 |
| iterm2 | 独立 CLI 进程 | iTerm2 原生分屏 + 文件邮箱 | macOS iTerm2 用户 |
// utils/swarm/backends/types.ts
export type BackendType = 'tmux' | 'iterm2' | 'in-process'
3.2 产卵流程
spawnTeammate() 是产卵的主入口,根据后端类型分发到不同处理器:
// tools/shared/spawnMultiAgent.ts
async function handleSpawn(input, context) {
// 优先检查 in-process 模式
if (isInProcessEnabled()) {
return handleSpawnInProcess(input, context)
}
// Pre-flight: 确保 pane 后端可用
try {
await detectAndGetBackend()
} catch (error) {
// auto 模式下自动回退到 in-process
if (getTeammateModeFromSnapshot() !== 'auto') throw error
markInProcessFallback()
return handleSpawnInProcess(input, context)
}
// 使用分屏或独立窗口
return input.use_splitpane !== false
? handleSpawnSplitPane(input, context)
: handleSpawnSeparateWindow(input, context)
}
3.3 进程内产卵详解
进程内模式是最核心的后端,其产卵流程:
1. resolveTeammateModel() → 解析模型('inherit' → 跟随 Leader)
2. generateUniqueTeammateName() → 生成唯一名称(冲突时加后缀 -2, -3)
3. sanitizeAgentName() → 清理 @ 字符防止 ID 冲突
4. formatAgentId() → 生成确定性 ID: "name@team"
5. assignTeammateColor() → 分配唯一 UI 颜色
6. spawnInProcessTeammate() → 创建 TeammateContext + AbortController
7. startInProcessTeammate() → fire-and-forget 启动执行循环
8. 注册到 TeamFile + AppState
关键差异:进程内队友的初始 prompt 不通过邮箱传递(避免重复消息),而是直接传递给 startInProcessTeammate()。
3.4 Tmux/iTerm2 分屏产卵
分屏模式的队友是完全独立的 Claude Code CLI 进程:
cd /path/to/cwd && env CLAUDECODE=... \
claude --agent-id "researcher@my-team" \
--agent-name "researcher" \
--team-name "my-team" \
--agent-color "blue" \
--parent-session-id "xxx" \
--model "claude-sonnet-4-20250514"
初始 prompt 通过邮箱发送,队友启动后通过 useInboxPoller 轮询收到第一条消息。
3.5 权限继承
队友继承 Leader 的权限配置:
function buildInheritedCliFlags(options) {
// plan_mode_required 时不继承 bypassPermissions(安全优先)
if (planModeRequired) {
// 不继承 --dangerously-skip-permissions
} else if (permissionMode === 'bypassPermissions') {
flags.push('--dangerously-skip-permissions')
} else if (permissionMode === 'auto') {
flags.push('--permission-mode auto')
}
// 继承 --model, --settings, --plugin-dir, --chrome
}
四、邮箱通信协议
4.1 基于文件的邮箱系统
Agent Teams 的通信核心是一个基于文件的邮箱系统——每个队友有一个 JSON 文件作为收件箱:
~/.claude/teams/{team_name}/inboxes/{agent_name}.json
消息格式:
// utils/teammateMailbox.ts
export type TeammateMessage = {
from: string // 发送者名称
text: string // 消息内容(可以是纯文本或 JSON 结构化消息)
timestamp: string // ISO 时间戳
read: boolean // 是否已读
color?: string // 发送者颜色
summary?: string // 5-10 字摘要(UI 预览用)
}
4.2 并发控制
多个 Agent 同时写入同一个邮箱时,使用 proper-lockfile 库实现文件锁:
const LOCK_OPTIONS = {
retries: {
retries: 10, // 10 次重试
minTimeout: 5, // 最小等待 5ms
maxTimeout: 100, // 最大等待 100ms
},
}
export async function writeToMailbox(recipientName, message, teamName) {
const lockFilePath = `${inboxPath}.lock`
const release = await lockfile.lock(inboxPath, { lockfilePath, ...LOCK_OPTIONS })
try {
const messages = await readMailbox(recipientName, teamName)
messages.push({ ...message, read: false })
await writeFile(inboxPath, JSON.stringify(messages, null, 2), 'utf-8')
} finally {
await release()
}
}
4.3 消息类型全景
邮箱中传递的消息类型远不止纯文本,完整的结构化协议消息包括:
| 类型 | 方向 | 用途 |
|---|---|---|
permission_request |
Worker → Leader | 请求工具使用权限 |
permission_response |
Leader → Worker | 权限审批结果 |
sandbox_permission_request |
Worker → Leader | 沙箱网络访问权限 |
sandbox_permission_response |
Leader → Worker | 沙箱权限结果 |
shutdown_request |
Leader → Worker | 请求队友关闭 |
shutdown_approved |
Worker → Leader | 确认关闭 |
shutdown_rejected |
Worker → Leader | 拒绝关闭(附原因) |
plan_approval_request |
Worker → Leader | 请求计划审批 |
plan_approval_response |
Leader → Worker | 计划审批结果 |
idle_notification |
Worker → Leader | 队友空闲通知 |
task_assignment |
Leader → Worker | 任务分配 |
team_permission_update |
Leader → All | 广播权限更新 |
mode_set_request |
Leader → Worker | 设置权限模式 |
4.4 SendMessageTool
SendMessageTool 是 LLM 使用的通信接口,支持多种路由方式:
// tools/SendMessageTool/SendMessageTool.ts
async call(input, context) {
// 1. UDS/Bridge 地址 → 跨会话发送
if (feature('UDS_INBOX') && parseAddress(input.to).scheme === 'bridge') { ... }
// 2. 进程内 subagent(通过 agentNameRegistry 查找)
if (typeof input.message === 'string' && input.to !== '*') {
const registered = appState.agentNameRegistry.get(input.to)
if (registered) {
// 运行中 → 直接加入 pendingMessages 队列
if (task.status === 'running') {
queuePendingMessage(agentId, input.message, ...)
}
// 已停止 → 自动恢复 (auto-resume)
else { await resumeAgentBackground(...) }
}
}
// 3. "*" → 广播到所有队友
if (input.to === '*') return handleBroadcast(...)
// 4. 名称 → 写入目标邮箱
return handleMessage(input.to, input.message, ...)
// 5. 结构化消息 → 分发到对应 handler
switch (input.message.type) {
case 'shutdown_request': return handleShutdownRequest(...)
case 'shutdown_response': ...
case 'plan_approval_response': ...
}
}
五、队友生命周期与主循环
5.1 进程内队友主循环
runInProcessTeammate() 是进程内队友的核心执行引擎:
// utils/swarm/inProcessRunner.ts
export async function runInProcessTeammate(config) {
while (!abortController.signal.aborted && !shouldExit) {
// 1. 创建 per-turn AbortController
// Escape 键可中断当前轮次但不杀死队友
const currentWorkAbortController = createAbortController()
// 2. 检查是否需要自动压缩
if (tokenCount > getAutoCompactThreshold(model)) {
const compactedSummary = await compactConversation(allMessages, ...)
contextMessages = buildPostCompactMessages(compactedSummary)
allMessages.length = 0
allMessages.push(...contextMessages)
}
// 3. 在双重上下文中执行 runAgent()
await runWithTeammateContext(teammateContext, async () => {
return runWithAgentContext(agentContext, async () => {
for await (const message of runAgent({
agentDefinition: iterationAgentDefinition,
promptMessages,
canUseTool: createInProcessCanUseTool(identity, ...),
forkContextMessages,
...
})) {
iterationMessages.push(message)
allMessages.push(message)
// 更新进度、追踪工具调用
}
})
})
// 4. 标记为 idle 并通知 Leader
updateTaskState(taskId, task => ({ ...task, isIdle: true }))
await sendIdleNotification(identity.agentName, identity.color, ...)
// 5. 等待唤醒(轮询邮箱 + 任务列表)
const waitResult = await waitForNextPromptOrShutdown(...)
switch (waitResult.type) {
case 'shutdown_request':
// 传递给 LLM 模型决策
currentPrompt = formatAsTeammateMessage(waitResult.request.from, ...)
break
case 'new_message':
currentPrompt = formatAsTeammateMessage(waitResult.from, waitResult.message, ...)
break
case 'aborted':
shouldExit = true
break
}
}
}
5.2 等待唤醒的优先级策略
waitForNextPromptOrShutdown() 的轮询策略经过精心设计:
优先级从高到低:
① pendingUserMessages — 进程内直接注入的用户消息(最高优先)
② shutdown_request — Leader 的关闭请求(扫描全部未读消息)
③ team-lead 消息 — Leader 的普通消息(代表用户意图)
④ 其他队友消息 — Peer DM(FIFO 顺序)
⑤ 任务列表 — 自动认领 pending + 无 owner 的任务
⑥ 无消息 — sleep(500ms) → 继续轮询
这个优先级设计确保了:
- 用户输入永远被最先处理
- 关闭请求不会被 peer-to-peer 消息洪水淹没
- Leader 消息优先于队友间通信
- 空闲队友自动认领任务,无需 Leader 手动分配
5.3 Tmux 队友初始化
Tmux 模式下的队友在启动时通过 initializeTeammateHooks() 初始化:
// utils/swarm/teammateInit.ts
export function initializeTeammateHooks(setAppState, sessionId, teamInfo) {
// 1. 读取 TeamFile 获取 Leader 信息
const teamFile = readTeamFile(teamName)
// 2. 应用团队级路径权限
for (const allowedPath of teamFile.teamAllowedPaths) {
setAppState(prev => ({
...prev,
toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, {
type: 'addRules',
rules: [{ toolName: allowedPath.toolName, ruleContent: `/${allowedPath.path}/**` }],
behavior: 'allow',
destination: 'session',
}),
}))
}
// 3. 注册 Stop 钩子 — 停止时自动发送 idle_notification
addFunctionHook(setAppState, sessionId, 'Stop', '', async (messages) => {
void setMemberActive(teamName, agentName, false)
const notification = createIdleNotification(agentName, { idleReason: 'available' })
await writeToMailbox(leadAgentName, { from: agentName, text: JSON.stringify(notification), ... })
return true
})
}
这段初始化代码只覆盖了“变成 idle 时写回 isActive = false”的下半程;对应的上半程在 REPL.tsx 中完成:每当队友开始新一轮 query,onQuery() 会调用 setMemberActive(teamName, agentName, true)。也就是说,config.json 里的 isActive 字段并不是一次性元数据,而是被运行时持续刷新,Teams UI 读取的 running/idle 状态,本质上就是这套文件级心跳机制的结果。
5.4 优雅关闭流程
关闭流程是协商式的——Leader 发请求,但最终由 LLM 模型决策:
1. Leader 调用 SendMessage(shutdown_request) → 写入队友邮箱
2. 队友在 waitForNextPromptOrShutdown() 中检测到请求
3. 请求被格式化为 <teammate-message> 传递给 LLM
4. LLM 决策:
- approve → 发送 shutdown_approved → abortController.abort() 或 gracefulShutdown()
- reject → 发送 shutdown_rejected (附原因) → 继续工作
强制杀死路径:
- 进程内:
killInProcessTeammate()→abortController.abort() - tmux/iTerm2:
getBackendByType(backendType).killPane(paneId)
六、权限同步模型
6.1 路径一:Leader UI 队列(首选)
进程内队友的权限请求直接加入 Leader 的 ToolUseConfirm 队列:
// utils/swarm/inProcessRunner.ts — createInProcessCanUseTool()
const setToolUseConfirmQueue = getLeaderToolUseConfirmQueue()
if (setToolUseConfirmQueue) {
return new Promise(resolve => {
setToolUseConfirmQueue(queue => [...queue, {
tool, description, input, toolUseContext, toolUseID,
permissionResult: result,
// Worker 标识 — 在 Leader UI 中显示名称和颜色
workerBadge: { name: identity.agentName, color: identity.color },
onAllow(updatedInput, permissionUpdates, feedback) {
persistPermissionUpdates(permissionUpdates)
// 权限更新回写到 Leader 的共享上下文
if (permissionUpdates.length > 0) {
setToolPermissionContext(updatedContext, { preserveMode: true })
}
resolve({ behavior: 'allow', updatedInput })
},
onReject(feedback) {
resolve({ behavior: 'ask', message: SUBAGENT_REJECT_MESSAGE })
},
}])
})
}
特点:实时 UI 交互、工具特定组件(Bash/FileEdit 各有专用 UI)、权限更新双向同步。
6.2 路径二:邮箱系统(回退)
当 UI 桥接不可用时,使用邮箱系统:
// Worker 侧:发送请求
const request = createPermissionRequest({ toolName, toolUseId, input, description, ... })
registerPermissionCallback({ requestId: request.id, onAllow, onReject })
await sendPermissionRequestViaMailbox(request)
// Worker 轮询响应 (每 500ms)
const pollInterval = setInterval(async () => {
const allMessages = await readMailbox(identity.agentName, identity.teamName)
for (const msg of allMessages) {
const parsed = isPermissionResponse(msg.text)
if (parsed && parsed.request_id === request.id) {
processMailboxPermissionResponse(parsed)
return
}
}
}, PERMISSION_POLL_INTERVAL_MS)
6.3 Bash 分类器优化
进程内队友对 Bash 命令有特殊优化——先等分类器结果,再决定是否弹出审批:
if (feature('BASH_CLASSIFIER') && tool.name === BASH_TOOL_NAME && result.pendingClassifierCheck) {
const classifierDecision = await awaitClassifierAutoApproval(
result.pendingClassifierCheck,
abortController.signal,
)
if (classifierDecision) {
return { behavior: 'allow', updatedInput: input, decisionReason: classifierDecision }
}
}
七、状态持久化与上下文隔离
7.1 文件系统持久化
~/.claude/
├── teams/{team-name}/
│ ├── config.json # TeamFile — 团队配置
│ ├── inboxes/
│ │ ├── team-lead.json # Leader 的收件箱
│ │ ├── researcher.json # 队友的收件箱
│ │ └── tester.json
│ └── permissions/
│ ├── pending/ # 待审批的权限请求
│ └── resolved/ # 已处理的权限请求
└── tasks/{team-name}/ # 任务列表(与团队 1:1 对应)
7.2 内存状态 (AppState)
// AppState.teamContext
teamContext: {
teamName: string
teamFilePath: string
leadAgentId: string
teammates: {
[agentId: string]: {
name: string
agentType?: string
color?: string
tmuxSessionName: string
tmuxPaneId: string
cwd: string
spawnedAt: number
}
}
}
// AppState.tasks[taskId] — InProcessTeammateTaskState
{
type: 'in_process_teammate'
identity: TeammateIdentity // agentId, agentName, teamName, color
prompt: string
abortController?: AbortController // 生命周期控制
currentWorkAbortController?: AbortController // 当前轮次控制
permissionMode: PermissionMode
messages?: Message[] // UI 镜像,上限 50 条
pendingUserMessages: string[] // 待传递的用户消息
isIdle: boolean
shutdownRequested: boolean
progress?: AgentProgress
}
7.3 AsyncLocalStorage 上下文隔离
进程内队友使用 Node.js 的 AsyncLocalStorage 实现上下文隔离:
// utils/teammateContext.ts
const teammateContextStorage = new AsyncLocalStorage<TeammateContext>()
export type TeammateContext = {
agentId: string // "researcher@my-team"
agentName: string // "researcher"
teamName: string
color?: string
planModeRequired: boolean
parentSessionId: string
isInProcess: true // 始终为 true
abortController: AbortController
}
// 在特定上下文中运行
export function runWithTeammateContext<T>(context: TeammateContext, fn: () => T): T {
return teammateContextStorage.run(context, fn)
}
身份解析优先级(teammate.ts):
1. AsyncLocalStorage(进程内队友)→ getTeammateContext()
2. dynamicTeamContext(tmux 队友通过 CLI 参数)
3. 传入的 teamContext(Leader 通过 AppState)
八、任务列表协调模式
8.1 任务驱动协调
团队通过共享的任务列表进行工作协调:
1. Leader 创建任务 → TaskCreate → ~/.claude/tasks/{team}/
2. 队友自动认领 → 空闲时扫描 pending + 无 owner 的任务
3. 按 ID 顺序优先 → 低 ID 先认领(保证依赖顺序)
4. 完成后标记 → TaskUpdate → status: completed
5. 检查依赖 → blockedBy 解除后才能认领
这里需要特别注意一个容易写错的实现细节:当前版本的任务列表解析优先级,已经明确偏向“团队名统一寻址”。getTaskListId() 的优先级是:CLAUDE_CODE_TASK_LIST_ID → 进程内队友的 teamName → 进程型队友的 CLAUDE_CODE_TEAM_NAME → Leader 侧的 leaderTeamName → sessionId 回退。因此 TeamCreateTool 中那次 setLeaderTeamName() 调用并不是装饰性代码,而是一个关键补丁:它确保 Team Lead 不会继续把任务写进自己的 session 目录,而是与所有队友共享同一个团队任务目录。
// utils/swarm/inProcessRunner.ts
function findAvailableTask(tasks: Task[]): Task | undefined {
const unresolvedTaskIds = new Set(
tasks.filter(t => t.status !== 'completed').map(t => t.id)
)
return tasks.find(task => {
if (task.status !== 'pending') return false
if (task.owner) return false
// 所有前置依赖必须已完成
return task.blockedBy.every(id => !unresolvedTaskIds.has(id))
})
}
8.2 空闲通知机制
每个队友完成一轮执行后,自动发送 idle_notification:
const notification = createIdleNotification(agentName, {
idleReason: workWasAborted ? 'interrupted' : 'available',
summary: getLastPeerDmSummary(allMessages), // 最后一条 peer DM 的摘要
completedTaskId,
completedStatus: 'resolved' | 'blocked' | 'failed',
})
Leader 收到通知后可以:
- 分配新任务
- 发送后续消息
- 忽略(队友会自动认领任务列表中的工作)
九、计划审批模式
当 plan_mode_required 为 true 时,队友必须先制定计划再实施:
1. 队友进入计划模式 → 制定实施方案
2. 发送 plan_approval_request → Leader 邮箱
{ planFilePath, planContent, requestId }
3. Leader 侧收到请求后,当前默认实现会在 useInboxPoller 中自动批准:
- approved = true
- permissionMode 继承 Leader 当前模式(如果 Leader 自己仍是 plan,则降级成 default)
4. 队友收到 plan_approval_response 后退出计划态,进入实施阶段
这里有一个很值得点出的实现现状:协议层保留了“手工批准 / 手工拒绝”的完整能力,但默认运行路径已经改成了 auto-approve。一方面,SendMessageTool 仍然实现了 plan_approval_response 的 approve / reject 处理逻辑,并且通过 isTeamLead() 限制只有 Team Lead 才能发送这类审批结果;另一方面,useInboxPoller 在 Leader 侧检测到 plan_approval_request 时,会直接构造批准消息写回队友邮箱,并把原消息继续作为普通消息透传给模型。
这意味着当前版本更像是一个**“协议先完整、产品策略后收敛”**的过渡态:底层消息协议已经支持严格的人工 gate,但默认 UX 为了减少阻塞,选择了自动批准。写文章时如果只说“Leader 手工审阅后批准”,就会和现状实现有偏差;更准确的描述应该是:手工审批是协议能力,自动批准是当前默认策略。
十、队友系统提示
所有队友在标准系统提示之后附加 TEAMMATE_SYSTEM_PROMPT_ADDENDUM:
# 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.
进程内队友还始终注入以下团队必需工具(即使 agent 定义限制了工具列表):
SendMessage— 通信TeamCreate/TeamDelete— 团队管理TaskCreate/TaskGet/TaskList/TaskUpdate— 任务协调
十一、总结与设计哲学
核心设计原则
| 原则 | 实现 |
|---|---|
| 基础设施简单 | 文件系统作为消息总线,无需额外中间件 |
| 确定性 ID | name@team 格式,可预测、人类可读 |
| 协商式关闭 | shutdown 由 LLM 决策而非强制终止 |
| 权限安全 | 计划模式下不继承 bypassPermissions |
| 自动回退 | in-process 可自动降级到 tmux/iTerm2 |
| 会话清理 | 退出时自动清理孤立的团队目录和窗格 |
架构层次
┌──────────────────────────────────────────┐
│ Coordination Layer │ TeamCreateTool / TaskListTool
├──────────────────────────────────────────┤
│ Communication Layer │ SendMessageTool / Mailbox
├──────────────────────────────────────────┤
│ Execution Layer │ inProcessRunner / tmux / iTerm2
├──────────────────────────────────────────┤
│ Permission Layer │ permissionSync / workerBadge
├──────────────────────────────────────────┤
│ Identity Layer │ agentId / teammateContext / ALS
├──────────────────────────────────────────┤
│ Storage Layer │ TeamFile / AppState / TaskList
└──────────────────────────────────────────┘
与传统多 Agent 框架的对比
| 特性 | Claude Code Agent Teams | AutoGen / CrewAI |
|---|---|---|
| 通信方式 | 文件邮箱 + 进程内队列 | 内存消息传递 |
| 进程模型 | 同进程 ALS / tmux 分屏 | 通常同进程 |
| 权限控制 | 两路径审批 + 计划模式 | 通常无权限隔离 |
| 任务协调 | 文件系统任务列表 + 自动认领 | 框架级编排 |
| 关闭方式 | 协商式(LLM 决策) | 通常编程式 |
| 状态持久化 | 文件 + 内存双写 | 通常仅内存 |
Agent Teams 的设计体现了 Claude Code 一贯的工程哲学:用最简单的基础设施(文件系统)实现最可靠的分布式协调,同时保持对安全性和用户体验的极致关注。
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付