d27f762d75
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
270 lines
12 KiB
Markdown
270 lines
12 KiB
Markdown
# 模型工具调用 - 00 原点
|
|
|
|
> 把"模型使用工具"这件事,完全用我已有的知识树(操作系统 / 进程 / IPC / 网络协议)来理解,不引入新的本体概念。
|
|
|
|
---
|
|
|
|
## 两个原点
|
|
|
|
整个"模型工具调用"机制,只需要两个原点就能展开:
|
|
|
|
### 原点 1:任何工具 = 一个进程
|
|
|
|
不管这个工具表面叫什么(`web_search`、`Gmail:search_threads`、`bash_tool`、`create_file`),背后都是某台机器上有一个**进程**在执行它。
|
|
|
|
差别只在三个维度:
|
|
|
|
- **位置**:进程在哪台主机上(本机 / 远程服务器)
|
|
- **生命周期**:常驻服务进程 / 一次性 fork 出来的子进程
|
|
- **特权级**:用户态业务进程 / 沙箱容器 / 受限子进程
|
|
|
|
### 原点 2:模型调用工具 = 进程间通讯(IPC)
|
|
|
|
模型本身也是一个进程——推理进程,通常跑在 GPU 服务器上。它"使用工具",本质就是**和另一个进程通讯**。
|
|
|
|
整个工具调用机制,就是在解决 IPC 的几个经典问题:
|
|
|
|
- **寻址**:怎么找到对方
|
|
- **编码**:参数和返回值怎么序列化
|
|
- **同步**:阻塞等结果还是异步回调
|
|
- **容错**:对方挂了 / 超时 / 网络断了怎么办
|
|
|
|
---
|
|
|
|
## 澄清:谁在调用工具?
|
|
|
|
这是一个非常容易被"模型调用工具"这个说法误导的关键认识。
|
|
|
|
### "模型调用工具"字面上是误导性的
|
|
|
|
模型本身是一个**纯函数**:输入 token → 输出 token。它不联网、不读文件、不开子进程。所谓"模型调用工具",物理上**模型只是输出了一段 JSON**,内容大致是:
|
|
|
|
```json
|
|
{"tool": "bash", "input": "ls -la"}
|
|
```
|
|
|
|
真正动手执行 `ls -la` 的,是**宿主进程 / 客户端**——也就是 Claude Code、Claude.ai 后端、或者用户自己写的 agent 程序。
|
|
|
|
### 三层分离的真实架构
|
|
|
|
```
|
|
[模型(纯推理函数)] ← 远端 GPU 服务器,只做 token 进/出
|
|
↑↓ HTTPS(总是跨主机)
|
|
[客户端 / 宿主进程] ← 本地或云端,负责"代理执行"工具
|
|
↑↓ 各种 IPC(本机 stdio / fork / 远程 HTTPS)
|
|
[工具进程] ← bash、Drive API、文件系统、MCP server……
|
|
```
|
|
|
|
**关键点**:一次"工具调用"在物理上**至少涉及两段 IPC**:
|
|
|
|
- **第一段**:模型 ↔ 客户端(总是跨主机 HTTPS,因为模型在远端 GPU 上)
|
|
- **第二段**:客户端 ↔ 工具(可本机可跨主机,看工具部署位置)
|
|
|
|
### "客户端"这个词的歧义
|
|
|
|
"客户端"在不同语境下指**不同的东西**,这是混淆的根源:
|
|
|
|
- **网络架构里的"客户端"** = 相对于某个服务,发起请求的那一方(位置概念)
|
|
- **产品里的"客户端"** = 用户用的那个 App / 界面(产品概念)
|
|
|
|
这两个常常**不重合**。讨论工具调用时,要用第一种含义。
|
|
|
|
### 三种产品形态对比
|
|
|
|
| 形态 | 界面在哪 | 工具执行进程在哪 | 能读本地文件吗 |
|
|
|---|---|---|---|
|
|
| Claude.ai 网页 | 你的浏览器 | Anthropic 服务器 | 不能(除非上传) |
|
|
| Claude Desktop(不接本地 MCP) | 你的电脑(WebView) | Anthropic 服务器 | 不能(除非上传) |
|
|
| Claude Desktop(接本地 MCP) | 你的电脑 | **你的电脑**(MCP server) | **能** |
|
|
| Claude Code | 你的终端 | **你的电脑**(本身就是 agent) | **能** |
|
|
|
|
**关键洞察**:问的不是"客户端在哪",而是"工具执行进程在哪台机器上"。
|
|
界面在哪反而不重要——浏览器、WebView、终端都只是"输入输出通道",真正决定能力的是**工具进程的位置**。
|
|
|
|
### "客户端" vs "agent" 的区分
|
|
|
|
这两个概念经常混用,但不是一回事:
|
|
|
|
- **客户端(client)** 是**位置概念**——相对于模型推理服务,谁是发起请求的那一方
|
|
- **agent(智能体)** 是**行为概念**——指"在循环里反复调模型 + 执行工具,直到任务完成"的控制器逻辑
|
|
|
|
通常 agent 逻辑就跑在客户端里,所以两者经常重合,但不必然:
|
|
|
|
- 一个简单的客户端只调一次模型、不循环——它是客户端,但不是 agent
|
|
- Claude Code 既是客户端,**也**是 agent(它有完整的 loop)
|
|
|
|
### 用原点重新表述更准确的说法
|
|
|
|
> **模型推理是一个进程(在远端服务器),它本身不能执行任何工具。所谓"模型调用工具",物理上是:模型推理进程通过 IPC(HTTPS)把"调用意图"以 JSON 形式发给客户端进程;客户端进程作为代理,再通过另一次 IPC(本地 stdio / fork / 远程 HTTPS)去调用真正的工具进程,把结果回传给模型,继续下一轮推理。**
|
|
|
|
---
|
|
|
|
## 远程工具:不是都通过 MCP
|
|
|
|
容易误解的点:**MCP 不是远程工具的必经之路**。
|
|
|
|
### 远程工具的两条路
|
|
|
|
**路径 A:客户端直接调用远程 API**
|
|
|
|
客户端代码里写死了"这个工具叫 web_search,实现就是去调某个搜索 API",直接发 HTTPS 请求,不经过 MCP。
|
|
|
|
```
|
|
[模型] → [客户端] ─── HTTPS ───→ [远程 API 服务器]
|
|
```
|
|
|
|
典型例子:
|
|
- Claude.ai 内置的 `web_search`、`image_search`(Anthropic 后端直接调搜索后端)
|
|
- ChatGPT 的 web browsing、DALL·E
|
|
- 自己用 API + function calling 写的程序,工具实现是自己代码里调的某个 API
|
|
|
|
**路径 B:客户端通过 MCP server 调用**
|
|
|
|
客户端只懂 MCP 协议,具体工具是哪个 MCP server 实现的,客户端不关心。MCP server 自己再去调真正的后端 API。
|
|
|
|
```
|
|
[模型] → [客户端] ─ MCP 协议 ─→ [MCP server] ─ HTTPS ─→ [远程 API]
|
|
```
|
|
|
|
典型例子:Claude.ai 接 Google Drive、Gmail、Notion;Claude Desktop 接 Filesystem MCP、Obsidian MCP。
|
|
|
|
### MCP 的存在意义
|
|
|
|
**MCP 解决的是标准化问题**——让"工具的实现方"和"模型的使用方"解耦。任何 MCP server 都能被任何 MCP 兼容的客户端用,不用每家客户端都重新对接一遍 Gmail / Slack / Notion。这跟 USB 标准化、ODBC 标准化是一个思路。
|
|
|
|
但如果**工具实现方和模型使用方是同一家公司**,根本不需要 MCP。Anthropic 自己实现的 web_search,直接进程内调就完事了,套个 MCP 反而是给自己加一层 IPC 开销。
|
|
|
|
### IPC 跳数对比
|
|
|
|
| 路径 | IPC 跳数 | 跳的内容 |
|
|
|---|---|---|
|
|
| 客户端直连 API | 2 跳 | 模型↔客户端,客户端↔API |
|
|
| 经 MCP server | 3 跳 | 模型↔客户端,客户端↔MCP,MCP↔API |
|
|
|
|
MCP 增加的那一跳,**换来的是接入标准化**——值不值,看你是不是"第三方"。
|
|
|
|
---
|
|
|
|
## 通讯距离:两类 IPC
|
|
|
|
按通讯双方是否在同一个内核内,工具调用的 IPC 分两大类。
|
|
|
|
### A. 同主机 IPC(同一个内核)
|
|
|
|
模型宿主和工具执行进程跑在同一台机器,通讯走**内核提供的本地机制**。
|
|
|
|
| 内核机制 | 在工具调用里的体现 |
|
|
|---|---|
|
|
| 管道 / stdio | MCP 的 stdio 传输:宿主 fork 一个 MCP server 子进程,通过 stdin/stdout 收发 JSON-RPC |
|
|
| Unix domain socket | 部分 MCP server 用本地 socket,比 TCP 快、有文件系统权限控制 |
|
|
| 共享内存 | 推理框架内部传 KV cache 用,工具调用层基本不用 |
|
|
| fork + exec | `bash_tool` 执行命令:宿主 fork 出 bash 子进程,等它退出后读 stdout |
|
|
| 信号 | 超时 / 取消工具调用时,宿主给子进程发 SIGTERM |
|
|
|
|
**典型场景**:Claude Code 在我自己电脑上跑——所有工具(读文件、跑命令、编辑代码)都是本地进程间通讯,没有走网络。
|
|
|
|
### B. 跨主机 IPC(不同内核)
|
|
|
|
模型在一台机器,工具在另一台,走**网络协议栈**。
|
|
|
|
| 协议层 | 在工具调用里的体现 |
|
|
|---|---|
|
|
| 传输层 | TCP(可靠);几乎不用 UDP |
|
|
| 安全层 | TLS 加密(远程工具几乎强制) |
|
|
| 应用层 | HTTPS REST(主流);HTTP + SSE(MCP 远程模式);WebSocket(实时);gRPC(企业内部) |
|
|
| 编码 | JSON(主流);Protobuf(gRPC);MessagePack(罕见) |
|
|
|
|
**典型场景**:Claude.ai 调 Google Drive、Gmail、web_search 这类工具——全部是跨主机 HTTPS。
|
|
|
|
---
|
|
|
|
## 完整链路示例:`create_file` 的一次调用
|
|
|
|
以"用 Google Drive 工具创建一个 .md 文件"为例,完整 IPC 链路:
|
|
|
|
```
|
|
[模型推理进程] GPU 服务器
|
|
│
|
|
│ ① 输出 tool_use JSON(进程内,序列化)
|
|
▼
|
|
[Anthropic 宿主进程 / 编排器] Anthropic 数据中心
|
|
│
|
|
│ ② 路由:这个工具属于 Google Drive MCP
|
|
│ ③ HTTPS 请求(跨主机 IPC,TLS 加密)
|
|
▼
|
|
[Google Drive MCP Server 进程] Google 数据中心
|
|
│
|
|
│ ④ 翻译成 Drive REST API 调用(又一次跨主机 IPC)
|
|
▼
|
|
[Drive 后端服务进程集群] Google 数据中心
|
|
│
|
|
│ ⑤ 写入存储(底层又是无数 IPC,略)
|
|
│ ⑥ 返回 file metadata,原路 JSON 回传
|
|
▼
|
|
[模型上下文窗口] ← tool_result 塞回这里,模型继续推理
|
|
```
|
|
|
|
**关键认识**:每一根箭头都是一次 IPC,只是距离不同、协议不同、可靠性不同。一次"模型用工具",在物理上可能涉及 5~10 次 IPC。
|
|
|
|
---
|
|
|
|
## 这个抽象的威力
|
|
|
|
一旦接受"工具 = 进程,调用 = IPC",很多看起来花哨的东西就回到熟悉的地盘:
|
|
|
|
- **工具调用为什么有超时** → 远程 IPC 必然有超时,本地阻塞 IPC 也得设上限,跟写网络程序一样
|
|
- **为什么要有权限沙箱** → fork 出去的子进程默认继承父进程权限,不限制就是安全漏洞
|
|
- **MCP 为什么要分 stdio 和 SSE 两种传输** → 对应"同主机 IPC"和"跨主机 IPC",底层机制完全不同
|
|
- **agentic loop 怎么处理工具失败** → 跟写网络程序处理 connection refused / timeout 一模一样
|
|
- **为什么所有大模型 API 都要 JSON 序列化** → IPC 必须有 wire format,JSON 是当前最低公共分母
|
|
- **为什么模型不能直接联网** → 推理进程没有网络栈访问权限(沙箱限制),必须通过宿主进程代理,这就是"工具"的存在意义
|
|
|
|
---
|
|
|
|
## 与已有知识树的对照
|
|
|
|
| 我已有的知识点 | 在工具调用中的对应 |
|
|
|---|---|
|
|
| 进程 | 模型推理进程 + 客户端宿主进程 + 工具执行进程(三层分离) |
|
|
| 内存 | 上下文窗口 = 工作内存;KV cache = 缓存 |
|
|
| shell / 命令行 | agentic loop 就是 LLM 版的 shell;每次工具调用 = 执行一条命令 |
|
|
| 系统库 | tool 定义 = 库函数声明;调用约定 = JSON Schema |
|
|
| 应用架构 | 客户端 = 模型宿主(实际执行者);服务端 = 工具实现 |
|
|
| 网络协议 | MCP over stdio / SSE / HTTP;OpenAI tool calling over HTTPS |
|
|
| 客户端进程 | LLM runtime,负责把模型输出解析成工具调用并代理执行 |
|
|
| 服务器端进程 | MCP server / 工具后端 |
|
|
| 监听端口 | 远程 MCP server 监听 HTTPS 端口;本地 MCP 用 stdio 不占端口 |
|
|
| 内核 | 类比"宿主调度器"——决定何时调用工具、何时返回模型 |
|
|
|
|
---
|
|
|
|
## 后续要展开的子节点
|
|
|
|
- `01-本机IPC在工具调用中的体现.md`
|
|
- stdio(MCP local)
|
|
- fork+exec(bash 类工具)
|
|
- Unix socket
|
|
- `02-跨主机IPC在工具调用中的体现.md`
|
|
- HTTPS REST(主流)
|
|
- HTTP + SSE(MCP remote)
|
|
- gRPC(企业)
|
|
- `03-编码与协议.md`
|
|
- JSON Schema(契约)
|
|
- JSON-RPC(MCP 用)
|
|
- `04-控制流-agentic-loop.md`
|
|
- 本质 = REPL + 多轮 IPC
|
|
- `05-横切关注点.md`
|
|
- 超时
|
|
- 权限 / 沙箱
|
|
- 失败重试
|
|
- 上下文管理(KV cache 当作模型的"工作内存")
|
|
|
|
平级旁支(更底层的通用基础):
|
|
- `06-互联网作为IPC基础设施.md`
|
|
|
|
---
|
|
|
|
## 一句话总结
|
|
|
|
> **模型用工具 = 一个进程通过 IPC 调用另一个进程。** 但要注意:模型本身不执行工具,执行者是客户端宿主进程;模型只是输出"调用意图"的 JSON。剩下所有概念(MCP、function calling、agentic loop、tool schema)都是这个本质在不同维度上的展开。
|