多 Agent 协作与通信:MessageBus 最小实现

如果你已经写过或读过「单 Agent + 工具循环」,多半遇到过这种尴尬:

  • 让模型去读一大段测试、扫全仓库------主对话被工具输出塞满
  • 想「一边规划、一边让人去查文档」------只能串行等 subagent 跑完
  • 后台任务完成了------还要你手动粘贴结果回主对话

Agent Teams 的思路很直白:Lead 负责统筹;Teammate 在独立线程里跑自己的 loop;彼此用 MessageBus 传话;Lead 通过 inbox 注入把结果接回主上下文。

一句话:

子任务从「同步函数调用」升级成「异步协作」:队友有自己的 messages,Lead 只收结论。


一、和「同步 Subagent」差在哪?

很多教学实现里,subagent 是这样工作的:

text 复制代码
Lead 调用 task 工具
    → 等 subagent 跑完(阻塞)
    → 整段历史作为 tool_result 塞回 Lead

适合短任务;不适合「Lead 继续想、队友并行查」。

Agent Teams 改成:

text 复制代码
Lead 调用 spawn_teammate
    → 后台线程启动 teammate_loop(不阻塞)
Lead 继续对话 / 做别的事
    → teammate 完成后 BUS.send → lead 的 inbox
    → queue_processor 发现 inbox → 自动唤醒 Lead 的 agent_loop

Teammate 不是 tool 的返回值,而是一个在跑的工人。


二、三张图看懂架构

1. 三条事件流,共用一个 Lead

text 复制代码
① 用户打字          → history → agent_loop
② Cron 到点         → _queue → inject → agent_loop
③ Teammate 回信     → inbox  → inject → agent_loop

Cron 和 Teammate 都不单独调模型「乱入」,而是变成 外部事件 ,由后台 queue_processor 抢锁后统一交给 Lead 的 agent_loop 消费------这是很多 Agent Runtime 的共同形状。

2. Lead / MessageBus / Teammate

text 复制代码
Lead agent loop                  MessageBus                 Teammate loop
+----------------+               +-----------+              +-------------+
| prompt + tools | --spawn----->  | .jsonl    | <---send---- | own history |
| inject inbox   | <---send------ | 邮箱      | ---inbox---> | own tools   |
+----------------+               +-----------+              +-------------+

3. 关键设计:共享磁盘,隔离上下文

  • 共享 :同一工作目录下的 bash / 读写文件(教学版未做权限隔离);
  • 隔离 :Lead 与 Teammate 不共享 messages 列表。

队友读了哪些文件、执行了哪些命令,留在队友自己的上下文里;Lead 最终只看到 inbox 里那条 result


三、MessageBus:用文件当邮箱(教学版)

生产里常见 Redis、Kafka、数据库队列表。教学代码用 每个 Agent 一个 JSONL 文件,足够把机制讲清楚。

目录示意:

text 复制代码
.mailboxes/
  lead.jsonl
  reviewer.jsonl

send():向目标文件 append 一行 JSON

json 复制代码
{
  "from": "reviewer",
  "to": "lead",
  "content": "Parser looks good",
  "type": "result",
  "ts": 1779780000.0
}

read_inbox()读光并删除文件(消费式),避免同一条消息反复注入。

注意 :当前实现用 threading.Lock 保护同一进程内的读写;多进程同时写同一文件需要更强的一致性方案。


四、Lead 多了三个「团队工具」

工具 作用
spawn_teammate 按 name / role / prompt 启动后台队友
send_message 给某个 agent 发消息(含队友之间)
check_inbox 主动读 Lead 邮箱(自动注入时也可不用)

spawn_teammate 本质是启动守护线程:

python 复制代码
threading.Thread(
    target=teammate_loop,
    args=(client, model_name, name, role, prompt),
    daemon=True,
).start()

Lead 立刻拿到「已启动」,不会卡在队友的十轮工具循环里。


五、Teammate 怎么跑? deliberately 受限

Teammate 有自己的 system prompt 和 messages,入口任务来自 Lead 的 prompt

工具白名单(教学版):

  • 有:bashread_filewrite_fileedit_filesend_message
  • 无:spawn_teammateschedule_cron ...(避免队友再 spawn 队友,复杂度爆炸)

轮数上限:最多 10 轮 tool loop,防止后台线程无限烧 token。

结束时无论成败,都会:

text 复制代码
BUS.send(队友名, "lead", 最后一段文本, msg_type="result")

并从 active_teammates 里注销。


六、Inbox Injection:为什么伪装成 user 消息?

Lead 每轮调模型前会执行:

python 复制代码
inject_inbox_messages(messages)  # 读 lead.jsonl → 删文件 → 追加一条 user 消息

注入内容类似:

text 复制代码
<inbox>
[reviewer:result] queue_processor 会监听外部事件并安全唤醒 Lead......
</inbox>

为什么是 role: user

对 Agent Loop 来说,inbox、cron 触发、用户打字都是 「外部世界发来的新事件」。模型下一轮统一按「用户侧输入」处理,无需为每种事件单独设计 role。

Cron 触发则用 <scheduled-work> 包一层,逻辑相同。


七、Queue Processor:cron 与 inbox 共用一个唤醒器

第 8 篇里,后台线程只盯 cron 队列。第 9 篇条件变成:

python 复制代码
if not SCHEDULER.has_queue() and not BUS.has_inbox("lead"):
    continue

有定时任务 有队友回信 → 尝试非阻塞获取 agent_lock → 跑一轮 agent_loop(history)

agent_lock 解决的是:用户正在终端输入时,后台不能同时改同一份 history。拿不到锁就 0.2 秒后再试。


八、和定时任务怎么拼在一起?

没有替换 cron,而是并入同一套「外部事件」模型:

text 复制代码
cron 到点  → tick 入队 → inject_cron_jobs → <scheduled-work>
队友完成  → send 到 lead → inject_inbox_messages → <inbox>
                    ↓
            queue_processor → agent_loop → 模型 + 工具

持久化方面:durable=True 的 cron 会写入 .scheduled_tasks.json,重启后 load() 恢复;已到点但未消费的队列仍在内存里,进程挂了就没了(教学边界)。


九、本地怎么跑?一句命令试全队

环境变量(.env):

text 复制代码
OPENAI_BASE_URL
OPENAI_API_KEY
MODEL_NAME

仓库根目录:

bash 复制代码
python3 harness_agent/09_agent_teams.py

推荐试一句(复制到终端即可):

text 复制代码
启动一个 teammate,名字叫 reviewer,角色是 code reviewer。
让它阅读 harness_agent/09_agent_teams.py,用几句话说明 MessageBus 怎么工作。

预期现象:

  1. Lead 调用 spawn_teammate,终端打印 [teammate] reviewer spawned
  2. 你可以继续输入别的,或等待;
  3. reviewer 完成后 [bus] reviewer -> lead[queue processor] delivering external work
  4. Lead 被自动唤醒,根据 inbox 内容回答你。

十、教学版刻意没做啥(读代码时的预期)

简化点 真实系统常见补法
文件 JSONL 邮箱 MQ / DB / gRPC
单进程锁 分布式锁、ack、重试
Teammate 最多 10 轮 长期 worker、idle 唤醒
共享工作目录 沙箱、per-agent 权限
无取消/超时/成本上限 lifecycle、budget、审计

知道边界,才知道下一版该往哪长。


十一、带走三句话

  1. 多 Agent ≠ 多个模型随便调 ------要有 消息边界 (MessageBus)和 主 loop 串行消费(queue processor + lock)。
  2. 协作靠异步,隔离靠独立 messages------Lead 不被队友的工具轨迹淹没。
  3. Cron、用户输入、队友回信------形态不同,本质都是「外部事件注入主 loop」。

从「一个人干活」到「一个小队分工」,这是 Coding Agent 走向工程化协作的第一步;后面还可以叠权限、结构化结果 schema、队友生命周期管理------但 邮箱 + 注入 + 后台唤醒 这条骨架已经够用。

相关推荐
Zhang~Ling1 小时前
二叉搜索树(BST)详解:插入、删除、查找与 Key/Value 实战场景
数据结构·c++·算法
8Qi81 小时前
LeetCode 76. 最小覆盖子串(Minimum Window Substring)
数据结构·算法·leetcode·滑动窗口·哈希表
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游
Bingorl1 小时前
机器学习之朴素贝叶斯算法
人工智能·算法·机器学习
8Qi81 小时前
LeetCode 209. 长度最小的子数组(Minimum Size Subarray Sum)
java·算法·leetcode·双指针·滑动窗口
狮子座明仔2 小时前
DeCoRL:把推理链拆成“乐团合奏“——AAAI 2026 一篇把 RLHF 推到 32B 打 GPT-4o 的工作
人工智能·深度学习·算法
QiLinkOS2 小时前
合肥气链科技有限公司创办与未来技术应用
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
妄想出头的工业炼药师2 小时前
追踪定位大模型
算法·开源
Solis程序员2 小时前
TreeMap 核心原理与实战
java·数据结构·算法