04 - 插件系统与扩展点
学习目标
本章聚焦 Hermes Agent 的插件系统实现。你将了解:
- PluginManager 的四种插件发现来源
- 三个插件表面的职责划分
- 生命周期钩子的注册与调用链
- 插件策略:禁止修改核心文件的约束
前置知识
无特定前置知识。
项目实践
PluginManager 发现机制
hermes_cli/plugins.py(1807 行)实现了插件管理器:
四种插件来源(按优先级从低到高):
| 来源 | 路径 | 说明 |
|---|---|---|
| Bundled | <repo>/plugins/ | 项目内置插件 |
| User | ~/.hermes/plugins/ | 用户安装的插件 |
| Project | ./.hermes/plugins/ | 项目本地插件(需 HERMES_ENABLE_PROJECT_PLUGINS=1) |
| Pip Entry Points | hermes_agent.plugins entry points | pip 包注册的插件 |
五种植插件类型:standalone、backend、exclusive、platform、model-provider。
三表面插件发现
Hermes 有三个独立的插件发现表面:
| 表面 | 发现路径 | 注册方式 |
|---|---|---|
| 通用插件 | ~/.hermes/plugins/ + pip entry points | ctx.register_tool()、ctx.register_cli_command() |
| 记忆提供者 | plugins/memory/<name>/ | register_provider() 到 MemoryManager |
| 模型提供者 | plugins/model-providers/<name>/ | register_provider() 到 ProviderProfile 注册 |
PluginContext 提供的注册能力
插件通过 PluginContext 对象注册扩展:
ctx.register_tool(name, handler) # 注册工具ctx.register_cli_command(name, handler) # 注册 CLI 命令ctx.register_hook(hook_name, handler) # 注册生命周期钩子ctx.register_command(name, handler) # 注册命令ctx.register_context_engine(...) # 注册上下文引擎ctx.register_image_gen_provider(...) # 注册图像生成提供者ctx.register_browser_provider(...) # 注册浏览器提供者ctx.register_tts_provider(...) # 注册 TTS 提供者ctx.register_platform(...) # 注册消息平台适配器ctx.register_auxiliary_task(...) # 注册辅助任务ctx.register_skill(...) # 注册技能生命周期钩子
VALID_HOOKS(插件可挂钩的生命周期点):
| 钩子 | 触发时机 |
|---|---|
pre_tool_call | 工具调用前 |
post_tool_call | 工具调用后 |
transform_terminal_output | 终端输出转换时 |
transform_tool_result | 工具结果转换时 |
transform_llm_output | LLM 输出转换时 |
pre_llm_call | LLM 调用前(可注入上下文) |
post_llm_call | LLM 调用后 |
pre_api_request | API 请求前 |
post_api_request | API 请求后 |
on_session_start | 会话开始时 |
on_session_end | 会话结束时 |
on_session_finalize | 会话最终化时 |
on_session_reset | 会话重置时 |
subagent_stop | 子代理停止时 |
pre_gateway_dispatch | 网关分发前 |
pre_approval_request | 审批请求前 |
post_approval_response | 审批响应后 |
策略约束
核心规则:插件禁止修改核心文件(run_agent.py、cli.py 等),必须通过扩展点。
启用控制:
plugins.enabled配置节控制哪些插件启用HERMES_ENABLE_PROJECT_PLUGINS环境变量控制项目本地插件
问题与规避
插件依赖缺失
问题:插件依赖的第三方库未安装,导致 import 失败。
对策:插件加载使用 try/except 包裹,单个插件失败不影响其他插件。缺失依赖的插件以 WARNING 级别记录日志。
插件钩子的性能影响
问题:pre_llm_call 钩子如果执行耗时操作,会阻塞 LLM 调用。
对策:钩子应轻量执行。耗时操作(如网络请求)应异步执行或使用后台线程。
设计取舍
三表面插件发现 vs 统一注册表
| 方案 | 优势 | 代价 |
|---|---|---|
| 三表面(Hermes) | 每类插件有专用的发现路径和生命周期 | 插件作者需要理解三种注册方式 |
| 统一注册表 | 单一入口、学习成本低 | 通用性受限、需要适配层 |
Hermes 选择三表面是因为记忆提供者和模型提供者有独特的生命周期(独立于通用插件的钩子系统),通用插件的注册机制不适用于它们。
参考来源
- 源码验证:
hermes_cli/plugins.py、AGENTS.md插件章节