online branch: main

2026-08-01

RSS llms.txt GitHub

【Hermes】Hook系统:扩展点必须有边界

$
Hermes Agent 生命周期Hook与扩展边界示意图

启动时检查磁盘、工具执行前记审计日志、会话结束后做清理,这些需求都发生在固定生命周期节点。每次都修改核心循环,扩展会逐渐变成分支和补丁。

Hermes 提供两套 hook,再用 BOOT.md 把启动自检变成可读的任务清单。

这篇只回答一个问题:怎样在启动、会话和工具调用等时刻加入定制逻辑,又不反复修改核心代码?

Hermes 给出的答案是:Hermes 用文件化 Gateway hooks 处理会话与平台事件,用 BOOT.md 执行启动自检,用代码注册的 Plugin hooks 处理工具和 LLM 细粒度事件。

先按事件粒度选择扩展点

Gateway hooks 关注平台启动、会话开始与结束、Agent 处理和斜杠命令,适合运维、监控和粗粒度自动化。Plugin hooks 位于工具与 LLM 调用前后,适合开发级定制。

两套机制看似重复,实际服务不同用户和部署方式。把所有事件统一到一种复杂注册方式,会抬高简单运维场景的门槛。

BOOT.md 把启动动作交给 Agent

BOOT.md 是绑定 gateway:startup 的内置 hook。启动时,Agent 读取清单并使用终端、浏览器或 MCP 工具执行检查,比固定 shell 脚本拥有更广的动作组合。

启动阶段没有用户在场,因此清单不能包含需要回答的问题。自检必须能自行完成、失败或记录结果。

Hook 能观察和阻断,不一定能改写

pre_tool_call 可以记录审计,也可通过返回 block 阻止执行;原材料指出直接修改收到的 args 不会改变实际工具参数。

扩展点必须明确可读、可写、可阻断的边界。否则开发者会以为修改了上下文对象,实际主流程没有变化,形成隐蔽错误。

扩展失败不能拖垮主链路

HookRegistry 捕获异常,避免一个坏 hook 让 Gateway 停止。工具调用前的 hook 也不应同步访问慢 API,否则每次工具执行都被额外延迟。

快速写本地记录或入队,耗时工作异步处理,是生命周期扩展的基本纪律。可插入不代表可以无限占用主链路时间。

三类需求应该挂在哪里

如果需求是“Gateway 启动时检查昨晚任务和磁盘”,应写入 BOOT.md。它绑定 gateway:startup,由 Agent 使用已有工具执行,但不能要求用户在场确认。

如果需求是“会话开始、结束或重置时记录事件”,适合 Gateway hooks。HOOK.yaml 声明事件,handler.py 提供 handle 入口,系统在平台与会话节点触发。

如果需求是“每次 terminal 调用前记审计日志”,则使用 Plugin hook 的 pre_tool_call;需要记录结果或耗时,使用 post_tool_call。这些细粒度 hook 位于核心执行链旁边,必须快速完成。

选择扩展点时,可以用三个问题判断:事件发生在哪一层、是否需要阻断、失败能否影响主流程。HookRegistry 默认隔离异常,但业务上的阻断语义仍要由对应 hook 明确返回。

三个容易走偏的地方

  • 在 pre_tool_call 中同步调用慢外部服务,所有工具都被阻塞。
  • handler.py 没有名为 handle 的入口,自动发现后无法执行。
  • 在 BOOT.md 中安排必须由用户回答的步骤。

产品经理可以怎样评审

评审这项能力,可以先检查三条:

  1. Hook 设计先定义触发时机、允许动作和失败语义,再开放注册能力。
  2. 粗粒度运维事件与细粒度开发事件可以共享理念,不必强求一套接口。
  3. 任何主链路 hook 都要有执行时间和异常隔离约束。

本文只基于原材料列出的两套 hook 行为,不推断热更新、事务性和完整插件治理。