跳转到内容

05 - 工具注册与分发

学习目标

本章聚焦 Hermes Agent 的工具系统。你将了解:

  • ToolRegistry 模块化单例如何实现自动注册
  • 工具集(toolset)分发如何通过 includes 递归组合
  • 五层审批门如何检测破坏性操作
  • 七种终端后端的隔离策略

前置知识

本章涉及工具调用的通用原理,建议先阅读:

下文直接聚焦 Hermes 的工具注册与分发实现。


项目实践

工具注册表:模块化单例

tools/registry.py(约 590 行)是模块级单例:

class ToolRegistry:
def register(name, toolset, schema, handler, ...) # 注册工具
def get_definitions(tool_names, quiet) -> List[dict] # 获取 schema
def dispatch(name, args, **kwargs) -> str # 分发执行

自动注册:每个 tools/*.py 文件在 import 时调用 registry.register(),无需手动维护导入列表。依赖链:

tools/registry.py (无依赖)
tools/*.py (import 时自动注册)
model_tools.py (触发工具发现)
run_agent.py / cli.py / gateway/run.py

线程安全:注册使用 RLockcheck_fn 有 30 秒 TTL 缓存,避免每次查询外部状态。

工具集(Toolset)分发

toolsets.py 定义 TOOLSETS 字典,支持 includes 递归组合:

TOOLSETS = {
"messaging": {
"description": "基础消息平台工具",
"tools": ["web_search", "read_file", "write_file", ...],
"includes": ["terminal", "file"], # 递归解析
},
# ... 27 个 toolset
}

特殊别名

  • all / *:解析为所有 toolset 中所有工具的并集
  • hermes-webhook:仅包含 web_searchweb_extractvision_analyzeclarify(不可信源安全子集)
  • hermes-acp:编辑器专用,不包含消息/音频/clarify

默认核心包_HERMES_CORE_TOOLS 包含 40+ 工具,所有平台的基础 toolset 都继承自它。修改一次 _HERMES_CORE_TOOLS,所有平台自动更新。

五层审批门

tools/approval.py(约 62558 行)实现五层检测:

层级检测内容可绕过?
Hardlinerm -rf /mkfs、fork bomb、dd 到块设备、shutdown/reboot不可(yolo 模式也不行)
Sudo stdin guard阻止密码猜测攻击不可
DANGEROUS_PATTERNS~47 个模式(递归删除、chmod 777、SQL DROP 等)用户确认
Smart Approval辅助 LLM 评估风险用户确认
Tirith外部安全扫描用户确认

批准持久化:用户可选择 once / session / always 三级批准,永久 allowlist 保存到 config.yaml

七种终端后端

tools/environments/ 提供七种终端后端:

后端隔离级别适用场景
本地(默认)信任 LLM 的个人开发
Docker容器隔离项目级隔离
SSH远程主机远程执行
SingularityHPC 容器科研计算
Modal云函数无服务器持久化
Daytona云沙箱无服务器持久化
Vercel Sandbox云沙箱低成本闲置

安全含义:终端后端隔离仅覆盖通过 shell/文件操作执行的内容,不覆盖 Python 进程内的代码执行工具(作为宿主子进程运行)、MCP 子进程、插件加载或技能加载。


问题与规避

工具 Schema 膨胀

问题:40+ 工具的 schema 会占用大量 token,尤其是包含详细参数描述时。

对策:每个平台使用不同的 toolset,只加载该平台需要的工具。hermes-webhook 安全子集仅 4 个工具。

审批门的图灵完备性

问题:Shell 是图灵完备的,字符串模式匹配不可能覆盖所有破坏性操作。

对策:Hermes 明确将审批门定位为”合作模式下的错误捕捉”,不是对抗性防护。对抗性场景需要 OS 级隔离(Docker/OpenShell)。

工具注册的导入时序陷阱

问题discover_plugins() 只作为导入 model_tools.py 的副作用运行。不导入 model_tools.py 的代码路径无法看到插件工具。

对策:需要读取插件状态的代码必须先显式调用 discover_plugins()(该函数是幂等的)。


设计取舍

自动注册 vs 显式注册

方案优势代价
自动注册(Hermes)新增工具只需创建文件 + 注册调用,无需修改中央列表导入时序依赖、隐式行为
显式注册导入顺序可控、行为可预测每次新增工具需修改中央文件

Hermes 选择自动注册,因为工具数量多(40+),手动维护中央列表容易遗漏。代价是需要理解导入时序。


参考来源

  • 源码验证:tools/registry.pytoolsets.pytools/approval.pytools/terminal_tool.pytools/environments/