第三方插件不再钻进主进程:plugins.isolation: host 把插件关进独立进程


假设你在同一台机器上跑了好几个 profile:自己的、团队共用的、还有一个专门给客户演示的。某天你装了一个社区插件,它只是个「把工单系统接到聊天里」的小工具——但从它被 import 的那一刻起,它就和你的所有会话、provider 密钥、内存里的 API Key 住在同一个 Python 进程里。插件作者没恶意也不代表它不会出事:一个全局变量、一次 monkeypatch、一个没关掉的线程,都可能碰到不该碰的东西。

Hermes 现在给了你一个开关:把第三方插件搬出主进程。配置只有一行 plugins.isolation: host,插件本身一行都不用改。

默认行为没有变,新行为是「一个 profile 一个宿主进程」

先说清楚现状,免得你误以为装完更新就变了:默认值仍然是 in_process,也就是插件和以前一样被导入 Hermes 进程。只有你显式打开 host,行为才会改变。

plugins:
  isolation: host        # 默认: in_process
  host:
    launcher: []         # 可选:宿主进程的 argv 前缀,例如接一个沙箱执行器

打开之后,每个 profile 会得到一个按需启动的独立 Python 进程——插件宿主(plugin host),里面只导入这个 profile 自己安装的插件。插件通过一条私有管道和 Hermes 通信。内置(bundled)插件不受影响,继续在主进程里跑。

这里有个容易被忽略但很关键的设计:宿主进程只带着这个 profile 自己的环境变量和密钥启动。以前一个插件能看到进程里所有 profile 共同的凭据,现在它看到的是自己这一份。在多租户、多用户的部署里,这就是「一个租户的插件代码不会和另一个租户的数据待在同一块内存里」的实际含义。

插件不用改:同一个 ctx,跨进程序列化

大多数隔离方案最烦人的地方是——你得改代码。这个不是。

插件在宿主机里拿到的 ctx 还是那个 ctx。工具、钩子(hooks)、斜杠命令、CLI 命令、技能、以及各类 provider 对象(图像/视频生成、web 搜索、浏览器、TTS/STT、memory、context engine、cron),注册方式和以前完全一样。Hermes 这一侧会注册对应的代理对象,它们在需要时回调宿主进程。官方文档把这层叫 RemotePluginContext。

它还是可重入的:插件在处理一个工具调用时,可以反过来调用 ctx.dispatch_tool、ctx.get_config、ctx.llm 回到 Hermes,而 Hermes 正在等它——就像插件还在本地一样。对写插件的人来说,这是一次「免费的」隔离。

代价有一份清单,而且是公开的:有些能力本质上必须在进程内才能工作,这类插件在 host 模式下会以明确原因拒绝加载,而不是悄悄少几个功能。目前文档列出的包括:网关平台适配器(register_platform)、审批传输通道、Telegram/平台处理器、会自建 SDK 客户端的 model-provider profile(create_client)、流式 dashboard 端点,以及会 monkeypatch Hermes 模块的插件。这些请继续用 isolation: in_process。

崩溃被关在宿主机里

隔离最直接的价值不是安全,是稳定。开启 host 之后:

  • 插件的模块永远不会进入 Hermes 进程,所以它读不到别的 profile 的内存数据,也补丁不到 Hermes 内部;
  • 插件崩溃或退出,死的是宿主进程,不是 Hermes。正在进行的那次调用返回一个工具错误,Hermes 重启宿主并重新加载它的插件(重启次数有上限),其余会话照常。

如果你遇到过「装了个插件,整个 gateway 跟着歇菜」这种事,这一条就是给你准备的。

怎么开、怎么确认、怎么锁死

开启就是前面那段 config.yaml。开启后有两处可以立刻验证:

# 检查某个插件在 host 模式下能不能跑,不能跑会说明原因
hermes plugins validate <dir>

# 看某个具体插件跑在哪儿
hermes plugins show <name>

host.launcher 是留给更严场景的:它接受一段 argv 前缀,用来把宿主进程塞进你自己的沙箱执行器里。文档同时建议把 agent 的终端也放到隔离后端(Docker、SSH 等)上,否则「插件被隔离了、但 agent 自己的终端还能读操作员的文件」,隔离就只做了一半。

最后是共享部署里最现实的问题:plugins.isolation 属于普通 profile 配置,能改某个 profile 的 config.yaml 的人,就能把它关掉。如果你隔离的对象正是这些 profile 的使用者,就应该把它钉在 managed scope 里(root 拥有的 /etc/hermes/config.yaml),managed 值优先于每个 profile 自己的配置,hermes config set 也拒绝改它:

# /etc/hermes/config.yaml(root 拥有,本机所有 profile 都会读)
plugins:
  isolation: host
  host:
    launcher: [...]

生态适配到什么程度了

一个很实际的问题是:真开了 host,我装的那一堆插件还能用吗?

文档给出了当时的统计:官方插件目录中 348 个条目里有 299 个在 host 模式下无需改动即可运行。这正是前面那份「跑不了」清单的价值——它把不确定性收敛成了一个可以提前检查的短名单,而不是让你开完开关一个个去试。

什么时候能用上

这个功能于 2026 年 10 月 3 日合入主分支。当前最新正式版是 v0.21.5(tag v2026.9.24),所以它现在只在 main 上,还没进任何正式发布版本。跟踪 main 的安装下一次 hermes update 就会带上;正式用户会等下一个 tag。

官方插件系统的整体介绍(安装来源、启用白名单、子类别目录)见插件目录指南,插件被目录下架与供应链的问题可以看这篇 CI 门禁的记录;如果你还在处理「插件改完要重启才生效」的流程,插件热重载那篇更对路。

小结

一句话:以前「装插件」等于「把别人的代码请进自己的进程」,现在你可以让它待在外面,而它自己甚至不知道区别。对单机个人用户,这行配置大概率不需要动;对把多个人、多个租户放在同一台机器上的部署,它是那种「平时想不起来、出事时才发现该早开」的开关。

如果你正在用共享部署,建议做两件事:先 hermes plugins validate 扫一遍现装插件,再把 plugins.isolation: host 写进 managed scope,而不是留给每个 profile 自己决定。