插件
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__.py 中 register_provider(ProviderProfile(...)) — 见模型提供商插件(使用单独的发现系统) |
插件发现
| 来源 | 路径 | 用例 |
|---|---|---|
| 捆绑 | <repo>/plugins/ | 随 Hermes 附赠 — 见内置插件 |
| 用户 | ~/.hermes/plugins/ | 个人插件 |
| 项目 | .hermes/plugins/ | 项目特定插件(需要 HERMES_ENABLE_PROJECT_PLUGINS=true) |
| pip | hermes_agent.plugins entry_points | 分发包 |
| Nix | services.hermes-agent.extraPlugins / extraPythonPackages | NixOS 声明式安装 — 见Nix 设置 |
名称冲突时后加载的源覆盖先加载的源,因此与捆绑插件同名的用户插件将替换它。
插件子类别
在每个源中,Hermes 还识别子类别目录,将插件路由到专门的发现系统:
| 子目录 | 内容 | 发现系统 |
||---|---|---|
| plugins/(根) | 通用插件——工具、钩子、斜杠命令、CLI 命令、打包技能 | PluginManager(kind: standalone 或 backend) |
| 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/langfuse、image_gen/openai、platforms/teams。该键(而非清单中的 name:)是传递给 hermes plugins enable … / disable … 的值,以及添加到 config.yaml 的 plugins.enabled 下的字符串。
插件是选择加入的(少数例外)
通用插件和用户安装的后端默认禁用——发现机制会找到它们(因此它们会出现在 hermes plugins 和 /plugins 中),但除非将插件名称添加到 ~/.hermes/config.yaml 的 plugins.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() 调用时发现并注册。用户通过 --provider 或 config.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_finalize | CLI/网关销毁活跃会话时(/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/ |
记忆提供商和上下文引擎是提供商插件——每种类型一次只能有一个活跃。模型提供商也是插件,但可同时加载多个;用户通过 --provider 或 config.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: command 在 tts.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:startup、session:start、agent:end、command:* 时触发) | 将 HOOK.yaml + handler.py 放入 ~/.hermes/hooks/<name>/ | 事件钩子 |
| Shell 钩子(在事件上运行 Shell 命令——通知、审计日志、桌面提醒) | 配置驱动 — 在 config.yaml 的 hooks: 下声明 | 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 list 在 Name 列中显示的内容。
交互式 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 enabled。hermes 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。
:::
请参阅**完整指南**了解处理函数契约、模式格式、钩子行为、错误处理和常见错误。