事件钩子
Hermes 有三套 hook 系统,可在关键生命周期节点执行自定义代码:
| System | Registered via | Runs in | Use case |
|---|---|---|---|
| Gateway hooks | ~/.hermes/hooks/ 下的 HOOK.yaml + handler.py | 仅 Gateway | 日志、告警、webhook |
| Plugin hooks | 在 plugin 中使用 ctx.register_hook() | CLI + Gateway | 工具拦截、指标、护栏 |
| Shell hooks | ~/.hermes/config.yaml 中的 hooks: 配置脚本 | CLI + Gateway | 阻断、自动格式化、上下文注入 |
三套系统都是非阻塞的,hook 报错只会记录日志,不会让智能体崩溃。
网关事件钩子
Gateway hooks 会在 网关 运行期间自动触发(Telegram、Discord、Slack、WhatsApp 等),且不会阻塞主 agent 流程。
创建 Hook
每个 hook 对应 ~/.hermes/hooks/ 下的一个目录,其中包含两个文件:
~/.hermes/hooks/
└── my-hook/
├── HOOK.yaml
└── handler.py
HOOK.yaml
name: my-hook
description: Log all agent activity to a file
events:
- agent:start
- agent:end
- agent:step
handler.py
import json
from datetime import datetime
from pathlib import Path
LOG_FILE = Path.home() / ".hermes" / "hooks" / "my-hook" / "activity.log"
async def handle(event_type: str, context: dict):
entry = {
"timestamp": datetime.now().isoformat(),
"event": event_type,
**context,
}
with open(LOG_FILE, "a") as f:
f.write(json.dumps(entry) + "
")
规则:
- 处理函数必须命名为
handle - 参数为
event_type和context - 可以是
async def或普通def - 所有错误都会被捕获并记日志
可用事件
包括 gateway:startup、session:start、session:end、session:reset、agent:start、agent:step、agent:end、command:* 等。
示例
- BOOT.md 内置 hook:gateway 启动时自动检查
~/.hermes/BOOT.md - 长任务告警:当 agent 超过 10 步时通知 Telegram
- 命令使用日志:追踪 slash 命令使用情况
- 会话启动 webhook:新会话创建时 POST 到外部服务
工作方式
- Gateway 启动时扫描
~/.hermes/hooks/ - 自动加载带
HOOK.yaml+handler.py的目录 - 按声明事件注册处理函数
- 在对应生命周期节点触发
- hook 出错只会记录日志,不会影响智能体
Gateway hooks 只在 gateway 中生效。CLI 不会加载它们。若要让 hooks 在 CLI 和 网关 中都工作,请使用 plugin hooks。
插件钩子
Plugins 可以注册在 CLI 与 gateway 中都生效的 hooks:
def register(ctx):
ctx.register_hook("pre_tool_call", my_tool_observer)
ctx.register_hook("post_tool_call", my_tool_logger)
ctx.register_hook("pre_llm_call", my_memory_callback)
ctx.register_hook("post_llm_call", my_sync_callback)
ctx.register_hook("on_session_start", my_init_callback)
ctx.register_hook("on_session_end", my_cleanup_callback)
通用规则:
- 回调始终接收 keyword arguments,应当接受
**kwargs - 回调崩溃时只会被跳过,不会影响其他 hooks 或智能体主流程
- 只有两个 hook 的返回值会影响行为:
pre_tool_call可以阻断工具调用,pre_llm_call可以注入上下文。其他 hooks 都是观察型的。
快速参考
| Hook | Fires when | Returns |
|---|---|---|
pre_tool_call | 工具执行前 | {"action": "block", "message": str} 可阻断调用 |
post_tool_call | 工具返回后 | ignored |
pre_llm_call | 每轮开始前 | {"context": str} 可向用户消息前追加上下文 |
post_llm_call | 每轮成功结束后 | ignored |
on_session_start | 新会话创建时 | ignored |
on_session_end | 会话结束时 | ignored |
on_session_finalize | CLI / 网关 销毁活跃会话时 | ignored |
on_session_reset | 网关 切换到新 session key 时 | ignored |
subagent_stop | delegate_task 子智能体退出后 | ignored |
pre_tool_call
在每次工具执行之前触发。适合做日志、审计、危险操作拦截、限流和按用户策略阻断。
post_tool_call
在每次工具执行之后触发。适合收集指标、记录结果、统计成功率 / 失败率。
pre_llm_call
每轮只触发一次,发生在 LLM 循环开始前。它是唯一一个可注入上下文的 hook:
return {"context": "Recalled memories:
- User likes Python"}
注入位置始终是用户消息,而不是系统提示,从而保留 prompt cache。
post_llm_call
每轮成功结束后触发一次。适合把对话同步到外部记忆系统、记录响应摘要或做后处理。
on_session_start
新会话第一次创建时触发一次。适合初始化会话级状态、预热缓存、记录会话开始。
on_session_end
每次 run_conversation() 结束时都会触发,无论成功、失败还是中断。适合刷盘、清理资源、记录持续时间。
on_session_finalize
当 CLI 或 网关 销毁当前活跃会话时触发,例如 /new、空闲回收或 CLI 退出。这是丢弃会话身份前最后一次刷状态的机会。
on_session_reset
当 网关 为某个聊天分配新的 session key 时触发,例如 /new、/reset、/clear。适合重建 session_id 相关缓存。
subagent_stop
每个 delegate_task 子智能体结束后触发一次。适合记录 orchestration 活动、计费、审计子任务结果。
Shell 钩子
你可以在 ~/.hermes/config.yaml 的 hooks: 中声明脚本型 hooks。对应事件触发时,Hermes 会把它们作为子进程执行。这样无需编写 Python 插件,也能完成阻断、自动格式化、上下文注入等逻辑。
适合 Shell hooks 的场景包括:
- 阻断工具调用:拒绝危险
terminal命令 - 在工具调用后运行脚本:自动格式化、触发 CI、记录日志
- 向下一轮 LLM 注入上下文:例如追加
git status - 观察生命周期事件:例如子智能体结束时写日志
配置格式
hooks:
<event_name>:
- matcher: "<regex>"
command: "<shell command>"
timeout: <seconds>
hooks_auto_accept: false
JSON 协议
每次事件触发时,Hermes 会把一个 JSON payload 通过 stdin 传给脚本,并从 stdout 读取 JSON 响应。pre_tool_call 可返回阻断指令,pre_llm_call 可返回 {"context": "..."} 注入上下文。
同意模型
每个唯一 (event, command) 组合首次使用时都会提示用户批准,随后决策会保存在 ~/.hermes/shell-hooks-allowlist.json。如果是非 TTY 运行(gateway、cron、CI),需要通过 --accept-hooks、HERMES_ACCEPT_HOOKS=1 或 hooks_auto_accept: true 跳过交互确认。
hermes hooks CLI
| Command | What it does |
|---|---|
hermes hooks list | 列出已配置 hooks、matcher、timeout 和授权状态 |
hermes hooks test <event> | 用模拟 payload 测试 hooks |
hermes hooks revoke <command> | 撤销某个命令的授权 |
hermes hooks doctor | 检查可执行位、allowlist、mtime 漂移、JSON 输出有效性等 |
安全性
Shell hooks 会以当前用户完整权限运行,因此应把 hooks: 视为高权限配置:
- 只运行你自己写的或完全审查过的脚本
- 最好把脚本集中放在
~/.hermes/agent-hooks/ - 拉取共享配置后先运行
hermes hooks doctor - 团队共用配置时,应像审查 CI 配置一样审查
hooks:变更
顺序与优先级
Python plugin hooks 与 shell hooks 都会流经同一个 invoke_hook() 分发器。Python 插件先注册,shell hooks 后注册,因此若两者都在 pre_tool_call 中尝试阻断,Python 版本优先。