Hermes Agent深度探索:Toolset 设计——为什么 Hermes 不把所有工具一股脑暴露给模型?
Agent 的工具数量,就像团队里的微服务:刚开始只有两三个,觉得清爽;半年后一数,Web 搜索、终端、文件读写、浏览器自动化、图像生成、语音合成、消息投递、Home Assistant、Kanban、MCP……全堆在一起。开发时很爽——想要什么能力随手一加;运行起来才发现,模型选择空间越大,提示越长,误调用概率越高,攻击面也跟着膨胀。
Hermes 给出的答案是 toolsets.py。它不是给工具贴个分类标签那么简单,而是把能力边界、安全等级、入口可信度、提示成本这四个维度捏在了一起,形成一套按场景暴露工具的治理模型。理解了 Toolset,你就能回答两个最常被问到的问题:为什么我注册的工具模型看不见?为什么 Webhook 入口和本地 CLI 不能拥有同一套工具?
文章目录
- Hermes Agent深度探索:Toolset 设计——为什么 Hermes 不把所有工具一股脑暴露给模型?
-
- 源码地图
- 一、Toolset 在工具链路中的位置:第二道门
- 二、`_HERMES_CORE_TOOLS`:一张承载产品定位的清单
-
- 工具可见性的四层责任模型
- 三、Toolset 是安全边界,不是装饰
-
- 入口可信度与能力上限的倒金字塔模型
- 四、Toolset 也是提示成本控制
-
- 场景化工具筛选:让模型只看到需要的
- 五、分类 Toolset 的语义:让用户知道自己在开什么
- 六、动态 gate:工具存在 ≠ 工具可用
-
- check_fn 缓存机制
- 七、常见错误与排查清单
-
- 工具不可见排查决策树
- 八、实战案例:新增数据库查询工具,该放进哪个集合?
- 总结
源码地图
| toolsets.py | 定义核心工具集合、分类工具集和 webhook 安全工具集 |
| tools/registry.py | 保存工具所属 toolset,管理 check_fn 缓存 |
| model_tools.py | 按 enabled/disabled toolsets 过滤工具,生成最终 schema |
| tools/kanban_tools.py | 通过 check_fn 控制看板 worker 工具的可见性 |
| tools/homeassistant_tool.py | 根据 Home Assistant 配置决定工具是否可用 |
| tools/computer_use_tool.py | 按驱动环境控制桌面操控工具 |
一、Toolset 在工具链路中的位置:第二道门
很多开发者以为"注册 = 模型可见",实际链路要复杂得多。下面这张图展示了工具从注册到最终出现在模型上下文里,中间要经过多少层筛选:
#mermaid-svg-fpajzRebAmRLx8p3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fpajzRebAmRLx8p3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fpajzRebAmRLx8p3 .error-icon{fill:#552222;}#mermaid-svg-fpajzRebAmRLx8p3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fpajzRebAmRLx8p3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fpajzRebAmRLx8p3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fpajzRebAmRLx8p3 .marker.cross{stroke:#333333;}#mermaid-svg-fpajzRebAmRLx8p3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fpajzRebAmRLx8p3 p{margin:0;}#mermaid-svg-fpajzRebAmRLx8p3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-fpajzRebAmRLx8p3 .cluster-label text{fill:#333;}#mermaid-svg-fpajzRebAmRLx8p3 .cluster-label span{color:#333;}#mermaid-svg-fpajzRebAmRLx8p3 .cluster-label span p{background-color:transparent;}#mermaid-svg-fpajzRebAmRLx8p3 .label text,#mermaid-svg-fpajzRebAmRLx8p3 span{fill:#333;color:#333;}#mermaid-svg-fpajzRebAmRLx8p3 .node rect,#mermaid-svg-fpajzRebAmRLx8p3 .node circle,#mermaid-svg-fpajzRebAmRLx8p3 .node ellipse,#mermaid-svg-fpajzRebAmRLx8p3 .node polygon,#mermaid-svg-fpajzRebAmRLx8p3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fpajzRebAmRLx8p3 .rough-node .label text,#mermaid-svg-fpajzRebAmRLx8p3 .node .label text,#mermaid-svg-fpajzRebAmRLx8p3 .image-shape .label,#mermaid-svg-fpajzRebAmRLx8p3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-fpajzRebAmRLx8p3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fpajzRebAmRLx8p3 .rough-node .label,#mermaid-svg-fpajzRebAmRLx8p3 .node .label,#mermaid-svg-fpajzRebAmRLx8p3 .image-shape .label,#mermaid-svg-fpajzRebAmRLx8p3 .icon-shape .label{text-align:center;}#mermaid-svg-fpajzRebAmRLx8p3 .node.clickable{cursor:pointer;}#mermaid-svg-fpajzRebAmRLx8p3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fpajzRebAmRLx8p3 .arrowheadPath{fill:#333333;}#mermaid-svg-fpajzRebAmRLx8p3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fpajzRebAmRLx8p3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fpajzRebAmRLx8p3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fpajzRebAmRLx8p3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fpajzRebAmRLx8p3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fpajzRebAmRLx8p3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fpajzRebAmRLx8p3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fpajzRebAmRLx8p3 .cluster text{fill:#333;}#mermaid-svg-fpajzRebAmRLx8p3 .cluster span{color:#333;}#mermaid-svg-fpajzRebAmRLx8p3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fpajzRebAmRLx8p3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fpajzRebAmRLx8p3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-fpajzRebAmRLx8p3 .icon-shape,#mermaid-svg-fpajzRebAmRLx8p3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fpajzRebAmRLx8p3 .icon-shape p,#mermaid-svg-fpajzRebAmRLx8p3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fpajzRebAmRLx8p3 .icon-shape .label rect,#mermaid-svg-fpajzRebAmRLx8p3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fpajzRebAmRLx8p3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fpajzRebAmRLx8p3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fpajzRebAmRLx8p3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
模型层
入口层
过滤层 – Toolset 核心作用域
存储层
发现层
注册层
tools/*.py 顶层 registry.register
插件 ctx.register_tool
AST 扫描识别注册模块
插件导入时注册
ToolRegistry保存所有 ToolEntry
resolve_toolset
enabled_toolsets 白名单
disabled_toolsets 黑名单
toolset check_fn
tool check_fn + TTL 缓存
CLI/TUI: 核心工具集
个人 Gateway: 多数工具 + 审批
群聊 Gateway: 保守工具集
Webhook: safe tools
Worker: 按需工具集
最终 tools schema传给模型 API
这条链路的核心思想是:工具的存在(Registry)只是第一步,是否暴露给模型,由入口场景、配置文件和运行时依赖共同决定。缺了任何一环,工具都会在 schema 中"隐身"。
二、_HERMES_CORE_TOOLS:一张承载产品定位的清单
toolsets.py 里的 _HERMES_CORE_TOOLS 并不是装饰性列表,它直接决定了本地 CLI 和主要消息平台默认拥有哪些能力。里面包含的工具几乎覆盖了一个现代 Agent 的完整技能树:Web 搜索、终端、进程管理、文件读写与 patch、视觉理解、图像生成、技能管理、浏览器操控、TTS、todo、memory、session_search、clarify、代码执行、子代理、定时任务、消息投递、Home Assistant、Kanban、桌面操控。
工具可见性的四层责任模型
下面这张图把这四个责任方的关系画了出来——从工具作者到最终模型的 schema,每一层都可能拦截一个工具:
#mermaid-svg-ziBTAhcOYgbLjhIU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ziBTAhcOYgbLjhIU .error-icon{fill:#552222;}#mermaid-svg-ziBTAhcOYgbLjhIU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ziBTAhcOYgbLjhIU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ziBTAhcOYgbLjhIU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ziBTAhcOYgbLjhIU .marker.cross{stroke:#333333;}#mermaid-svg-ziBTAhcOYgbLjhIU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ziBTAhcOYgbLjhIU p{margin:0;}#mermaid-svg-ziBTAhcOYgbLjhIU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster-label text{fill:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster-label span{color:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster-label span p{background-color:transparent;}#mermaid-svg-ziBTAhcOYgbLjhIU .label text,#mermaid-svg-ziBTAhcOYgbLjhIU span{fill:#333;color:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU .node rect,#mermaid-svg-ziBTAhcOYgbLjhIU .node circle,#mermaid-svg-ziBTAhcOYgbLjhIU .node ellipse,#mermaid-svg-ziBTAhcOYgbLjhIU .node polygon,#mermaid-svg-ziBTAhcOYgbLjhIU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ziBTAhcOYgbLjhIU .rough-node .label text,#mermaid-svg-ziBTAhcOYgbLjhIU .node .label text,#mermaid-svg-ziBTAhcOYgbLjhIU .image-shape .label,#mermaid-svg-ziBTAhcOYgbLjhIU .icon-shape .label{text-anchor:middle;}#mermaid-svg-ziBTAhcOYgbLjhIU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ziBTAhcOYgbLjhIU .rough-node .label,#mermaid-svg-ziBTAhcOYgbLjhIU .node .label,#mermaid-svg-ziBTAhcOYgbLjhIU .image-shape .label,#mermaid-svg-ziBTAhcOYgbLjhIU .icon-shape .label{text-align:center;}#mermaid-svg-ziBTAhcOYgbLjhIU .node.clickable{cursor:pointer;}#mermaid-svg-ziBTAhcOYgbLjhIU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ziBTAhcOYgbLjhIU .arrowheadPath{fill:#333333;}#mermaid-svg-ziBTAhcOYgbLjhIU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ziBTAhcOYgbLjhIU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ziBTAhcOYgbLjhIU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ziBTAhcOYgbLjhIU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ziBTAhcOYgbLjhIU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ziBTAhcOYgbLjhIU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster text{fill:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU .cluster span{color:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ziBTAhcOYgbLjhIU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ziBTAhcOYgbLjhIU rect.text{fill:none;stroke-width:0;}#mermaid-svg-ziBTAhcOYgbLjhIU .icon-shape,#mermaid-svg-ziBTAhcOYgbLjhIU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ziBTAhcOYgbLjhIU .icon-shape p,#mermaid-svg-ziBTAhcOYgbLjhIU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ziBTAhcOYgbLjhIU .icon-shape .label rect,#mermaid-svg-ziBTAhcOYgbLjhIU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ziBTAhcOYgbLjhIU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ziBTAhcOYgbLjhIU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ziBTAhcOYgbLjhIU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
第四层: 运行时环境
第三层: 用户/管理员
第二层: Toolset 设计者
第一层: 工具作者
实现 handler + schema
调用 registry.register
编写 check_fn
声明 toolset 归属
决定工具进入哪个 toolset
是否加入 CORE_TOOLS
是否加入 WEBHOOK_SAFE
toolset 间依赖关系
enabled_toolsets 配置
disabled_toolsets 配置
环境变量/secret 配置
check_fn 探测外部依赖
入口场景判断
审批/guardrail 拦截
最终工具可见性
常见误区:很多人写完工具、注册完成就以为大功告成,结果发现模型从来不调。排查路径应该是:注册 → toolset → 配置 → check_fn,而不是一上来就怀疑模型能力。
三、Toolset 是安全边界,不是装饰
最能体现 Toolset 安全设计的是 _HERMES_WEBHOOK_SAFE_TOOLS。Webhook 的输入可能来自 GitHub Issue 标题、第三方系统回调甚至公开表单,这些内容本质上不可信。如果让外部输入直接触发终端命令、文件写入或消息投递,一次简单的提示注入就能变成本地执行漏洞。
因此 Webhook 默认工具集被严格限制,主要包含:
- web_search(网页搜索)
- web_extract(内容提取)
- vision_analyze(图像分析)
- clarify(澄清追问)
这些工具仍有信息泄露或误导的风险,但远小于直接拿到 shell 的破坏力。
入口可信度与能力上限的倒金字塔模型
背后这条设计原则可以总结为一句话:入口可信度决定能力上限。下面这个倒金字塔把六类入口的信任层级和对应工具范围做了清晰对齐:
#mermaid-svg-vAUZlgV8PJF8rEr4{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vAUZlgV8PJF8rEr4 .error-icon{fill:#552222;}#mermaid-svg-vAUZlgV8PJF8rEr4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vAUZlgV8PJF8rEr4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .marker.cross{stroke:#333333;}#mermaid-svg-vAUZlgV8PJF8rEr4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vAUZlgV8PJF8rEr4 p{margin:0;}#mermaid-svg-vAUZlgV8PJF8rEr4 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster-label text{fill:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster-label span{color:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster-label span p{background-color:transparent;}#mermaid-svg-vAUZlgV8PJF8rEr4 .label text,#mermaid-svg-vAUZlgV8PJF8rEr4 span{fill:#333;color:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .node rect,#mermaid-svg-vAUZlgV8PJF8rEr4 .node circle,#mermaid-svg-vAUZlgV8PJF8rEr4 .node ellipse,#mermaid-svg-vAUZlgV8PJF8rEr4 .node polygon,#mermaid-svg-vAUZlgV8PJF8rEr4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .rough-node .label text,#mermaid-svg-vAUZlgV8PJF8rEr4 .node .label text,#mermaid-svg-vAUZlgV8PJF8rEr4 .image-shape .label,#mermaid-svg-vAUZlgV8PJF8rEr4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vAUZlgV8PJF8rEr4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .rough-node .label,#mermaid-svg-vAUZlgV8PJF8rEr4 .node .label,#mermaid-svg-vAUZlgV8PJF8rEr4 .image-shape .label,#mermaid-svg-vAUZlgV8PJF8rEr4 .icon-shape .label{text-align:center;}#mermaid-svg-vAUZlgV8PJF8rEr4 .node.clickable{cursor:pointer;}#mermaid-svg-vAUZlgV8PJF8rEr4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .arrowheadPath{fill:#333333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vAUZlgV8PJF8rEr4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vAUZlgV8PJF8rEr4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vAUZlgV8PJF8rEr4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster text{fill:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 .cluster span{color:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vAUZlgV8PJF8rEr4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vAUZlgV8PJF8rEr4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vAUZlgV8PJF8rEr4 .icon-shape,#mermaid-svg-vAUZlgV8PJF8rEr4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vAUZlgV8PJF8rEr4 .icon-shape p,#mermaid-svg-vAUZlgV8PJF8rEr4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vAUZlgV8PJF8rEr4 .icon-shape .label rect,#mermaid-svg-vAUZlgV8PJF8rEr4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vAUZlgV8PJF8rEr4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vAUZlgV8PJF8rEr4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vAUZlgV8PJF8rEr4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
不可信
低可信度
中低可信度
中等可信度
中高可信度
高可信度
本地 CLI用户本人直接操作核心工具集 50+ 工具危险命令需审批
个人私聊 GatewayTelegram/Discord 私聊多数工具可用 30+需用户授权
群聊 Gateway多人环境工具集 20+更严格授权,谨慎终端
Cron 定时任务自动化触发工具集 10+保守策略,禁高危工具
Webhook 入口外部系统回调safe tools 仅 4 个禁终端/文件/消息
公开接口无需认证仅 clarify + 纯文本无任何工具
很多 Agent 项目的安全问题,不是缺审批,而是没有按入口做工具降级。Hermes 的分层 Toolset 给出了一个可以直接参考的方案。
四、Toolset 也是提示成本控制
每个工具的 schema 描述都会进入模型请求的 tools 字段。工具越多,提示越长;描述越多,模型在相似工具之间犹豫或误选的概率也越高。
场景化工具筛选:让模型只看到需要的
举一个很实际的例子:你在写代码时,read_file、search_files、patch 很有用,但图像生成、TTS、Home Assistant 此时就是噪声,反而会让模型分心。反过来,在处理智能家居任务时,Home Assistant 工具至关重要,代码 patch 工具则完全多余。
#mermaid-svg-ODhlKkBAWjtRmVoQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ODhlKkBAWjtRmVoQ .error-icon{fill:#552222;}#mermaid-svg-ODhlKkBAWjtRmVoQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ODhlKkBAWjtRmVoQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .marker.cross{stroke:#333333;}#mermaid-svg-ODhlKkBAWjtRmVoQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ODhlKkBAWjtRmVoQ p{margin:0;}#mermaid-svg-ODhlKkBAWjtRmVoQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster-label text{fill:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster-label span{color:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster-label span p{background-color:transparent;}#mermaid-svg-ODhlKkBAWjtRmVoQ .label text,#mermaid-svg-ODhlKkBAWjtRmVoQ span{fill:#333;color:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .node rect,#mermaid-svg-ODhlKkBAWjtRmVoQ .node circle,#mermaid-svg-ODhlKkBAWjtRmVoQ .node ellipse,#mermaid-svg-ODhlKkBAWjtRmVoQ .node polygon,#mermaid-svg-ODhlKkBAWjtRmVoQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .rough-node .label text,#mermaid-svg-ODhlKkBAWjtRmVoQ .node .label text,#mermaid-svg-ODhlKkBAWjtRmVoQ .image-shape .label,#mermaid-svg-ODhlKkBAWjtRmVoQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-ODhlKkBAWjtRmVoQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .rough-node .label,#mermaid-svg-ODhlKkBAWjtRmVoQ .node .label,#mermaid-svg-ODhlKkBAWjtRmVoQ .image-shape .label,#mermaid-svg-ODhlKkBAWjtRmVoQ .icon-shape .label{text-align:center;}#mermaid-svg-ODhlKkBAWjtRmVoQ .node.clickable{cursor:pointer;}#mermaid-svg-ODhlKkBAWjtRmVoQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .arrowheadPath{fill:#333333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ODhlKkBAWjtRmVoQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ODhlKkBAWjtRmVoQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ODhlKkBAWjtRmVoQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster text{fill:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ .cluster span{color:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ODhlKkBAWjtRmVoQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ODhlKkBAWjtRmVoQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-ODhlKkBAWjtRmVoQ .icon-shape,#mermaid-svg-ODhlKkBAWjtRmVoQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ODhlKkBAWjtRmVoQ .icon-shape p,#mermaid-svg-ODhlKkBAWjtRmVoQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ODhlKkBAWjtRmVoQ .icon-shape .label rect,#mermaid-svg-ODhlKkBAWjtRmVoQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ODhlKkBAWjtRmVoQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ODhlKkBAWjtRmVoQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ODhlKkBAWjtRmVoQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
智能家居场景
被过滤
用户: 客厅灯太亮了
可见工具
隐藏工具
Home Assistant
web_search
clarify
patch
terminal
execute_code
代码场景
被过滤
用户: 修复这个 bug
可见工具
隐藏工具
read_file
search_files
patch
terminal
execute_code
Home Assistant
TTS
图像生成
消息投递
Toolset 的核心价值之一,就是把模型上下文从"所有可能的能力"缩小到"当前场景合理的能力"。这不仅降低了 token 消耗,也减少了模型误用工具的概率——尤其是在使用较小模型或长对话时,效果尤为明显。
五、分类 Toolset 的语义:让用户知道自己在开什么
TOOLSETS 字典里的分类并非随意命名,它们各自对应一类清晰的场景能力:
| web | web_search, web_extract | 搜索与内容提取 |
| terminal | terminal, process | 命令执行与进程管理 |
| file | read_file, write_file, patch, search_files | 代码与文档修改 |
| browser | navigate, click, type, scroll 等 | 真实网页交互 |
| skills | skills list/view/manage | 技能检索与维护 |
| memory | memory | 长期记忆读写 |
| cronjob | cronjob | 定时任务管理 |
| messaging | send_message | 跨平台消息投递 |
| computer_use | computer_use | 桌面控制 |
分类清晰,用户在 hermes tools 或配置文件中启用 / 禁用时,才能准确理解自己正在打开什么能力,也在关闭什么风险。如果把"浏览器、数据库查询、消息投递"打包成一个叫 advanced 的集合,用户启用时根本不知道后果,这违反了工具治理的基本原则。
六、动态 gate:工具存在 ≠ 工具可用
有些工具的可用性取决于外部环境,而不是简单的配置开关:
- Home Assistant 需要 token 和服务地址
- Computer use 需要对应的驱动和权限
- Kanban 工具只应在 worker 或显式启用时出现
- Browser 工具需要浏览器实例或 CDP 环境
- 云后端工具需要 SDK 或有效认证
这些不应该靠用户记忆来避免误用。Hermes 通过 check_fn 和 toolset 级别的可用性检查,在生成 schema 之前就把不可用工具过滤掉。
check_fn 缓存机制
tools/registry.py 中为 check_fn 加了一个约 30 秒的 TTL 缓存——既避免了每轮对话都做昂贵探测,又保证环境变化后能及时反映:
#mermaid-svg-t4aHSxgSXYiKNnCY{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t4aHSxgSXYiKNnCY .error-icon{fill:#552222;}#mermaid-svg-t4aHSxgSXYiKNnCY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t4aHSxgSXYiKNnCY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t4aHSxgSXYiKNnCY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY .marker.cross{stroke:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t4aHSxgSXYiKNnCY p{margin:0;}#mermaid-svg-t4aHSxgSXYiKNnCY defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-t4aHSxgSXYiKNnCY g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-t4aHSxgSXYiKNnCY g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-t4aHSxgSXYiKNnCY g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-t4aHSxgSXYiKNnCY g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-t4aHSxgSXYiKNnCY .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-t4aHSxgSXYiKNnCY .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-t4aHSxgSXYiKNnCY .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-t4aHSxgSXYiKNnCY .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-t4aHSxgSXYiKNnCY .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-t4aHSxgSXYiKNnCY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t4aHSxgSXYiKNnCY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t4aHSxgSXYiKNnCY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t4aHSxgSXYiKNnCY .edgeLabel .label text{fill:#333;}#mermaid-svg-t4aHSxgSXYiKNnCY .label div .edgeLabel{color:#333;}#mermaid-svg-t4aHSxgSXYiKNnCY .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-t4aHSxgSXYiKNnCY .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-t4aHSxgSXYiKNnCY .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-t4aHSxgSXYiKNnCY .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY #statediagram-barbEnd{fill:#333333;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t4aHSxgSXYiKNnCY .cluster-label,#mermaid-svg-t4aHSxgSXYiKNnCY .nodeLabel{color:#131300;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-t4aHSxgSXYiKNnCY .note-edge{stroke-dasharray:5;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-note text{fill:black;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram-note .nodeLabel{color:black;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagram .edgeLabel{color:red;}#mermaid-svg-t4aHSxgSXYiKNnCY #dependencyStart,#mermaid-svg-t4aHSxgSXYiKNnCY #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-t4aHSxgSXYiKNnCY .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t4aHSxgSXYiKNnCY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
check_fn() 返回 True
check_fn() 返回 False
记录时间戳, TTL=30s
记录时间戳, TTL=30s
TTL 过期, 重新检查
TTL 过期, 重新检查
TTL 未过期, 直接返回
TTL 未过期, 直接返回
未检查
可用
不可用
缓存可用
缓存不可用
这也意味着,"工具已注册但模型不调用"的另一个常见原因,就是 check_fn 返回了 false,导致工具被静默移除。
七、常见错误与排查清单
在实际使用和二次开发中,与 Toolset 相关的错误非常集中:
| 只注册工具,不加入任何 toolset | 模型永远看不到该工具 |
| 把高风险工具加入 WEBHOOK_SAFE_TOOLS | 外部提示注入风险急剧放大 |
| 工具分类命名模糊 | 用户启用后不清楚自己暴露了什么能力 |
| 缺少 check_fn | 模型可能调用一个当前环境根本不存在的工具,浪费一轮 API 调用 |
| 核心集合无节制膨胀 | 所有用户和平台的提示成本一起上涨 |
| 禁用 toolset 后缓存未刷新 | 配置与实际 schema 不一致,排查困难 |
工具不可见排查决策树
如果你在调试"模型为什么不调我的工具",建议按下面的决策树排查,而不是直接质疑模型:
#mermaid-svg-9SVvIFFcAILs3YoA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9SVvIFFcAILs3YoA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9SVvIFFcAILs3YoA .error-icon{fill:#552222;}#mermaid-svg-9SVvIFFcAILs3YoA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9SVvIFFcAILs3YoA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9SVvIFFcAILs3YoA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9SVvIFFcAILs3YoA .marker.cross{stroke:#333333;}#mermaid-svg-9SVvIFFcAILs3YoA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9SVvIFFcAILs3YoA p{margin:0;}#mermaid-svg-9SVvIFFcAILs3YoA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9SVvIFFcAILs3YoA .cluster-label text{fill:#333;}#mermaid-svg-9SVvIFFcAILs3YoA .cluster-label span{color:#333;}#mermaid-svg-9SVvIFFcAILs3YoA .cluster-label span p{background-color:transparent;}#mermaid-svg-9SVvIFFcAILs3YoA .label text,#mermaid-svg-9SVvIFFcAILs3YoA span{fill:#333;color:#333;}#mermaid-svg-9SVvIFFcAILs3YoA .node rect,#mermaid-svg-9SVvIFFcAILs3YoA .node circle,#mermaid-svg-9SVvIFFcAILs3YoA .node ellipse,#mermaid-svg-9SVvIFFcAILs3YoA .node polygon,#mermaid-svg-9SVvIFFcAILs3YoA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9SVvIFFcAILs3YoA .rough-node .label text,#mermaid-svg-9SVvIFFcAILs3YoA .node .label text,#mermaid-svg-9SVvIFFcAILs3YoA .image-shape .label,#mermaid-svg-9SVvIFFcAILs3YoA .icon-shape .label{text-anchor:middle;}#mermaid-svg-9SVvIFFcAILs3YoA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9SVvIFFcAILs3YoA .rough-node .label,#mermaid-svg-9SVvIFFcAILs3YoA .node .label,#mermaid-svg-9SVvIFFcAILs3YoA .image-shape .label,#mermaid-svg-9SVvIFFcAILs3YoA .icon-shape .label{text-align:center;}#mermaid-svg-9SVvIFFcAILs3YoA .node.clickable{cursor:pointer;}#mermaid-svg-9SVvIFFcAILs3YoA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9SVvIFFcAILs3YoA .arrowheadPath{fill:#333333;}#mermaid-svg-9SVvIFFcAILs3YoA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9SVvIFFcAILs3YoA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9SVvIFFcAILs3YoA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9SVvIFFcAILs3YoA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9SVvIFFcAILs3YoA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9SVvIFFcAILs3YoA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9SVvIFFcAILs3YoA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9SVvIFFcAILs3YoA .cluster text{fill:#333;}#mermaid-svg-9SVvIFFcAILs3YoA .cluster span{color:#333;}#mermaid-svg-9SVvIFFcAILs3YoA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9SVvIFFcAILs3YoA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9SVvIFFcAILs3YoA rect.text{fill:none;stroke-width:0;}#mermaid-svg-9SVvIFFcAILs3YoA .icon-shape,#mermaid-svg-9SVvIFFcAILs3YoA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9SVvIFFcAILs3YoA .icon-shape p,#mermaid-svg-9SVvIFFcAILs3YoA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9SVvIFFcAILs3YoA .icon-shape .label rect,#mermaid-svg-9SVvIFFcAILs3YoA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9SVvIFFcAILs3YoA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9SVvIFFcAILs3YoA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9SVvIFFcAILs3YoA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
否
是
否
是
是
否
否
是
是
否
否
是
否
是
模型不调用我的工具
1. 顶层 registry.register?
修复: 添加顶层注册
2. 已加入 toolset?
修复: 加入对应 toolset
3. toolset 在 enabled 列表?
修复: 启用该 toolset
4. toolset 在 disabled 列表?
修复: 从 disabled 列表移除
5. check_fn 返回 True?
修复: 检查环境依赖
6. 当前入口是否限制工具集?
修复: 换入口或调整入口工具集
7. schema 描述是否清晰?
修复: 优化 description 和 parameters
8. 用户 prompt 是否匹配工具意图?
建议: 调整 prompt 触发方式
排查其他因素:模型能力/温度/历史污染
八、实战案例:新增数据库查询工具,该放进哪个集合?
假设团队需要新增一个 database_query 工具,允许模型查询内部只读数据库。这个工具看似无害——只读、不写文件、不执行 shell。但它是否应该进入 _HERMES_CORE_TOOLS?
决策流程如下:
#mermaid-svg-7MFKAw04YoZlWsch{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7MFKAw04YoZlWsch .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7MFKAw04YoZlWsch .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7MFKAw04YoZlWsch .error-icon{fill:#552222;}#mermaid-svg-7MFKAw04YoZlWsch .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7MFKAw04YoZlWsch .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7MFKAw04YoZlWsch .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7MFKAw04YoZlWsch .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7MFKAw04YoZlWsch .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7MFKAw04YoZlWsch .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7MFKAw04YoZlWsch .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7MFKAw04YoZlWsch .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7MFKAw04YoZlWsch .marker.cross{stroke:#333333;}#mermaid-svg-7MFKAw04YoZlWsch svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7MFKAw04YoZlWsch p{margin:0;}#mermaid-svg-7MFKAw04YoZlWsch .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-7MFKAw04YoZlWsch .cluster-label text{fill:#333;}#mermaid-svg-7MFKAw04YoZlWsch .cluster-label span{color:#333;}#mermaid-svg-7MFKAw04YoZlWsch .cluster-label span p{background-color:transparent;}#mermaid-svg-7MFKAw04YoZlWsch .label text,#mermaid-svg-7MFKAw04YoZlWsch span{fill:#333;color:#333;}#mermaid-svg-7MFKAw04YoZlWsch .node rect,#mermaid-svg-7MFKAw04YoZlWsch .node circle,#mermaid-svg-7MFKAw04YoZlWsch .node ellipse,#mermaid-svg-7MFKAw04YoZlWsch .node polygon,#mermaid-svg-7MFKAw04YoZlWsch .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7MFKAw04YoZlWsch .rough-node .label text,#mermaid-svg-7MFKAw04YoZlWsch .node .label text,#mermaid-svg-7MFKAw04YoZlWsch .image-shape .label,#mermaid-svg-7MFKAw04YoZlWsch .icon-shape .label{text-anchor:middle;}#mermaid-svg-7MFKAw04YoZlWsch .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7MFKAw04YoZlWsch .rough-node .label,#mermaid-svg-7MFKAw04YoZlWsch .node .label,#mermaid-svg-7MFKAw04YoZlWsch .image-shape .label,#mermaid-svg-7MFKAw04YoZlWsch .icon-shape .label{text-align:center;}#mermaid-svg-7MFKAw04YoZlWsch .node.clickable{cursor:pointer;}#mermaid-svg-7MFKAw04YoZlWsch .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7MFKAw04YoZlWsch .arrowheadPath{fill:#333333;}#mermaid-svg-7MFKAw04YoZlWsch .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7MFKAw04YoZlWsch .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7MFKAw04YoZlWsch .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7MFKAw04YoZlWsch .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7MFKAw04YoZlWsch .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7MFKAw04YoZlWsch .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7MFKAw04YoZlWsch .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7MFKAw04YoZlWsch .cluster text{fill:#333;}#mermaid-svg-7MFKAw04YoZlWsch .cluster span{color:#333;}#mermaid-svg-7MFKAw04YoZlWsch div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7MFKAw04YoZlWsch .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7MFKAw04YoZlWsch rect.text{fill:none;stroke-width:0;}#mermaid-svg-7MFKAw04YoZlWsch .icon-shape,#mermaid-svg-7MFKAw04YoZlWsch .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7MFKAw04YoZlWsch .icon-shape p,#mermaid-svg-7MFKAw04YoZlWsch .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7MFKAw04YoZlWsch .icon-shape .label rect,#mermaid-svg-7MFKAw04YoZlWsch .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7MFKAw04YoZlWsch .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7MFKAw04YoZlWsch .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7MFKAw04YoZlWsch :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
是
否
是
是
不应
应
否
否
是
是
否
否
新增 database_query 工具
所有 Hermes 用户都需要?
做成插件工具, opt-in toolset
是否需要数据库凭据?
有无 check_fn 验证连接?
添加 check_fn
可能泄露敏感数据?
是否应进入 webhook safe?
明确标记为不安全
结论: 不应进入 safe tools
查询是否为只读?
添加审批或 guardrail
输出可能很大?
添加 limit/分页/摘要
可以进入对应 toolset
答案通常是不进入核心集合,理由是:
- 它依赖特定的数据库凭据,所有用户默认获得这个工具会带来安全风险
- 查询结果可能包含敏感数据,不应在 Webhook 等低可信入口暴露
- 输出可能非常大,需要额外处理分页和截断
更合理的做法是:做成插件工具,归属一个名为 database 的独立 toolset,默认不启用,由有需求的团队在配置中显式开启。同时为它添加 check_fn 验证数据库连接,并在 handler 中限制单次返回行数。
总结
Toolset 是 Hermes 工具系统的"策略层"。Registry 决定了工具的存在,Toolset 决定了工具在什么场景下可见、对谁可见、需要付出多少提示成本和安全代价。下面这张图将 Toolset 的三重角色——安全边界、成本控制、动态门禁——聚合在一个模型中,方便你整体理解它的设计逻辑:
#mermaid-svg-TMZO0ydQwYkWz9LI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TMZO0ydQwYkWz9LI .error-icon{fill:#552222;}#mermaid-svg-TMZO0ydQwYkWz9LI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TMZO0ydQwYkWz9LI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TMZO0ydQwYkWz9LI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TMZO0ydQwYkWz9LI .marker.cross{stroke:#333333;}#mermaid-svg-TMZO0ydQwYkWz9LI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TMZO0ydQwYkWz9LI p{margin:0;}#mermaid-svg-TMZO0ydQwYkWz9LI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster-label text{fill:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster-label span{color:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster-label span p{background-color:transparent;}#mermaid-svg-TMZO0ydQwYkWz9LI .label text,#mermaid-svg-TMZO0ydQwYkWz9LI span{fill:#333;color:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI .node rect,#mermaid-svg-TMZO0ydQwYkWz9LI .node circle,#mermaid-svg-TMZO0ydQwYkWz9LI .node ellipse,#mermaid-svg-TMZO0ydQwYkWz9LI .node polygon,#mermaid-svg-TMZO0ydQwYkWz9LI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TMZO0ydQwYkWz9LI .rough-node .label text,#mermaid-svg-TMZO0ydQwYkWz9LI .node .label text,#mermaid-svg-TMZO0ydQwYkWz9LI .image-shape .label,#mermaid-svg-TMZO0ydQwYkWz9LI .icon-shape .label{text-anchor:middle;}#mermaid-svg-TMZO0ydQwYkWz9LI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TMZO0ydQwYkWz9LI .rough-node .label,#mermaid-svg-TMZO0ydQwYkWz9LI .node .label,#mermaid-svg-TMZO0ydQwYkWz9LI .image-shape .label,#mermaid-svg-TMZO0ydQwYkWz9LI .icon-shape .label{text-align:center;}#mermaid-svg-TMZO0ydQwYkWz9LI .node.clickable{cursor:pointer;}#mermaid-svg-TMZO0ydQwYkWz9LI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TMZO0ydQwYkWz9LI .arrowheadPath{fill:#333333;}#mermaid-svg-TMZO0ydQwYkWz9LI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TMZO0ydQwYkWz9LI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TMZO0ydQwYkWz9LI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TMZO0ydQwYkWz9LI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TMZO0ydQwYkWz9LI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TMZO0ydQwYkWz9LI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster text{fill:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI .cluster span{color:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TMZO0ydQwYkWz9LI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TMZO0ydQwYkWz9LI rect.text{fill:none;stroke-width:0;}#mermaid-svg-TMZO0ydQwYkWz9LI .icon-shape,#mermaid-svg-TMZO0ydQwYkWz9LI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TMZO0ydQwYkWz9LI .icon-shape p,#mermaid-svg-TMZO0ydQwYkWz9LI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TMZO0ydQwYkWz9LI .icon-shape .label rect,#mermaid-svg-TMZO0ydQwYkWz9LI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TMZO0ydQwYkWz9LI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TMZO0ydQwYkWz9LI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TMZO0ydQwYkWz9LI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Toolset 三重角色
安全边界
提示成本控制
动态门禁
入口可信度分层
Webhook: 仅 4 个安全工具
CLI: 50+ 核心工具
Gateway: 按聊类型降级
场景化暴露
代码任务: 隐藏 Home Assistant/TTS
家居任务: 隐藏 patch/terminal
减少 token 消耗
降低模型误选率
运行时可用性
check_fn: Docker/浏览器/Token
30s TTL 缓存
静默过滤不可用工具
一个成熟的 Agent 系统,不该把所有能力一股脑塞给模型。真正好的设计,是让模型在正确的场景看到正确的工具,并让用户清楚自己打开了什么,也清楚自己关掉了什么。Hermes 的 toolsets.py,正是在朝着这个方向持续演进。
下一篇,我们将深入 Hermes 的插件系统,看看如何通过 hooks 和 middleware,在不修改核心代码的前提下,为 Agent 扩展自定义行为。欢迎持续关注。
