插件
Hermes 提供了完整的插件系统,你可以在不改动核心代码的前提下,为它添加自定义工具、生命周期钩子和外部集成。
→ 构建一个 Hermes 插件 — 一份带完整可运行示例的分步指南。
快速概览
只要把一个目录放进 ~/.hermes/plugins/,并在里面放好 plugin.yaml 和对应的 Python 代码,它就会被 Hermes 识别为插件:
~/.hermes/plugins/my-plugin/
├── plugin.yaml # manifest
├── __init__.py # register() — wires schemas to handlers
├── schemas.py # tool schemas (what the LLM sees)
└── tools.py # tool handlers (what runs when called)
重新启动 Hermes 后,你的工具就会和内置工具一起出现,模型也能立即调用它们。
最小可运行示例
以下是一个完整的插件示例,它添加了一个 hello_world 工具,并通过钩子记录每次工具调用。
~/.hermes/plugins/hello-world/plugin.yaml
name: hello-world
version: "1.0"
description: A minimal example plugin
~/.hermes/plugins/hello-world/__init__.py
"""Minimal Hermes plugin — registers a tool and a hook."""
def register(ctx):
# --- Tool: hello_world ---
schema = {
"name": "hello_world",
"description": "Returns a friendly greeting for the given name.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name to greet",
}
},
"required": ["name"],
},
}
def handle_hello(params):
name = params.get("name", "World")
return f"Hello, {name}! 👋 (from the hello-world plugin)"
ctx.register_tool("hello_world", schema, handle_hello)
# --- Hook: log every tool call ---
def on_tool_call(tool_name, params, result):
print(f"[hello-world] tool called: {tool_name}")
ctx.register_hook("post_tool_call", on_tool_call)
把这些文件放进 ~/.hermes/plugins/hello-world/ 后,重启 Hermes,模型就能直接调用 hello_world。示例中的钩子还会在每次工具执行后打印一条日志。
位于 ./.hermes/plugins/ 下的项目级本地插件默认是禁用的。只有在你确认仓库可信时,才建议在启动 Hermes 前设置 HERMES_ENABLE_PROJECT_PLUGINS=true 来启用它们。
插件能做什么
| 能力 | 实现方式 |
|---|---|
| 添加工具 | ctx.register_tool(name, schema, handler) |
| 添加钩子 | ctx.register_hook("post_tool_call", callback) |
| 添加 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" |
| 打包技能 | 在加载时将 skill.md 复制到 ~/.hermes/skills/ |
| 根据环境变量控制启用 | requires_env: [API_KEY] 在 plugin.yaml 中 — 在 hermes plugins install 期间提示输入 |
| 通过 pip 分发 | [project.entry-points."hermes_agent.plugins"] |
插件发现机制
| 来源 | 路径 | 使用场景 |
|---|---|---|
| 用户 | ~/.hermes/plugins/ | 个人插件 |
| 项目 | .hermes/plugins/ | 项目专用插件(需设置 HERMES_ENABLE_PROJECT_PLUGINS=true) |
| pip | hermes_agent.plugins entry_points | 分发的包 |
可用钩子
插件可以为以下生命周期事件注册回调。完整说明、回调签名和示例请见 事件钩子页面。
| 钩子 | 触发时机 |
|---|---|
pre_tool_call | 任意工具执行前 |
post_tool_call | 任意工具返回后 |
pre_llm_call | 每轮开始前触发一次,可返回 {"context": "..."} 来向用户消息注入上下文 |
post_llm_call | 每轮结束后触发一次(仅成功轮次) |
on_session_start | 新会话创建时触发(仅第一轮) |
on_session_end | 每次 run_conversation 调用结束时,以及 CLI 退出处理时触发 |
插件类型
Hermes 有三种类型的插件:
| 类型 | 功能 | 选择方式 | 位置 |
|---|---|---|---|
| 通用插件 | 添加工具、钩子、CLI 命令 | 多选(可启用/禁用) | ~/.hermes/plugins/ |
| 记忆提供商 | 替换或扩展内置记忆功能 | 单选(仅一个激活) | plugins/memory/ |
| 上下文引擎 | 替换内置上下文压缩器 | 单选(仅一个激活) | plugins/context_engine/ |
记忆提供商和上下文引擎都属于提供商类插件,每种类型在同一时间只能有一个处于激活状态;通用插件则可以自由组合启用。
管理插件
hermes plugins # unified interactive UI
hermes plugins list # table view with enabled/disabled status
hermes plugins install user/repo # install from Git
hermes plugins update my-plugin # pull latest
hermes plugins remove my-plugin # uninstall
hermes plugins enable my-plugin # re-enable a disabled plugin
hermes plugins disable my-plugin # disable without removing
交互式界面
直接运行不带参数的 hermes plugins,会打开统一的交互式界面:
Plugins
↑↓ navigate SPACE toggle ENTER configure/confirm ESC done
General Plugins
→ [✓] my-tool-plugin — Custom search tool
[ ] webhook-notifier — Event hooks
Provider Plugins
Memory Provider ▸ honcho
Context Engine ▸ compressor
- 通用插件部分 —— 以复选框形式展示,按空格键即可切换启用状态
- 提供商插件部分 —— 显示当前选择。按回车后可进入单选列表,挑选一个当前生效的提供商
提供商类插件的选择会被保存到 config.yaml:
memory:
provider: "honcho" # empty string = built-in only
context:
engine: "compressor" # default built-in compressor
禁用通用插件
被禁用的插件仍然保留在本地,只是在加载阶段会被跳过。禁用列表会写入 config.yaml 的 plugins.disabled:
plugins:
disabled:
- my-noisy-plugin
在运行中的会话里,你也可以通过 /plugins 查看当前已经加载的插件。
注入消息
插件可通过 ctx.inject_message() 将消息注入到当前对话中:
ctx.inject_message("New data arrived from the webhook", role="user")
签名: ctx.inject_message(content: str, role: str = "user") -> bool
它的行为规则如下:
- 如果代理处于 空闲状态(等待用户输入),这条消息会排队成为下一次输入,并触发新一轮对话。
- 如果代理处于 运行中,这条消息会打断当前操作,效果等同于用户手动输入一条新消息并按下回车。
- 对于非
"user"的角色,内容前会加上[role](例如[system] ...)。 - 若消息成功排队,返回
True;若无 CLI 引用可用(如网关模式),则返回False。
因此,远程控制面板、消息桥接器、Webhook 接收器这类插件,都可以把外部事件直接注入当前对话。
inject_message 仅在 CLI 模式下可用。在网关模式下,没有 CLI 引用,该方法将返回 False。
参见 完整指南,了解处理器契约、模式格式、钩子行为、错误处理和常见陷阱。