Skip to content

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_discovera2a_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/mailboxmq9s://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:

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: 300

post_setup hook:

Hermes 启动时自动跑——读 config、连 mq9 broker、申请 inbox mailbox、注册自己的 AgentCard、起后台订阅 loop 处理外部消息。开发者不用写代码,配置 ~/.hermes/config.yaml 加几行就启用。

yaml
plugins:
  enabled:
    - mq9

mq9:
  broker: "mq9://broker.internal:4332"
  agent_id: "hermes-prod-001"
  ttl: 600
  capabilities:
    streaming: true
    push_notifications: true

mq9_discover 工具:

Hermes agent 在对话中遇到"找一个能写 Python 代码的 agent"这种意图,自动调 mq9_discover

python
@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 工具:

python
@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:

python
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 定期上报状态(在线、负载等):

python
@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 字段:

python
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 配置里手动覆盖:

yaml
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、想接手做一部分,可以从前面"落地路径"的某一步切入。每一步的边界都比较清楚,可以独立完成。如果某个具体环节卡住了,可以单独讨论。

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