02 · 工具调用
给 AI 接上手:工具调用的完整工程解析
LLM 本质上只做一件事:输入文字 → 输出文字。它不能创建文件、不能发消息、不能打开网页。
它是一个只能说话的嘴,没有手。工具 = 给这只嘴接上的手。
Function Calling:核心协议
技术实现的核心机制——模型本身没有执行任何操作,它只是输出一个"我想调用什么工具、传什么参数"的意图声明,真正干活的是外层 Agent Harness:
用户:"帮我查北京天气"
↓ 模型输出(结构化 JSON):
{
"tool": "get_weather",
"arguments": {"city": "北京"}
}
↓ Agent Harness 真正执行 → 结果返回
↓ 模型拿到结果,组织成自然语言
五层架构
| 层级 | 能力 | 典型工具 |
| 第5层 |
应用层 |
飞书消息、GitHub、Notion |
| 第4层 |
外部服务 |
搜索引擎、图片生成、语音合成 |
| 第3层 |
编程运行时 |
Python 沙盒、JS 执行 |
| 第2层 |
操作系统 |
终端命令、文件读写、进程管理 |
| 第1层 |
硬件抽象 |
GPU、网络、文件系统 |
工具 vs 命令
一个关键区分:工具是 Agent 的能力接口(约 30 个),命令是在终端里执行的具体指令(无限个)。终端是最危险也是最强大的工具——通过它,Agent 理论上可以在电脑上执行任意操作。
💡 安全模型分三层:
① 用户权限(OS 层):Agent 进程跑在哪个用户下,继承哪个用户的权限
② 文件权限(OS 层):rwx 位控制读写执行
③ 工具权限(Agent Harness 层):就算 OS 允许,Agent Harness 也可以拦截——"没有工具权限"≠ OS 不让你用,是框架自己的安全策略
四大框架工具集对比
| 能力 | HERMES | Claude Code | OpenClaw |
| 文件操作 | 4个专用工具 | Read/Write/Edit | read/write/edit/exec |
| 终端 | terminal | Bash(核心) | exec(支持bg/pty/elevated) |
| 浏览器 | 9个专用工具 | ❌ 无 | 1个统一 browser 工具 |
| 多模态 | 内置(图/音/视) | ❌ 无 | image_generate/video_generate/music_generate |
| 消息发送 | send_message | ❌ 无 | message(多渠道) |
| 子Agent | delegate_task | 有限支持 | sessions_spawn |
| 设计哲学 | ~30个精细工具 | ~12个 + Bash万能 | browser统一接管 + exec万能 |
11个工程深水区
工具调用远不止"调个函数"那么简单。以下是实际工程中必须处理的深层问题:
- Function Calling 协议:JSON Schema 定义工具 → 模型输出结构化 JSON → 结果回传。arguments 是 JSON 字符串而非对象,这是"JSON parse error"偶发的根因。
- 工具描述工程:description 写得好坏直接决定模型会不会选对工具。模糊描述 → 模型选错工具 → 任务失败。
- Token 成本:每次对话都要把工具定义塞进上下文。30个工具 × 每个200 token = 6000 token 额外开销。
- 并行 vs 串行:同时调多个独立工具节省时间,但有依赖关系的必须串行。
- 返回值设计:太大撑爆上下文,太小信息不够。截断策略、分页、结构化返回。
- 错误处理与重试:JSON parse error、超时、权限不足——需要分级重试策略。
- 安全边界与沙盒:terminal 是最危险的工具——rm -rf / 通过 OS 权限 + 框架权限两层拦截。
- 工具注册机制:静态注册(启动时加载)vs 动态注册(运行时增删)。
- MCP 协议:Model Context Protocol——让工具变成可插拔的标准化组件,跨框架复用。
- Human-in-the-Loop:高风险操作弹确认——阻塞式、异步式、白名单自动过。
- 可观测性:日志(每次调用记录)+ 追踪(串成调用链)+ 指标(统计成功率/耗时)。
💡 工具描述工程是门手艺。description 不只要写"这个工具干什么",还要写"什么时候不该用这个工具"。例如:search_files 的 description 写明"仅用于文件内容搜索,不要用于网页搜索",才能避免模型在需要搜索网页时错误调用文件搜索工具。