Hermes Agent v0.2.0 真正的头条功能,不是某个模型集成,也不是技能系统。是那个多平台消息网关——一个 Hermes 进程同时听着七个聊天平台的动静:Telegram、Discord、Slack、WhatsApp、Signal、邮件(IMAP/SMTP)、Home Assistant。到 4 月 8 日那版发出来的时候,这个列表已经长到了十三个,中间陆续加上了 Matrix、飞书、企业微信、Mattermost、钉钉,还有走 Twilio 的短信。
这里面很容易被忽略的一点是:写一个 Telegram bot 并不难。难的是同时跑七个聊天集成,而且还要让它们共用同一个 agent、同一份记忆、同一个工具注册表——真正的架构工作都压在这里。这篇文章讲 Hermes 到底是怎么把这件事做出来的。
最朴素的做法,以及它为什么行不通
如果让你来做"一个既能在 Telegram 上回话、也能在 Discord 上回话的 AI bot",第一反应大概是起两个独立的 bot。每个 bot 自己一个进程、一套数据库、一份状态,底下都调同一家 LLM 的接口。概念上说,Telegram 那边的用户和 Discord 那边的用户面对的是同一个 agent;但实际上他们面对的是两个互相不认识的 agent。
现在大部分 bot 项目就是这么做的,它糟糕的地方要过一阵才显出来:
- •记忆会分叉。 用户在 Telegram 上说过自己对花生过敏,跑到 Discord 上问做饭问题时,这条信息就消失了。同一件事,agent 得被教两遍。
- •工具状态会漂。 你在 Slack 里建的一个 cron 任务,用户在 Telegram 里查 cron 的时候看不见。会话历史是碎的。共享资源的授权 token(比如一个 Notion 集成),得在每个 bot 里各装一遍。
- •运维成本成倍涨。 N 个 bot 意味着 N 个进程、N 个服务、N 份日志流、N 份配置,以及 N 个可能坏的点。复杂度跟平台数量成正比。
- •安全规则会碎掉。 在一个 bot 上把危险命令的审批策略收紧了之后,你还得记得去别的 bot 上同步一遍。安全策略的漂移是默认状态。
Hermes 选了另一条路:只有一个 agent 进程、一个会话数据库、一个记忆存储、一个工具注册表。各家平台只是适配器——一层薄薄的"前门",把消息倒进和倒出那个共用的 agent。
这套网关长什么样
Hermes 网关在内部分三层。
最底层是 agent 本体——就是你在 CLI 模式下跑的那个 Hermes 内核。它对聊天平台的存在一无所知。它收到消息,跑自己的 agent 循环(LLM 调用、工具调用、记忆查询、checkpoint),然后吐出回应。它对外唯一的接口是一套基于队列的 API。
中间层是网关内核——会话管理、用户/平台路由、审批分发、cron 调度、流式输出、媒体处理都在这一层。它负责把"来自平台 X、用户 Y、频道 Z 的一条消息"翻译成"在会话 S 里、带这组权限的一次 agent 运行"。同时它也统一处理那些跨平台的公共事务:限流、防刷、消息去重、基于不活动的超时、审批按钮的路由、跨平台的状态。
最上面是各家平台的适配器。一个平台一个:Telegram 适配器、Discord 适配器、Slack 适配器,依此类推。每个适配器的职责很窄——连上自己那个平台(用轮询、长轮询、WebSocket、webhook,或者它家 SDK 偏好的任何方式),把平台原生的事件翻译成网关的内部消息格式,再把网关输出的回应翻译回那个平台吃得下的样子(Markdown、MarkdownV2、mrkdwn、Discord 富嵌入、Matrix HTML、Slack blocks、邮件 MIME 全都在里面)。
适配器故意做得很小。加一个新平台(v0.4.0 那会儿一位社区贡献者用不到 300 行 Python 加上了 Mattermost)主要就是把那家 SDK 的事件映射到网关内部的消息形状,再反着映射一次。
会话在跨平台的时候是怎么运作的
Hermes 里的一个会话,是一串有自己记忆窗口、工具状态和运行历史的对话。只看一个平台,会话的对应关系很自然——一个 Telegram 聊天是一个会话,一个 Discord 频道是一个,一个 Slack thread 是一个。一跨到多平台上,事情就有趣起来了。
Hermes 默认的做法是把"平台 + 会话"这个组合当成一个独立会话。你在 Telegram 上跟 bot 的私聊、在 Slack thread 里跟 bot 的对话、在 Discord 频道里跟 bot 的对话,是三段各跑各上下文的独立会话,但是它们通过那层可插拔的记忆提供者共用同一份长期记忆(v0.7.0 之后默认是 Honcho)。所以那些你希望能跨会话带过去的信息——"这个用户对花生过敏"、"这个用户叫 Alice"、"这个用户的项目叫 Atlas"——是挂在记忆层上的;而每段对话的短期上下文,仍然紧紧锁在你当下用的那个平台里。
就是这套设计,让同一个助手在每个平台上用起来都像同一个助手,同时又不至于把每条消息都变成一场纠缠不清的全局广播。
为什么"线程内分用户的会话"那么重要
v0.4.0 里 Hermes 加了一个特性,叫线程内默认分用户会话——在群聊里,同一个线程下,每个用户各有各的会话。这件事听起来像个小改动,但凡你在群里跑过多用户 bot 的都知道,它是个大事。
在群里如果没有按人隔开的会话,每条消息都属于同一段共享对话。Alice 问了个问题,三十秒后 Bob 又问了一个不相干的问题,bot 的上下文就变成了两人混在一起的一团糨糊。答案开始串味,Alice 的记忆里被塞进了 Bob 的数据,群里人越多,网关模式表现越差。
有了线程内分用户会话之后,Alice 和 Bob 在同一个线程里各有各的私有会话。他们在聊天界面上仍然能看到彼此的消息,但 agent 对这两人维护的是两套独立的上下文和两份独立的记忆写入。v0.8.0 开始,这成了所有网关平台的默认行为。这种修复在你没被另一种做法烧过之前是完全看不见的,一旦被烧过一次,你就再也不想回去了。
这套网关到底是干嘛用的
把这套架构多盯一会儿,你会慢慢觉得网关不再像"一种在聊天平台上跑 bot 的方式",而越来越像它本来的样子:一层协调层,连接的一头是人(不管这个人现在用的是哪个 app),另一头是一个共享记忆和工具的、独立的 AI 助手。
聊天平台不是产品。它们是入口。产品是躲在入口后面的那个助手,以及"你完全不用去想自己现在走的是哪个入口"这件事本身。
这才是 Hermes 第一天就发出来的那个功能。后面四个礼拜发生的一切,都是这层协调层一点点学会新本事的故事。