Skip to content

我们的答案:mq9

上一篇文章《消息队列的下一个十年:忘了 Kafka 吧》里,我提了几个判断:append only log 不是通信的全部,S3 和数据湖叙事是这个框架里的尽头,底层必须原子化,评价体系要从"量"转向"多"。

那篇文章只提问题,不提方案。这篇聊我们的解法。


出发点

RobustMQ 做了三年,最初解决的是多协议割裂的问题——统一存储层,协议只是读写接口,一条消息写入一次,MQTT、Kafka、NATS、AMQP 各自按自己的语义读取。

做着做着,发现这个架构有一个副产品:新增一个协议,代价极轻。 存储层不动,协议只是加一层语义解析。

mq9 就是从这个地基上长出来的第五个协议——专门为 AI Agent 通信设计。


为什么是邮箱

上一篇我说,通信的形态远比 append only log 丰富——邮箱、广播、最新值、请求响应、临时通道。mq9 选了最基本的一个:邮箱。

为什么?因为 Agent 不是服务。服务是长期在线的,你发请求它就响应。Agent 是临时的,可能只活几秒,随时上下线。今天 Agent A 给 Agent B 发消息,B 不在线,消息直接丢了。

每个构建多 Agent 系统的团队都在用临时方案绕这个问题——Redis pub/sub、轮询数据库、自研任务队列。能用,但都是绕路。

mq9 直接解决它:我发出去,你空了来取,不需要同时在线。不是消息队列,不是流,是邮件系统。

邮箱比喻不是修辞,是设计本身:

  • 开通邮箱,拿到地址 → MAILBOX.CREATE
  • 寄信,对方在不在家不管,信在邮箱等着 → MAILBOX.MSG.{mail_id}.{priority}
  • 收信,急件先看,普通件后看 → 订阅即全量推送,高优先级先到
  • 回信,信里有对方地址 → reply_to
  • 长期没人用,邮局自动注销 → TTL 自动销毁,无需手动删除
  • 公开邮箱,任何人能找到、往里发 → public: true,注册到 PUBLIC.LIST
  • 公告栏贴了一个活,只能一个人接 → queue group

一个概念,三件事

mq9 只有一个核心概念:邮箱。

全部操作只有三件事:

操作说明
MAILBOX.CREATE创建邮箱
MAILBOX.MSG.{mail_id}.{priority}发消息
MAILBOX.MSG.{mail_id}.*订阅邮箱

没有 QUERY——每次订阅全量推送所有未过期消息,订阅本身就是查询。没有 DELETE——纯 TTL 管理,到期自动清理。

mq9 只管通信——消息怎么可靠送达。消息里装什么、业务语义是什么,mq9 不管。消息内容是 byte 数组,不解析、不校验、不限制。

私有邮箱和公开邮箱

邮箱分两种,区别只有一个:可发现性

私有邮箱——mail_id 系统生成,不可猜测。知道 mail_id 就能发、能订阅,不知道就无从操作。没有 token,没有 ACL,mail_id 的不可猜测性就是安全边界。用于点对点私信、任务结果回传。

公开邮箱——mail_id 用户自定义,有意义的名字。创建时指定 public: true,自动注册到 $mq9.AI.PUBLIC.LIST,任何 Agent 可发现。用于任务队列、公共频道、能力公告。

三个优先级

级别语义典型场景
high紧急,优先处理任务中断、紧急指令
normal常规通信任务分发、结果返回
low后台,不紧急日志、状态上报

存储层保证优先级顺序,消费方无需自行排序。边缘设备离线 8 小时后上线,先收到紧急停机指令,再收到常规配置更新。

发现公开邮箱

$mq9.AI.PUBLIC.LIST 是系统内置地址,broker 维护,不接受用户写入,TTL 永不过期。

订阅即全量推送——当前所有公开邮箱立即推送,后续新增和移除实时推送。Agent 上线订阅一次,就能持续感知网络里有哪些公开频道。不需要注册中心,PUBLIC.LIST 本身就是目录。

bash
nats sub '$mq9.AI.PUBLIC.LIST'

几个关键设计决策

mail_id 不绑定 Agent 身份。 mq9 只认 mail_id,不认 agent_id。一个 Agent 可以为不同任务申请不同的 mail_id,用完不管,TTL 自动清理。邮箱是通道级的,不是身份级的。

先存储后推送。 消息到达先写存储,再推给在线订阅者。持久化是默认行为,不是可选项。

客户端去重。 每条消息有唯一 msg_id,服务端不追踪消费状态,客户端自行去重。

CREATE 幂等。 重复创建静默成功,TTL 以第一次为准。不提供状态查询,CREATE 永远返回成功。


八个真实场景

一个概念,三件事,能解决什么?

异步任务回传。 主 Agent 创建私有邮箱,子 Agent 完成后发结果到主 Agent 邮箱。不需要阻塞等待,空了来取。

全局状态感知。 Worker 创建公开邮箱定期上报状态,主 Agent 订阅感知全局。TTL 过期自动感知消亡,新 Worker 加入自动感知。

任务广播与竞争消费。 创建公开邮箱发布任务,Worker 用 queue group 竞争消费,每条任务只被一个 Worker 处理。离线的 Worker 上线后也能收到未过期的任务。

异常告警广播。 创建公开邮箱发异常事件,订阅者自行响应。离线的 handler 上线后也能收到未过期的告警。发布方不需要知道谁在处理。

边缘离线消息积压。 发到边缘 Agent 邮箱的 high 和 normal 消息,边缘不在线持久化等待,联网后按优先级取——high 先于 normal。

异步请求-响应。 A 发请求到 B 的邮箱带 reply_to 和 correlation_id,B 上线处理后发响应到 A 的邮箱。离线不丢,不阻塞。

能力注册与发现。 Agent 启动时创建公开邮箱声明能力,自动注册到 PUBLIC.LIST,其他 Agent 订阅即可感知整个网络的能力分布。去中心化,零额外服务。

人机混合工作流。 Agent 发审批请求到人类审批员邮箱,审批员处理后发结果回 Agent 邮箱。人类和 Agent 用完全相同的协议,流程不中断。


不是 append only,是邮箱

上一篇我说 Kafka 的一切都来自 append only log 这个设计决策。mq9 选了另一条路:邮箱模型,基于 NATS 协议。

这个选择决定了 mq9 跟 Kafka 系是完全不同的东西。在邮箱语义下,append only log 的每一个假设都是多余的:消息不需要顺序回放,需要按优先级取;不需要 offset 管理,需要 TTL 自动清理;不需要 partition,需要动态 mail_id;不需要三副本持久化每一条消息,需要按场景选择存储级别。

mq9 在 NATS 生态中的定位介于 Core pub/sub 和 JetStream 之间。Core NATS 太轻——没有持久化,离线就丢。JetStream 太重——stream、consumer、offset、replay,一整套对标 Kafka 的语义。mq9 在 pub/sub 的基础上加了持久化、优先级、TTL 自动管理,但不引入 stream、consumer group、offset 这些重概念。刚好够用,不多不少。

需要说清楚:mq9 不是 NATS,是一个独立的 broker,兼容 NATS 协议。 所有语言的 NATS SDK 开箱即用,零生态成本。$mq9.AI.* 的命名空间让协议本身成为文档——$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.critical,一行字符串,看到 subject 就懂语义。


总结

mq9 看起来像消息队列,像 pub/sub,像 JetStream,像谁都有点像。但它是它——一个邮箱,三件事,为 Agent 场景专门设计,今天没有人做过。

就像电子邮件不是数据库,虽然邮件系统用数据库存邮件。mq9 不是消息队列,虽然 mq9 用消息队列的能力实现邮箱。

只要你需要跑很多 Agent,你就需要 mq9。 一键启动,三件事,各种场景。

问题提完了,这是我们的答案。还在路上,慢慢干。

🎉 既然都登录了 GitHub,不如顺手给我们点个 Star 吧!⭐ 你的支持是我们最大的动力 🚀