Agent Team 实现

多代理协作——共享任务列表、成员间消息通信与实时 Team Card UI

Agent Team 解决什么问题?

现有的 task 工具生成一次性 subagent:接收 prompt、隔离工作、交付产物、结束。没有协调,没有通信,没有共享状态。这对独立子任务有效,但当工作需要多个代理协作时就不够用了——共享发现、排序依赖任务、或根据其他成员的发现调整策略。

Agent Team 引入了持久化、协调的多代理会话:Lead Agent 生成专门化的成员,它们并行工作、通过消息通信、并在带有依赖解析的共享任务列表中推进。

何时使用

场景方式
快速的一次性生成task 工具(subagent)
独立的研究或分析task 工具(subagent)
跨多个模块的代码审查Agent Team
有顺序阶段的复杂项目Agent Team

架构概览

Agent Team — 服务端架构 ① setupTeamForLead() → team-registry { coordinator, execution } 单例 ② createTools(leadContext) 无 memberId → 按 Lead 角色执行 team_create team_dissolve team_replan team_status team_send_message ↓ Lead Agent (kind: "main") ⑤ createTools(memberContext) 有 memberId → 按 Member 角色执行 team_create team_dissolve team_replan team_status team_send_message ↓ Member Agent (kind: "team") ③ 状态 ③ team_create / team_replan / team_dissolve ⑥ team_send_message · 经 complete 交付 TeamCoordinator 团队协调器 任务依赖 消息路由 生命周期 自动解锁 TeamRepository PG / InMemory TeamExecutionService 团队执行服务 runAgentLoop prepareStep AbortController TOOL_STREAM consumeMessages UI ④ launchMember → memberContext Member A 共享沙箱 Member B 共享沙箱 Member C 共享沙箱 ⑦ 经 complete 交付 执行流程 (Execution Flow) ① setupTeamForLead → registerTeamServices(...) + ctx.team = {} ② runAgentLoop(leadContext) → createTools() → 同一套 5 个工具,按 Lead 角色执行 ③ LLM 调用 team_create → getTeamServices() 查找 → coord 状态 + execution.launchMember ④ launchMember → memberContext.team = { memberId, teamId }(仅身份) ⑤ runAgentLoop(memberContext) → createTools() 再次调用 → 同一套 5 个工具,按 Member 执行 ⑥⑦ 成员 → coord: team_send_message、team_status(snapshot);经 complete 交付

Agent Team 在现有 TaskOrchestrator 流程内运行——没有独立的 orchestrator。Setup 阶段 setupTeamForLead() 将团队服务注册到进程级 team-registry(幂等),并把 Lead context 标记为 ctx.team = {};团队工具随后在标准 agent 循环内与 filesystemexecute 等一起工作,需要服务时通过 getTeamServices() 在使用点查找。若 "team" 不在agent 的工具列表中,setup 被跳过——零开销。

三个服务

TeamCoordinator——大脑。管理团队生命周期、解析任务依赖、路由成员间消息。底层由 TeamRepository 支撑(Server 用 PG,测试用内存实现)。当一个任务完成时,coordinator 自动解除下游任务的阻塞。

TeamExecutionService——肌肉。经 runAgentLoop 启动和管理并发的成员 agent stream。每个成员获得自己的 RuntimeContext与 Lead 1:1 共享 sandbox 与 workspace(无按成员子目录)、TEAM_INSTRUCTIONS 提示词(kind: "team"),以及一个 prepareStep 钩子——在每次 LLM 调用前注入待处理消息。通过 AbortController 追踪每个成员以支持解散时优雅关闭。

team.tool.ts——接口。5 个 AI SDK 工具封装,Lead 和 Member 看到同一套,桥接 LLM 调用到 coordinator / execution service。模型是成员中心的 DAG:不向 LLM 暴露任何 UUID(一律按成员名引用),任何工具也不接 teamId(框架从上下文解析当前唯一团队):

  • team_create——Lead 一次性声明整张 DAG:成员及每个成员的 dependsOn。框架拓扑排序、拒绝环、启动成员;成员在其上游全部完成后自动运行
  • team_dissolve——Lead 解散团队
  • team_replan——Lead 的失败恢复:原子地 cancel 失败成员并/或 add 新成员(失败成员的下游保持 blocked——框架自动解锁)
  • team_status——团队状态快照(成员中心,无 UUID)。Lead long-poll 到事件为止;Member 拿到普通快照
  • team_send_message——发给成员名、"Lead""broadcast"(共享)

成员通过通用 complete 工具交付结果(按 ctx.team?.memberId 分支),而非团队专用工具。工具集跨角色完全一致——模型的 tool block 在团队生命周期内保持缓存稳定;角色限制在每个 execute 内部(Member 调 team_create 抛错,team_replan 仅 Lead,等等)。

端到端流程:一个具体场景

Agent Team — 生命周期 创建 → 分配 → 监控 → 解散 ① 创建 (Create) Lead 调用 team_create → Coordinator 创建团队 + 自动注册 Lead 成员 DAG 一次性声明;每个成员是并发 stream:共享 Lead 沙箱、TEAM_INSTRUCTIONS、prepareStep 钩子 → 客户端: initTeam(store) — Team Card出现在聊天流中 ② 分配 + 工作 (Assign + Work) 依赖来自 team_create 的 dependsOn → 自动解析: blocked → in_progress → completed 成员在上游完成后自动运行 → 使用工具执行 → 经通用 complete 工具交付 → 客户端: updateMember / updateTeam 通过 TOOL_STREAM kind:"team" ③ 监控 + 综合 (Monitor + Synthesize) Lead long-poll team_status(默认 waitForChangeMs 30s)→ 结构化进度快照 team_send_message 进行路线修正;综合所有结果产出最终交付物 → 客户端: updateTeam(store) — Team Card 静默刷新;内联显示 "状态已刷新" ack ④ 解散 (Dissolve) Lead 调用 team_dissolve → Execution 停止所有成员 (AbortController.abort()) Coordinator 标记所有成员完成,更新团队状态为 "completed" → 客户端: dissolveTeam(store) — 终态,5 分钟后自动清理

用户消息:“审查我们 auth 模块的安全性、性能和测试覆盖率。”

主 agent——就是处理每条用户消息的那个 agent——收到这条请求。如果 tier 启用了 "team",主 agent 的工具集中会包含 5 个团队工具,和 read_fileexecute 等并列。主 agent 阅读请求后判断需要三个专家并行工作,自主决定调用 team_create。从这一刻起,主 agent 充当团队的 Lead 角色——不是新创建的实体,只是同一个 agent 多了一个角色。

① 创建——Lead 启动团队

Lead 在一次 team_create 调用里声明整张 DAG——每个成员携带自己的 prompt(任务)和可选的 dependsOn(上游成员名列表):

team_create({
  name: "Auth 模块审查",
  members: [
    { name: "SecurityReviewer", agentType: "code-reviewer", prompt: "审查 auth 模块的安全漏洞..." },
    { name: "PerfAnalyzer",     agentType: "code-reviewer", prompt: "分析 auth 端点的性能瓶颈..." },
    { name: "TestReviewer",     agentType: "code-reviewer", prompt: "审计 auth 模块的测试覆盖率..." },
    { name: "Reporter", agentType: "general", prompt: "综合三份审查产出汇总报告...",
      dependsOn: ["SecurityReviewer", "PerfAnalyzer", "TestReviewer"] }
  ]
})

幕后:框架对 DAG 拓扑排序(拒绝环),coordinator 创建团队 + 成员记录,execution service 经 runAgentLoop 把所有成员作为并发 agent 流启动。每个成员与 Lead 1:1 共享 sandbox 与 workspace(协作靠文件名,而非隔离),运行 TEAM_INSTRUCTIONS 提示词。无 dependsOn 的成员(三个审查者)立即开工;有 dependsOn 的成员(Reporter)也 spawn 但保持 blocked,在其上游全部完成后自动运行。成员看到跟 Lead 完全相同的 5 个团队工具,但 team_create / team_dissolve / team_replan 被 Member 调用时抛错;成员的 toolKeys 里也排除了 task / ask_user_question / confirm——不能嵌套 subagent,不能与用户交互。

此时,用户的聊天界面出现 Team Card,显示团队状态。

② 依赖解析——DAG 自行运转

没有单独的任务创建步骤:依赖图就是 team_create 的成员列表。coordinator 自动解析——三个审查者无 dependsOn,并行运行;Reporter 保持 blocked 直到三者全部完成,然后其流自动解锁运行。每个成员的状态走 blocked → in_progress → completed(或 failed),某成员完成会重扫下游、解锁依赖已满足者。

③ 工作——成员自主执行、经 complete 交付

成员不需要等待指令——各自跑自己的 agent 循环。每次 LLM 调用前,prepareStep 钩子检查 coordinator 的新消息(来自 Lead 或其他成员)并注入为 <system-reminder> 文本。

一个典型的成员工作流:

  1. SecurityReviewer 无 dependsOn,立即运行 → 阅读 auth 代码 → 把发现写入 workspace 文件 → 调用通用 complete({ summary, paths }) 工具交付(与普通 agent 同一停止工具;按 ctx.team.memberId 分支)
  2. SecurityReviewer 完成后重扫 DAG——三个审查者都完成后 Reporter 解锁运行
  3. PerfAnalyzer 与 TestReviewer 全程并行
  4. SecurityReviewer 发现 token 泄露 → 调用 team_send_message({ to: "broadcast", content: "在 /auth/callback 发现暴露的 refresh token" })
  5. 其他成员在下一次 prepareStep 中收到并相应调整

若某成员失败,框架自动解锁其下游——由 Lead 用 team_replan 恢复(取消失败成员,并/或添加带新 dependsOn 图的替补)。

④ 监控——Lead long-poll 等待进度

成员工作期间,Lead 调用 team_status()(不传 teamId——框架从上下文解析当前团队),该调用在 server 端阻塞直到团队任何状态变更(成员状态变更、完成、消息送达)——或到默认 long-poll 窗口为准。返回的结构化数据是成员中心的(用名字,无 UUID):

{
  "members": [
    { "name": "SecurityReviewer", "status": "in_progress", "dependsOn": [] },
    { "name": "PerfAnalyzer", "status": "in_progress", "dependsOn": [] },
    { "name": "TestReviewer", "status": "completed", "dependsOn": [] },
    { "name": "Reporter", "status": "blocked", "dependsOn": ["SecurityReviewer", "PerfAnalyzer", "TestReviewer"] }
  ],
  "pendingMessages": 1
}

如果某人卡住了,Lead 通过 team_send_message 发送指导。Team Card 通过 TOOL_STREAM 事件实时更新——用户无需等待 Lead 开口就能看到进展。

⑤ 综合——交付物

依赖三个审查者的 Reporter 成员在它们完成后自动运行,从共享 workspace 综合它们的交付物,经 complete 交付。Lead 从 team_status 读取终态,向用户写出最终回复:“以下是 auth 模块审查的发现:3 个安全问题、2 个性能瓶颈、87% 测试覆盖率……”(对于没有综合成员的简单团队,Lead 直接读取成员 artifact 自行综合。)

⑥ 解散——清理

Lead 调用 team_dissolve。所有成员 stream 被停止(AbortController.abort()),coordinator 标记团队为 "completed",Team Card 显示终态。Zustand store 在 5 分钟后自动清理。

幕后实现

团队工具使用同一个 createTools() 函数。Lead 和 Member 拿到的返回对象 shape 完全相同——都是同一套 5 个工具,键名都一样。不同的是每个 execute 内部的运行时行为:

  • Lead(ctx.team 已设,无 memberId):team_create / team_dissolve / team_replan 正常执行;team_status long-poll 到事件为止
  • Member(ctx.team.memberId 已设):team_create / team_dissolve / team_replan 抛错;team_status 返回普通快照;交付经通用 complete 工具

服务实例(coordinatorexecution)挂在 ctx.team 上——它们是进程级单例,通过 team-registrygetTeamServices() 在使用点查找。Context 只携带 per-call 身份(memberId)。

Execution service 为每个成员构建 RuntimeContext(复用 Lead 的 sandbox),通过 runAgentLoopkind: "team" 启动,选择 TEAM_INSTRUCTIONS 而非 MAIN_INSTRUCTIONS。详见架构图的完整组件关系。

核心机制

任务依赖解析

每个成员是 team_create 声明的 DAG 中的一个节点;coordinator 自动解析成员状态:

blocked → in_progress → completed(或 failed / cancelled

  • 有未完成 dependsOn 的成员保持 blocked——其流已 spawn 但被 park
  • 其上游全部完成后,自动解锁并运行——无手动认领步骤
  • 完成后,coordinator 重扫下游成员并解锁已满足依赖者
  • 某成员失败不会自动解锁其下游;由 Lead 的 team_replan 恢复

成员通信

成员通过 prepareStep 钩子接收消息——不需要单独的通信通道。每次 LLM 调用前,钩子检查 coordinator 的邮箱、消费未读消息、并将其注入为 <system-reminder> 文本。每个成员只做自己被分配的任务(其 DAG 节点)——没有任务挑选。这复用了现有的 reminder 机制,零新增基础设施。

上下文隔离

属性Lead AgentTeam Member
PromptMAIN_INSTRUCTIONS + Lead 团队工具指导TEAM_INSTRUCTIONS + Member 团队工具指导
历史完整对话仅自己的 prompt(任务简报)
沙箱任务工作区同一个 sandbox + workspace,1:1
Tool block跟 Member 完全相同的 5 个团队工具跟 Lead 完全相同的 5 个团队工具
仅 Lead 可调team_create / team_dissolve / team_replan被调用时抛错
交付写出最终用户回复经通用 complete 工具交付
team_statuslong-poll 到事件为止普通快照
用户交互是(ask_user_questionconfirm)否(不在成员的 toolKeys 里)
嵌套代理是(task)否(不在成员的 toolKeys 里)

客户端

Team Card

核心 UX 洞察:团队在聊天中应表现为一个持续演变的实体,而非一系列重复的状态快照。team_create 渲染唯一的团队卡片,订阅 useTeamStore。所有其他工具渲染极简内联徽章。team_status 只渲染一个 ack(“状态已刷新”),同时静默刷新 store。

数据通道

数据通过两个通道从后端流向前端:

工具输出(同步)——LLM 调用团队工具时,输出作为标准 ToolUIPart 返回客户端。工具的 React 组件通过 useEffect 写入 useTeamStore。涵盖 team_createteam_replanteam_statusteam_dissolve

DataPartEvent(异步推送)——成员状态在后台变更时,TeamExecutionService 推送 TOOL_STREAM 事件,kind: "team"。客户端 use-task-events.ts 检测到 chunk.kind === "team" 后分发到 useTeamStore。Team Card 即时重渲染。

数据源触发时机Store Action
team_create output团队 + 成员创建initTeam()
team_replan output成员取消 / 添加updateTeam()
team_status outputLLM 检查状态updateTeam()(全量刷新)
team_dissolve output团队解散dissolveTeam()
后端 TOOL_STREAM 事件成员状态变更updateMember()

数据库

各表从父 team 表级联删除。没有单独的任务表——每条成员行本身就是它的任务节点:

  • team——每个团队会话一条,引用父对话 task
  • team_member——每个生成的成员(包括自动注册的 Lead);携带该成员的 promptdependsOn、状态和结果
  • team_message——成员间消息,带已读追踪

成员状态转换(blocked → in_progress → completed)使用条件更新确保原子性——成员绝不重复运行。

Tier 控制

Agent Team 通过 "team" 配置键控制,当前仅在 ultra tier 启用。"team" 键映射到 5 个工具名(team_createteam_dissolveteam_replanteam_statusteam_send_message)。管理员可通过 Settings > Tiers UI 为其他 tier 启用。

这页有帮助吗?