Claude Code Agent Teams:多 Agent 协作的生命周期与实现机制

多 Agent 协作不是简单的"同时启动几个模型"。

当你让多个 Agent 一起完成一个复杂项目时,真正的问题不是它们能不能同时运行,而是如何让它们像一支团队那样工作:有明确的组织架构、共享的任务状态、可靠的消息通道,以及完整的生命周期管理------从创建到运行再到最终的清理。

Claude Code 的 Agent Teams 解决的就是这个问题。它不是单一功能,而是一套贯穿多 Agent 协作完整生命周期的机制:

  • 团队容器(Team)------划定协作边界,建立成员归属
  • 成员身份(Teammate)------区别于普通 subagent 的长期运行实体
  • 共享任务表(Task List)------记录任务状态、归属和依赖关系
  • 消息通道(Mailbox)------成员间的异步通信机制
  • 生命周期管理------从创建、运行到最终清理的完整闭环

注意,teammate 和普通 subagent 的关键区别:

  • teammate 在同一个 Node.js 进程内长期运行,通过独立的上下文实现隔离,而非任务结束即销毁。这让多个 Agent 能像一个团队一样持续协作。
  • subagent 运行完成后立即销毁

理解 Agent Teams,不应把它当作"功能列表"来记忆,而是跟随一条 Agent Team 从创建、运行到最终清理的完整生命周期,看清每个环节如何衔接。


1. 核心概念

在深入实现之前,先明确几个关键概念:

概念 定位 类比
team 团队容器,划定协作边界 项目组
teammate 团队中的成员身份 项目组成员
Task List 共享任务表,记录任务状态和归属 项目看板
mailbox 消息通道,在成员之间传递通知 团队群聊/通知
runner 管理 teammate 生命周期的代码层(非独立进程) 项目经理(协调者)

关键区分

  • team vs teammate:team 是容器,teammate 是成员。一个 team 可以有多个 teammate。
  • Task List vs mailbox:Task List 是"状态事实",mailbox 是"消息通知"。前者解决"任务归谁",后者解决"告诉谁"。
  • teammate vs runner:teammate 是执行任务的 agent,runner 是管理 teammate 生命周期的代码层。runner 负责启动、idle 循环、接收消息、传递 prompt。

2. 创建团队容器

Agent Teams 的生命周期从 TeamCreate 工具开始。这是一个真实的内置工具,定义在 src/tools/TeamCreateTool/TeamCreateTool.ts

2.1 工具注册:从全局声明到具体实现

TeamCreateTool 通过懒加载注册到全局工具列表,避免循环依赖:

ts 复制代码
// src/tools.ts
const getTeamCreateTool = () =>
  require('./tools/TeamCreateTool/TeamCreateTool.js').TeamCreateTool;

// 条件启用:仅当 Agent Swarms 功能开启时注册
const allTools = [
  // ... 其他工具
  ...(isAgentSwarmsEnabled() ? [getTeamCreateTool(), getTeamDeleteTool()] : []),
  // ...
];

它不是一个独立进程或外部服务,而是 Claude Code 内置的 buildTool 实现:

ts 复制代码
// src/tools/TeamCreateTool/TeamCreateTool.ts
export const TeamCreateTool: Tool<InputSchema, Output> = buildTool({
  name: 'TeamCreate',
  searchHint: 'create a multi-agent swarm team',
  shouldDefer: true, // 让模型优先决定是否调用
  isEnabled() {
    return isAgentSwarmsEnabled();
  },

  async description() {
    return 'Create a new team for coordinating multiple agents';
  },

  async validateInput(input) {
    /* team_name 必填校验 */
  },
  async call(input, context) {
    /* 核心逻辑 */
  },
});

buildTool 是 Claude Code 的工具构造器,负责统一处理输入校验、输出格式化、权限检查等通用逻辑。TeamCreateTool 通过实现 call 方法注入具体的团队创建逻辑。

TeamCreateTool 会被 当前 Agent 调用,模型根据上下文决定是否需要创建团队:

ts 复制代码
TeamCreateTool.call({
  team_name: "my-team",      // 团队名称(必填)
  description: "...",        // 可选描述
  agent_type: "researcher"   // leader 角色类型(可选)
}, context)

注意两个限制:

  • 一个 leader 只能管理一个 team,如果已在团队中再次调用会报错
  • teammate 不能调用------只有非 teammate 身份的 Agent 才能创建团队

2.2 核心实现:写入配置、准备 Task List、更新运行时状态

call 方法内部做了五件事:

ts 复制代码
async call(input, context) {
  const { setAppState, getAppState } = context
  const { team_name, description, agent_type } = input

  // 1. 限制:一个 leader 只能管理一个 team
  const existingTeam = getAppState().teamContext?.teamName
  if (existingTeam) {
    throw new Error('A leader can only manage one team at a time...')
  }

  // 2. 生成唯一 team name(名称冲突时自动生成新的)
  const finalTeamName = generateUniqueTeamName(team_name)

  // 3. 写入团队配置到 ~/.claude/teams/{team-name}/config.json
  const teamFile: TeamFile = {
    name: finalTeamName,
    description,
    createdAt: Date.now(),
    leadAgentId: formatAgentId(TEAM_LEAD_NAME, finalTeamName),
    leadSessionId: getSessionId(),
    members: [{
      agentId: leadAgentId,
      name: TEAM_LEAD_NAME,
      agentType: leadAgentType,
      model: leadModel,
      joinedAt: Date.now(),
      cwd: getCwd(),
      subscriptions: [],
    }],
  }
  await writeTeamFileAsync(finalTeamName, teamFile)

  // 4. 准备空的共享 Task List
  const taskListId = sanitizeName(finalTeamName)
  await resetTaskList(taskListId)
  await ensureTasksDir(taskListId)

  // 5. 绑定 leader 到 team Task List,后续任务写入共享表
  setLeaderTeamName(taskListId)

  // 6. 更新 AppState 运行时团队状态
  setAppState(prev => ({ ...prev, teamContext: { teamName: finalTeamName, ... } }))
  // ...
}

这段代码做了五件事:

步骤 做了什么 存储位置
1 写入团队配置 磁盘 ~/.claude/teams/{team-name}/config.json
2 准备空 Task List 目录 文件系统
3 绑定 leader 到 team 内存 leaderTeamName
4 更新运行时团队状态 内存 AppState.teamContext
5 注册清理任务 内存(session 结束时自动清理)

2.3 关键细节

leader 不是 teammate。源码中有这样一段注释:

ts 复制代码
// We intentionally don't set CLAUDE_CODE_AGENT_ID for the team lead because:
// 1. The lead is not a "teammate" - isTeammate() should return false for them
// 2. Their ID is deterministic (team-lead@teamName) and can be derived when needed
// 3. Setting it would cause isTeammate() to return true, breaking inbox polling

leader 不会被设置 CLAUDE_CODE_AGENT_ID,所以 isTeammate() 对 leader 返回 false------这是设计上的区分:leader 负责管理,teammate 负责执行。

TeamCreate 不创建 teammate 。它只建立团队容器,真正的 teammate 是后续通过 Agent(name + team_name) 创建的。

TeamCreate 也不创建具体任务 。它只准备一张空的 Task List 目录,真正写入任务是后面调用 TaskCreate 时才发生的。

2.4 团队配置文件 vs 运行时团队状态

TeamCreate 的产物分为两层:

  • 团队配置文件(TeamFile------持久化到磁盘的 JSON 文件,用于跨会话恢复团队信息
  • 运行时团队状态(AppState.teamContext ------内存中的临时状态,包含 teamNameleadAgentIdteammates(队友列表)等,供当前会话快速访问

团队容器的结构关系可以这样理解:它不是"启动几个 Agent",而是先建立组织架构、共享状态和运行时上下文。容器就绪后,真正的 teammate 才会在下一步通过 Agent 工具创建。


3. 创建 teammate

创建团队的 leader(当前 Agent)通过调用 AgentTool 来拉新成员进团队。

3.1 Leader 调用 AgentTool

AgentTool 有两种用法,取决于有没有 team_name

  • 没有 team_name:创建普通 subagent,任务结束即销毁
  • team_name:创建 teammate,加入团队协作

Leader 已经创建了团队,所以调用 AgentTool 时带有 team_name,创建的就是 teammate。

ts 复制代码
export const AgentTool = buildTool({
  name: 'Agent',
  async description() {
    return 'Launch a new agent';
  },
  get inputSchema() {
    /* ... */
  },
  get outputSchema() {
    /* ... */
  },

  async call(
    {
      prompt,
      subagent_type,
      description,
      model: modelParam,
      name,
      team_name,
      mode: spawnMode,
    }: AgentToolInput,
    toolUseContext,
    canUseTool,
    assistantMessage,
    onProgress?,
  ) {
    const appState = toolUseContext.getAppState();
    const teamName = resolveTeamName({ team_name }, appState);

    // 有 team_name 和 name 时,创建 teammate
    if (teamName && name) {
      const result = await spawnTeammate(
        {
          name,
          prompt,
          description,
          team_name: teamName,
          use_splitpane: true,
          plan_mode_required: spawnMode === 'plan',
          model: modelParam,
          agent_type: subagent_type,
        },
        toolUseContext,
      );

      return { data: { status: 'teammate_spawned', ...result.data } };
    }
    // ...
  },
});

Task List 只是任务状态表;teammate 是通过 Agent(name + team_name) 显式创建出来的团队成员。

Agent Teams 还有一个组织边界:teammate 不能继续创建 teammate。

ts 复制代码
if (isTeammate() && teamName && name) {
  throw new Error('Teammates cannot spawn other teammates');
}

原因很简单:团队名册是扁平的。leader 负责管理 team,teammate 负责做任务;如果 teammate 继续扩张团队组织结构,成员归属就会变得混乱。teammate 仍然可以用普通 subagent 辅助自己,但不能继续拉新 teammate 进团队。

3.2 生成 teammate 身份和运行环境

Leader 调用 AgentTool 后,系统开始真正创建 teammate。这个过程包括:生成唯一 ID、分配颜色、准备运行环境。

ts 复制代码
async function handleSpawn(
  input: SpawnInput,
  context: ToolUseContext,
): Promise<{ data: SpawnOutput }> {
  const appState = getAppState();
  const teamName = input.team_name || appState.teamContext?.teamName;
  const uniqueName = await generateUniqueTeammateName(name, teamName);
  const sanitizedName = sanitizeAgentName(uniqueName);
  const teammateId = formatAgentId(sanitizedName, teamName);
  const teammateColor = assignTeammateColor(teammateId);

  const config: InProcessSpawnConfig = {
    name: sanitizedName,
    teamName,
    prompt,
    color: teammateColor,
    planModeRequired: plan_mode_required ?? false,
    model,
  };

  const result = await spawnInProcessTeammate(config, context);
  // ...
}

这段逻辑生成了 teammate 的关键身份信息:

接下来,teammate 不会新开进程,而是在同一个 Node.js 进程里运行。系统会为它创建独立的身份上下文,避免多个 teammate 之间互相干扰。

ts 复制代码
export async function spawnInProcessTeammate(
  config: InProcessSpawnConfig,
  context: SpawnContext,
): Promise<InProcessSpawnOutput> {
  const { name, teamName, prompt, color, planModeRequired, model } = config;
  const { setAppState } = context;

  const agentId = formatAgentId(name, teamName);
  const taskId = generateTaskId('in_process_teammate');
  const abortController = createAbortController();
  const parentSessionId = getSessionId();

  const identity: TeammateIdentity = {
    agentId,
    agentName: name,
    teamName,
    color,
    planModeRequired,
    parentSessionId,
  };

  // 创建 teammate 上下文,用于同进程隔离
  const teammateContext = createTeammateContext({
    agentId,
    agentName: name,
    teamName,
    color,
    planModeRequired,
    parentSessionId,
    abortController,
  });

  // 创建 task state 并注册到 AppState
  const taskState: InProcessTeammateTaskState = {
    ...createTaskStateBase(
      taskId,
      'in_process_teammate',
      description,
      context.toolUseId,
    ),
    type: 'in_process_teammate',
    status: 'running',
    identity,
    prompt,
    model,
    abortController,
    // ...
  };
  registerTask(taskState, setAppState);
  // ...
}
对象 定位 典型使用场景
teammateContext teammate 特有的运行时上下文 3 个 teammate 同时运行,都调用 getAgentName()teammateContext 让每个调用返回自己的名字(researcher/coder/tester),不会互相混淆。 还用于中断控制(abortController)。 只有 teammate 有这个机制,普通 Agent 没有。
taskState 单个 Agent 的状态 每个 Agent(无论是不是 teammate)都有自己的 taskState,记录自己的运行状态(running/idle/completed)、消息等。 Leader 通过 researcher 的 taskState 查看它是否在忙。 普通 Agent、teammate、shell 任务都有各自的 taskState

两者关系:teammateContexttaskState 平行存在,前者管"团队身份",后者管"运行状态"。

3.3 启动执行循环

创建身份和 task state 之后,teammate 还没有真正开始工作。真正让它跑起来的是执行循环。

spawn 成功后,系统会立即启动 in-process runner(teammate 的执行循环):

ts 复制代码
export function startInProcessTeammate(config: InProcessRunnerConfig): void {
  const agentId = config.identity.agentId;
  void runInProcessTeammate(config).catch(error => {
    logForDebugging(
      `[inProcessRunner] Unhandled error in ${agentId}: ${error}`,
    );
  });
}

// 启动时传入的配置
startInProcessTeammate({
  identity: {
    agentId: teammateId,
    agentName: sanitizedName,
    teamName,
    color: teammateColor,
    planModeRequired: plan_mode_required ?? false,
    parentSessionId: result.teammateContext.parentSessionId,
  },
  taskId: result.taskId,
  prompt,
  teammateContext: result.teammateContext,
  toolUseContext: { ...context, messages: [] }, // 注意:空数组,不从 leader 继承历史
  abortController: result.abortController,
  // ...
});

注意 messages: []。in-process teammate 不直接继承 leader 的完整对话历史,而是从自己的初始 prompt 开始构建上下文。这能避免 teammate 长期持有 leader 的大量上下文,也能让它的会话更像独立成员。

执行循环里,teammate 会建立自己的 agent context:

ts 复制代码
export async function runInProcessTeammate(
  config: InProcessRunnerConfig,
): Promise<InProcessRunnerResult> {
  const {
    identity,
    taskId,
    prompt,
    teammateContext,
    toolUseContext,
    abortController,
    model,
  } = config;
  const { setAppState } = toolUseContext;

  // 创建 AgentContext 用于分析归因
  const agentContext: AgentContext = {
    agentId: identity.agentId,
    parentSessionId: identity.parentSessionId,
    agentName: identity.agentName,
    teamName: identity.teamName,
    agentColor: identity.color,
    planModeRequired: identity.planModeRequired,
    isTeamLead: false,
    agentType: 'teammate',
    invocationKind: 'spawn',
  };

  // 在 teammateContext 和 agentContext 包裹下运行 agent loop
  await runWithTeammateContext(teammateContext, async () => {
    return runWithAgentContext(agentContext, async () => {
      for await (const message of runAgent({
        promptMessages,
        toolUseContext,
        canUseTool: createInProcessCanUseTool(
          identity,
          currentWorkAbortController,
        ),
        model,
        // ...
      })) {
        // 处理 agent 输出、工具进度和状态同步
      }
    });
  });
}

这说明 in-process teammate 不是"假 agent"。它仍然跑完整的 agent loop,只是运行在同一个进程里,并通过 runWithTeammateContext() 获得自己的团队身份。

执行循环启动后,teammate 才从"名册里的成员"变成"正在工作的成员":它能接收 prompt,调用工具,更新状态,并在一轮工作结束后回到 idle(空闲等待状态,保持存活但暂不执行)。

teammate 怎么知道要领任务? 答案在 system prompt 和 idle 循环的配合里。

teammate 启动时,system prompt 会被注入一段团队协作指令(TEAMMATE_SYSTEM_PROMPT_ADDENDUM),拼接到原有的 System Prompt 后面,告诉它:你是团队成员,工作通过"任务系统"和 teammate 消息来协调,也能用 SendMessage 工具与其他成员通信。

ts 复制代码
const fullSystemPromptParts = await getSystemPrompt(
  toolUseContext.options.tools,
  toolUseContext.options.mainLoopModel,
  undefined,
  toolUseContext.options.mcpClients,
);

const systemPromptParts = [
  ...fullSystemPromptParts,
  TEAMMATE_SYSTEM_PROMPT_ADDENDUM,
];
ts 复制代码
// 注入 teammate 的 system prompt 附加指令
export const 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.

The user interacts primarily with the team lead. Your work is coordinated through the task system and teammate messaging.
`;

// 中文翻译:重要提示:你正在作为团队中的 agent 运行。要与团队中的任何人通信:使用 SendMessage 工具,`to: "<name>"` 发给特定 teammate,`to: "*"` 谨慎用于团队广播。仅仅在文本中回复,团队其他成员是看不到的 ------ 你必须使用 SendMessage 工具。用户主要与团队 leader 交互。你的工作通过任务系统(task system)和 teammate 消息来协调。

一轮工作完成后,teammate 不会退出,而是进入 idle 状态,启动一个等待循环 waitForNextPromptOrShutdown。这个循环每 500ms 检查三件事:

  1. 有没有新消息 ------ 来自 leader 或其他 teammate 的 SendMessage
  2. 有没有 shutdown 请求 ------ leader 要求结束 teammate
  3. Task List 里有没有可领取的任务 ------ 通过 tryClaimNextTask 检查
ts 复制代码
// idle 循环里检查 Task List
const taskPrompt = await tryClaimNextTask(taskListId, identity.agentName);
if (taskPrompt) {
  return {
    type: 'new_message',
    message: taskPrompt, // 格式化后的任务描述
    from: 'task-list',
  };
}

如果 Task List 里有满足条件(pending、无人负责、前置依赖已完成)的任务,tryClaimNextTask 会把它格式化成 prompt,返回给 waitForNextPromptOrShutdown,后者再把这个 prompt 交给下一轮 agent loop 执行。这个过程对 teammate 来说是透明的 ------ 它只需要在 idle 循环里等待,有任务时 runner 会自动把任务内容作为新的 prompt 传递给它。

这也解释了为什么第 3 步叫"Task List 负责共享任务事实" ------ teammate 不是被硬编码去"领任务",而是通过 system prompt 知道有"任务系统"存在,再通过 runner 的 idle 循环自动检查 Task List 获取任务分配。

3.4 登记到成员名册

teammate 运行起来后,还必须被团队其他机制识别。否则它虽然在跑,但 leader 不知道团队里有这个成员,任务和消息也不好路由。

谁来登记 :Leader 调用 AgentToolspawnTeammatehandleSpawnSplitPane(或 handleSpawnSeparateWindow),在 spawn 流程中完成登记。

成员登记会同时进入两个位置:leader 的运行时团队状态(内存),以及持久化的团队名册(磁盘)。

ts 复制代码
async function registerTeammateInAppState(
  teammateId: string,
  sanitizedName: string,
  teamName: string,
  teammateColor: string,
  agent_type: string,
  setAppState: SetAppStateFn,
) {
  // 1. 登记到运行时团队状态(内存中,供快速访问)
  setAppState(prev => ({
    ...prev,
    teamContext: {
      ...prev.teamContext,
      teammates: {
        ...prev.teamContext?.teammates,
        [teammateId]: {
          name: sanitizedName,
          agentType: agent_type,
          color: teammateColor,
          tmuxPaneId: 'in-process',
          cwd: getCwd(),
          spawnedAt: Date.now(),
        },
      },
    },
  }));

  // 2. 登记到团队名册(持久化到磁盘)
  const teamFile = await readTeamFileAsync(teamName);
  if (!teamFile) {
    throw new Error(`Team "${teamName}" does not exist`);
  }
  teamFile.members.push({
    agentId: teammateId,
    name: sanitizedName,
    agentType: agent_type,
    color: teammateColor,
    joinedAt: Date.now(),
    tmuxPaneId: 'in-process',
    cwd: getCwd(),
    subscriptions: [],
  });
  await writeTeamFileAsync(teamName, teamFile);
}

成员名册的结构关系可以画成这样:

leader、teammate、消息系统、清理逻辑都可以围绕这份成员信息工作。团队不是靠模型记忆维持,而是有一份可查询、可更新的成员名册。


4. Agent 完成工作后如何继续

teammate 跑完一轮 prompt 后,不会直接消失。它会进入 idle 状态,等待下一个输入。这个输入可能来自 Task List(可领取的任务),也可能来自 mailbox(leader 或其他 teammate 的消息,详见 4.2 节)。

mailbox 是团队成员间的消息通道,用于传递指派、通知和协作消息。teammate 通过 runner 每 500ms 轮询读取,消息被读取后从队列中移除。

idle 状态的等待循环按优先级处理多个来源:

下面分别讲 Task List 和 mailbox 这两个协作通道是如何工作的。

4.1 Task List:runner 自动领取任务

当 teammate 完成一轮工作进入 idle 状态时,它的外层 runner(inProcessRunner)会自动检查 Task List,寻找可领取的任务。这个过程对 teammate 是透明的:

  1. tryClaimNextTask 开始执行:
    1. 调用 listTasks() 获取所有任务
    2. findAvailableTask(tasks) 从列表中筛选出可领取的任务(pending、无人负责、前置依赖已完成)
    3. claimTask() 把任务登记到 teammate 名下
    4. 把任务格式化成 prompt,传给 teammate 执行
ts 复制代码
// runner 的 idle 循环中自动执行
const taskPrompt = await tryClaimNextTask(taskListId, identity.agentName);
if (taskPrompt) {
  // 把任务作为新的 prompt 传给 teammate
  return { type: 'new_message', message: taskPrompt, from: 'task-list' };
}

注意:claimTask 不是 Agent 可调用的工具,而是 runner 内部函数。teammate 不需要主动"领取"任务(Agent 领取不可靠,必须代码领取),它只需要在 idle 循环里等待,runner 会自动把可领取的任务作为新 prompt 传递给它。

Task List 记录的任务结构如下:

一个任务能被领取,要满足三个条件:还没开始、无人负责、前置依赖已完成。

4.2 mailbox:消息传递和再次唤醒

除了 Task List,teammate 还可以通过 mailbox 接收消息。

mailbox 的实现机制 :每个 teammate 有一个独立的 inbox 文件,存储在 .claude/teams/{team_name}/inboxes/{agent_name}.json。这是一个基于文件系统的消息队列,而非内存广播:

  • 点对点写入 :发送方通过 writeToMailbox 将消息写入接收方的 inbox 文件,使用文件锁防止并发冲突
  • 消息格式 :每条消息包含 from(发送者)、text(内容)、timestamp(时间戳)、read(是否已读),以及可选的 colorsummary
  • 广播实现to: "*" 时,系统遍历团队成员列表,向每个成员的 inbox 写入同一条消息
  • 消息消费 :消息被读取后不会立即删除,而是标记为 read: true,便于追溯历史

谁负责接收消息 :teammate 自己不直接检查 mailbox,而是由它的 runner(inProcessRunner)负责。runner 在一轮工作结束后会进入 waitForNextPromptOrShutdown 循环,每 500ms 轮询检查 mailbox 和其他来源。

接收消息后会发生什么

  • 如果是 shutdown 请求 → 进入收尾流程,准备退出
  • 如果是 leader 消息 / 其他 teammate 消息 → 把消息格式化成新的 prompt,传给 teammate 执行下一轮
  • 如果 没有任何消息 → 继续等待,保持 idle 状态

runner 在一轮工作结束后,会把 task 标记为 idle,并向 leader 发送 idle 通知:

ts 复制代码
async function markTeammateIdle(
  taskId: string,
  identity: TeammateIdentity,
  workWasAborted: boolean,
  setAppState: SetAppStateFn,
) {
  // 标记为 idle
  updateTaskState(taskId, task => ({ ...task, isIdle: true }), setAppState);

  // 通知 leader
  await sendIdleNotification(
    identity.agentName,
    identity.color,
    identity.teamName,
    { idleReason: workWasAborted ? 'interrupted' : 'available' },
  );
}

之后,runner 会进入等待循环 waitForNextPromptOrShutdown,按上面流程图的优先级处理各种输入来源。

这就形成了 Agent Teams 的两条协作通道:

Task List 解决"状态是否一致",mailbox 解决"消息是否送达"。两者合在一起,teammate 才能在无人手动提醒时继续推进下一轮工作。

4.3 生命周期循环与关闭

in-process teammate 的生命周期不是"一次 prompt 跑完就结束"。更准确地说,它会在 running 和 idle 之间循环:

如果收到普通消息,它会把消息格式化成新的 user prompt,继续下一轮 runAgent()。如果收到 shutdown request(leader 通过 SendMessage 发送的优雅退出请求),它会把关闭请求交给模型处理,让 teammate 决定如何响应或收尾。

最终退出循环时,runner 会把 teammate task 标记为 completed,并发送终止事件:

ts 复制代码
async function completeTeammateTask(
  taskId: string,
  setAppState: SetAppStateFn,
) {
  updateTaskState(
    taskId,
    task => ({
      ...task,
      status: 'completed',
      completedAt: Date.now(),
    }),
    setAppState,
  );

  emitTaskTerminatedSdk(taskId, 'completed', metrics);
}

如果 leader 要清理整个 team,则会先检查是否还有 active teammate;如果还有,就要求先请求 shutdown。

ts 复制代码
export const TeamDeleteTool = buildTool({
  async call(_input, context) {
    const { setAppState, getAppState } = context;
    const appState = getAppState();
    const teamName = appState.teamContext?.teamName;

    // 1. 检查是否还有 active teammate
    const teamFile = readTeamFile(teamName);
    const nonLeadMembers = teamFile.members.filter(
      m => m.name !== TEAM_LEAD_NAME,
    );
    const activeMembers = nonLeadMembers.filter(m => m.isActive !== false);

    if (activeMembers.length > 0) {
      return {
        success: false,
        message: `Cannot cleanup team with ${activeMembers.length} active member(s). Use requestShutdown to gracefully terminate teammates first.`,
      };
    }

    // 2. 清理团队
    await cleanupTeamDirectories(teamName);
    clearTeammateColors();
    clearLeaderTeamName();
    setAppState(prev => ({
      ...prev,
      teamContext: undefined,
      inbox: { messages: [] },
    }));

    return { success: true, team_name: teamName };
  },
});

生命周期在清理完成后才真正结束。


5. 把整条链路串起来

Agent Teams 的核心流程可以压缩成下面这张图:

这条链路的核心不是"把模型复制几份",而是给每个 agent 补齐团队成员所需的运行上下文:

如果只说"Claude Code 可以启动多个 agent",会漏掉最重要的部分:这些 agent 为什么能像一个团队一样持续协作。

Agent Teams 的答案是:先有团队容器,再有成员身份,再有执行循环,最后用共享任务表和消息通道把成员连接起来。

回到开头的团队协作类比:TeamCreate 像建立项目组,Agent(name + team_name) 像把成员拉进项目组,Task List 像共享任务池,mailbox 像团队通知。这个类比只服务于理解主线;真正的实现边界,仍然是 team、teammate、runner、Task List 和 mailbox 之间的职责分工。

从时序看,一次典型的协作流程如下:


6. 总结

Agent Teams 把多 Agent 协作从"启动几个模型"升级为一套完整的团队生命周期。回顾整条链路:

  1. TeamCreate 建立团队容器------写入配置文件、准备 Task List 目录、绑定 leader 到团队运行时状态
  2. Agent(name + team_name) 创建 teammate------在同进程内生成身份、上下文和执行循环
  3. idle 循环 + Task List 实现自动任务领取------teammate 无需手动指派,runner 自动筛选可领取任务
  4. mailbox 实现异步消息传递------基于文件系统的双向通信,支持点对点、广播、shutdown 等场景
  5. TeamDelete 完成清理------检查 active teammate、移除团队目录、释放运行时状态

这套机制的本质是把团队协作的隐性契约变成显式状态:谁负责什么任务、消息发给谁、成员是否还在运行,都有明确的记录和通道,而不是依赖模型自己去记忆和协调。

更多 AI 工程、Agent 和技术实践相关内容,欢迎关注公众号。

相关推荐
IT_陈寒6 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端
tq10866 小时前
认知连续性与组织墙的崩塌:AI原生时代的架构重构
人工智能·架构
淇奥76 小时前
【MyBatis-Plus】MyBatis-Plus 学习笔记
后端
_code_bear_6 小时前
OpenSpec CLI 与 OPSX 工作流说明
前端·后端·架构
志凌海纳SmartX7 小时前
浅析 kernel bypass 网卡及其在超融合架构的性能表现
架构·网卡·高可用·低延迟·smartx·榫卯超融合
用户8356290780517 小时前
使用 Python 在 PowerPoint 中添加并控制音频播放
后端·python
400分7 小时前
吃透RAG核心-----语义检索与关键字检索底层原理
算法·架构
用户8356290780517 小时前
使用 Python 在 PowerPoint 中生成并自定义饼图与环形图
后端·python
念何架构之路7 小时前
Go语言常见并发模式
开发语言·后端·golang