模型只会“发请求”,Hermes 才会“真执行”:Tool Call 从模型输出到真实动作的完整链路
1、先把问题说透:Tool Call 到底是什么?
很多人第一次接触 Agent,容易以为“大模型自己打开网页、自己改代码、自己运行命令”。这个理解并不准确。更准确的说法是:模型不会直接操作电脑,它只是根据当前上下文输出一个结构化的工具调用请求;真正执行动作的是 Hermes Agent 的运行时系统。
在 Hermes 里,模型输出的 tool_call 通常包含三类关键信息:工具名、参数和调用 ID。工具名决定要找哪个函数,参数决定这个函数怎么执行,调用 ID 负责把执行结果和原始工具调用准确对应起来。
所以 Tool Call 的本质不是“模型拥有超能力”,而是 Agent Runtime 给模型提供了一套可控的外部能力接口。模型负责判断“该用哪个工具”,Hermes 负责“能不能用、怎么执行、怎么回填、出错怎么办”。
2.1、模型为什么知道有哪些工具可以用?
在模型真正输出 tool_call 之前,Hermes 会先把当前可用工具整理成 Tools Schema。这个 Schema 就像一本“工具说明书”,告诉模型:有哪些工具、每个工具适合干什么、参数是什么类型、哪些参数是必填。
官方 Tools Runtime 文档说明,Hermes 的工具是自注册函数,按 toolsets 分组,并通过中央 registry/dispatch 系统执行。model_tools.py 负责发现工具模块,并构建给模型使用的 schema 列表。
2.2、工具从哪里来:Registry 自注册机制
Hermes 不是在一个巨大列表里手动维护所有工具,而是让每个工具模块在导入时调用 registry.register(...) 完成自注册。一个 ToolEntry 里通常包含 name、toolset、schema、handler、check_fn、requires_env、is_async 等信息。
这个设计有一个很大的工程价值:新增工具时,不需要到处改代码,只要工具文件完成注册,Hermes 就可以在启动或发现阶段把它纳入工具系统。后续是否暴露给模型,再由 toolset 和可用性检查决定。
3、不是所有工具都会给模型看:Toolsets 和可用性过滤
一个成熟的 Agent 不能把所有工具一股脑塞给模型。工具太多会增加 token 成本,也会增加模型误调用的概率。Hermes 用 toolsets 做第一层过滤,用 check_fn 做第二层可用性检查。
例如 web_search 可能需要 API Key,browser 工具可能需要浏览器环境,terminal 工具可能需要特定运行后端。如果 check_fn 判断不可用,这个工具就不会出现在本轮 schema 里。模型看不到它,自然就不应该调用它。
官方文档还提到,Hermes 会对部分动态 schema 做修补:比如 execute_code 或 browser_navigate 的描述会根据实际可用工具调整,避免模型在描述里看到一个实际上不可用的工具名后产生幻觉调用。
4.1、模型输出之后,AIAgent 怎么判断下一步?
当模型返回 response 后,Hermes 的 Agent Loop 会先看里面有没有 tool_calls。如果没有,说明模型已经给出了文本回复,可以保存会话、flush memory,然后把最终回答返回给用户。
如果 response 里有 tool_calls,Hermes 就不会立刻把这段内容当最终答案,而是进入工具执行分支:解析每个工具调用,找到对应 handler,执行真实动作,然后把结果以 role=tool 的消息写回历史,再进入下一轮模型调用。
4.2、核心链路:从 tool_call 到 handler 执行
官方 Tools Runtime 文档给出的主链路很清楚:模型返回 tool_call 后,run_agent.py 的 agent loop 接管;然后调用 model_tools.handle_function_call(name, args, task_id, user_task);普通工具再进入 registry.dispatch(name, args, **kwargs);registry 根据工具名查找 ToolEntry,最后执行对应 handler。
这里可以把 Hermes 理解成一个“工具调度中心”:模型只说“我要调用 read_file,参数是 path=xxx”,调度中心负责确认 read_file 是否存在、是否可用、参数是否合理、应该同步执行还是异步执行、执行失败怎么包装。
5.1、为什么有些工具会被 AIAgent 提前拦截?
并不是所有工具都会走普通 registry.dispatch。Hermes 文档明确列出,有四类 agent-level tools 会被 run_agent.py 提前拦截:todo、memory、session_search、delegate_task。
原因很简单:这些工具不是普通外部函数,它们需要直接操作 Agent 自己的内部状态。todo 要读写任务状态,memory 要写持久记忆文件,session_search 要访问当前会话数据库,delegate_task 要创建子 Agent 和子会话。
这类工具的 schema 仍然可以注册给模型看,但真正执行时由 Agent Loop 直接处理,避免把内部状态管理泄露给普通工具派发层。
6.1、多个 Tool Call 怎么执行:顺序还是并发?
一次模型回复里可能包含多个 tool_calls。Hermes 会根据工具性质决定执行方式:单个工具调用通常直接执行;多个工具调用可以通过 ThreadPoolExecutor 并发执行;但如果工具需要用户交互,比如 clarify,就会强制顺序执行。
并发执行的结果并不是谁先完成就谁先写回。官方文档说明,多个工具结果会按原始 tool_call 顺序重新插入历史,这样可以避免模型下一轮看到错位的结果。
7.1、真实动作最危险的地方:Terminal 工具如何加安全阀
工具系统里最需要谨慎的是 terminal。它可以执行命令,能力强,也最容易造成破坏。因此 Hermes 在 terminal 工具里集成了危险命令审批系统。
官方文档说明,tools/approval.py 中定义了 DANGEROUS_PATTERNS,用来识别 rm -rf、mkfs、dd、DROP TABLE、curl | sh、systemctl stop 等风险命令。一旦命中危险模式,CLI 会弹出交互审批,Gateway 模式会通过平台回调发起审批。
这个设计说明了一个重要原则:Agent 能调用工具,不等于工具可以无条件执行。能改文件、删文件、跑命令的能力,必须配套权限、审批、隔离和回滚。
8.1、工具出错怎么办:错误也要变成上下文
真实世界里的工具一定会失败:文件不存在、网络超时、API Key 缺失、参数类型不对、浏览器没启动、命令执行报错。Hermes 的处理方式不是让程序崩掉,而是把错误包装成模型可读的结果。
Tools Runtime 文档说明,registry.dispatch 会捕获 handler 异常并返回 JSON 错误;handle_function_call 外层还会再做一次 try/except 兜底。这样模型下一轮能看到失败原因,然后决定重试、换工具、修正参数,或者向用户解释无法完成。
9.1、MCP 和插件工具如何接入同一条链路?
Hermes 的工具不只来自内置 tools/*.py。外部 MCP Server 和插件也能注册工具。关键是:不管工具来自哪里,最终都要被转换成统一的工具 schema,并进入统一的 registry/dispatch 体系。
这让 Hermes 可以把 GitHub、数据库、文件系统、浏览器栈、内部 API 等外部系统接进来,而 Agent Loop 不需要为每一种系统写一套新流程。模型看到的仍然是 name + arguments,调度层看到的仍然是 ToolEntry 和 handler。
10.1、用一个真实任务串起来:修复代码报错
假设用户说:“帮我修复这个项目的测试报错。”Hermes 不会一次性凭空给答案,而是可能先调用 read_file 读取相关源码,再调用 terminal 跑测试,然后根据失败日志调用 patch 修改代码,再跑 terminal 验证结果。
这就是 Tool Call 的真正价值:它把“模型的推理”变成了“可验证的行动链”。每一步都有输入、执行、输出和回填,模型不是孤立地产生文本,而是在工具结果推动下持续推进任务。
11.1、源码应该怎么读:顺着执行链路,不要乱翻
如果你想从源码层面理解 Tool Call,不建议直接从 15k+ 行的 run_agent.py 头读到尾。更好的方式是顺着链路读:run_agent.py 看模型返回后怎么处理 tool_calls;model_tools.py 看工具定义和 handle_function_call;tools/registry.py 看 register 和 dispatch;最后再看具体工具文件。
再深入一点,可以继续看 toolsets.py 理解工具集展开,看 tools/approval.py 理解危险命令审批,看 tools/mcp_tool.py 理解外部 MCP 工具如何注册。这样读,脑子里会有一条清晰主线。
12.1、最后总结:Tool Call 是 Agent 从“会说”走向“会做”的关键桥梁
第一,模型不直接执行动作。它输出的是结构化 tool_call,请求 Hermes 去调用某个工具。
第二,工具必须先注册、过滤、暴露。Registry 管工具,Toolsets 管范围,check_fn 管可用性。
第三,执行由 Agent Runtime 负责。run_agent.py、model_tools.py、registry.py 串起派发链路。
第四,工具结果会回到对话历史。role=tool 的结果会进入下一轮模型推理,形成闭环。
第五,安全和错误处理是核心工程能力。危险命令审批、异常包装、顺序/并发控制,决定 Agent 能不能稳定落地。
一句话概括:Tool Call 不是让模型变成操作系统,而是在模型和真实世界之间加了一层可控、可审计、可回滚、可扩展的执行系统。Hermes Agent 的价值,正是把这条链路工程化了。
