Agent Team:从"派一次差"到"养一支班底"
摘要:Subagent 适合一次性派工:创建独立上下文,完成任务,回传总结,然后销毁。但长期项目常常需要固定角色反复交接,比如 coder 实现、reviewer 审查、再回到 coder 修改。Agent Team 要解决的就是这种持续协作问题:固定队友、文件 inbox、团队状态和总控调度。
标签:Agent Team、多 Agent、协作、消息总线、Python

Subagent 办完即散
前面我们用 subagent 隔离了执行细节。它非常适合一次性任务:读几个文件、抓几个网页、整理一段总结。主 Agent 不需要看所有中间输出,只要最终回禀。
但 subagent 的生命周期很短:
text
创建独立上下文 → 执行差事 → 回传总结 → 销毁
这种模式不适合长期交接。比如你希望 alice 负责写代码,bob 负责审查。Alice 写完后,Bob 检查;Bob 提出问题后,Alice 再修改;后面可能还有第二轮、第三轮。这时你需要的不是"一次性工具",而是一支有名字、有角色、有状态的固定班底。
这就是 Agent Team 的出发点。


Agent Team 的最小组成
项目里的团队实现主要在 agent/team.py 和 agent/tools/team.py。
它没有引入数据库,也没有上复杂消息队列,而是用一套最小结构把协作跑起来:
text
lead 总控
teammate 固定队友线程
.team/config.json 团队花名册
.team/inbox/*.jsonl 文件收件箱

这四个部分各有职责。
lead 仍然是主 Agent。它理解用户目标,决定召入谁,给谁发消息,什么时候读回禀,最后怎么汇总。
teammate 是固定队友。每个队友在自己的线程里运行,有自己的 system prompt、messages 和工具 registry。办完当前任务后,它不会销毁,而是回到 idle 等下一封消息。
config 是团队花名册,记录成员名字、职司和状态。
inbox 是通信机制。发送消息就是向某个 JSONL 文件追加一行;读取消息就是读完后清空。
MessageBus:文件就是邮箱
MessageBus 是团队通信的底座。
发送消息时,它会校验 sender 和收件人,组装 JSON,再追加到目标 inbox:
python
msg = {
"type": msg_type,
"from": sender,
"content": content,
"timestamp": time.time(),
}
with inbox_path.open("a", encoding="utf-8") as f:
f.write(json.dumps(msg, ensure_ascii=False) + "\n")
读取消息时,则是 drain 语义:
python
lines = inbox_path.read_text(encoding="utf-8").splitlines()
inbox_path.write_text("", encoding="utf-8")
也就是说,读走就清空。
文件 inbox 看起来简单,但非常适合教学。你可以直接打开 .team/inbox/alice.jsonl 看 lead 给 alice 发了什么,也可以看 lead.jsonl 里队友回禀了什么。协作不再藏在模型调用里,而是落到了可观察的文件上。
TeammateManager:身份和状态
固定队友需要被管理。TeammateManager 负责配置、状态、线程启动和队友循环。
团队配置大概长这样:
json
{
"team_name": "default",
"members": [
{"name": "alice", "role": "coder", "status": "offline"}
]
}
状态分为几类:
text
working 当前进程里线程活着,正在工作
idle 当前进程里线程活着,等待消息
offline config 里有成员,但当前进程没有线程
shutdown 队友已收到退出请求并停止
offline 是一个很真实的状态。Python 线程不会跨进程保存。程序重启后,旧线程已经消失,但 .team/config.json 还在。启动时系统会把遗留的 working/idle 标记为 offline,提醒你需要重新唤回队友。
这比假装队友还在线更可靠。
spawn_teammate:召入或唤回队友
主 Agent 通过 spawn_teammate 召入固定队友。参数包括:
text
name 队友名字,例如 alice、reviewer
role 队友职司,例如 coder、reviewer
prompt 第一件差事
如果队友已经在线,新任务会被送入它的 inbox;如果队友离线,则重新启动线程。
队友线程里的 system prompt 会明确告诉它自己的身份:
text
你是大内团队中的固定队友,名叫 {name},职司是 {role}。
你不是一次性小太监,而是 agent team 的持久成员。
你可以通过 send_message 给 lead 或其他队友发消息。
这个 prompt 的目的不是营造戏剧效果,而是明确生命周期:teammate 不是一次性函数,办完后要回禀,然后等待下一封 inbox。
队友能用哪些工具
队友拥有基础工具:
python
_BASE_TOOL_NAMES = (
"run_command",
"web_fetch",
"load_skill",
"read_file",
"write_file",
"glob",
"grep",
)
还会加上通信工具:
text
send_message
read_inbox
注意,这里没有 dispatch_subagent,也没有 update_todos。固定队友可以做事、可以发消息、可以读自己的 inbox,但它不应该递归调度子代理,也不应该污染主 Agent 的计划状态。
这和 subagent 的白名单设计是一脉相承的:多角色不是放开所有权限,而是更需要清晰边界。
一次完整协作链路
一个最小团队闭环可以这样跑:
text
1. 用户交代一个需要实现和审查的任务
2. lead 召入 alice 作为 coder
3. alice 完成实现后 send_message 给 lead
4. lead 读取 inbox,再把结果交给 bob 审查
5. bob 回禀问题或通过意见
6. lead 根据审查结果继续派发或最终总结

这和 subagent 最大的区别在生命周期。Subagent 是一次工具调用;teammate 是固定组织成员。它可以多次收到消息,状态可以从 working 变成 idle,再变回 working。
团队不是多调几次模型
"多 Agent"很容易被讲得很玄,但工程上要警惕一个误区:多个模型调用本身不等于团队。
团队至少要有四件事:
- 固定身份:谁是 coder,谁是 reviewer;
- 状态记录:谁在线,谁空闲,谁离线;
- 通信机制:消息怎么送达,怎么读取;
- 调度者:谁决定下一步交给谁。
没有这些,模型调用再多,也只是散乱的并发请求。Agent Team 的重点不是模型数量,而是协作协议。
为什么不用共享 history
最直接的多角色方案,是把所有成员消息塞进同一个 history。这样简单,但很快会乱。
Alice 的中间过程、Bob 的审查意见、lead 的调度指令会混在一起。每个成员都看到不该看的细节,上下文也会迅速膨胀。
文件 inbox 的好处是通信显式化。队友不是天然共享一切,而是通过 send_message 把关键结果发出去;接收方通过 read_inbox 获取消息。这个模型更接近真实团队:每个人有自己的工作上下文,通过消息交接必要信息。
底层以后可以换成数据库、消息队列或事件流,但语义不会变:
text
消息有发送者
消息有接收者
消息有类型
消息可以被读取
成员状态可以被观察
先用 JSONL 文件把语义写清楚,比一开始接复杂基础设施更适合学习。
什么任务适合 Agent Team
Agent Team 适合天然包含角色交接的任务。
例如:
text
让 alice 作为 coder 实现一个最小 demo;
让 bob 作为 reviewer 检查实现;
如果 bob 发现问题,把意见转给 alice 修改;
最后由 lead 汇总结果。
这个任务会同时触发召入队友、发送消息、读取 inbox 和状态变化。你可以用 /team 看谁在 working、idle 或 offline,用 /inbox 查看 lead 收到的回禀。
如果任务只是一次性查资料,subagent 更轻。只有当任务需要固定角色、多轮交接、持续等待消息时,Agent Team 才真正体现价值。
Agent Team 的成本
团队协作带来能力,也带来成本。每个 teammate 都有自己的模型调用、上下文和工具执行。队友越多,调度越复杂,消息也越多。如果只是一个简单问题,开团队反而浪费。
所以使用 Agent Team 前,可以先问三个问题:
text
这个任务是否需要固定角色?
这些角色是否需要多轮交接?
中间结果是否需要被保留下来等待后续消息?
如果答案是否定的,普通工具或 subagent 就够了。如果答案是肯定的,Team 才有意义。
这也是多 Agent 系统很容易被误用的地方。不是角色越多越高级,而是角色边界越清楚越可靠。一个清晰的 coder + reviewer,往往比五个职责重叠的"专家"更好用。
inbox 模型的好处
文件 inbox 看起来朴素,但它解决了一个关键问题:消息传递是显式的。
在共享 history 模式下,所有人都读同一本流水账。信息当然完整,但也嘈杂。固定队友可能看到不相关的中间过程,甚至被其他角色的指令干扰。
inbox 模型则更像现实协作。lead 把需要 alice 处理的消息发给 alice,把需要 bob 审查的消息发给 bob。每个队友只处理送到自己 inbox 的内容,办完后再回禀 lead。
这种显式通信还有一个好处:可调试。你可以打开 JSONL 文件,看到消息是谁发的、发给谁、内容是什么、时间是什么。系统出了问题时,不必只靠模型总结来猜。
自己动手验证
一个适合验证团队协作的任务是:
text
召入 alice 作为 coder,写一个最小函数;
召入 bob 作为 reviewer,检查这个函数;
如果 bob 发现问题,把意见转给 alice;
最后由 lead 汇总实现和审查结果。
观察几个点:
.team/config.json是否出现 alice 和 bob;/team是否能看到 working、idle 或 offline;.team/inbox/lead.jsonl是否收到队友回禀;- bob 的审查意见是否能再次转给 alice。
如果这些都成立,说明这个系统已经不是单纯多次调用模型,而是具备了最小组织结构。
和真实团队一样,角色要少而清楚
做 Agent Team 时,最常见的冲动是创建很多角色:架构师、开发、测试、审查、文档、产品、研究员。听起来很完整,但实际运行时很容易变成调度噪声。
角色越多,lead 越需要判断谁该处理什么;消息越多,队友越容易收到和自己无关的信息;上下文越分散,最终结论越需要额外汇总。
更稳的做法是从两个固定角色开始:一个负责执行,一个负责审查。等这个闭环稳定,再考虑加入第三个角色。团队不是角色名称越多越专业,而是每个角色的职责边界越清楚越可靠。
团队状态为什么要落盘
如果 teammate 的状态只存在内存里,进程一停,团队就失忆了。对于一次性任务这没关系,但持续协作需要跨轮次接住上下文。
.team/config.json 和 JSONL inbox 的意义就在这里:它们让团队协作从"当前进程里的几次调用"变成"可以被恢复的工作流"。你能打开文件看到 alice 是否 idle,bob 是否刚收到消息,也能追溯某个结论是从哪封消息传过来的。
这对调试尤其重要。多 Agent 系统最怕"看起来大家都在干活,但不知道信息怎么流动"。落盘后的 inbox 把消息流显式化:谁发给谁、什么时候发、内容是什么,都有记录。
协作闭环比自动化表演更重要
一个团队系统不需要一开始就很热闹。真正值得关注的是闭环是否成立。
lead 能不能把任务拆给合适队友;队友能不能在自己的工具边界内完成工作;完成后能不能把结果发回;另一名队友能不能基于这条消息继续处理;最后 lead 能不能汇总状态并给出可信结论。
只要这个闭环稳定,系统就有扩展空间。否则再多角色、再多并发、再复杂的 prompt,都只是让运行过程更难解释。
inbox 不是聊天室
文件 inbox 的目标不是让所有队友随时闲聊,而是让关键交接被记录。消息应该尽量短、具体、可执行:完成了什么,发现了什么,需要谁继续做什么。
如果 inbox 里塞满寒暄、重复背景和大段无关日志,团队很快会退化成另一个拥挤的 history。好的消息传递应该像工程交接单:接收者打开后,马上知道自己要判断什么、执行什么、回禀什么。
小结
Agent Team 解决的是持续协作问题。
Subagent 适合"派一次差":独立上下文,办完回传,随即销毁。Agent Team 适合"养一支班底":固定身份,记录状态,通过 inbox 交换消息,办完后继续等待下一封任务。
到这里,一个从零实现的 Agent 已经具备了更完整的形态:能调用工具,能保存记忆,能维护计划,能把局部细节交给子代理,也能组织固定队友协作。真正的 Agent 工程,不只是让模型更会说话,而是让模型进入一套可观察、可约束、可持续推进任务的系统。
视频与源码
如果你想看完整演示,可以在主页的《从零手搓 Agent》合集里按顺序观看视频版:
文章里的示例代码和完整项目也放在这里:
- 📦 教学仓库:github.com/TheSyart/cl...
- ⚔️ 实战项目:github.com/TheSyart/em...
我会持续更新 Agent 教学与实战内容。觉得有用的话,欢迎给项目点个 Star ⭐,也谢谢你一路看到这里。