一次看懂 Hermes Agent 工具扩展:自注册、发现机制与 3 个自定义场景
2026/8/28 11:40:40 网站建设 项目流程

一次看懂 Hermes Agent 工具扩展:自注册、发现机制与 3 个自定义场景

【免费下载链接】hermes-agentThe agent that grows with you项目地址: https://gitcode.com/GitHub_Trending/he/hermes-agent

给 Hermes Agent 加工具,不需要改主流程:放一个会自注册的文件,编排层自己会找到它。本文拆开工具系统的内部流转,配一个最小示例和三个常见扩展场景,读完可以直接动手。

工具系统内部是怎么跑的

从实现角度看,整套机制只有三个角色:

  • 工具文件:每个工具是一个独立 Python 模块,在模块顶层调用registry.register()把自己登记到注册表。这一步发生在 import 时,是"自注册"的关键。
  • 编排层model_tools.py是对tools/registry.py的一层薄封装,负责发现(触发所有工具模块的导入)、收集 schema、按名字分发调用。仓库里没有一个手维护的"工具导入清单"——发现靠 import 副作用完成。
  • 工具集(toolset):按功能把工具分组,配置层决定哪些组启用。启用的工具集会进系统提示词,也会决定模型能看到哪些 schema。

一次调用的数据流大致是:模型输出 tool_call → 编排层按名字查注册表拿到 handler → 执行 → 触发后置钩子 → 结果写回上下文。这里有个容易踩的坑:工具在发现阶段会过一次可用性检查,缺 API key 或依赖二进制的工具会直接不出现在 schema 列表里——模型看不到,自然也不会调用。这是设计使然,不是报错。

最小示例:一个能跑的工具文件

新建tools/time_report.py,内容如下:

from tools.registry import registry def time_report(args): # 真正的工作:取执行环境当前时间并返回 return {"time": args.get("timezone", "local")} SCHEMA = { # 模型"看到"的接口定义 "name": "time_report", "description": "Report the current time of the execution environment", "parameters": { "type": "object", "properties": {"timezone": {"type": "string"}}, }, } # 关键行:import 即自注册,编排层无需手动导入 registry.register(name="time_report", schema=SCHEMA, handler=time_report)

实际跑起来之后会发现:只要这个文件被工具发现流程导入过一次,hermes tools里就能看到它,用hermes doctor可以确认注册是否生效。其余样板(日志、超时、返回结构)按仓库里现有工具文件照抄即可。

常见扩展场景

三个场景并列,按需取用。

把工具归组,按功能整套启用

场景描述:单个工具只是起点。真实项目里工具会到几十个,schema 预算有限,需要按功能打包、按需启用。

关键改动:注册时把新工具挂到对应 toolset 字段;在配置里启用该 toolset。系统提示词和可用 schema 列表按启用的工具集组装,组装逻辑可以看 agent/prompt_builder.py。

注意事项:工具 schema 的 description 里不要提及其他工具集的工具名。仓库开发规范(AGENTS.md)明确约束了这一点——被引用的工具可能因工具集未启用而不存在,写死引用会让模型幻觉出不存在的调用。需要交叉说明时,在编排层动态补充。

通过 MCP 把内部工具暴露给外部客户端

场景描述:你希望外部 MCP 客户端(比如 ACP 适配器)也能复用 agent 内部已有的工具。

关键改动:MCP 侧暴露的工具由白名单EXPOSED_TOOLS控制,定义在 agent/transports/hermes_tools_mcp_server.py,把工具名加进这个元组即可。

注意事项:白名单刻意保守。像delegate_taskmemory这类依赖内部会话状态的工具不在暴露范围,它们的上下文绑定在内部 loop 上,移出后状态不完整。只暴露无状态、参数自包含的工具。

给工具加前后置钩子,不动工具本体

场景描述:想在工具执行前做权限拦截、执行后做审计日志,但不希望改动工具实现本身。

关键改动:钩子挂在编排层,工具文件一行不用改。post_tool_call的发射方式在 agent/shell_hooks.py 里有完整注释和示例。

注意事项:有一条隐蔽路径——如果某段代码没有先导入编排层就去读钩子状态,钩子发现逻辑不会自动执行,需要显式调用discover_plugins()

高频报错排查表

现象原因解法
工具注册了,但模型可调列表里没有模块从未被 import,发现流程没跑到它确认新文件在工具发现流程的导入范围内;跑hermes doctor自检
工具存在,启用后仍不可用可用性检查没通过(缺 API key / 依赖二进制)补全配置。缺依赖时工具被静默隐藏,属预期行为
模型调用不存在的工具,反复报错schema description 提到了未启用工具集的工具名,诱导模型幻觉description 不写死跨工具集引用,改在编排层动态补充
钩子完全不生效执行路径未导入编排层,钩子未被发现在读钩子状态前显式调用discover_plugins()
工具报错后 agent 一直重试同一调用返回值缺少可识别的错误结构,分类逻辑判不出失败返回值带显式错误字段,分类规则见 agent/tool_result_classification.py

生产环境建议

  • 性能:工具发现发生在 import 期,模块顶层只留轻量引用;浏览器、第三方客户端这类重依赖放到 handler 内部懒加载。
  • 可维护性:工具集边界对齐功能边界,新增工具先回答"它属于哪个工具集",而不是单开一个。
  • 兼容性:注册层是薄封装,升级成本相对低;大版本升级前先跑hermes doctor验证注册与可用性检查是否全部通过。
  • 安全:执行命令、写文件类工具一律走现有防护(如agent/file_safety.pyagent/estop.py),不要为了省事在 handler 里绕过。

往哪走

工具系统拆开看就三块代码:自注册、发现、白名单。难点不在写那行注册调用,而在理解注册之后发生了什么——schema 收集、可用性过滤、钩子顺序,都有固定路径。想继续深入的话,可以挑一个方向:依赖不可用时工具如何优雅降级,还是自定义工具集如何跨 profile 共享?你更想先解决哪个问题?

【免费下载链接】hermes-agentThe agent that grows with you项目地址: https://gitcode.com/GitHub_Trending/he/hermes-agent

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询