我们的答案: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 本身就是目录。
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。 一键启动,三件事,各种场景。
问题提完了,这是我们的答案。还在路上,慢慢干。
