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
关键点只有三个:
- 名册:记录团队成员的身份和状态
- 邮箱:队友之间的通信渠道
- 独立循环:每个队友都有自己的 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 团队系统让你能够:
- 分工协作:不同角色专注于自己的领域
- 并行处理:多个任务同时进行,提高效率
- 上下文隔离:不同任务的上下文不会互相污染
- 长期存在:队友可以持续接活,不需要每次重新创建
- 可扩展性:可以根据需要添加新的角色
推荐的实现步骤
- 第一步:实现 TeamManager 类,管理团队成员和配置
- 第二步:实现队友创建和管理功能
- 第三步:实现邮箱系统,支持队友之间的通信
- 第四步:实现队友的独立循环,处理任务
- 第五步:创建团队相关的工具,暴露给模型
- 第六步:集成到 Agent Loop,支持团队操作
Agent 团队系统与后续章节的关系
- s15 Agent 团队:解决团队成员如何长期存在、互相发消息的问题
- s16 团队协议:解决团队成员之间如何进行结构化协作的问题
- s17 自主智能体:解决智能体如何自主工作、主动认领任务的问题
所以 Agent 团队系统是构建复杂智能体系统的基础组件。
下一章预告
有了 Agent 团队系统,你的多个 Agent 已经能够长期存在并互相通信。下一章我们将探讨团队协议系统,让团队成员之间的协作更加结构化、可追踪。
一句话总结:Subagent 是一次性外包助手,Teammate 是长期在线队友。
如果觉得有帮助,欢迎关注,我会持续更新「从零构建 Coding Agent」系列文章。