1. 为什么工具系统是 Agent 最危险的部分一个只会聊天的模型最多是说错话。一个能调用工具的 Agent可能会删文件、跑命令、发消息、调用外部 API、读取密钥、修改代码。所以生产级 Agent 的安全边界不在 prompt而在工具系统。可以用一句话概括模型负责提出动作Harness 负责判断能不能做。Hermes 的源码非常适合说明这件事。它不是把工具写成一堆散函数而是有完整的工具注册、工具集过滤、危险命令审批、沙箱执行、MCP 工具接入和凭证隔离机制。2. 第一层ToolRegistry 让工具先变成“受管对象”Hermes 的工具注册中心在tools/registry.py。文件开头就说明了设计Central registry for all hermes-agent tools. Each tool file calls registry.register() at module level to declare its schema, handler, toolset membership, and availability check. 核心注册结构可以简化为classToolEntry:def__init__(self,name,toolset,schema,handler,check_fn,requires_env,is_async,description,emoji,max_result_size_charsNone):self.namename self.toolsettoolset self.schemaschema self.handlerhandler self.check_fncheck_fn self.requires_envrequires_env self.is_asyncis_async self.descriptiondescription self.emojiemoji self.max_result_size_charsmax_result_size_chars这说明工具不是普通函数而是带元数据的受管对象。一个工具至少要说清楚它叫什么属于哪个 toolset参数 schema 是什么由哪个 handler 执行需要哪些环境变量是否可用最大输出多大。对新手来说可以把ToolRegistry理解成“工具仓库管理员”。模型不能随便拿工具必须先通过这个仓库。3. 第二层工具自动发现Hermes 不是在model_tools.py里手写一个巨大工具列表而是扫描tools/*.py找模块顶层是否调用了registry.register(...)def_module_registers_tools(module_path:Path)-bool:sourcemodule_path.read_text(encodingutf-8)treeast.parse(source,filenamestr(module_path))returnany(_is_registry_register_call(stmt)forstmtintree.body)然后导入这些模块defdiscover_builtin_tools(tools_dirNone):module_names[ftools.{path.stem}forpathinsorted(tools_path.glob(*.py))ifpath.namenotin{__init__.py,registry.py,mcp_tool.py}and_module_registers_tools(path)]formod_nameinmodule_names:importlib.import_module(mod_name)这个设计有两个好处。第一新增工具不用改中心文件只要在工具文件里注册。第二只有真正声明了注册行为的文件才会被加载减少误导入。4. 第三层toolset 控制模型能看到哪些工具工具注册后并不代表模型一定能用。toolsets.py把工具按场景分组TOOLSETS{web:{description:Web research and content extraction tools,tools:[web_search,web_extract],includes:[]},terminal:{description:Terminal/command execution and process management tools,tools:[terminal,process],includes:[]},file:{description:File manipulation tools,tools:[read_file,write_file,patch,search_files],includes:[]},}model_tools.py会根据 enabled / disabled toolsets 计算最终工具集合ifenabled_toolsetsisnotNone:fortoolset_nameinenabled_toolsets:resolvedresolve_toolset(toolset_name)tools_to_include.update(resolved)filtered_toolsregistry.get_definitions(tools_to_include,quietquiet_mode)通俗解释工具不是全员上桌而是按场景发通行证。如果你做客服 Agent可能只给知识库和工单工具如果做代码 Agent才给文件、终端、测试工具。5. 第四层工具 schema 会动态修正Hermes 里一个很专业的细节是工具 schema 不是完全静态的。例如execute_code工具可能允许在沙箱里调用部分工具但这必须根据当前会话真实可用工具来决定ifexecute_codeinavailable_tool_names:sandbox_enabledSANDBOX_ALLOWED_TOOLSavailable_tool_names dynamic_schemabuild_execute_code_schema(sandbox_enabled,mode_get_execution_mode())为什么要这么做因为如果 schema 里写着“你可以用 web_search”但实际 web_search 没有配置 API key模型就容易幻觉调用不可用工具。Hermes 通过动态 schema 降低这种错配。这就是生产系统和 demo 的差别demo 只求跑通生产系统要减少模型误会。6. 第五层危险命令审批Hermes 的危险命令审批在tools/approval.py。文件开头写得很清楚Dangerous command approval -- detection, prompting, and per-session state. This module is the single source of truth for the dangerous command system: - Pattern detection - Per-session approval state - Smart approval via auxiliary LLM - Permanent allowlist persistence 核心流程可以理解成defcheck_dangerous_command(command,env_type,approval_callbackNone):is_dangerous,pattern_key,descriptiondetect_dangerous_command(command)ifnotis_dangerous:return{approved:True}choiceprompt_dangerous_approval(command,description)ifchoicedeny:return{approved:False,message:BLOCKED: User denied this potentially dangerous command}return{approved:True}真实源码比这个复杂支持manual人工审批smart辅助 LLM 判断低风险命令是否可自动批准off关闭审批yolo当前 session 放行once / session / always / deny多种用户选择。新手要记住危险命令审批不是“模型自己判断危险不危险”而是代码里的安全模块判断。7. 第六层会话级审批隔离Hermes 使用contextvars绑定当前审批会话_approval_session_keycontextvars.ContextVar(approval_session_key,default,)defset_current_session_key(session_key:str):return_approval_session_key.set(session_keyor)为什么重要因为 Hermes 可以跑 CLI、gateway、多平台消息、子 Agent。多个会话可能同时等待审批。如果审批状态用全局变量乱放就可能出现 A 会话批准了命令B 会话误以为自己也被批准。contextvars的作用就是让并发上下文里的“当前会话”不串线。8. 第七层终端执行有沙箱后端Hermes 的tools/terminal_tool.py文件开头说明Supports local execution, containerized backends, and Modal cloud sandboxes.这说明终端工具不只是subprocess.run()。Hermes 会根据配置选择执行环境管理 sandbox 生命周期处理后台进程、工作目录、清理逻辑等。生产场景里终端工具最危险。Hermes 的做法是把它包成受控服务模型提出 command - approval.py 判断是否危险 - terminal_tool 选择执行环境 - sandbox 执行 - 输出清洗和截断 - 返回 tool result如果用普通人的话说模型不是直接摸服务器而是隔着一个带门禁的操作台。9. 第八层凭证不能随便进沙箱Hermes 里有tools/credential_files.py和tools/env_passthrough.py。这两个模块非常现实。很多安全事故不是因为命令本身有多危险而是因为命令拿到了不该拿的凭证。例如沙箱需要某个 API key 才能运行测试远程环境需要少量 credential file但不能把整个 home 目录挂进去也不能把所有环境变量透传进去。所以 Hermes 用专门模块管理“哪些凭证可以进沙箱”。这比简单说“我们有沙箱”更专业。10. 第九层MCP 工具也进入统一注册Hermes 的tools/mcp_tool.py很大说明 MCP 不是随便拼接进去的。源码里能看到 MCP 相关能力连接 MCP serverOAuth 恢复HTTP / stdio transport工具发现工具名冲突保护动态注册和注销断线重连关闭时清理子进程。MCP server 提供的工具最终也会注册进 Hermes 的 registry。也就是说外部工具进入系统后也要经过 Hermes 的工具生命周期管理。11. 给新手的完整心智模型可以把 Hermes 工具系统想成一栋工厂ToolRegistry是工具仓库toolsets.py是工种权限表model_tools.py是调度员approval.py是安检terminal_tool.py是危险设备操作间sandbox 是隔离车间MCP 是外部供应商接入通道credential/env 管理是钥匙柜。模型不是老板不是它说用什么就用什么。模型只是提出需求Harness 才决定能不能执行。12. 结论结合 Hermes 源码看工具系统的专业性体现在工具统一注册工具按 toolset 暴露schema 根据可用能力动态修正Agent-level 工具由主循环截获高风险命令进入审批沙箱执行隔离副作用凭证透传受控MCP 工具纳入统一生命周期。所以Tool Permission 与 Sandbox 不是可选增强而是 Agent 从玩具走向生产的底线工程。参考资料Hermes 源码tools/registry.pyHermes 源码model_tools.pyHermes 源码toolsets.pyHermes 源码tools/approval.pyHermes 源码tools/terminal_tool.pyHermes 源码tools/code_execution_tool.pyHermes 源码tools/mcp_tool.pyMCP Security Best Practices: https://modelcontextprotocol.io/docs/tutorials/security/security_best_practicesOWASP Top 10 for LLM Applications: https://owasp.org/www-project-top-10-for-large-language-model-applications/