mq9-hermes plugin:一些技术思考
起点
Hermes Agent(NousResearch 出的 self-improving AI agent)想加 A2A 协议支持。Issue #514 是他们的官方提案——2026 年 3 月开的,到现在还是 open 状态。提案写得很完整,三阶段实施计划、SDK 选型、依赖分析都有,但实际代码进展缓慢。
读了这个 issue,再看 mq9 的当前能力,发现一个机会:mq9 可以做一个 Hermes plugin,让 Hermes 用 mq9 作为 A2A 的传输层和发现层,绕过 Issue #514 里大部分自己实现的工作量。
这篇文章把这件事想清楚——为什么做、做什么、可能怎么做。
Hermes Agent 当前的通信形态
先把 Hermes 当前能干什么、不能干什么列清楚。
能干的:
- 通过 messaging gateway 接入 18 个平台(Telegram、Discord、Slack、WhatsApp、Signal、Email、Feishu/Lark、Microsoft Teams 等),让人和 agent 对话
- 进程内本地 delegation——派生子 agent,最多 3 并发,深度 2
- 通过外部 CLI 调用其他 agent(claude-code、codex 等)
- MCP 客户端调用工具(
tools/mcp_tool.py,1047 行,production-ready)
不能干的:
- 跨进程、跨机器和其他 agent 通信
- Agent 发现机制(除了写死的 URL 配置)
- 把自己暴露为可被外部 agent 调用的服务
- 异步任务(比如离线消息、长时间运行的协作任务)
更具体的现实场景:社区有个 hermes-plugins 项目,写了个 MQTT plugin 让 Hermes 订阅事件流。开发者用 MQTT 凑合做 agent 通信,因为没有更合适的工具。
Issue #514 的核心内容
这个提案的整体结构是把 A2A 集成拆成三阶段:
Phase 1:A2A Client。 实现 a2a_discover 和 a2a_call 两个工具,让 Hermes 能主动调用远程 A2A agent。需要新文件 tools/a2a_tool.py,引入 a2a-sdk[http-server] 依赖,配置 ~/.hermes/config.yaml 里的 a2a_agents 块。
Phase 2:A2A Server。 实现 AgentExecutor 包装 Hermes AIAgent,自动从 toolsets/skills 生成 Agent Card,起 HTTP server 暴露端点(独立或集成到 gateway),处理 streaming 和认证。
Phase 3:Multi-Agent Orchestration。 维护多 URL agent registry、基于 task description 自动选择 agent、支持 fan-out 工作流、和 delegate_tool 集成做混合本地/远程编排。
提案里诚实承认的问题:
- A2A SDK 是 pre-1.0(v0.3.24),2026 年中才 GA,可能 break
- 又多一个协议要维护(已经有 MCP)
- 网络暴露 agent 端点的安全面(认证、HTTPS、输入清理)
- Agent Card 不签名可被伪造(JWS 签名是可选的)
- 和现有
delegate_tool概念重叠
几个开放问题:
- A2A server 模式的端口归属(独立还是和 gateway 共用)
- A2A 工具是核心还是 opt-in
- 凭证存储位置(config、env 还是 vault)
- 应该等 A2A SDK v1.0 还是基于 v0.3.x 现在就做
- A2A agent 发现要不要和 MCP server listings 集成(MCP-as-registry 模式)
最后两个开放问题特别值得注意——前者反映 A2A 标准化进程的不稳定,后者反映注册中心在 agent 生态里还没共识方案。NousResearch 自己都在纠结要不要复用 MCP 来做 agent 注册。
Issue #514 的工作量
把 Phase 1 + Phase 2 + Phase 3 加起来看,要做的工作不少:
协议层:
- 集成 a2a-sdk,处理 JSON-RPC over HTTP、SSE 流、Task 状态机
- Agent Card 生成、缓存、刷新
- Multi-turn 任务的状态管理(INPUT_REQUIRED、AUTH_REQUIRED 等)
- 认证和凭证管理
网络层:
- 起 HTTP server 暴露
/.well-known/agent.json和 A2A 端点 - 端口规划、TLS 配置
- 客户端连接管理、重试、超时
编排层:
- 多 agent 注册表
- 基于自然语言匹配 agent 能力
- Multi-agent 工作流和 delegate_tool 集成
Pre-1.0 SDK 的维护成本:
- 跟踪 spec 变化
- pin 版本,每次升级回归测试
这是个真实的大工程。Issue 开了 2 个月没合并,不是因为优先级低,是因为做完整是真的费劲。
mq9 的能力盘点
回头看 mq9 当前已经做到的:
协议层:
- mailbox 抽象,pub/sub 模式
- 持久化消息(Agent 离线时消息留存,上线全量推送)
- 优先级(normal/urgent/critical 三档)
- TTL 自动清理
- 按 key 压缩(同 key 只保留最新版,对 status update 类语义友好)
- DeliverPolicy(all/new/from_timestamp)
- QUERY 接口(按 key 直接查最新状态)
注册中心:
- AGENT.REGISTER / DISCOVER / UNREGISTER / REPORT
- 内置 fastembed-rs + LanceDB 做语义检索
- tag 精确检索
- raw_card 原样存取(mq9 不解析 AgentCard 内容,只提取 mailbox 和 tags 建索引)
URI scheme:
mq9://broker/mailbox和mq9s://broker/mailbox- 任何接受 URL 的协议字段都能用
值得对照 Issue #514 看的几点:
mq9 的 mailbox 模型天然解决 A2A push notification 的可靠性问题——A2A spec 说 push 是 webhook,但 webhook 失败处理 spec 没规定。mq9 mailbox 持久化消息、TTL 清理,client 离线后再上线全量推送,这些是 webhook 没有的能力。
mq9 的注册中心和 Phase 3 的"基于 task description 自动选择 agent"高度重合。Issue #514 里这个能力是要 Hermes 自己实现的,mq9 已经有了。
mq9 的 URI scheme 让 A2A 的 pushNotification.url 字段可以直接填 mq9://...——A2A 协议本身一行不用改。
一个想法:mq9-hermes plugin
如果 Hermes 通过 plugin 接入 mq9,Issue #514 里的大部分工作可以省掉。
具体的:
Phase 1(A2A Client)的 mq9 等价物:
mq9_discover替代a2a_discover:从 mq9 注册中心拿 AgentCard,支持 tag 和语义检索mq9_call替代a2a_call:通过 mq9 mailbox 发 A2A 消息
不需要 a2a-sdk 依赖,不需要 HTTP 客户端逻辑,不需要 SSE 流处理。mq9 SDK 是 plugin 的唯一依赖。
Phase 2(A2A Server)的 mq9 等价物:
mq9_serve:Hermes 启动时往 mq9 注册自己(带 AgentCard),订阅自己的 mailbox 接收 A2A 任务
不需要起 HTTP server,不需要管端口,不需要管 Agent Card 的 well-known URL 暴露。mq9 broker 就是事实上的"agent 网关"。
Phase 3(编排)的 mq9 等价物:
- 注册中心已经在 mq9 broker 里
- 基于 task description 的语义检索是 mq9 注册中心的能力
- Multi-agent 工作流在应用层,mq9 提供 mailbox 灵活组合
没省掉的部分:
- AgentCard 的具体内容怎么写(这是 Hermes 的责任)
- 任务状态机的语义(A2A 协议本身的内容)
- 凭证管理(mq9 不管,应用层负责)
- 安全模型(mq9 提供 mail_address 不可猜测的基础保证,复杂场景应用层加签名)
plugin 的具体形态
设想 Hermes plugin 长这样:
~/.hermes/plugins/mq9/
├── plugin.yaml # 元数据
├── mq9_plugin.py # 主入口
├── tools/
│ ├── mq9_register.py
│ ├── mq9_discover.py
│ ├── mq9_call.py
│ └── mq9_serve.py
└── config_schema.py # 配置校验plugin.yaml:
name: mq9
version: 0.1.0
description: A2A communication and discovery via mq9
provides_tools:
- mq9_register
- mq9_discover
- mq9_call
- mq9_serve
provides_hooks:
- post_setup # 启动时自动注册到 mq9
- shutdown # 关闭时注销
config:
broker:
type: string
default: "mq9://localhost:4332"
agent_id:
type: string
required: true
ttl:
type: integer
default: 300post_setup hook:
Hermes 启动时自动跑——读 config、连 mq9 broker、申请 inbox mailbox、注册自己的 AgentCard、起后台订阅 loop 处理外部消息。开发者不用写代码,配置 ~/.hermes/config.yaml 加几行就启用。
plugins:
enabled:
- mq9
mq9:
broker: "mq9://broker.internal:4332"
agent_id: "hermes-prod-001"
ttl: 600
capabilities:
streaming: true
push_notifications: truemq9_discover 工具:
Hermes agent 在对话中遇到"找一个能写 Python 代码的 agent"这种意图,自动调 mq9_discover:
@tool
def mq9_discover(query: str = None, tag: str = None, limit: int = 5):
"""
Discover agents through mq9 registry.
Args:
query: Natural language query for semantic discovery
tag: Exact tag for tag-based filtering
limit: Max number of agents to return
Returns:
List of AgentCards
"""
if query:
return mq9_client.discover_semantic(query, limit)
elif tag:
return mq9_client.discover_by_tag(tag, limit)返回的是 raw AgentCard JSON——mq9 不转换不包装,存什么返什么。Hermes 拿到后照常解析处理。
mq9_call 工具:
@tool
def mq9_call(target_mailbox: str, message: dict, callback_mailbox: str = None):
"""
Call a remote agent via mq9 mailbox.
Args:
target_mailbox: Target agent's mailbox (e.g., "mq9://broker/agent.coder.inbox")
message: A2A-format message
callback_mailbox: Optional callback mailbox; auto-created if None
"""
if callback_mailbox is None:
callback_mailbox = mq9_client.create_mailbox(ttl=86400)
# 把 callback 嵌入 A2A 消息的 pushNotification.url
message["pushNotification"] = {"url": callback_mailbox}
# 发送
mq9_client.send(target_mailbox, message)
# 订阅 callback 拿响应
return mq9_client.subscribe(callback_mailbox)注意 message 本身是 A2A 格式(JSON-RPC + Task),mq9 不解析。这保证 A2A 协议演进时 plugin 不用改。
mq9_serve hook:
Hermes 启动时后台启动一个订阅 loop:
def on_post_setup(ctx, config):
inbox = config.get("inbox") or auto_create_inbox()
# 注册到 mq9
mq9_client.register({
"mailbox": inbox,
"name": ctx.agent_name,
"description": ctx.agent_description,
"skills": derive_skills_from_toolsets(ctx),
"ttl": config["ttl"]
})
# 后台订阅
asyncio.create_task(serve_loop(inbox, ctx))
async def serve_loop(inbox, ctx):
async for msg in mq9_client.subscribe(inbox):
# msg.body 是 A2A tasks/send 请求
task = parse_a2a_task(msg.body)
result = await ctx.agent.execute(task)
# 把结果写回 callback mailbox
callback = task.get("pushNotification", {}).get("url")
if callback:
mq9_client.send(callback, build_a2a_response(result))心跳:
REPORT 接口让 Hermes 定期上报状态(在线、负载等):
@scheduled(interval=60)
def heartbeat():
mq9_client.report({
"mailbox": inbox,
"status": "online",
"active_tasks": ctx.active_task_count,
"load": ctx.current_load
})几个具体的设计决策
决策 1:plugin 不依赖 a2a-sdk。
Issue #514 提案里要 a2a-sdk[http-server]。mq9-hermes plugin 应该不依赖这个。原因:
- a2a-sdk 是 pre-1.0,可能 break
- mq9 协议层不解析 A2A 内容,plugin 也不应该解析
- A2A 消息就是 JSON,Hermes 自己用 json 模块解析就够
plugin 的 mq9_call 接收一个 dict(A2A 消息),原样发送。plugin 的 serve_loop 收到一个 byte 串,调用 Hermes agent 时也只需要 dict。协议透传,不引入 SDK 依赖。
未来 a2a-sdk 稳定后,可以考虑加 optional 依赖做 message 校验,但不是必须。
决策 2:plugin 不起 HTTP server。
Issue #514 Phase 2 要起 HTTP server 暴露 /.well-known/agent.json 和 A2A 端点。mq9-hermes plugin 不需要。
Hermes 通过 mq9 注册中心暴露 AgentCard,通过 mq9 mailbox 接收任务。所有通信走 mq9,不需要直接监听端口。这让 plugin 部署变简单——Hermes 进程不需要新开端口、不需要配置 TLS、不需要担心防火墙。
决策 3:AgentCard 由 plugin 自动生成。
Hermes 已经有 toolsets 和 skills 的概念。plugin 在注册时遍历这些,生成 AgentCard 的 skills 字段:
def derive_skills_from_toolsets(ctx):
skills = []
for toolset_name, toolset in ctx.toolsets.items():
for tool in toolset.tools:
skills.append({
"id": f"{toolset_name}-{tool.name}",
"name": tool.display_name or tool.name,
"description": tool.description,
"tags": [toolset_name, *tool.tags],
"examples": tool.examples or []
})
return skills用户也可以在 plugin 配置里手动覆盖:
mq9:
agent_card:
name: "Custom Hermes Coder"
description: "Specialized coding agent"
skills:
- id: "rust-codegen"
name: "Rust code generation"
description: "Generate Rust code from natural language"
tags: ["rust", "codegen"]
examples: ["Write a Rust HTTP server"]自动生成做默认,手动配置做覆盖。
决策 4:和 Hermes 现有 delegate_tool 共存。
Hermes 已经有 delegate_tool(本地派生子 agent)。mq9_call 是远程调用。两者并存:
- 本地任务用
delegate_tool(快、无网络开销) - 跨机器/跨框架用
mq9_call - 文档里给清楚的判断指引
不要替代 delegate_tool,保持互补。
决策 5:plugin 的 minimal install。
plugin 应该能在没有 mq9 broker 的机器上"安装但不启用"。即配置里没指定 mq9.broker,plugin 静默跳过初始化,不报错。这样开发者可以默认装上,按需启用。
几个还没想清楚的问题
问题 1:Hermes 的 streaming 怎么映射到 mq9。
A2A 支持 SSE 流式响应。Hermes agent 也支持 token-by-token streaming。这两个流怎么经过 mq9 mailbox?
可能的方案:每个 token 一条消息,按 task_id 用 key 压缩(保留最新累积内容),或者保留全部消息让 client 拼接。前者节省存储但语义有损(中间 token 会被覆盖),后者完整但消息量大。
需要实测看效果。
问题 2:双向 multi-turn 怎么落地。
A2A 的 INPUT_REQUIRED 状态需要 server agent 暂停、等 client 提供输入、继续执行。在 Hermes 当前架构里,agent 是单 turn 处理的。要支持暂停和恢复,可能要改 Hermes 核心,或者 plugin 自己维护状态机。
这是 plugin 实现的难点之一。需要跟 NousResearch 的人讨论 Hermes 内部 API 是否支持这种 hook。
问题 3:和 Hermes 现有 messaging gateway 的关系。
Hermes 的 gateway 让人通过 Telegram 等平台和 agent 对话。mq9-hermes plugin 让 agent 和 agent 通信。两者是独立的还是有交集?
一个值得想的场景:人通过 Telegram 给 agent A 发消息,agent A 通过 mq9 找 agent B 协作,agent B 的回复经 mq9 回给 agent A,agent A 再通过 Telegram 回给人。这条链路 plugin 怎么支持?
可能需要在 mq9 消息里携带原始 messaging gateway 的上下文(来自哪个平台、哪个用户),让 agent B 知道任务的最终接收者。
问题 4:错误处理和可观测性。
Hermes 有自己的 telemetry plugin(结构化 JSON 日志、auto-rotating event log)。mq9 plugin 的事件(注册成功、订阅消息、调用远程)应该和 Hermes 现有 telemetry 集成。
具体怎么集成、需要 Hermes 暴露什么 hook,要看 NousResearch 的 plugin 系统设计。
问题 5:plugin 的语言。
Hermes 是 Python,plugin 系统也是 Python。mq9 SDK 当前是什么状态?如果只有 Rust SDK,需要 Python binding。
看 mq9 现状是 Python SDK 已经在做(属于 0.4.0 的工作之一)。但 plugin 用到的 mq9 客户端能力(subscribe、send、register、discover)需要确认 Python SDK 都覆盖了。
一个延伸:不只 Hermes
把 mq9-hermes plugin 这条路径推广,可以给其他 agent 框架做类似的适配器:
mq9-langchain # LangChain Tool 集成
mq9-autogen # AutoGen agent transport
mq9-crewai # CrewAI agent communication
mq9-openclaw # OpenClaw plugin
mq9-claude-code # Claude Code(如果它开放 plugin)每个适配器干的事都类似:把 mq9 的 mailbox 操作封装成框架原生 API、让框架的 agent 注册到 mq9 注册中心、让 agent 通过 mq9 收发 A2A 消息。
不同框架的 plugin 形态会不一样(Hermes 是 drop-in 目录,LangChain 是 pip 包,AutoGen 是配置项),但核心抽象是一致的:
common interface:
- register(agent_card)
- discover(query | tag)
- send(target_mailbox, a2a_message)
- subscribe(my_mailbox, handler)可以把这些封装成 mq9 的官方 SDK,每个框架的 plugin 只是 thin wrapper。这样维护成本低,跨框架行为一致。
落地路径
如果你想接手这个工作,建议的路径:
第一步:把 mq9 Python SDK 跑通。
确认 mq9 已经支持 Python 客户端的 register、discover、send、subscribe 这些核心操作。如果还有 gap,先补齐。
第二步:写一个最小 PoC plugin。
不集成 Hermes,只验证 plugin 形态:
- 一个独立 Python 脚本注册到 mq9,AgentCard 是写死的
- 另一个 Python 脚本通过 mq9_discover 找到这个 agent
- 第二个脚本通过 mq9_call 给第一个发消息
- 第一个脚本订阅 inbox 收消息、回复
这个 PoC 跑通后,证明 mq9 协议和 SDK 在真实双向通信场景下能工作。
第三步:fork hermes-agent,加 plugin。
按 Hermes plugin 系统的规范写:drop 进 ~/.hermes/plugins/mq9/、plugin.yaml 声明 hooks 和 tools、post_setup 启动注册、mq9_call 等工具实现。
第一版只做 Phase 1(client)+ Phase 2 minimal(被动 serve)。Phase 3 编排是后续的。
第四步:在真实场景测。
跑两个 Hermes 实例(Hermes-A 和 Hermes-B),通过 mq9 互相发现互相调用。验证场景:
- Hermes-A 启动注册自己
- Hermes-B 启动注册自己
- 用户在 Hermes-A 里说"找个会写 Python 的 agent 帮我写个 HTTP server"
- Hermes-A 调 mq9_discover 找到 Hermes-B
- Hermes-A 调 mq9_call 把任务发给 Hermes-B
- Hermes-B 处理完通过 mq9 回复
- Hermes-A 把结果给用户
跑通这个 e2e 场景后,可以发到 Hermes plugin 生态。
第五步:和 NousResearch 沟通。
把 PoC 和 demo 整理给 Issue #514 的维护者看。Issue 里他们已经在思考"应该等 SDK v1.0 还是基于 v0.3.x 现在做"——mq9 plugin 给了第三个选项:"不依赖 a2a-sdk,通过 mq9 transport 实现 A2A"。
他们可能感兴趣,也可能不感兴趣。无所谓,plugin 本身对 Hermes 用户有价值就够了。
最后
这篇文章的目的是把 mq9-hermes plugin 的思路讲清楚,不是给一个完整的实现方案。具体落地时会遇到很多文章里没覆盖的细节——Hermes plugin 系统的某个 API 不好用、A2A 的某个状态在 mailbox 上语义不直观、Python SDK 某个能力还没做完。这些都需要实际写代码时再处理。
如果你看完觉得思路 OK、想接手做一部分,可以从前面"落地路径"的某一步切入。每一步的边界都比较清楚,可以独立完成。如果某个具体环节卡住了,可以单独讨论。
