Technical Guide

09. Tools 源码:registry、toolsets、tool_executor 怎么配合

按源码文件拆解 Hermes 工具从注册、发现、筛选到执行的完整链路。

这篇解决什么问题

上一篇讲 Tools 的概念,这一篇看源码。

目标不是把每个工具都读完,而是看懂工具系统的骨架。看懂骨架后,再读 terminal_tool.pyweb_tools.pyfile_tools.py 才不会乱。

先看 tools/registry.py

tools/registry.py 是中央注册表。

文件注释里已经说明了导入链路:

tools/registry.py

tools/*.py 通过 registry.register 注册

model_tools.py 导入 registry 和所有工具模块

run_agent.py / cli.py / batch_runner.py 使用 model_tools

这句话基本就是工具系统地图。

registry.register 做什么

每个工具文件会在模块顶层调用 registry.register()

注册内容包括:

name        工具名
toolset     所属工具集
schema      给模型看的参数说明
handler     实际执行函数
check_fn    可用性检查
requires_env 依赖环境变量

比如 tools/web_tools.py 会注册 web_searchweb_extract

tools/terminal_tool.py 会注册 terminal

discover_builtin_tools 怎么工作

registry.py 里有 discover_builtin_tools()

它会扫描 tools/ 目录,找包含顶层 registry.register(...) 的 Python 文件,然后 import 这些模块。

import 的副作用就是完成注册。

这是一种常见插件式设计:工具文件自己声明自己,中央模块只负责发现。

toolsets.py 负责分组

toolsets.py 里有 _HERMES_CORE_TOOLSTOOLSETS

_HERMES_CORE_TOOLS 是 CLI 和消息平台共享的核心工具列表,里面包括:

web_search / web_extract
terminal / process
read_file / write_file / patch / search_files
vision_analyze / image_generate
skills_list / skill_view / skill_manage
browser_* 系列
todo / memory / session_search
clarify / execute_code / delegate_task / cronjob

TOOLSETS 则定义工具分组,例如:

web
search
vision
terminal
skills
browser
cronjob
file
tts

model_tools.py 是薄编排层

model_tools.py 文件注释说得很清楚:它是工具 registry 上的一层薄编排。

主要公开 API 包括:

get_tool_definitions(...)
handle_function_call(...)
get_all_tool_names()
get_toolset_for_tool(name)
check_tool_availability(...)

其中最关键是两个:

get_tool_definitions   给模型准备工具 schema
handle_function_call   按工具名执行 handler

tool_executor.py 负责执行策略

agent/tool_executor.py 处理工具调用执行。

它关心的是:

  • 顺序执行还是并发执行;
  • 工具调用被中断怎么办;
  • 工具结果怎么包装成 tool message;
  • 工具输出太大怎么办;
  • 执行过程怎么展示;
  • destructive command 是否要审批。

所以它不关心 web_search 内部怎么搜。它关心的是“模型发来了这一批 tool_calls,我怎么安全、稳定地执行完”。

一条源码链路

把几个文件串起来:

tools/web_tools.py
→ registry.register(name="web_search", ...)
→ model_tools.discover_builtin_tools()
→ registry.get_definitions()
→ model_tools.get_tool_definitions()
→ AIAgent 把 schema 发给模型
→ 模型返回 web_search tool_call
→ agent/tool_executor.py 执行
→ model_tools.handle_function_call("web_search", args)
→ registry.dispatch / handler
→ 工具结果回到 messages

这条线看懂,就能自己加工具。

新增工具应该改哪里

通常只需要:

1. 新建 tools/your_tool.py
2. 在文件里实现 handler
3. 调用 registry.register(...)
4. 如果要纳入某个默认 toolset,更新 toolsets.py
5. 写 check_fn,避免依赖缺失时暴露工具

不要直接在 Agent loop 里硬塞特殊工具逻辑。

下一篇看什么

下一篇看 Skills。

Tools 解决“能做什么”,Skills 解决“遇到某类任务时应该怎么做”。