§18二十多个平台与会自生长的Gateway

Twenty-Plus Platforms and a Self-Growing Gateway

4月第一版写的是「6个一级平台、统一Gateway进程」,6月第二版写的是官方口径「23个消息平台」,到10月官方干脆不再给整数了。真正变了的其实不是数字,是加一个新平台的方式,从「改核心代码」

变成了「往一个目录里丢个文件夹」。

先说那个会被反复念叨的数字

从Telegram、Discord、Slack、WhatsApp、Signal、SMS、邮件,到钉钉、飞书、企业微信、微信、QQ、腾讯元宝,再到Microsoft Teams、Google Chat、LINE、Matrix、Home Assistant,到6月时的第23个ntfy,再到后来加的iMessage(通过Photon)、Raft、WhatsApp官方商业接口(这三个是v0.17)和Buzz(v0.20)。你能想到的主流IM,Hermes基本都能当成它的「前台」。

我得先帮你把这个数字祛魅。6月时官方确实有个整数,「23个消息平台」,来自v0.15.0发布说明里那句「ntfy as the 23rd messaging platform」。但口径一直有点浮动,官方文档把浏览器也算了进去,源码里另外还躺着IRC、API Server、Webhook这些接入通道。

到了v0.22.2,官方文档首页写的是「20+ platforms」,消息网关那页的能力对比表列了27行;我在§08截的浏览器面板上,聊天渠道页写的是「0 of 32 channels configured」。同一个版本,三个地方三个数,因为每处数的东西不一样(算不算webhook、算不算A2A这种给agent用的通道)。所以这一版我只说「二十多个」,要精确就说清楚是哪一处的数。

这些平台是分批长出来的,6月之前每一个官方都给了「第几个」的编号,时间线很清楚(下表是那段历史;之后官方不再编号)。

平台进来的版本官方计数
Google Chatv0.13.0(2026.5.7)第20个
LINE + SimpleX Chatv0.14.0(2026.5.16)累计到22个
Microsoft Teams(端到端打通)v0.14.0(2026.5.16)
ntfyv0.15.0(2026.5.28)第23个

4月到6月两个月里加了五个能对话的平台,6月到10月又加了五六个。第一版书那会儿连「第N个」这种官方计数都还没有,只写了6个一级加9个二级扩展。一个数字的膨胀不算什么,关键是膨胀的代价没跟着涨。这才是这一节真正想讲的。

从「改核心」到「丢文件夹」

旧书那个版本,加一个平台是件挺重的活。你得去核心代码里改一堆if/elif分支:消息进来要判断它从哪个平台来,消息出去要判断该往哪个平台发,配置解析、用户授权、状态展示,每一处都得为新平台改一行。平台越多,核心越臃肿,改一处怕碰坏另一处。

v0.16.0的做法不一样了。核心有个叫PlatformRegistry的注册表(在gateway/platform_registry.py里)。它是个单例,谁要加平台,就往这个注册表里登记一下自己。Gateway收到消息先查注册表,查不到才回落到老的内建路径。

新平台不再写进核心,而是住进plugins/platforms/目录,一个平台一个文件夹,里面放一个plugin.yaml和一个adapter.py 。插件启动时调用ctx.register_platform()自己注册进去。

新建一个文件夹

写adapter + plugin.yaml自注册进Registry

核心代码一行不改官方文档里那句话说得很直接:插件路径「requires zero changes to core Hermes code」。adapter创建、配置解析、用户授权、cron投递、消息路由、系统提示、状态展示、安装向导,这一整套插件系统全自动接管。

那一个平台到底要给Registry交代清楚哪些事?这就是PlatformEntry这个数据结构干的活。它把「Gateway想认识一个平台需要知道的一切」做成了声明式的字段清单。我挑几个有意思的列出来,你能感觉到设计者考虑得多细。

字段它解决什么问题
max_message_length这个平台单条消息最长多少,超了自动智能分块
pii_safe会话描述要不要脱敏(比如SMS的电话号码别明文显示)
platform_hint注入系统提示的平台指引,比如告诉Agent「你现在在IRC上,别用markdown」
standalone_sender_fncron任务和Gateway不在同一个进程时,临时开个连接把消息发出去
apply_yaml_config_fn让插件自己解析自己的配置,核心config.py不必懂每个平台的schema

你看最后那个字段就明白整个设计的用心了。核心代码刻意把自己变「笨」,它故意不去懂每个平台的配置长什么样。懂不懂是插件自己的事。核心只负责「有一个平台注册进来了,按它声明的规则办」。平台的复杂性被关进了各自的文件夹里,不会渗进核心。

这条线和这本书的主线「缰绳会自己长」是呼应的。Agent的能力会自己长(skill自改进),它的接入层也在自己长,多一个平台,核心不动一根毫毛。Gateway从一个「内置一堆平台的大进程」,长成了「一个带注册表的平台宿主」。

一个细节:6月时Discord、Home Assistant、Mattermost这几个,在老的内建枚举里有,在新的插件目录里也有同名,那是历史内建平台正一个个迁成插件的痕迹。到v0.22这件事基本迁完了:v0.18起Slack、钉钉、

WhatsApp、Matrix、飞书、Telegram、企业微信、邮件、短信都改成了随包附带的插件,Home Assistant干

脆搬去了插件目录要单独装。兼容路还留着:查不到注册表,就回落老路径。

平台越加越多,启动反而越来越快

按常理,平台越多,进程启动该越慢,要拉起的东西更多了。Hermes反着来。

窍门在懒加载。QQ和元宝这两个适配器特别重,元宝是逆向出来的私有协议加WebSocket栈,QQ要拉起分块上传的整套东西。以前它们在每次CLI调用时都会被急着导入,光这一下就吃掉约48毫秒、8MB内存,而你这次根本没用QQ。

现在改成了用到才加载。配合v0.14.0砍掉约19秒冷启动、v0.15.0每次对话少调用47%的函数,整个Gateway的性能故事就成了一句挺反直觉的话:平台越加越多,启动越来越快。

「Telegram开始,Discord继续」——这句话得改

跨平台对话连续性,是Hermes一直主打的卖点。旧书里只写了个结论:你在Telegram上聊到一半,换到Discord能接着聊。听起来很魔法。

但我把源码翻了一遍,发现这句话照字面理解是会出问题的,懂行的读者一抠就露馅。我得把真实机制讲清楚,不然这是个准确性地雷。

第一层,是会话身份。源码里有个函数专门生成「会话的唯一身份」,长这样:

agent:main:{platform}:dm:{chat_id}agent:main:{platform}:{chat_type}:{chat_id}

(中间那个main是默认profile的名字;如果你建了别的profile,这里换成agent:<profile名>: 。)

注意看,这个key是以平台名开头的。也就是说,同一个人在Telegram和在Discord,严格讲是两条不同的会话。它们并不是字面意义上「共用同一条对话历史」。

那连续性是怎么来的?靠的是另一套机制,源码里叫session mirroring(会话镜像)。当一条消息被发到某个平台时,系统会往目标会话的对话记录里追加一条「镜像记录」,让接收那一侧的Agent知道刚才发生了什么、发了什么。

所以真正撑起「跨平台连续」体验的,是三件事叠在一起。

同一个Agent,同一套记忆和Skill二十多个平台共享同一个Agent实例。你在哪个平台说的偏好,都进同一个记忆库。换平台,它对你的理解不会清零。

跨平台发消息时做transcript镜像消息跨平台流动时,内容会被镜像进目标会话的记录,对侧Agent因此有了上下文,不至于一脸懵。

桌面App还能跨profile引用会话新出的桌面App里可以用@session直接引用另一个会话,把不同来源的对话串起来。

讲清楚这层机制,不是为了显得严谨。是因为一旦你照着「同一条session无缝延续」的想象去用,碰到边界情况就会困惑:为什么有时候它好像「忘了」。理解了「共享记忆 + 镜像」这个真相,你才知道它的能力边界在哪。

注意

写这一章时我专门列了几个容易踩的坑:别照抄「Telegram开始Discord继续」的字面;平台数别写成一个整数,6月的「23」现在已经不对了,要写就说清楚是哪一处的数;GitHub星标这种高波动数字只写量级、注明日期,别写死。拿不准的对外声称,宁可写保守、写机制,也别写没核实的数字。

这意味着什么

把这一节收一下。平台的数字是表象,注册表才是里子。

一个产品愿意为了「加平台不改核心」去专门设计一套注册表机制,说明它赌的是平台数量还会长、而且要长很久。它不想让自己的核心在第30个、第50个平台时被改烂。这是一种对未来的预付:现在多写一层抽象,换将来无数次「丢个文件夹就完事」的轻松。

对你这个用户来说,好处很直接:你想接的那个小众IM,哪怕官方没做,社区也能用一个文件夹补上,不用等核心团队排期,更不用fork整个项目。接入的口子,被彻底打开了。

下一节我们看Gateway另一面的进化,它终于长出了一张脸:原生桌面App和浏览器管理面板。从「躲在后台的进程」,变成你能直接点的东西。