Skip to content

第 13 章:Agent Teams —— 从独行到协作

Subagent 是一次性函数调用——spawn、执行、返回摘要、销毁;Agent Team 的核心跃迁在于将 Agent 从无状态的函数调用升级为持久化的协作实体:每个 teammate 有身份、有状态、有自己的邮箱,能跨越多轮对话持续存在并相互通信。


13.1 从 Subagent 到 Team 的进化

Subagent 的局限

上一章的 Subagent 模型可以类比为同步函数调用:

text
result = subagent(prompt)   # 阻塞等待,返回后子 Agent 销毁

这意味着三个根本限制:

  1. 无状态:每次调用都从空白上下文开始,无法记住上次做了什么
  2. 无身份:不知道自己是谁,也不知道"团队里还有谁"
  3. 无通信:子 Agent 之间无法直接交流,所有信息必须经过父 Agent 中转

当任务足够大——比如"一个 Agent 写前端、一个写后端、一个跑测试"——Subagent 模型就力不从心了。三个 subagent 各自独立执行,没有办法在发现接口不兼容时实时协商修改。

Teammate 模型

Team 模式引入了根本性的改变:

python
Subagent:  spawn → execute → return summary → destroyed
Teammate:  spawn → WORKINGIDLEWORKING...SHUTDOWN

关键区别:

维度SubagentTeammate
生命周期一次性持久化,可在 idle/working 之间切换
身份匿名有名字、角色、状态
通信只能通过父 Agent 中转任意 teammate 之间直接通信
上下文执行完即丢弃在线程中持续累积
运行方式同步阻塞独立线程,异步运行
状态存储config.json + JSONL 邮箱

Teammate 的本质是一个持续运行的 Agent 循环,绑定在一个独立线程上,拥有自己的 messages[]、system prompt 和工具集。它不是"完成一个任务就退出",而是"完成任务后进入 idle,等待新消息唤醒"。


13.2 Team 架构

整个 Team 系统由三个核心组件构成:TeammateManager 负责生命周期管理,MessageBus 负责消息传递,JSONL 文件充当持久化邮箱。

文件系统布局

text
.team/
├── config.json          ← 团队花名册 + 成员状态
└── inbox/
    ├── alice.jsonl       ← alice 的收件箱(append-only)
    ├── bob.jsonl         ← bob 的收件箱
    └── lead.jsonl        ← lead 的收件箱

整个团队的协作状态完全由文件系统承载——无数据库、无消息队列、无网络调用。

TeammateManager

TeammateManager 是团队的注册中心,核心职责有三:

  1. 维护 config.json:记录所有成员的 name、role、status
  2. Spawn 成员:创建线程并启动 Agent 循环
  3. 查询团队状态:列出所有成员及其当前状态
python
class TeammateManager:
    def __init__(self, team_dir: Path):
        self.dir = team_dir
        self.config_path = self.dir / "config.json"
        self.config = self._load_config()
        self.threads = {}

    def _load_config(self) -> dict:
        if self.config_path.exists():
            return json.loads(self.config_path.read_text())
        return {"team_name": "default", "members": []}

config.json 的结构:

json
{
  "team_name": "default",
  "members": [
    {"name": "alice", "role": "coder", "status": "working"},
    {"name": "bob", "role": "tester", "status": "idle"}
  ]
}

spawn() 方法的逻辑:先检查成员是否已存在(如果存在且状态为 idle 或 shutdown,则复用;否则报错),然后创建 daemon 线程启动 Agent 循环:

python
def spawn(self, name: str, role: str, prompt: str) -> str:
    member = self._find_member(name)
    if member:
        if member["status"] not in ("idle", "shutdown"):
            return f"Error: '{name}' is currently {member['status']}"
        member["status"] = "working"
        member["role"] = role
    else:
        member = {"name": name, "role": role, "status": "working"}
        self.config["members"].append(member)
    self._save_config()
    thread = threading.Thread(
        target=self._teammate_loop,
        args=(name, role, prompt),
        daemon=True,
    )
    self.threads[name] = thread
    thread.start()
    return f"Spawned '{name}' (role: {role})"

注意 daemon=True:当主进程退出时,所有 teammate 线程自动终止,无需手动清理。但这也意味着 teammate 如果在写文件中途被杀,文件会处于不完整状态——这正是后面 shutdown 协议要解决的问题。

MessageBus

MessageBus 是通信层,提供三个操作:send(向指定 teammate 发送消息)、read_inbox(读取并清空自己的收件箱)、broadcast(向所有 teammate 广播)。

python
class MessageBus:
    def __init__(self, inbox_dir: Path):
        self.dir = inbox_dir
        self.dir.mkdir(parents=True, exist_ok=True)

    def send(self, sender, to, content, msg_type="message", extra=None):
        msg = {
            "type": msg_type,
            "from": sender,
            "content": content,
            "timestamp": time.time(),
        }
        if extra:
            msg.update(extra)
        with open(self.dir / f"{to}.jsonl", "a") as f:
            f.write(json.dumps(msg) + "\n")

    def read_inbox(self, name):
        inbox_path = self.dir / f"{name}.jsonl"
        if not inbox_path.exists():
            return []
        messages = []
        for line in inbox_path.read_text().strip().splitlines():
            if line:
                messages.append(json.loads(line))
        inbox_path.write_text("")   # drain
        return messages

    def broadcast(self, sender, content, teammates):
        count = 0
        for name in teammates:
            if name != sender:
                self.send(sender, name, content, "broadcast")
                count += 1
        return f"Broadcast to {count} teammates"

消息格式是一个扁平 JSON 对象:

json
{
  "type": "message",
  "from": "alice",
  "content": "API 模块重构完毕,新接口在 /api/v2",
  "timestamp": 1711234567.89
}

5 种消息类型覆盖了所有通信需求:

类型方向用途
message任意 → 任意普通文本通信
broadcast一 → 全体全局通知
shutdown_requestlead → teammate请求关机
shutdown_responseteammate → lead批准/拒绝关机
plan_approval_responselead ↔ teammate计划审批

Teammate 的 Agent 循环

每个 teammate 在独立线程中运行一个完整的 Agent 循环。关键设计:每轮 LLM 调用前,先检查收件箱,将新消息注入 messages[] 作为新的 user 输入。

python
def _teammate_loop(self, name, role, prompt):
    sys_prompt = f"You are '{name}', role: {role}. Use send_message to communicate."
    messages = [{"role": "user", "content": prompt}]
    tools = self._teammate_tools()

    for _ in range(50):                        # 安全上限防止无限循环
        inbox = BUS.read_inbox(name)           # 1. 检查邮箱
        for msg in inbox:
            messages.append({"role": "user", "content": json.dumps(msg)})

        response = client.messages.create(     # 2. LLM 调用
            model=MODEL, system=sys_prompt,
            messages=messages, tools=tools, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use": # 3. 非工具调用则退出
            break

        results = []
        for block in response.content:         # 4. 执行工具
            if block.type == "tool_use":
                output = self._exec(name, block.name, block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": str(output),
                })
        messages.append({"role": "user", "content": results})

    # 循环结束,状态回到 idle
    member = self._find_member(name)
    if member and member["status"] != "shutdown":
        member["status"] = "idle"
        self._save_config()

数据流全景:

mermaid
graph TB
    subgraph "Lead Agent(主线程)"
        L_LOOP[Agent 循环]
        L_INBOX[lead.jsonl]
    end

    subgraph "Teammate: alice(线程 1)"
        A_LOOP[Agent 循环]
        A_INBOX[alice.jsonl]
    end

    subgraph "Teammate: bob(线程 2)"
        B_LOOP[Agent 循环]
        B_INBOX[bob.jsonl]
    end

    subgraph "共享文件系统"
        CONFIG[config.json]
        FS[项目文件]
    end

    L_LOOP -->|send_message| A_INBOX
    L_LOOP -->|send_message| B_INBOX
    A_LOOP -->|send_message| B_INBOX
    A_LOOP -->|send_message| L_INBOX
    B_LOOP -->|send_message| L_INBOX

    A_LOOP -->|read/write| FS
    B_LOOP -->|read/write| FS
    L_LOOP -->|read/write| FS

    L_LOOP -->|spawn/update| CONFIG

Lead 与 Teammate 的工具差异

Lead(主 Agent)和 Teammate 拥有不同的工具集:

工具LeadTeammate说明
bash / read_file / write_file / edit_file基础文件和命令操作
spawn_teammate只有 lead 能创建新成员
list_teammates只有 lead 能查询团队状态
send_message双方都能发消息
read_inbox双方都能读自己的邮箱
broadcast只有 lead 能广播

这种设计体现了一个架构原则:管理权限集中于 lead,通信能力对等。Teammate 不能创建新成员,但可以自由地向任何人发消息,包括直接向其他 teammate 发消息而无需经过 lead 中转。


13.3 JSONL Mailbox 协议

为什么选 JSONL

JSONL(JSON Lines)作为邮箱格式的选择看似简单,背后有严格的工程理由:

Append-only 写入open(path, "a") 是 POSIX 原子操作(对于合理大小的单行写入)。多个线程同时向同一个 .jsonl 文件追加消息时,不需要显式加锁——操作系统的文件追加操作在大多数场景下保证原子性。

python
# 写入:追加一行 JSON
with open(inbox_path, "a") as f:
    f.write(json.dumps(msg) + "\n")

无锁设计:对比使用 SQLite 或共享内存的方案,JSONL 文件不需要数据库连接、不需要 mutex、不需要信号量。代价是读写不能同时发生——但 drain-after-read 模式恰好避免了这个问题。

可增量读取:每行是一个完整的 JSON 对象,可以逐行解析。即使文件在追加过程中被读取,前面已完成的行仍然是有效的 JSON。

可调试:直接 cat alice.jsonl 就能看到所有消息历史。对比二进制协议或内存中的消息队列,JSONL 的可观测性是免费的。

对比其他候选方案:

方案原子写入无锁可调试问题
JSONL是(append)读写竞争需 drain 模式
SQLite是(事务)一般需要连接管理、WAL 模式
共享内存 + mutexN/A进程崩溃时 mutex 泄露
Redis/消息队列一般引入外部依赖
单个 JSON 文件(全量覆写)并发写入直接损坏文件

JSONL 的核心优势:零依赖 + 并发安全 + 人类可读。对于进程内多线程通信这个场景,它是最简方案。

Drain-After-Read

收件箱的读取采用 drain 模式:读取全部内容后立即清空文件。

python
def read_inbox(self, name):
    inbox_path = self.dir / f"{name}.jsonl"
    if not inbox_path.exists():
        return []
    messages = []
    for line in inbox_path.read_text().strip().splitlines():
        if line:
            messages.append(json.loads(line))
    inbox_path.write_text("")   # 清空 = drain
    return messages

为什么必须 drain?

  1. 防止重复处理:如果不清空,下次 read_inbox 会再次读到同样的消息,teammate 的 LLM 上下文中会出现重复的 user message
  2. 控制文件增长:没有 drain 的话,长时间运行的 teammate 邮箱文件会无限增长
  3. 简化状态管理:读完就清空,不需要维护"哪些消息已处理"的标记

潜在问题:read_text() 和 write_text("") 之间存在竞争窗口——如果在这个间隙有新消息追加,新消息会被一起清空。在当前的线程模型中,这个窗口极小(微秒级),对实际使用影响不大。但在分布式场景中,需要更精细的机制(如 rename-and-recreate 或 file locking)。

收件箱轮询

Teammate 的 Agent 循环在每轮 LLM 调用前检查邮箱:

python
for _ in range(50):
    inbox = BUS.read_inbox(name)        # 轮询
    for msg in inbox:
        messages.append({"role": "user", "content": json.dumps(msg)})
    response = client.messages.create(...)

轮询频率由 Agent 循环的迭代速度决定——每次工具调用后就检查一次。这不是定时轮询(无 sleep),而是事件驱动式轮询:有工具调用就检查,没有就退出循环。

Lead 的处理方式略有不同,收到的邮箱消息被包裹在 <inbox> 标签中:

python
inbox = BUS.read_inbox("lead")
if inbox:
    messages.append({
        "role": "user",
        "content": f"<inbox>{json.dumps(inbox, indent=2)}</inbox>",
    })
    messages.append({
        "role": "assistant",
        "content": "Noted inbox messages.",
    })

<inbox> 标签让 LLM 能够区分"用户输入"和"teammate 发来的消息"。紧跟的 assistant 回复 "Noted inbox messages." 是为了满足 API 的消息交替要求(user → assistant → user → ...)。


13.4 Team 协议

消息总线提供了通信能力,但通信本身不等于协作。协作需要协议——双方就"我发什么、你回什么、状态怎么变"达成一致。

关机协议:Graceful Shutdown

问题:直接杀线程(thread.join() 或进程退出时 daemon 线程被强制终止)会导致 teammate 的工作中途被打断——文件写了一半、git commit 执行到一半、代码修改不完整。

解决方案:shutdown 请求-响应协议,通过消息总线协商关机。

mermaid
stateDiagram-v2
    [*] --> pending: lead 发送 shutdown_request
    pending --> approved: teammate 回复 approve=true
    pending --> rejected: teammate 回复 approve=false
    approved --> [*]: teammate 完成清理并退出
    rejected --> pending: lead 可选择重新请求

Lead 侧:生成唯一 request_id,发送 shutdown_request 类型的消息,并在 tracker 中记录状态为 pending。

python
shutdown_requests = {}   # {request_id: {"target": name, "status": "pending"}}

def handle_shutdown_request(teammate: str) -> str:
    req_id = str(uuid.uuid4())[:8]
    shutdown_requests[req_id] = {"target": teammate, "status": "pending"}
    BUS.send(
        "lead", teammate, "Please shut down gracefully.",
        "shutdown_request", {"request_id": req_id},
    )
    return f"Shutdown request {req_id} sent to '{teammate}' (status: pending)"

Teammate 侧:通过 shutdown_response 工具回复,决定是批准还是拒绝。批准后,Agent 循环设置退出标志。

python
if tool_name == "shutdown_response":
    req_id = args["request_id"]
    approve = args["approve"]
    with _tracker_lock:
        if req_id in shutdown_requests:
            shutdown_requests[req_id]["status"] = "approved" if approve else "rejected"
    BUS.send(
        sender, "lead", args.get("reason", ""),
        "shutdown_response", {"request_id": req_id, "approve": approve},
    )
    return f"Shutdown {'approved' if approve else 'rejected'}"

Teammate 循环中处理退出标志:

python
should_exit = False
for _ in range(50):
    # ...收件箱检查、LLM 调用...
    for block in response.content:
        if block.type == "tool_use":
            output = self._exec(name, block.name, block.input)
            if block.name == "shutdown_response" and block.input.get("approve"):
                should_exit = True
    if should_exit:
        break

# 循环结束后更新状态
member["status"] = "shutdown" if should_exit else "idle"
self._save_config()

为什么 teammate 有权拒绝关机?因为 teammate 正在执行的任务可能处于不可中断的关键阶段(如正在写入一组相互依赖的文件,中途停止会导致不一致)。拒绝关机让 teammate 有机会完成当前操作后再接受关机。

计划审批 FSM

问题:当 lead 给 teammate 分配一个高风险任务(如"重构 auth 模块"),teammate 直接开始执行。如果 teammate 的实现思路有问题,等到发现时已经改了一堆代码。

解决方案:在执行前加一个审批关卡——teammate 提交计划,lead 审阅后批准或驳回。

mermaid
sequenceDiagram
    participant T as Teammate
    participant BUS as MessageBus
    participant L as Lead

    T->>T: plan_approval(plan="1. 抽取接口<br>2. 重构实现<br>3. 更新测试")
    T->>BUS: send(plan, type=plan_approval_response)
    Note over T: 生成 request_id=xyz<br>plan_requests[xyz] = {status: pending}
    BUS->>L: 消息到达 lead 收件箱

    L->>L: 审阅计划文本
    alt 批准
        L->>BUS: plan_approval(request_id=xyz, approve=true)
        BUS->>T: {approve: true, feedback: "LGTM"}
        T->>T: 开始执行计划
    else 驳回
        L->>BUS: plan_approval(request_id=xyz, approve=false, feedback="先加测试")
        BUS->>T: {approve: false, feedback: "先加测试"}
        T->>T: 修改计划后重新提交
    end

Teammate 侧提交计划:

python
if tool_name == "plan_approval":
    plan_text = args.get("plan", "")
    req_id = str(uuid.uuid4())[:8]
    plan_requests[req_id] = {"from": sender, "plan": plan_text, "status": "pending"}
    BUS.send(
        sender, "lead", plan_text, "plan_approval_response",
        {"request_id": req_id, "plan": plan_text},
    )
    return f"Plan submitted (request_id={req_id}). Waiting for lead approval."

Lead 侧审阅计划:

python
def handle_plan_review(request_id, approve, feedback=""):
    req = plan_requests[request_id]
    req["status"] = "approved" if approve else "rejected"
    BUS.send(
        "lead", req["from"], feedback, "plan_approval_response",
        {"request_id": request_id, "approve": approve, "feedback": feedback},
    )
    return f"Plan {req['status']} for '{req['from']}'"

统一的 Request-Response 模式

观察 shutdown 和 plan approval 两个协议,底层模式完全相同:

text
1. 发起方生成 request_id
2. 发起方通过 MessageBus 发送请求
3. 发起方在 tracker 中记录 {request_id: {status: "pending"}}
4. 接收方通过 MessageBus 回复,携带同一个 request_id
5. 发起方根据回复更新 tracker 状态为 approved 或 rejected

两个协议的唯一区别:

维度ShutdownPlan Approval
发起方LeadTeammate
接收方TeammateLead
Trackershutdown_requestsplan_requests
附加数据reasonplan text, feedback

状态机(FSM)完全复用:

text
[pending] ─── approve ──→ [approved]

    └──── reject ───→ [rejected]

这种设计的好处是可扩展:如果未来需要新协议(如代码审查请求、资源申请),只需定义新的 tracker 和消息类型,FSM 逻辑不变。

边界情况处理

Teammate 崩溃后的恢复

当前实现中,如果 teammate 的 Agent 循环因 API 异常退出,其状态在 config.json 中会被标记为 idle(而非 shutdown)。这允许 lead 重新 spawn 同名 teammate 继续工作——因为 spawn() 检查 status not in ("idle", "shutdown"),只有 working 状态的成员不可重新创建。

python
member = self._find_member(name)
if member:
    if member["status"] not in ("idle", "shutdown"):
        return f"Error: '{name}' is currently {member['status']}"
    member["status"] = "working"   # 复用已有成员

但恢复后的 teammate 从空白上下文开始——之前的 messages[] 在线程退出时已丢弃。它能感知到的唯一"记忆"是文件系统上的工作产物和邮箱中可能残留的未读消息。

消息丢失检测

当前实现没有消息确认机制。消息可能在以下场景"丢失":

  1. Drain 竞争read_text()write_text("") 之间有新消息追加,新消息被一起清空
  2. Teammate 退出:teammate 循环退出后,后续发给它的消息仍然会被追加到 .jsonl 文件中,但没有人读取

生产级改进方向:

  • 给每条消息加 sequence number,接收方检测序号连续性
  • 使用 rename-and-recreate 替代 drain:先 rename(alice.jsonl, alice.jsonl.processing),创建新的空 alice.jsonl,然后处理旧文件中的消息

死锁避免

理论上,如果 alice 等待 bob 的回复,bob 又等待 alice 的回复,就形成死锁。但当前实现不存在阻塞等待——teammate 的循环是 poll 模型(每轮检查邮箱),不会因为"等消息"而阻塞线程。最坏情况是两个 teammate 都在空转(不断调用 LLM 但没有新消息),直到达到 50 轮上限自然退出。

这也暴露了 poll 模型的代价:空转浪费。如果 teammate 在等待其他 teammate 的回复,它每轮循环仍然会调用 LLM(消耗 token),尽管没有新消息需要处理。更高效的方案是引入条件变量或事件通知,让等待中的 teammate 真正 sleep 直到收件箱非空。

Tracker 的线程安全

shutdown_requests 和 plan_requests 两个 dict 被多个线程并发访问(lead 线程读写、teammate 线程读写),因此需要锁保护:

python
_tracker_lock = threading.Lock()

# 写入
with _tracker_lock:
    shutdown_requests[req_id] = {"target": teammate, "status": "pending"}

# 读取 + 更新
with _tracker_lock:
    if req_id in shutdown_requests:
        shutdown_requests[req_id]["status"] = "approved" if approve else "rejected"

注意 MessageBus 的 send() 方法不需要加锁——因为 JSONL 的 append 写入在 OS 层面是原子的(单行写入,不超过 PIPE_BUF 大小)。


13.5 Team 模式在生产中的体现

Lead 的工具集从 9 到 12

从 Subagent 到 Team 再到 Team Protocol,lead 的工具集逐步扩展:

阶段工具数新增工具
基础 Agent4bash, read_file, write_file, edit_file
Team(无协议)9+ spawn_teammate, list_teammates, send_message, read_inbox, broadcast
Team + Protocol12+ shutdown_request, shutdown_response(查询), plan_approval

每次扩展都遵循同一模式:新增工具 → 新增 handler → 注册到 TOOL_HANDLERS。架构不变,能力叠加。

消息调度设计差异

不同的 Agent Team 实现在消息调度上有根本性的设计差异:

文件邮箱模型(本章实现):每个 teammate 有独立的 .jsonl 文件作为收件箱,消息通过文件追加传递。优点是零依赖、可调试;缺点是 poll 模型有延迟和空转浪费。

内存消息队列:用 queue.Queueasyncio.Queue 替代文件。优点是零延迟、无序列化开销;缺点是进程退出后消息丢失、不可调试。

中心化调度器:所有消息经过一个中心 dispatcher,由它决定路由。优点是可以实现优先级调度、负载均衡;缺点是 dispatcher 成为单点瓶颈。

事件驱动模型:teammate 注册事件监听器,新消息到达时触发回调而非 poll。优点是无空转浪费;缺点是回调地狱、调试困难。

生产系统通常混合使用:进程内用内存队列(低延迟),跨进程/跨机器用持久化消息(可靠性)。本章的 JSONL 方案虽然朴素,但它清晰地展示了消息传递的核心语义——谁发的、发给谁、什么内容、什么时间——而这四个字段在任何方案中都是不变的。

ACP (Agent Communication Protocol) 的设计思路

当 Agent Team 从单机多线程扩展到多机多进程甚至多服务时,需要一个标准化的通信协议。ACP (Agent Communication Protocol) 试图定义这样一个标准。

ACP 的核心设计思路:

Agent 即服务:每个 Agent 暴露为一个 HTTP 端点,通过标准的 REST API 接收消息和返回结果。消息不再写入本地文件,而是通过网络传递。

消息格式标准化:所有消息遵循统一的 schema,包含 sender、receiver、content、metadata。这与本章的 {"type", "from", "content", "timestamp"} 结构高度相似——差异主要在传输层(HTTP vs 文件追加)而非数据层。

能力声明:每个 Agent 声明自己的 capabilities(能处理什么类型的消息、提供什么工具),类似本章中 _teammate_tools() 返回的工具列表。调用方根据声明选择合适的 Agent。

生命周期管理:Agent 的创建、状态查询、关闭通过标准 API 操作。这与 TeammateManager 的 spawn / list_all / shutdown 功能对应。

从本章的实现到 ACP 的跃迁:

本章实现ACP 等效概念
.team/config.jsonAgent Registry(注册中心)
.team/inbox/*.jsonlMessage Channel(消息通道)
spawn_teammate()POST /agents(创建 Agent)
list_teammates()GET /agents(查询 Agent 列表)
send_message()POST /agents/{id}/messages(发送消息)
read_inbox()GET /agents/{id}/messages(拉取消息)
shutdown_requestDELETE /agents/{id}(请求关闭)

本质没变:注册 → 通信 → 协议。文件系统是最简单的消息通道,HTTP API 是最通用的消息通道,但上层的协议模式(request-response、FSM、tracker)是独立于传输层的。

Team vs Subagent 的选择

并非所有多 Agent 场景都需要 Team 模式。选择依据:

mermaid
flowchart TD
    A[多 Agent 任务] --> B{Agent 之间需要通信?}
    B -->|否| C{任务间有时序依赖?}
    B -->|是| D[Team 模式<br>持久化 + 邮箱]

    C -->|否| E[并行 Subagent<br>同一消息多个 tool_use]
    C -->|是| F[串行 Subagent<br>依次 spawn]

    D --> G{需要结构化协议?}
    G -->|否| H[基础 Team<br>message + broadcast]
    G -->|是| I[Team + Protocol<br>shutdown + plan approval]

关键判断标准:Agent 之间是否需要直接通信。如果任务可以完全分解为互不相关的子任务(各自独立执行,最后合并结果),Subagent 就够了。如果 Agent A 的输出会影响 Agent B 的决策,或者 Agent A 需要在执行过程中向 Agent B 请求协助,则需要 Team 模式的消息通信能力。

第二个判断标准:Agent 是否需要长期存活。一个只跑 3 轮工具调用的子任务用 Subagent 更经济。一个需要持续监听消息、等待指令的 Agent(如 CI 监控 Agent、code review Agent)则必须是 Teammate。