Hermes Agent 源码解析(八):插件系统 —— 三层插拔架构
我刚开始读 Hermes 源码时,以为插件就是一套东西——一个 PluginManager 全管了。翻完代码才发现,它其实是三个各自独立、互不感知的子系统。
三层总览
1. 通用插件(General Plugins)生命周期钩子 + 自定义工具 + CLI 子命令入口: hermes_cli/plugins.py2. Model Provider 插件推理后端适配器(OpenRouter / Anthropic / Gemini…)入口: plugins/model-providers/+providers/3. Memory Provider 插件记忆后端适配器(Honcho / Mem0 / Supermemory…)入口: plugins/memory/
第一层:通用插件(hermes_cli/plugins.py)
PluginManager 从四个来源发现插件:
1. Bundled — <repo>/plugins/<name>/(随仓库发布)2. User — ~/.hermes/plugins/<name>/(用户安装)3. Project — ./.hermes/plugins/<name>/(项目级,需HERMES_ENABLE_PROJECT_PLUGINS开启)4. Pip — 暴露 hermes_agent.pluginsentry-point 的 pip 包
后三个覆盖前面的同名插件,允许用户替换内置插件。
插件清单与注册
每个插件目录必须包含:

register(ctx) 中可以做的事:
defregister(ctx): ctx.register_hook("pre_tool_call", my_pre_tool_hook) ctx.register_hook("post_tool_call", my_post_tool_hook) ctx.register_hook("on_session_start", my_start_hook) ctx.register_tool(name="my_tool", toolset="custom", ...)支持的钩子
• pre_tool_call— 工具执行前• post_tool_call— 工具执行后• pre_llm_call— LLM 调用前• post_llm_call— LLM 调用后• on_session_start— 会话开始• on_session_end— 会话结束
钩子的调用位置:
• Pre/Post tool → model_tools.py• Lifecycle → run_agent.py
发现时机坑
discover_plugins() 只在 model_tools.py 被 import 时副作用触发。如果代码路径在没 import model_tools.py 前就读取插件状态,需要显式调用 discover_plugins()(它是幂等的)。
第二层:Model Provider 插件
每个推理后端(OpenRouter、Anthropic、Gemini、Bedrock、DeepSeek……)是一个独立的插件目录:
plugins/model-providers/ ├── openrouter/ │ ├── __init__.py ← register_provider(ProviderProfile(...)) │ └── ... ├── anthropic/ ├── gmi/ ├── deepseek/ └── ... 10+独立发现系统
这不是通过通用 PluginManager 加载的。providers/__init__.py._discover_providers() 是独立的、惰性的系统 —— 首次调用 get_provider_profile() 或 list_providers() 时才扫描。
扫描顺序:
1. Bundled: <repo>/plugins/model-providers/<name>/2. User: $HERMES_HOME/plugins/model-providers/<name>/3. Legacy: <repo>/providers/<name>.py(向后兼容)
用户插件同名覆盖内置 —— 第三方可替换任意内置提供商配置。
ProviderProfile
每个提供商通过 register_provider(ProviderProfile(...)) 注册自己的配置:
ProviderProfile( name="openrouter", display_name="OpenRouter", models=["claude-opus-4", "gpt-4o", ...], requires_env=["OPENROUTER_API_KEY"],)第三层:Memory Provider 插件
plugins/memory/ 目录下的插件实现 MemoryProvider ABC(agent/memory_provider.py):
classMemoryProvider:asyncdefsync_turn(self, turn_messages: list): ...asyncdefprefetch(self, query: str) -> str: ...asyncdefshutdown(self): ...asyncdefpost_setup(self, hermes_home, config): ...内置提供者:Honcho、Mem0、Supermemory、ByteRover、Hindsight、Holographic、OpenViking、RetainDB。
政策(2026 年 5 月):不再接受新的内置 memory provider PR。新后端应作为独立插件仓库发布。
核心规则
插件不得修改核心文件。 如果插件需要框架未暴露的能力,扩展通用插件表面(新钩子、新 ctx 方法),永远不要将插件专用逻辑硬编码到核心。
这条规则在 PR #5295 后确立,当时移除了 main.py 中 95 行硬编码的 honcho argparse 代码。
欢迎一起讨论! 三层插件系统有三套发现机制,你觉得应该统一吗?你是怎么设计插件系统的?
📢 关注我,获取最新开源更新日志和开发者工具动态。如果觉得这篇文章有用,欢迎转发给身边的开发者朋友。
下一篇讲 Skills —— Agent 的自我进化闭环。
夜雨聆风