hermes-agent/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/tools-runtime.md
Teknium 76135b329d
docs(i18n): translate all docs into Simplified Chinese (zh-Hans) (#31942)
Translates the full English docs corpus (335 files) into Simplified
Chinese under website/i18n/zh-Hans/. Combined with PR #31895 (cross-
locale link fix), the 简体中文 locale toggle now serves a complete
Chinese site with working cross-page navigation.

Pipeline:
- Claude Sonnet 4.6 via OpenRouter, 8-way concurrent
- Preserves frontmatter keys, code blocks, MDX/JSX, link URLs, brand
  names, and technical jargon (prompt/token/hook/MCP/ACP/etc.)
- Translates only frontmatter title/description and prose
- Two largest files (configuration.md 93KB, research-paper-writing.md
  107KB) retried with 64K max_tokens after initial fence-drift
- 3 manual post-fixes for MDX edge cases the model didn't escape:
  < in optional-skills-catalog table, double-quotes in an alt= tag,
  and a bare URL adjacent to a full-width period

Cost: ~$30 total (Sonnet 4.6 input $3/M + output $15/M).

Verified `npm run build` succeeds for both en and zh-Hans locales,
no double-prefixed /docs/zh-Hans/docs/ URLs in rendered output,
all in-page navigation resolves correctly.

Translations are machine-generated and may need human review on
specific pages — but they're an enormous improvement over the
previous state (3 zh-Hans pages out of 335).
2026-05-25 01:47:38 -07:00

9.7 KiB
Raw Blame History

sidebar_position title description
9 工具运行时 工具注册表、toolset、调度及终端环境的运行时行为

工具运行时

Hermes 工具是自注册函数,按 toolset工具集分组并通过中央注册表/调度系统执行。

主要文件:

  • tools/registry.py
  • model_tools.py
  • toolsets.py
  • tools/terminal_tool.py
  • tools/environments/*

工具注册模型

每个工具模块在导入时调用 registry.register(...)

model_tools.py 负责导入/发现工具模块,并构建供模型使用的 schema 列表。

registry.register() 的工作原理

tools/ 中的每个工具文件在模块级别调用 registry.register() 来声明自身。函数签名如下:

registry.register(
    name="terminal",               # 唯一工具名称(用于 API schema
    toolset="terminal",            # 该工具所属的 toolset
    schema={...},                  # OpenAI function-calling schema描述、参数
    handler=handle_terminal,       # 工具被调用时执行的函数
    check_fn=check_terminal,       # 可选:返回 True/False 表示是否可用
    requires_env=["SOME_VAR"],     # 可选:所需的环境变量(用于 UI 显示)
    is_async=False,                # handler 是否为异步协程
    description="Run commands",    # 人类可读的描述
    emoji="💻",                    # 用于 spinner/进度显示的 emoji
)

每次调用都会创建一个 ToolEntry,以工具名称为键存储在单例 ToolRegistry._tools 字典中。若不同 toolset 之间出现名称冲突,会记录警告,后注册的条目覆盖前者。

发现机制:discover_builtin_tools()

model_tools.py 被导入时,会调用 tools/registry.py 中的 discover_builtin_tools()。该函数使用 AST 解析扫描所有 tools/*.py 文件,找出包含顶层 registry.register() 调用的模块,然后导入它们:

# tools/registry.py简化版
def discover_builtin_tools(tools_dir=None):
    tools_path = Path(tools_dir) if tools_dir else Path(__file__).parent
    for path in sorted(tools_path.glob("*.py")):
        if path.name in {"__init__.py", "registry.py", "mcp_tool.py"}:
            continue
        if _module_registers_tools(path):  # AST 检查顶层 registry.register()
            importlib.import_module(f"tools.{path.stem}")

这种自动发现机制意味着新工具文件会被自动识别——无需手动维护列表。AST 检查只匹配顶层的 registry.register() 调用(不匹配函数内部的调用),因此 tools/ 中的辅助模块不会被导入。

每次导入都会触发模块的 registry.register() 调用。可选工具中的错误(例如图像生成工具缺少 fal_client)会被捕获并记录——不会阻止其他工具加载。

核心工具发现完成后,还会发现 MCP 工具和插件工具:

  1. MCP 工具tools.mcp_tool.discover_mcp_tools() 读取 MCP 服务器配置,并注册来自外部服务器的工具。
  2. 插件工具hermes_cli.plugins.discover_plugins() 加载用户/项目/pip 插件,这些插件可能注册额外的工具。

工具可用性检查(check_fn

每个工具可以选择性地提供一个 check_fn——一个可调用对象,在工具可用时返回 True,否则返回 False。典型的检查包括:

  • API 密钥是否存在 — 例如,lambda: bool(os.environ.get("SERP_API_KEY")) 用于网络搜索
  • 服务是否运行 — 例如,检查 Honcho 服务器是否已配置
  • 二进制文件是否已安装 — 例如,验证浏览器工具的 playwright 是否可用

registry.get_definitions() 为模型构建 schema 列表时,会运行每个工具的 check_fn()

# 简化自 registry.py
if entry.check_fn:
    try:
        available = bool(entry.check_fn())
    except Exception:
        available = False   # 异常 = 不可用
    if not available:
        continue            # 完全跳过该工具

关键行为:

  • 检查结果按调用缓存——若多个工具共享同一个 check_fn,只运行一次。
  • check_fn() 中的异常被视为"不可用"(故障安全)。
  • is_toolset_available() 方法检查某个 toolset 的 check_fn 是否通过,用于 UI 显示和 toolset 解析。

Toolset 解析

Toolset 是工具的命名集合。Hermes 通过以下方式解析它们:

  • 显式启用/禁用的 toolset 列表
  • 平台预设(hermes-clihermes-telegram 等)
  • 动态 MCP toolset
  • 精选的特殊用途集合,如 hermes-acp

get_tool_definitions() 如何过滤工具

主入口点为 model_tools.get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)

  1. 若提供了 enabled_toolsets — 仅包含这些 toolset 中的工具。每个 toolset 名称通过 resolve_toolset() 解析,将复合 toolset 展开为单个工具名称。

  2. 若提供了 disabled_toolsets — 从所有 toolset 开始,减去已禁用的。

  3. 若两者均未提供 — 包含所有已知 toolset。

  4. 注册表过滤 — 解析后的工具名称集合传递给 registry.get_definitions(),后者应用 check_fn 过滤并返回 OpenAI 格式的 schema。

  5. 动态 schema 修补 — 过滤后,execute_codebrowser_navigate 的 schema 会被动态调整,仅引用实际通过过滤的工具(防止模型幻觉出不可用的工具)。

旧版 toolset 名称

带有 _tools 后缀的旧版 toolset 名称(例如 web_toolsterminal_tools)通过 _LEGACY_TOOLSET_MAP 映射到其现代工具名称,以保持向后兼容性。

调度

运行时,工具通过中央注册表调度,但部分 agent 级别的工具(如 memory/todo/session-search 处理)由 agent 循环直接处理。

调度流程:模型 tool_call → handler 执行

当模型返回 tool_call 时,流程如下:

模型响应包含 tool_call
    ↓
run_agent.py agent 循环
    ↓
model_tools.handle_function_call(name, args, task_id, user_task)
    ↓
[Agent 循环工具?] → 由 agent 循环直接处理todo、memory、session_search、delegate_task
    ↓
[插件 pre-hook] → invoke_hook("pre_tool_call", ...)
    ↓
registry.dispatch(name, args, **kwargs)
    ↓
按名称查找 ToolEntry
    ↓
[异步 handler] → 通过 _run_async() 桥接
[同步 handler]  → 直接调用
    ↓
返回结果字符串(或 JSON 错误)
    ↓
[插件 post-hook] → invoke_hook("post_tool_call", ...)

错误包装

所有工具执行在两个层级进行错误处理:

  1. registry.dispatch() — 捕获 handler 抛出的任何异常,并以 JSON 形式返回 {"error": "Tool execution failed: ExceptionType: message"}

  2. handle_function_call() — 将整个调度包裹在次级 try/except 中,返回 {"error": "Error executing tool_name: message"}

这确保模型始终收到格式正确的 JSON 字符串,而不会遇到未处理的异常。

Agent 循环工具

以下四个工具在注册表调度之前被拦截,因为它们需要 agent 级别的状态TodoStore、MemoryStore 等):

  • todo — 规划/任务跟踪
  • memory — 持久化 memory 写入
  • session_search — 跨会话召回
  • delegate_task — 生成子 agent 会话

这些工具的 schema 仍在注册表中注册(供 get_tool_definitions 使用),但若调度以某种方式直接到达它们,其 handler 会返回一个存根错误。

异步桥接

当工具 handler 为异步时,_run_async() 将其桥接到同步调度路径:

  • CLI 路径(无运行中的事件循环) — 使用持久化事件循环以保持缓存的异步客户端存活
  • Gateway 路径(有运行中的事件循环) — 使用 asyncio.run() 启动一个一次性线程
  • 工作线程(并行工具) — 使用存储在线程本地存储中的每线程持久化循环

DANGEROUS_PATTERNS 审批流程

终端工具集成了定义在 tools/approval.py 中的危险命令审批系统:

  1. 模式检测DANGEROUS_PATTERNS 是一个 (regex, description) 元组列表,涵盖破坏性操作:

    • 递归删除(rm -rf
    • 文件系统格式化(mkfsdd
    • SQL 破坏性操作(DROP TABLE、不带 WHEREDELETE FROM
    • 系统配置覆写(> /etc/
    • 服务操控(systemctl stop
    • 远程代码执行(curl | sh
    • Fork bomb、进程终止等
  2. 检测 — 在执行任何终端命令之前,detect_dangerous_command(command) 会对所有模式进行检查。

  3. 审批提示 — 若发现匹配:

    • CLI 模式 — 交互式提示要求用户批准、拒绝或永久允许
    • Gateway 模式 — 异步审批回调将请求发送至消息平台
    • 智能审批 — 可选地,辅助 LLM 可自动批准匹配模式但风险较低的命令(例如,rm -rf node_modules/ 是安全的,但匹配"递归删除"模式)
  4. 会话状态 — 审批按会话跟踪。一旦在某个会话中批准了"递归删除",后续的 rm -rf 命令不会再次提示。

  5. 永久允许列表 — "永久允许"选项会将该模式写入 config.yamlcommand_allowlist,跨会话持久化。

终端/运行时环境

终端系统支持多种后端:

  • local
  • docker
  • ssh
  • singularity
  • modal
  • daytona
  • vercel_sandbox

还支持:

  • 按任务的 cwd 覆盖
  • 后台进程管理
  • PTY 模式
  • 危险命令的审批回调

并发

工具调用可以顺序执行,也可以并发执行,具体取决于工具组合和交互需求。

相关文档