插件

Hermes 拥有插件系统,用于添加自定义工具、钩子和集成,无需修改核心代码。

如果您想为自己、团队或单个项目创建自定义工具,这通常是正确的路径。开发者指南中的添加工具页面适用于位于 tools/toolsets.py 中的内置 Hermes 核心工具。

构建 Hermes 插件 — 包含完整可运行示例的分步指南。

快速概览

将一个包含 plugin.yaml 和 Python 代码的目录放入 ~/.hermes/plugins/

~/.hermes/plugins/my-plugin/
├── plugin.yaml      # 清单文件
├── __init__.py      # register() — 将模式连接到处理函数
├── schemas.py       # 工具模式(LLM 所见)
└── tools.py         # 工具处理函数(调用时执行)

启动 Hermes——您的工具会与内置工具一同出现。模型可立即调用它们。

最小工作示例

以下是一个完整的插件,添加了一个 hello_world 工具并通过钩子记录每次工具调用。

~/.hermes/plugins/hello-world/plugin.yaml

name: hello-world
version: "1.0"
description: 一个最小的示例插件

~/.hermes/plugins/hello-world/__init__.py

"""最小的 Hermes 插件——注册一个工具和一个钩子。"""
 
import json
 
 
def register(ctx):
    # --- 工具: hello_world ---
    schema = {
        "name": "hello_world",
        "description": "返回给定姓名的友好问候。",
        "parameters": {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "要问候的姓名",
                }
            },
            "required": ["name"],
        },
    }
 
    def handle_hello(params, **kwargs):
        del kwargs
        name = params.get("name", "World")
        return json.dumps({"success": True, "greeting": f"Hello, {name}!"})
 
    ctx.register_tool(
        name="hello_world",
        toolset="hello_world",
        schema=schema,
        handler=handle_hello,
        description="返回给定姓名的友好问候。",
    )
 
    # --- 钩子: 记录每次工具调用 ---
    def on_tool_call(tool_name, params, result):
        print(f"[hello-world] 工具被调用: {tool_name}")
 
    ctx.register_hook("post_tool_call", on_tool_call)

将两个文件放入 ~/.hermes/plugins/hello-world/,重启 Hermes,模型即可立即调用 hello_world。每次工具调用后钩子会打印一行日志。

项目本地插件位于 ./.hermes/plugins/ 下默认是禁用的。仅在受信任的仓库中通过设置 HERMES_ENABLE_PROJECT_PLUGINS=true 来启用它们。

插件能做什么

以下是插件 register(ctx) 函数中可用的每个 ctx.* API。

能力方式
添加工具ctx.register_tool(name=..., toolset=..., schema=..., handler=...)
添加钩子ctx.register_hook("post_tool_call", callback)
添加斜杠命令ctx.register_command(name, handler, description) — 在 CLI 和网关会话中添加 /name
从命令分发工具ctx.dispatch_tool(name, args) — 使用自动连接的父代理上下文调用已注册的工具
添加 CLI 命令ctx.register_cli_command(name, help, setup_fn, handler_fn) — 添加 hermes <plugin> <subcommand>
注入消息ctx.inject_message(content, role="user") — 见注入消息
附带数据文件Path(__file__).parent / "data" / "file.yaml"
打包技能ctx.register_skill(name, path) — 命名空间为 plugin:skill,通过 skill_view("plugin:skill") 加载
环境变量门控requires_env: [API_KEY] 在 plugin.yaml 中 — hermes plugins install 时提示
通过 pip 分发[project.entry-points."hermes_agent.plugins"]
注册网关平台(Discord、Telegram、IRC 等)ctx.register_platform(name, label, adapter_factory, check_fn, ...) — 见添加平台适配器
注册图片生成后端ctx.register_image_gen_provider(provider) — 见图片生成提供商插件
注册视频生成后端ctx.register_video_gen_provider(provider) — 见视频生成提供商插件
注册上下文压缩引擎ctx.register_context_engine(engine) — 见上下文引擎插件
注册记忆后端plugins/memory/<name>/__init__.py 中继承 MemoryProvider — 见记忆提供商插件(使用单独的发现系统)
运行宿主拥有的 LLM 调用ctx.llm.complete(...) / ctx.llm.complete_structured(...) — 借用用户的活跃模型+认证进行一次性补全,支持可选的 JSON 模式验证。见插件 LLM 访问
注册推理后端(LLM 提供商)plugins/model-providers/<name>/__init__.pyregister_provider(ProviderProfile(...)) — 见模型提供商插件(使用单独的发现系统)

插件发现

来源路径用例
捆绑<repo>/plugins/随 Hermes 附赠 — 见内置插件
用户~/.hermes/plugins/个人插件
项目.hermes/plugins/项目特定插件(需要 HERMES_ENABLE_PROJECT_PLUGINS=true
piphermes_agent.plugins entry_points分发包
Nixservices.hermes-agent.extraPlugins / extraPythonPackagesNixOS 声明式安装 — 见Nix 设置

名称冲突时后加载的源覆盖先加载的源,因此与捆绑插件同名的用户插件将替换它。

插件子类别

在每个源中,Hermes 还识别子类别目录,将插件路由到专门的发现系统:

| 子目录 | 内容 | 发现系统 | ||---|---|---| | plugins/(根) | 通用插件——工具、钩子、斜杠命令、CLI 命令、打包技能 | PluginManager(kind: standalonebackend) | | plugins/platforms/<name>/ | 网关通道适配器(ctx.register_platform()) | PluginManager(kind: platform,深一层) | | plugins/image_gen/<name>/ | 图片生成后端(ctx.register_image_gen_provider()) | PluginManager(kind: backend,深一层) | | plugins/memory/<name>/ | 记忆提供商(继承 MemoryProvider) | plugins/memory/__init__.py 中的自有加载器(kind: exclusive——一次仅一个活跃) | | plugins/context_engine/<name>/ | 上下文压缩引擎(ctx.register_context_engine()) | plugins/context_engine/__init__.py 中的自有加载器(一次仅一个活跃) | | plugins/model-providers/<name>/ | LLM 提供商配置(register_provider(ProviderProfile(...))) | providers/__init__.py 中的自有加载器(在首次 get_provider_profile() 调用时惰性扫描) |

位于 ~/.hermes/plugins/model-providers/<name>/~/.hermes/plugins/memory/<name>/ 的用户插件会覆盖同名的捆绑插件——在 register_provider() / register_memory_provider() 中采用后写入者获胜。放入目录即可替换内置,无需编辑仓库。

子类别插件在 hermes plugins list 和交互式 hermes plugins UI 中以路径派生键显示——例如 observability/langfuseimage_gen/openaiplatforms/teams。该键(而非清单中的 name:)是传递给 hermes plugins enable … / disable … 的值,以及添加到 config.yamlplugins.enabled 下的字符串。

插件是选择加入的(少数例外)

通用插件和用户安装的后端默认禁用——发现机制会找到它们(因此它们会出现在 hermes plugins/plugins 中),但除非将插件名称添加到 ~/.hermes/config.yamlplugins.enabled,否则包含钩子或工具的插件不会加载。这阻止了未经您明确同意运行的第三方代码。

plugins:
  enabled:
    - my-tool-plugin
    - disk-cleanup
  disabled:       # 可选的拒绝列表——如果名称同时出现在两个列表中,始终优先
    - noisy-plugin

三种切换状态的方式:

hermes plugins                    # 交互式切换(空格键勾选/取消勾选)
hermes plugins enable <name>      # 添加到允许列表
hermes plugins disable <name>     # 从允许列表中移除并添加到禁用列表

执行 hermes plugins install owner/repo 后,会询问 Enable 'name' now? [y/N] — 默认否。对于脚本安装,使用 --enable--no-enable 跳过提示。

允许列表不控制的内容

以下几类插件绕过 plugins.enabled——它们是 Hermes 内置功能的一部分,如果默认被门控会破坏基本功能:

插件种类激活方式
捆绑的平台插件(IRC、Teams 等,位于 plugins/platforms/ 下)自动加载,使每个附带的网关通道可用。实际通道通过 config.yaml 中的 gateway.platforms.<name>.enabled 开启。
捆绑的后端(位于 plugins/image_gen/ 等下的图片生成提供商)自动加载,使默认后端”开箱即用”。通过 config.yaml 中的 <category>.provider 选择(例如 image_gen.provider: openai)。
记忆提供商plugins/memory/全部被发现;恰好一个活跃,由 config.yaml 中的 memory.provider 选择。
上下文引擎plugins/context_engine/全部被发现;一个活跃,由 config.yaml 中的 context.engine 选择。
模型提供商plugins/model-providers/plugins/model-providers/ 下的所有捆绑提供商在首次 get_provider_profile() 调用时发现并注册。用户通过 --providerconfig.yaml 一次选择一个。
通过 pip 安装的 backend 插件通过 plugins.enabled 选择加入(与通用插件相同)。
用户安装的平台(位于 ~/.hermes/plugins/platforms/ 下)通过 plugins.enabled 选择加入——第三方网关适配器需要明确同意。

简而言之:捆绑的”始终可用”基础设施自动加载;第三方通用插件是选择加入的。 plugins.enabled 允许列表专门用于用户放入 ~/.hermes/plugins/ 的任意代码的门控。

现有用户的迁移

当您升级到具有选择加入插件的 Hermes 版本(配置模式 v21+)时,已安装在 ~/.hermes/plugins/ 下但尚未在 plugins.disabled 中的任何用户插件会自动被祖父条款纳入 plugins.enabled。您现有的设置继续工作。捆绑的独立插件不会被祖父化——即使是现有用户也必须明确选择加入。(捆绑的平台/后端插件从不需要祖父化,因为它们从未被门控。)

可用的钩子

插件可以为这些生命周期事件注册回调。有关完整详情、回调签名和示例,请参阅**事件钩子页面**。

钩子触发时机
pre_tool_call任何工具执行之前
post_tool_call任何工具返回之后
pre_llm_call每轮一次,在 LLM 循环之前——可返回 {"context": "..."}将上下文注入用户消息
post_llm_call每轮一次,在 LLM 循环之后(仅成功轮次)
on_session_start新会话创建时(仅第一轮)
on_session_end每次 run_conversation 调用结束时 + CLI 退出处理程序
on_session_finalizeCLI/网关销毁活跃会话时(/new、GC、CLI 退出)
on_session_reset网关换入新会话密钥时(/new/reset/clear、闲置轮换)
subagent_stop每个子代理在 delegate_task 完成后一次
pre_gateway_dispatch网关收到用户消息后,在认证+分发之前。返回 `{“action”: “skip"

插件类型

Hermes 有四种插件:

类型功能选择位置
通用插件添加工具、钩子、斜杠命令、CLI 命令多选(启用/禁用)~/.hermes/plugins/
记忆提供商替换或增强内置记忆单选(一个活跃)plugins/memory/
上下文引擎替换内置上下文压缩器单选(一个活跃)plugins/context_engine/
模型提供商声明推理后端(OpenRouter、Anthropic 等)多注册,通过 --provider / config.yaml 选择plugins/model-providers/

记忆提供商和上下文引擎是提供商插件——每种类型一次只能有一个活跃。模型提供商也是插件,但可同时加载多个;用户通过 --providerconfig.yaml 一次选择一个。通用插件可以任意组合启用。

可插拔接口——各找对应的文档

上表显示了四种插件类别,但在”通用插件”中,PluginContext 暴露了多个不同的扩展点——Hermes 也在 Python 插件系统之外接受扩展(配置驱动的后端、Shell 钩子命令、外部服务器等)。使用此表找到适合您要构建的文档:

| 想添加… | 方式 | 编写指南 | ||---|---|---| | 一个 LLM 可调用的工具 | Python 插件 — ctx.register_tool() | 构建 Hermes 插件 · 添加工具 | | 一个生命周期钩子(pre/post LLM、会话开始/结束、工具过滤) | Python 插件 — ctx.register_hook() | 钩子参考 · 构建 Hermes 插件 | | 一个 CLI/网关的斜杠命令 | Python 插件 — ctx.register_command() | 构建 Hermes 插件 · 扩展 CLI | | 一个 hermes <thing>子命令 | Python 插件 — ctx.register_cli_command() | 扩展 CLI | | 一个插件附带的捆绑技能 | Python 插件 — ctx.register_skill() | 创建技能 | | 一个推理后端(LLM 提供商:兼容 OpenAI、Codex、Anthropic-Messages、Bedrock) | 提供商插件 — 在 plugins/model-providers/<name>/register_provider(ProviderProfile(...)) | 模型提供商插件 · 添加提供商 | | 一个网关通道(Discord / Telegram / IRC / Teams 等) | 平台插件 — 在 plugins/platforms/<name>/ctx.register_platform() | 添加平台适配器 | | 一个记忆后端(Honcho、Mem0、Supermemory 等) | 记忆插件 — 在 plugins/memory/<name>/ 中继承 MemoryProvider | 记忆提供商插件 | | 一个上下文压缩策略 | 上下文引擎插件 — ctx.register_context_engine() | 上下文引擎插件 | | 一个图片生成后端(DALL·E、SDXL 等) | 后端插件 — ctx.register_image_gen_provider() | 图片生成提供商插件 | | 一个视频生成后端(Veo、Kling、Pixverse、Grok-Imagine、Runway 等) | 后端插件 — ctx.register_video_gen_provider() | 视频生成提供商插件 | | 一个TTS 后端(任何 CLI——Piper、VoxCPM、Kokoro、xtts、语音克隆脚本等) | 配置驱动 — 在 config.yaml 中使用 type: commandtts.providers.<name> 下声明 | TTS 设置 | | 一个STT 后端(自定义 Whisper 二进制、本地 ASR CLI) | 配置驱动 — 设置 HERMES_LOCAL_STT_COMMAND 环境变量为 Shell 模板 | 语音消息转录 (STT) | | 通过 MCP 的外部工具(文件系统、GitHub、Linear、Notion、任何 MCP 服务器) | 配置驱动 — 在 config.yaml 中声明 mcp_servers.<name> 并设置 command: / url:。Hermes 自动发现服务器的工具并与内置工具一起注册。 | MCP | | 额外的技能来源(自定义 GitHub 仓库、私有技能索引) | CLI — hermes skills tap add <repo> | 技能中心 · 发布自定义 tap | | 网关事件钩子(在 gateway:startupsession:startagent:endcommand:* 时触发) | 将 HOOK.yaml + handler.py 放入 ~/.hermes/hooks/<name>/ | 事件钩子 | | Shell 钩子(在事件上运行 Shell 命令——通知、审计日志、桌面提醒) | 配置驱动 — 在 config.yamlhooks: 下声明 | Shell 钩子 |

:::note 并非所有东西都是 Python 插件。某些扩展表面故意使用配置驱动的 Shell 命令(TTS、STT、Shell 钩子),这样您已有的任何 CLI 都无需编写 Python 即可成为插件。其他的则是代理连接并自动注册工具的外部服务器(MCP)。还有一些是放入式目录(网关钩子),拥有自己的清单格式。请根据适合您用例的集成风格选择正确的扩展表面;上表中的编写指南涵盖了占位符、发现和示例。 :::

NixOS 声明式插件

在 NixOS 上,可以通过模块选项声明式地安装插件——无需 hermes plugins install。请参阅 Nix 设置指南 了解完整详情。

services.hermes-agent = {
  # 目录插件(包含 plugin.yaml 的源码树)
  extraPlugins = [ (pkgs.fetchFromGitHub { ... }) ];
  # 入口点插件(pip 包)
  extraPythonPackages = [ (pkgs.python312Packages.buildPythonPackage { ... }) ];
  # 在配置中启用
  settings.plugins.enabled = [ "my-plugin" ];
};

声明式插件以 nix-managed- 前缀符号链接——它们与手动安装的插件共存,并在从 Nix 配置中移除时自动清理。

管理插件

hermes plugins                                       # 统一交互式 UI
hermes plugins list                                  # 表格: 已启用 / 已禁用 / 未启用
hermes plugins install user/repo                     # 从 Git 安装,然后提示 Enable? [y/N]
hermes plugins install user/repo --enable            # 安装并启用(无提示)
hermes plugins install user/repo --no-enable         # 安装但保持禁用(无提示)
hermes plugins update my-plugin                      # 拉取最新
hermes plugins remove my-plugin                      # 卸载
hermes plugins enable my-plugin                      # 添加到允许列表(平面插件)
hermes plugins enable observability/langfuse         # 添加到允许列表(子类别插件)
hermes plugins disable my-plugin                     # 从允许列表移除并添加到禁用列表

对于子类别目录下的插件(例如 plugins/observability/langfuse/plugins/image_gen/openai/),使用完整的 <category>/<plugin> 键——这正是 hermes plugins listName 列中显示的内容。

交互式 UI

运行不带参数的 hermes plugins 会打开一个组合交互界面:

Plugins
  ↑↓ 导航  SPACE 切换  ENTER 配置/确认  ESC 完成

  General Plugins
 → [✓] my-tool-plugin — 自定义搜索工具
   [ ] webhook-notifier — 事件钩子
   [ ] disk-cleanup — 临时文件自动清理 [bundled]
   [ ] observability/langfuse — 将轮次/LLM 调用/工具追踪到 Langfuse [bundled]

  Provider Plugins
     Memory Provider          ▸ honcho
     Context Engine           ▸ compressor
  • 通用插件部分 — 复选框,用 SPACE 切换。勾选 = 在 plugins.enabled 中,未勾选 = 在 plugins.disabled 中(明确关闭)。
  • 提供商插件部分 — 显示当前选择。按 ENTER 进入单选选择器,选择一个活跃的提供商。
  • 捆绑插件在同一个列表中显示 [bundled] 标签。

提供商插件选择保存到 config.yaml

memory:
  provider: "honcho"      # 空字符串 = 仅内置
 
context:
  engine: "compressor"    # 默认内置压缩器

已启用 vs 已禁用 vs 未启用

插件处于三种状态之一:

状态含义plugins.enabled 中?plugins.disabled 中?
enabled下次会话加载
disabled明确关闭——即使也在 enabled 中也不会加载(无关)
not enabled已发现但从未选择加入

新安装或捆绑插件的默认状态是 not enabledhermes plugins list 显示所有三种不同状态,因此您可以区分哪些是明确关闭的,哪些只是在等待启用。

在运行中的会话中,/plugins 显示当前已加载的插件。

注入消息

插件可以使用 ctx.inject_message() 向活跃对话注入消息:

ctx.inject_message("来自 webhook 的新数据已到达", role="user")

签名: ctx.inject_message(content: str, role: str = "user") -> bool

工作原理:

  • 如果代理处于空闲状态(等待用户输入),消息将作为下一个输入排队并开始新的一轮。
  • 如果代理处于轮次中(正在运行),消息会中断当前操作——与用户键入新消息并按 Enter 相同。
  • 对于非 "user" 角色,内容前会加上 [role](例如 [system] ...)。
  • 如果消息成功排队返回 True,如果没有可用的 CLI 引用(例如在网关模式下)返回 False

这使得远程控制查看器、消息桥接或 webhook 接收器等插件能够从外部源向对话中馈送消息。

:::note inject_message 仅在 CLI 模式下可用。在网关模式下,没有 CLI 引用,方法返回 False。 :::

请参阅**完整指南**了解处理函数契约、模式格式、钩子行为、错误处理和常见错误。