词元之母TOK.MOM - 平台充值汇率 1:1 即 1 人民币充值到账 1 美元,支持一个 Key 调用近 600+ 海内外模型,限时特价模型低至 1 折,欢迎上岸!
| 系统 | 注册方式 | 运行环境 | 使用场景 |
|---|---|---|---|
| Gateway hooks | ~/.hermes/hooks/ 下的 HOOK.yaml + handler.py | 仅 Gateway | 日志、告警、webhook |
| Plugin hooks | 插件中的 ctx.register_hook() | CLI + Gateway | 工具拦截、指标采集、护栏 |
| Shell hooks | ~/.hermes/config.yaml 中 hooks: 块指向的 shell 脚本 | CLI + Gateway | 用于阻断、自动格式化、上下文注入的即插即用脚本 |
~/.hermes/hooks/ 下的一个目录,包含两个文件:~/.hermes/hooks/
└── my-hook/
├── HOOK.yaml # 声明要监听的事件
└── handler.py # Python 处理函数events 列表决定哪些事件会触发你的处理器。可以订阅任意事件组合,包括 command:* 这样的通配符。handleevent_type(字符串)和 context(字典)async def 或普通 def——两者均可| 事件 | 触发时机 | Context 键 |
|---|---|---|
gateway:startup | Gateway 进程启动 | platforms(活跃平台名称列表) |
session:start | 新消息会话创建 | platform、user_id、session_id、session_key |
session:end | 会话结束(重置前) | platform、user_id、session_key |
session:reset | 用户执行 /new 或 /reset | platform、user_id、session_key |
agent:start | Agent 开始处理消息 | platform、user_id、session_id、message |
agent:step | 工具调用循环的每次迭代 | platform、user_id、session_id、iteration、tool_names |
agent:end | Agent 完成处理 | platform、user_id、session_id、message、response |
command:* | 任意斜杠命令执行 | platform、user_id、command、args |
command:* 的处理器会在任何 command: 事件(command:model、command:reset 等)触发时执行。通过单个订阅即可监控所有斜杠命令。~/.hermes/BOOT.md 放置一个 Markdown 检查清单,让 agent 在每次 gateway 启动时执行一次。适用于"每次启动时检查隔夜 cron 失败情况,若有失败则在 Discord 上通知我",或"汇总过去 24 小时的 deploy.log 并发布到 Slack #ops"等场景。~/.hermes/BOOT.md 放置一个包含自然语言启动指令的文件。gateway:startup 的 gateway hook,它会生成一个一次性 agent,使用 gateway 已解析的模型和凭据,执行 BOOT.md 中的指令。[SILENT] 约定,让 agent 在没有内容需要汇报时选择不发送消息。~/.hermes/BOOT.md。像给人类助手下达指令一样编写:~/.hermes/hooks/boot-md/
├── HOOK.yaml
└── handler.py~/.hermes/hooks/boot-md/HOOK.yaml~/.hermes/hooks/boot-md/handler.py_resolve_gateway_model() 读取 gateway 当前配置的模型。_resolve_runtime_agent_kwargs() 以与普通 gateway 轮次相同的方式解析 provider 凭据——包括 API 密钥、base URL、OAuth token 和凭据池。AIAgent() 会回退到内置默认值,并在任何非默认端点上返回 401。Running BOOT.md (N chars),随后是 boot-md completed: ...(agent 执行内容的摘要)或 boot-md completed (nothing to report)(agent 回复了 [SILENT])。~/.hermes/BOOT.md 即可禁用检查清单——hook 保持加载状态,但在文件不存在时会静默跳过。datetime.now().weekday() 进行判断("如果是周一,还需检查每周部署日志")。指令是自由格式文本,agent 能推理的内容都可以使用。STARTUP.md、MORNING.md 等),并为每个文件注册独立的 hook 目录。AIAgent,直接通过 httpx 在处理器中发送固定通知。更轻量、更快速,且无 provider 依赖。HookRegistry.discover_and_load() 扫描 ~/.hermes/hooks/HOOK.yaml + handler.py 的子目录都会被动态加载hooks.emit() 触发所有匹配的处理器register() 函数中的 ctx.register_hook() 以编程方式注册。**kwargs 以保持向前兼容性——未来版本可能会在不破坏插件的情况下添加新参数。pre_tool_call 可以阻断工具,pre_llm_call 可以注入上下文到 LLM 调用中。其他所有 hook 均为即发即忘的观察者。| Hook | 触发时机 | 返回值 |
|---|---|---|
pre_tool_call | 任意工具执行前 | {"action": "block", "message": str} 用于否决调用 |
post_tool_call | 任意工具返回后 | 忽略 |
pre_llm_call | 每轮一次,工具调用循环前 | {"context": str} 用于在用户消息前追加上下文 |
post_llm_call | 每轮一次,工具调用循环后 | 忽略 |
on_session_start | 新会话创建(仅第一轮) | 忽略 |
on_session_end | 会话结束 | 忽略 |
on_session_finalize | CLI/gateway 销毁活跃会话(刷新、保存、统计) | 忽略 |
on_session_reset | Gateway 换入新会话 key(如 /new、/reset) | 忽略 |
subagent_stop | delegate_task 子 agent 退出 | 忽略 |
pre_gateway_dispatch | Gateway 收到用户消息,认证和分发前 | {"action": "skip" | "rewrite" | "allow", ...} 用于影响流程 |
pre_approval_request | 危险命令需要用户审批,提示/通知发送前 | 忽略 |
post_approval_response | 用户响应审批提示(或超时) | 忽略 |
transform_tool_result | 任意工具返回后,结果交还给模型前 | str 替换结果,None 保持不变 |
transform_terminal_output | terminal 工具内部,截断/ANSI 剥离/脱敏前 | str 替换原始输出,None 保持不变 |
transform_llm_output | 工具调用循环完成后,最终响应交付前 | str 替换响应文本,None/空值保持不变 |
pre_tool_call| 参数 | 类型 | 描述 |
|---|---|---|
tool_name | str | 即将执行的工具名称(如 "terminal"、"web_search"、"read_file") |
args | dict | 模型传递给工具的参数 |
task_id | str | 会话/任务标识符。未设置时为空字符串。 |
model_tools.py 中的 handle_function_call() 内,工具处理器运行前。每次工具调用触发一次——若模型并行调用 3 个工具,则触发 3 次。message 作为返回给模型的错误短路该工具调用。第一个匹配的 block 指令生效(Python 插件优先,然后是 shell hooks)。任何其他返回值均被忽略,因此仅作观察用途的现有回调无需修改。post_tool_call| 参数 | 类型 | 描述 |
|---|---|---|
tool_name | str | 刚刚执行的工具名称 |
args | dict | 模型传递给工具的参数 |
result | str | 工具的返回值(始终为 JSON 字符串) |
task_id | str | 会话/任务标识符。未设置时为空字符串。 |
duration_ms | int | 工具分发耗时,单位毫秒(使用 time.monotonic() 在 registry.dispatch() 前后测量)。 |
model_tools.py 中的 handle_function_call() 内,工具处理器返回后。每次工具调用触发一次 。若工具抛出未处理异常,不会触发(错误被捕获并以错误 JSON 字符串返回,post_tool_call 以该错误字符串作为 result 触发)。pre_llm_call| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str | 当前会话的唯一标识符 |
user_message | str | 本轮用户的原始消息(技能注入前) |
conversation_history | list | 完整消息列表的副本(OpenAI 格式:[{"role": "user", "content": "..."}]) |
is_first_turn | bool | 新会话的第一轮为 True,后续轮次为 False |
model | str | 模型标识符(如 "anthropic/claude-sonnet-4.6") |
platform | str | 会话运行环境:"cli"、"telegram"、"discord" 等 |
run_agent.py 中的 run_conversation() 内,上下文压缩后、主 while 循环前。每次 run_conversation() 调用触发一次(即每个用户轮次一次),而非工具循环内每次 API 调用触发一次。"context" 键的字典,或非空的普通字符串,该文本会追加到当前轮次的用户消息 。返回 None 表示不注入。post_llm_call| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str | 当前会话的唯一标识符 |
user_message | str | 本轮用户的原始消息 |
assistant_response | str | Agent 本轮的最终文本响应 |
conversation_history | list | 轮次完成后完整消息列表的副本 |
model | str | 模型标识符 |
platform | str | 会话运行环境 |
run_agent.py 中的 run_conversation() 内,工具循环以最终响应退 出后。受 if final_response and not interrupted 保护——因此当用户在轮次中途中断,或 agent 在未产生响应的情况下达到迭代上限时,不会触发。on_session_start| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str | 新会话的唯一标识符 |
model | str | 模型标识符 |
platform | str | 会话运行环境 |
run_agent.py 中的 run_conversation() 内,新会话第一轮期间——具体在系统 prompt 构建后、工具循环开始前。检查条件为 if not conversation_history(无历史消息 = 新会话)。on_session_endrun_conversation() 调用结束时触发,无论结果如何。若用户在 agent 处理过程中退出,也会从 CLI 的退出处理器 触发。| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str | 会话的唯一标识符 |
completed | bool | Agent 产生最终响应时为 True,否则为 False |
interrupted | bool | 轮次被中断时为 True(用户发送新消息、/stop 或退出) |
model | str | 模型标识符 |
platform | str | 会话运行环境 |
run_agent.py — 每次 run_conversation() 调用结束时,所有清理完成后。始终触发,即使轮次出错。cli.py — CLI 的 atexit 处理器中,但仅当 agent 在退出时处于处理中状态(_agent_running=True)。这捕获了处理过程中的 Ctrl+C 和 /exit。此时 completed=False,interrupted=True。on_session_start 中初始化的资源。on_session_finalize/new、gateway GC 了空闲会话,或 CLI 在 agent 活跃时退出。这是在会话身份消失前刷新与该会话绑定状态的最后机会。| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str 或 None | 即将销毁的会话 ID。若无活跃会话则可能为 None。 |
platform | str | "cli" 或消息平台名称("telegram"、"discord" 等)。 |
cli.py(/new / CLI 退出时)和 gateway/run.py(会话重置或 GC 时)。在 gateway 侧始终与 on_session_reset 配对。on_session_reset/new、/reset、/clear,或适配器在空闲窗口后选择了新会话。这让插件能在不等待下一个 on_session_start 的情况下响应对话状态已被清除这一事实。| 参数 | 类型 | 描述 |
|---|---|---|
session_id | str | 新会话的 ID(已轮换为新值)。 |
platform | str | 消息平台名称。 |
gateway/run.py 中,新会话 key 分配后、下一条入站消息处理前立即触发。在 gateway 侧,顺序为:on_session_finalize(old_id) → 切换 → on_session_reset(new_id) → 第一条入站消息时的 on_session_start(new_id)。session_id 为键的每会话缓存、发出"会话已轮换"分析事件、初始化新状态桶。subagent_stopdelegate_task 完成后,每个子 agent 触发一次。无论你委托了单个任务还是三个任务的批次,此 hook 对每个子 agent 各触发一次,在父线程上串行执行。| 参数 | 类型 | 描述 |
|---|---|---|
parent_session_id | str | 委托父 agent 的会话 ID |
child_role | str | None | 子 agent 上设置的编排角色标签(若功能未启用则为 None) |
child_summary | str | None | 子 agent 返回给父 agent 的最终响应 |
child_status | str | "completed"、"failed"、"interrupted" 或 "error" |
duration_ms | int | 运行子 agent 的挂钟时间,单位毫秒 |
tools/delegate_tool.py 中,ThreadPoolExecutor.as_completed() 排空所有子 future 后。触发被编排到父线程,因此 hook 作者无需考虑并发回调执行问题。subagent_stop 每轮会触发多次。保持回调快速执行;将耗时操作推送到后台队列。pre_gateway_dispatchMessageEvent 触发一次,在内部事件守卫之后、认证/配对和 agent 分发之前。这是 gateway 级消息流策略(只听不回窗口、人工接管、按聊天路由等)的拦截点,这些策略不适合放在任何单一平台适配器中。| 参数 | 类型 | 描述 |
|---|---|---|
event | MessageEvent | 标准化的入站消息(包含 .text、.source、.message_id、.internal 等)。 |
gateway | GatewayRunner | 活跃的 gateway 运行器,插件可调用 gateway.adapters[platform].send(...) 进行旁路回复(所有者通知等)。 |
session_store | SessionStore | 用于通过 session_store.append_to_transcript(...) 静默摄入转录。 |
gateway/run.py 中的 GatewayRunner._handle_message() 内,is_internal 计算后立即触发。内部事件完全跳过此 hook(它们是系统生成的——后台进程完成等——不得被面向用户的策略拦截)。None 或字典。第一个被识别的 action 字典生效;其余插件结果被忽略。插件回调中的异常会被捕获并记录;gateway 在出错时始终回退到正常分发。| 返回值 | 效果 |
|---|---|
{"action": "skip", "reason": "..."} | 丢弃消息——无 agent 回复、无配对流程、无认证。假定插件已处理(如静默摄入到转录)。 |
{"action": "rewrite", "text": "new text"} | 替换 event.text |