<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tool-System | 有志者事竟成</title><link>https://www.liwenshen.com/tags/tool-system/</link><atom:link href="https://www.liwenshen.com/tags/tool-system/index.xml" rel="self" type="application/rss+xml"/><description>Tool-System</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh</language><lastBuildDate>Wed, 01 Jul 2026 10:00:00 +0800</lastBuildDate><image><url>https://www.liwenshen.com/media/icon_hu_dd5d76fef920c49e.png</url><title>Tool-System</title><link>https://www.liwenshen.com/tags/tool-system/</link></image><item><title>工具系统：从注册到调度</title><link>https://www.liwenshen.com/note/hermes-agent-engineering/03-tool-system/</link><pubDate>Wed, 01 Jul 2026 10:00:00 +0800</pubDate><guid>https://www.liwenshen.com/note/hermes-agent-engineering/03-tool-system/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;通过 Hermes 探秘 Agent 工程 | 第 3 篇&lt;/strong&gt; · &lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/"&gt;查看全部&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上一篇：&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/system-prompt/"&gt;System Prompt：身份、上下文与策略的三层架构&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;[系列文章导航]&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;文章&lt;/th&gt;
&lt;th&gt;定位&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/agent-loop/"&gt;Agent Loop：Agent 的核心执行循环&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;入口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/system-prompt/"&gt;System Prompt：身份、上下文与策略的三层架构&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;认知层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;工具系统：从注册到调度&lt;/td&gt;
&lt;td&gt;工具层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/tool-dispatch/"&gt;工具调度系统：从注册到执行的完整生命周期&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;调度层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/security/"&gt;安全防护体系：当 Agent 拥有终端时如何防止「做出格」的事&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;安全层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/sandbox/"&gt;沙箱与代码执行：让 Agent 安全跑代码的 RPC 架构&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;执行层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/context-compression/"&gt;上下文压缩：让 Agent 在有限窗口里「记得住」&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;记忆层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/memory-system/"&gt;记忆系统：跨会话持久化的工程实现&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;记忆层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/skills-system/"&gt;技能系统：Agent 如何把经验变成可复用的程序化记忆&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;记忆层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/provider-abstraction/"&gt;Provider 抽象层：让 Hermes 同时驾驭 30+ 个 LLM 提供商&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;模型层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/gateway/"&gt;Gateway 网关：连接 20+ 平台的统一消息路由&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;接入层&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/multi-agent/"&gt;多 Agent 协作：委托、调度与看板&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;协作层&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="工具系统解决了什么问题"&gt;工具系统解决了什么问题？&lt;/h2&gt;
&lt;p&gt;一个 Agent 再聪明，如果只能&amp;quot;说话&amp;quot;不能&amp;quot;做事&amp;quot;，就只是一个聊天机器人。工具系统就是 Agent 的手脚——让模型能读写文件、执行命令、搜索网络、操作浏览器、管理定时任务……&lt;/p&gt;
&lt;p&gt;但工具越多，管理越复杂：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;怎么让新增的工具被 Agent 感知又不改核心代码？&lt;/li&gt;
&lt;li&gt;怎么让不同平台（CLI vs 网关 vs 子代理）看到不同的工具集？&lt;/li&gt;
&lt;li&gt;怎么让工具调用安全可控？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hermes 用三个机制解决这些问题：&lt;strong&gt;自注册、toolset 分层、capabilities 检查&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="机制一模块级自注册"&gt;机制一：模块级自注册&lt;/h2&gt;
&lt;p&gt;最精巧的设计是——每个工具文件自己注册自己。&lt;/p&gt;
&lt;p&gt;Hermes 规定：任何放在 &lt;code&gt;tools/&lt;/code&gt; 目录下的 Python 文件，只要在模块顶层调用 &lt;code&gt;registry.register()&lt;/code&gt;，就会被系统自动发现。你不需要在一个&amp;quot;总清单&amp;quot;里添加新工具的引用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;registry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;register(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;read_file&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toolset&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;file&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; schema&lt;span style="color:#f92672"&gt;=&lt;/span&gt;READ_FILE_SCHEMA,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handler&lt;span style="color:#f92672"&gt;=&lt;/span&gt;_handle_read_file,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; check_fn&lt;span style="color:#f92672"&gt;=&lt;/span&gt;_check_file_reqs,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; emoji&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;📖&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_result_size_chars&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100_000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;工具名，模型用这个名称调用工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;toolset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;工具归属的分组，用于批量启用/禁用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;schema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI function calling 格式的参数描述&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handler&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;实际执行函数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;check_fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;可用性检查函数（比如检查 Docker 是否可用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;emoji&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;显示用图标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_result_size_chars&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;输出结果的最大字符数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="发现过程ast-扫描"&gt;发现过程：AST 扫描&lt;/h3&gt;
&lt;p&gt;你可能会问：系统怎么知道 &lt;code&gt;tools/&lt;/code&gt; 下哪些文件调用了 &lt;code&gt;registry.register&lt;/code&gt;？&lt;/p&gt;
&lt;p&gt;答案很巧妙——&lt;strong&gt;不用运行代码就能发现&lt;/strong&gt;。Harmes 的 &lt;code&gt;discover_builtin_tools()&lt;/code&gt; 函数在导入模块之前，先对文件做 AST（抽象语法树）扫描，寻找模块顶层的 &lt;code&gt;registry.register()&lt;/code&gt; 调用。只有被 AST 判定为&amp;quot;会注册工具&amp;quot;的模块，才会被真正导入。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;tools/*.py → AST 扫描 → 发现 registry.register() → 导入模块 → 执行注册
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个设计的好处是&lt;strong&gt;零配置扩展&lt;/strong&gt;：丢一个新文件到 &lt;code&gt;tools/&lt;/code&gt;，加一行 &lt;code&gt;registry.register()&lt;/code&gt;，Agent 下次启动就能用。&lt;/p&gt;
&lt;h2 id="机制二toolset-分层"&gt;机制二：Toolset 分层&lt;/h2&gt;
&lt;p&gt;光有注册还不够。50+ 个工具不能同时塞给所有场景——CLI 用户需要 terminal，Telegram bot 不需要；子代理可能只给 read_file 和 write_file；webhook 回调必须严格限制为只读工具。&lt;/p&gt;
&lt;p&gt;Hermes 的办法是&lt;strong&gt;toolset（工具集）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;toolsets.py&lt;/code&gt; 定义了一组命名的工具集，比如 &lt;code&gt;web&lt;/code&gt;（web_search, web_extract）、&lt;code&gt;terminal&lt;/code&gt;（terminal, process）、&lt;code&gt;file&lt;/code&gt;（read_file, write_file, patch, search_files）等。每个 toolset 可以引用其他 toolset（&lt;code&gt;includes&lt;/code&gt; 字段），形成组合。&lt;/p&gt;
&lt;h3 id="三层过滤"&gt;三层过滤&lt;/h3&gt;
&lt;p&gt;最终给模型的工具列表，经过三层过滤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;启用集（enabled_toolsets）&lt;/strong&gt;：用户配置启用了哪些 toolset，取并集&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;禁用集（disabled_toolsets）&lt;/strong&gt;：从并集中减去这些&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;capabilities 检查&lt;/strong&gt;：对每个工具调用 &lt;code&gt;check_fn()&lt;/code&gt;，去掉当前环境不支持的&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="机制三capabilities-检查与缓存"&gt;机制三：capabilities 检查与缓存&lt;/h2&gt;
&lt;p&gt;很多工具的可用性取决于外部环境。比如 &lt;code&gt;terminal&lt;/code&gt; 工具在非 local 后端可能不可用，&lt;code&gt;browser&lt;/code&gt; 需要 Playwright 安装。&lt;/p&gt;
&lt;h3 id="ttl-缓存"&gt;TTL 缓存&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;check_fn&lt;/code&gt; 的结果缓存 30 秒，同一个会话里连续调用不重复检查。&lt;/p&gt;
&lt;h3 id="瞬断抑制"&gt;瞬断抑制&lt;/h3&gt;
&lt;p&gt;更精妙的是&amp;quot;&lt;strong&gt;最近一次成功抑制&lt;/strong&gt;&amp;ldquo;机制：如果某个 &lt;code&gt;check_fn&lt;/code&gt; 最近成功过（证明功能确实可用），那么接下来 60 秒内的失败会被当作&amp;quot;瞬断&amp;quot;忽略掉——工具仍然可用，不报错。&lt;/p&gt;
&lt;h2 id="分发handle_function_call"&gt;分发：handle_function_call()&lt;/h2&gt;
&lt;p&gt;当模型返回工具调用时，Agent 通过 &lt;code&gt;handle_function_call()&lt;/code&gt; 分发。这个函数是整个工具系统的&amp;quot;路由器&amp;rdquo;。&lt;/p&gt;
&lt;h3 id="路由逻辑"&gt;路由逻辑&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;模型输出 tool_calls
↓
handle_function_call(name, args, task_id, ...)
↓
参数类型强制转换（字符串 &amp;#34;42&amp;#34; → 整数 42）
↓
工具 Search bridge 特殊处理
↓
查找 ToolEntry → 调用 handler(args) → 返回 JSON 结果
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="参数类型强制转换"&gt;参数类型强制转换&lt;/h3&gt;
&lt;p&gt;不同模型的 function calling 实现有差异——有些模型会把数字参数传成字符串。&lt;code&gt;handle_function_call()&lt;/code&gt; 会自动根据 schema 做类型强制转换。&lt;/p&gt;
&lt;h2 id="并行执行"&gt;并行执行&lt;/h2&gt;
&lt;p&gt;如果模型一次返回多个独立的工具调用，Hermes 支持并行执行。并行执行的关键是每个工具 handler 要线程安全。&lt;/p&gt;
&lt;h2 id="为什么这样设计"&gt;为什么这样设计？&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;原则&lt;/th&gt;
&lt;th&gt;对应机制&lt;/th&gt;
&lt;th&gt;为什么&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;扩展不改核心&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AST 自注册&lt;/td&gt;
&lt;td&gt;新增工具加一行代码，零配置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;平台差异化&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;toolset 分层&lt;/td&gt;
&lt;td&gt;CLI 和 Telegram 看到不同工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;弹性可用&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;瞬断抑制&lt;/td&gt;
&lt;td&gt;外部服务偶尔抖动不丢工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;按需加载&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool Search Bridge&lt;/td&gt;
&lt;td&gt;工具多时不爆上下文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;类型安全&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;强制转换&lt;/td&gt;
&lt;td&gt;不同模型输出的格式差异被抹平&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;工具系统是 Agent 可靠性的基础。Hermes 用自注册让扩展零成本，用 toolset 让多平台共享代码，用 capabilities 检查让工具可用性可感知，用并行分发让执行不阻塞。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;下一篇：&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/tool-dispatch/"&gt;工具调度系统：从注册到执行的完整生命周期&lt;/a&gt; — Agent 如何在调用 LLM 之前发现哪 50 个工具可用、如何在运行时动态加载/卸载工具集&lt;/p&gt;
&lt;/blockquote&gt;</description></item></channel></rss>