不用重启网关:Hermes 让运行中新加的 profile 立刻上线

凌晨两点,你用 hermes profile create 建好了第三个机器人的 profile,粘好 bot token,然后在 Telegram 里发了句 hello……没有任何回应。桌面还显示这个 profile 的网关是停的。你去查文档,答案是“重启网关”。可另外两个 profile 正在跑长任务,重启就意味着把它们一起打断。
这个两难现在不用再二选一了。2026-09-12 合并进 main 的 PR #109239,让运行中的多路复用网关能自己发现新出现的 profile 并直接把它托管起来——不用重启,也不用打扰已经在干活的那几个。
先说清楚:多路复用网关是什么
默认情况下,一个 profile 就是一套独立配置加一个机器人。想让它在线,就得为它单独跑一个网关进程(hermes -p 名字 gateway run)。profile 一多,进程就多,内存、日志、端口都要各管一份。
多路复用(multiplex)网关换了个做法:一个网关进程同时托管多个 profile 的机器人适配器。打开方式是配置里的 gateway.multiplex_profiles: true;之后不用再为每个 profile 单独起进程,所有机器人共享同一个网关,而各 profile 仍然是彼此独立的运行时(记忆、SQLite、agent 缓存各自分开)。
这个设计的代价落在“发现”这一步。多路复用器此前只在启动的那一刻枚举一次 profiles/ 目录:_start_secondary_profile_adapters 按当时的列表建适配器,_record_served_profiles 记下“我托管了谁”,cron 也复用同一份 profile_homes 快照。启动之后新建的 profile 不在名单里——没有适配器、没有轮询,它的机器人永远连不上,桌面显示该 profile 的网关已停止;反过来,被删掉的 profile 会一直留在 served_profiles 里。
在“一个机器人一个进程”的世界里这不算问题:新 bot 就新起一个进程。可在多路复用下,profiles/ 目录是会变的,而网关只看过它一次。新增 profile 的合理预期本来就是“建完就能用”,不该要用户为了一个不存在的适配器去重启整个网关,顺带打断别人的长任务。
三种情况,三种处理
PR #109239(2026-09-12 合并,+641 / −43,涉及 27 个文件)把多路复用器从“启动时枚举一次”改成“持续对账”:运行中的网关不断把 profiles/ 目录和自己手上的“已托管集合”比对,再按差异行动。三种最常见的情况各自有明确路径。
| 情况 | 触发方式 | 网关做什么 |
|---|---|---|
| 新建 profile | hermes profile create、dashboard 的 POST /api/profiles、Desktop 的 New profile |
立即建适配器并托管;同时会去 ping 网关控制套接字(新增 rescan-profiles 动词) |
| 建完之后才补凭据 | 被托管 profile 的 config.yaml / .env 发生变化 |
以文件签名变化为信号触发重扫,在该 profile 自己的运行时作用域下构建适配器;已经活着的平台跳过,重复 token 命中同一个守卫 |
| 删除 profile | 从 profiles/ 目录移除 |
拆除适配器、取消路由、释放 pairing / busy / agent-cache / SQLite / 记忆句柄 |
新建 → 立即托管。 三条入口都通:命令行 hermes profile create、dashboard 的 POST /api/profiles、以及 Desktop 里的 New profile。它们建完 profile 后会去 ping 网关控制套接字,用一个新增的 rescan-profiles 动词让网关马上重扫一次,而不是等下一轮。
建完之后才补凭据。 先建 profile、再粘 bot token 是最常见的顺序,而凭据属于“启动之后才出现的配置”。这种情况靠被托管 profile 的 config.yaml 或 .env 的 mtime 变化来触发重扫,适配器会在该 profile 自己的运行时作用域下构建。已经活着的平台会被跳过;重复的 token 命中与启动时同一个重复凭据守卫——搁置并给出警告,绝不产生第二个 poller,否则同一个 bot token 会被两个 poller 抢消息。
删除 → 拆除并取消路由。 重连被取消,pairing / busy / agent-cache / SQLite / 记忆句柄被释放,该 profile 的 /p/名称/ 路径随之消失,served_profiles 同步更新。不会留下半死不活的适配器继续占内存、继续攥着 bot token。
那个 30 秒的兜底重扫
ping 控制套接字是快路径,但它会失败:网关可能正在启动、控制套接字还没监听,或者干脆没拿到应答。所以这次改动带了一个受监管(supervised)的 watcher,每 30 秒重扫一次 profiles/(源码常量 _PROFILE_RESCAN_INTERVAL_SECS = 30.0)。也就是说,即时托管是尽力而为的快路径,正确性最终由周期性重扫兜住。
控制套接字客户端是 rescan_gateway_profiles(home, timeout=8.0),底层向网关发送 rescan-profiles,正常返回一个包含 served_profiles、added、removed 等字段的结果。这里有个刻意的降级设计:如果没有网关应答,或者应答的是旧版本网关,这个函数返回 None —— 调用方不会报错,而是退回周期性重扫,在命令行上就是保留那句“需要重启”的提醒。反过来也一样:老版本 Hermes 不会因为新客户端多问了一句话就出问题。
重扫本身很便宜,因为签名检测只看两个文件——config.yaml 与 .env,比较它们的 (mtime_ns, size),源码常量是 _PROFILE_SIGNATURE_FILES = ("config.yaml", ".env")。这也正是“补 token 能触发重扫”成立的原因:你粘贴 token 保存后,文件 mtime 变了,签名跟着变。其它文件的变动(多出来的日志、缓存之类)不会造成误触发。
实现落在新模块 gateway/run_profile_reconcile.py,以 GatewayProfileReconcileMixin 的形式挂进网关运行时——它更像一个只盯着 profiles/ 目录的常驻对账器,而不是又一套独立的进程管理逻辑。
顺带一起对齐的:cron、MCP 与日志路由
新增的 profile 不只是“多一个机器人”。cron 的定时器这次改用了活的枚举器,所以新建 profile 的定时任务会正常触发,不会因为“它不在启动快照里”而被跳过;新 profile 同时也会走 MCP 发现与日志路由。这几条加在一起,才让“建完就能用”这句话真的站得住——否则你的新 bot 能回消息,但它自己的定时任务永远不动。
想让每个 profile 用自己那个 bot 跑定时任务,让 profile 用自己的 bot 跑 cron 那篇给了完整示例。
其他 profile 正在干的活不会被打断
热托管最该问的问题是:会不会顺手把别人重启一遍?答案是不会。整段对账过程中,其它已经托管的 profile 全程不受影响——同样的 pid、同样的轮询计数,正在进行的对话不中断。对账只对差异项动手:新增的加进来,删除的拆掉,已托管且没变化的原样留着。
这也是它和“重启网关”最本质的区别。重启是把整个进程连同所有适配器推倒重来;对账是只改动需要改动的那一处。对多路复用网关来说,这才是“热”字应有的含义。
你会看到的界面变化
最直观的信号是那个“需要重启”的横幅消失了。平台配置接口(PUT 到 /api/messaging/platforms/,末尾带平台 id)现在会返回一个 hot_served 字段;当它为 true 时,Desktop(6 种语言)与 dashboard 都会跳过“需要重启”的横幅。
hermes profile create 也会直接确认热托管的结果:只有当多路复用器确实没接上这个 profile 时,才会保留重启提醒——比如网关根本没在跑,或者控制套接字没有应答。
怎么用
- 确认配置里开着多路复用:
gateway.multiplex_profiles: true。 - 建 profile:
hermes profile create nightshift。 - 把 bot token 写进它的
.env或config.yaml。先粘后建、先建后粘都可以;后粘的情况由签名变化触发适配器构建。 - 用
hermes gateway status检查,确认这个 profile 已经出现在托管列表里。 - 如果它没出现:先等最多 30 秒让兜底重扫跑一轮。仍然不出现,就用
hermes gateway restart兜底。旧版本网关不认识rescan-profiles这个动词,这种情况下除了重启没有别的路径——所以先确认你的 Hermes 是不是新到带这次改动。
小结
这次改动没有加一条新命令,改的是多路复用网关对“变化”的反应方式:启动时枚举一次,变成运行中持续对账。新建立刻托管、补凭据重建适配器、删除即时拆除路由,30 秒重扫兜住快路径失灵的情况,已托管的 profile 全程不动。想验证的话,tests/gateway/test_multiplex_hot_serve.py 里有三个用例正好对应这三条路径:新建会被托管、补凭据会建适配器、删除会取消路由。
多路复用的整体配置看 多 profile 实例那篇;想让哪些 profile 的机器人被托管、哪些不放行,网关 profile 白名单那篇讲得更细。
最后把版本说清楚:这次改动是 2026-09-12 合并进 main 的,属于 v0.21.2(2026-09-11 发布)之后进入仓库的新改动,还没有随任何正式版本发布。想立刻用上,就跟着 main 走。