Agent 开发进阶(十五):Agent 团队系统,让多个智能体协同工作

Agent 开发进阶(十五):Agent 团队系统,让多个智能体协同工作

本文是「从零构建 Coding Agent」系列的第十五篇,适合想让多个 Agent 协同工作的开发者。

先问一个问题

当你的任务变得越来越复杂时,你是否希望:

  • 有专门的编码 Agent 负责写代码
  • 有专门的测试 Agent 负责测试
  • 有专门的文档 Agent 负责写文档
  • 这些 Agent 能够长期存在并协同工作

如果你的答案是肯定的,那么你需要一个 Agent 团队系统。

单 Agent 的「能力边界」问题

到了这一阶段,你的 Agent 已经具备了多种能力:

  • 核心循环运行
  • 工具使用与分发
  • 会话内规划(TodoWrite)
  • 子智能体机制(Subagent)
  • 技能加载
  • 上下文压缩
  • 权限系统
  • Hook 系统
  • Memory 系统
  • 系统提示词组装
  • 错误恢复
  • 任务系统
  • 后台任务系统
  • 定时调度系统

但当面对复杂任务时,单 Agent 会遇到明显限制:

  • 能力有限:一个 Agent 难以精通所有领域
  • 上下文冲突:不同类型的任务会污染上下文
  • 并行度低:只能串行处理任务
  • 可维护性差:代码和逻辑越来越复杂

虽然 s04 的 Subagent 可以帮助拆小任务,但它的生命周期是:

rust 复制代码
创建 -> 执行 -> 返回摘要 -> 消失

这很适合一次性的小委派,但不适合长期协作。

所以到了这个阶段,我们需要一个 Agent 团队系统:

一批有身份、能长期存在、能反复协作的队友。

Agent 团队系统的核心设计:名册、邮箱、独立循环

用一个图来表示 Agent 团队系统的工作流程:

lua 复制代码
lead
  |
  +-- spawn alice (coder)
  +-- spawn bob (tester)
  |
  +-- send message --> alice inbox
  +-- send message --> bob inbox

alice
  |
  +-- 自己的 messages
  +-- 自己的 inbox
  +-- 自己的 agent loop

bob
  |
  +-- 自己的 messages
  +-- 自己的 inbox
  +-- 自己的 agent loop

关键点只有三个:

  1. 名册:记录团队成员的身份和状态
  2. 邮箱:队友之间的通信渠道
  3. 独立循环:每个队友都有自己的 Agent Loop

几个必须搞懂的概念

队友(Teammate)

这里的 teammate 指的是:

一个拥有名字、角色、消息入口和生命周期的持久 agent。

名册(Roster)

名册就是团队成员列表。

它回答的是:

  • 现在队伍里有谁
  • 每个人是什么角色
  • 每个人现在是空闲、工作中还是已关闭

邮箱(Inbox)

邮箱就是每个队友的收件箱。

别人把消息发给它, 它在自己的下一轮工作前先去收消息。

消息信封(Message Envelope)

envelope 这个词本来是"信封"的意思。 程序里用它表示:

把消息正文和元信息一起包起来的一条记录。

最小实现

1. Team Manager

python 复制代码
import os
import json
import threading
import time
import uuid
from pathlib import Path

class TeamManager:
    """团队管理器"""
    
    def __init__(self, team_dir=".team"):
        self.team_dir = Path(team_dir)
        self.team_dir.mkdir(exist_ok=True)
        
        # 创建必要的目录
        self.config_dir = self.team_dir
        self.inbox_dir = self.team_dir / "inbox"
        self.inbox_dir.mkdir(exist_ok=True)
        
        # 加载配置
        self.config_path = self.config_dir / "config.json"
        self.config = self._load_config()
        
        # 存储队友线程
        self.teammate_threads = {}
    
    def _load_config(self):
        """加载配置"""
        if self.config_path.exists():
            try:
                return json.loads(self.config_path.read_text(encoding="utf-8"))
            except Exception as e:
                print(f"加载配置失败: {e}")
        return {"team_name": "default", "members": []}
    
    def _save_config(self):
        """保存配置"""
        self.config_path.write_text(
            json.dumps(self.config, indent=2, ensure_ascii=False),
            encoding="utf-8"
        )
    
    def spawn(self, name, role, prompt):
        """创建队友"""
        # 检查是否已存在
        for member in self.config["members"]:
            if member["name"] == name:
                return f"队友 {name} 已存在"
        
        # 添加到配置
        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,
        )
        thread.start()
        self.teammate_threads[name] = thread
        
        return f"队友 {name} ({role}) 已创建"
    
    def list(self):
        """列出所有队友"""
        if not self.config["members"]:
            return "团队暂无成员"
        
        lines = ["# 团队成员\n"]
        for member in self.config["members"]:
            lines.append(f"- **{member['name']}** ({member['role']}) [{member['status']}]")
        
        return "\n".join(lines)
    
    def send(self, sender, to, content):
        """发送消息"""
        # 检查接收者是否存在
        recipient_exists = False
        for member in self.config["members"]:
            if member["name"] == to:
                recipient_exists = True
                break
        
        if not recipient_exists:
            return f"队友 {to} 不存在"
        
        # 发送消息到邮箱
        inbox_file = self.inbox_dir / f"{to}.jsonl"
        message = {
            "type": "message",
            "from": sender,
            "content": content,
            "timestamp": time.time(),
        }
        
        with open(inbox_file, "a", encoding="utf-8") as f:
            f.write(json.dumps(message, ensure_ascii=False) + "\n")
        
        return f"消息已发送给 {to}"
    
    def _read_inbox(self, name):
        """读取邮箱"""
        inbox_file = self.inbox_dir / f"{name}.jsonl"
        if not inbox_file.exists():
            return []
        
        messages = []
        try:
            with open(inbox_file, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line:
                        messages.append(json.loads(line))
            
            # 清空邮箱
            inbox_file.write_text("", encoding="utf-8")
        except Exception as e:
            print(f"读取邮箱失败 {name}: {e}")
        
        return messages
    
    def _teammate_loop(self, name, role, prompt):
        """队友循环"""
        print(f"队友 {name} ({role}) 已启动")
        
        # 初始化消息
        messages = [{
            "role": "system",
            "content": f"你是 {name},一个 {role}。请专注于你的职责,完成任务后等待新的指示。"
        }, {
            "role": "user",
            "content": prompt
        }]
        
        while True:
            # 读取邮箱
            inbox = self._read_inbox(name)
            if inbox:
                print(f"队友 {name} 收到 {len(inbox)} 条消息")
                for msg in inbox:
                    messages.append({
                        "role": "user",
                        "content": f"来自 {msg['from']} 的消息: {msg['content']}"
                    })
            
            # 这里应该调用模型,但为了简化,我们模拟一下
            if messages:
                print(f"队友 {name} 正在处理任务...")
                # 模拟处理时间
                time.sleep(2)
                
                # 模拟回复
                response = f"{name} 已收到并处理了消息"
                messages.append({"role": "assistant", "content": response})
            
            # 等待一段时间再检查邮箱
            time.sleep(5)
    
    def shutdown(self):
        """关闭团队"""
        # 这里可以添加清理逻辑
        pass

2. 团队工具

python 复制代码
def create_team_tools(team_manager):
    """创建团队相关的工具"""
    
    def team_spawn(name, role, prompt):
        """创建队友"""
        return team_manager.spawn(name, role, prompt)
    
    def team_list():
        """列出所有队友"""
        return team_manager.list()
    
    def team_send(to, content):
        """发送消息"""
        return team_manager.send("lead", to, content)
    
    return {
        "team_spawn": team_spawn,
        "team_list": team_list,
        "team_send": team_send,
    }

3. 集成到 Agent Loop

python 复制代码
def agent_loop_with_team(state):
    """带团队系统的 Agent Loop"""
    # 初始化团队管理器
    team_manager = TeamManager()
    
    # 创建团队工具
    team_tools = create_team_tools(team_manager)
    state["tools"] = state.get("tools", []) + [
        {
            "name": "team_spawn",
            "description": "创建队友",
            "parameters": {
                "name": {"type": "string", "description": "队友名字"},
                "role": {"type": "string", "description": "队友角色"},
                "prompt": {"type": "string", "description": "队友提示词"}
            }
        },
        {
            "name": "team_list",
            "description": "列出所有队友",
            "parameters": {}
        },
        {
            "name": "team_send",
            "description": "发送消息给队友",
            "parameters": {
                "to": {"type": "string", "description": "接收者名字"},
                "content": {"type": "string", "description": "消息内容"}
            }
        }
    ]
    
    # 主循环
    while True:
        # 调用模型
        response = call_model(state["messages"])
        
        if response.stop_reason != "tool_use":
            return response.content
        
        results = []
        for block in response.content:
            if hasattr(block, "type") and block.type == "tool_use":
                tool_name = block.name
                tool_input = block.input
                
                # 执行团队工具
                if tool_name in team_tools:
                    output = team_tools[tool_name](**tool_input)
                else:
                    # 执行其他工具
                    output = run_tool(tool_name, tool_input)
                
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output
                })
        
        if results:
            state["messages"].append({"role": "user", "content": results})

核心功能说明

1. 创建队友

创建编码队友

python 复制代码
team_manager.spawn("alice", "coder", "你是 Alice,一名专业的 Python 开发者。请负责编写高质量的代码。")

创建测试队友

python 复制代码
team_manager.spawn("bob", "tester", "你是 Bob,一名专业的测试工程师。请负责测试代码的质量和功能。")

2. 管理团队

列出所有队友

python 复制代码
team_manager.list()
# 查看团队成员列表

发送消息

python 复制代码
team_manager.send("lead", "alice", "请编写一个 JSON 解析器")
# 向 Alice 发送任务

3. 队友循环

每个队友都有自己的循环:

  • 读取邮箱中的消息
  • 处理任务
  • 等待新的消息

这样队友可以长期存在,反复接活。

4. 持久化

团队配置会被保存到 .team/config.json 文件:

json 复制代码
{
  "team_name": "default",
  "members": [
    {
      "name": "alice",
      "role": "coder",
      "status": "working"
    },
    {
      "name": "bob",
      "role": "tester",
      "status": "working"
    }
  ]
}

这样即使程序重启,团队成员信息也不会丢失。

Teammate vs Subagent vs Runtime Task 的边界

特性 Teammate (队友) Subagent (子智能体) Runtime Task (运行时任务)
生命周期 长期存在 一次性 短期(任务完成后结束)
身份 有名字、角色、邮箱 临时身份 无独立身份
通信 邮箱系统 直接返回结果 通知队列
上下文 独立上下文 临时上下文 无独立上下文
用途 长期协作 一次性委派 后台执行慢命令

使用建议

  • 对于长期需要的角色:使用 Teammate
  • 对于一次性的小任务:使用 Subagent
  • 对于耗时较长的命令:使用 Runtime Task

新手最容易犯的 4 个错

1. 把队友当成「名字不同的 subagent」

python 复制代码
# ❌ 错误
# 生命周期还是执行完就销毁
def spawn_subagent(name, task):
    # 执行任务
    result = execute_task(task)
    # 返回结果后销毁
    return result

# ✅ 正确
# 队友长期存在
def spawn_teammate(name, role, prompt):
    # 创建队友
    member = {"name": name, "role": role, "status": "working"}
    # 启动独立循环
    thread = threading.Thread(target=teammate_loop, args=(name, role, prompt))
    thread.start()
    # 队友继续存在
    return f"队友 {name} 已创建"

2. 队友之间共用同一份 messages

python 复制代码
# ❌ 错误
# 所有队友共用一个消息列表
class TeamManager:
    def __init__(self):
        self.messages = []
    
    def spawn(self, name):
        # 所有队友都用 self.messages
        pass

# ✅ 正确
# 每个队友有自己的消息列表
def teammate_loop(name, role, prompt):
    # 每个队友有自己的 messages
    messages = [{
        "role": "system",
        "content": f"你是 {name},一个 {role}。"
    }]
    # 处理自己的消息
    while True:
        pass

3. 没有持久名册

python 复制代码
# ❌ 错误
# 名册只存在内存中
class TeamManager:
    def __init__(self):
        self.members = []

# ✅ 正确
# 名册持久化到磁盘
class TeamManager:
    def __init__(self, team_dir=".team"):
        self.team_dir = Path(team_dir)
        self.config_path = self.team_dir / "config.json"
        self.config = self._load_config()
    
    def _load_config(self):
        if self.config_path.exists():
            return json.loads(self.config_path.read_text(encoding="utf-8"))
        return {"team_name": "default", "members": []}

4. 没有邮箱,靠共享变量直接喊话

python 复制代码
# ❌ 错误
# 直接使用共享变量通信
shared_messages = {}

def send_message(to, content):
    if to not in shared_messages:
        shared_messages[to] = []
    shared_messages[to].append(content)

def get_messages(to):
    return shared_messages.get(to, [])

# ✅ 正确
# 使用邮箱系统通信
def send(self, sender, to, content):
    inbox_file = self.inbox_dir / f"{to}.jsonl"
    message = {
        "type": "message",
        "from": sender,
        "content": content,
        "timestamp": time.time(),
    }
    with open(inbox_file, "a", encoding="utf-8") as f:
        f.write(json.dumps(message, ensure_ascii=False) + "\n")

为什么这很重要

因为一个真正强大的系统,往往需要多个专业角色的协同工作。

Agent 团队系统让你能够:

  1. 分工协作:不同角色专注于自己的领域
  2. 并行处理:多个任务同时进行,提高效率
  3. 上下文隔离:不同任务的上下文不会互相污染
  4. 长期存在:队友可以持续接活,不需要每次重新创建
  5. 可扩展性:可以根据需要添加新的角色

推荐的实现步骤

  1. 第一步:实现 TeamManager 类,管理团队成员和配置
  2. 第二步:实现队友创建和管理功能
  3. 第三步:实现邮箱系统,支持队友之间的通信
  4. 第四步:实现队友的独立循环,处理任务
  5. 第五步:创建团队相关的工具,暴露给模型
  6. 第六步:集成到 Agent Loop,支持团队操作

Agent 团队系统与后续章节的关系

  • s15 Agent 团队:解决团队成员如何长期存在、互相发消息的问题
  • s16 团队协议:解决团队成员之间如何进行结构化协作的问题
  • s17 自主智能体:解决智能体如何自主工作、主动认领任务的问题

所以 Agent 团队系统是构建复杂智能体系统的基础组件。

下一章预告

有了 Agent 团队系统,你的多个 Agent 已经能够长期存在并互相通信。下一章我们将探讨团队协议系统,让团队成员之间的协作更加结构化、可追踪。


一句话总结:Subagent 是一次性外包助手,Teammate 是长期在线队友。


如果觉得有帮助,欢迎关注,我会持续更新「从零构建 Coding Agent」系列文章。

相关推荐
飞龙14775657467502 小时前
Agent 开发进阶(十三):后台任务系统,让慢命令不阻塞主循环
agent
Pkmer2 小时前
工程师眼中的Prompt提示词
llm·agent
Pkmer2 小时前
LLM应用的“外挂大脑”:Embedding、向量数据库与RAG
llm·agent
前进的李工2 小时前
智能Agent实战指南:从入门到精通(工具)
开发语言·人工智能·架构·langchain·agent·tool·agentexecutor
chaors2 小时前
LangGraph 入门到精通0x00:HelloLangGraph
langchain·llm·agent
打酱油的D2 小时前
Claude Code Harness Agent 架构深度解析
agent
阿维的博客日记3 小时前
了解哪些其他的 Agent 设计范式?
agent
霪霖笙箫3 小时前
「JS全栈AI Agent学习」六、当AI遇到矛盾,该自己决定还是问你?—— Human-in-the-Loop
前端·面试·agent
Old Uncle Tom3 小时前
Claude Code 记忆系统架构分析
人工智能·ai·系统架构·agent