Chapter 01

核心概念与本质

起点页给了一句话本质:模型只发出请求、harness 负责执行。这一章把这句话拆成 5 个你能复述的角色,并完整走一遍一次工具调用的往返。

本章你将建立的 schema

  • ① 工具调用是「请求—停机—执行—回填」的循环,模型只负责「请求」那一步;
  • ② 一次往返的 5 个角色:工具定义 / 模型决策 / tool_use 请求 / harness 执行 / tool_result 回填;
  • ③ function calling 与 tool use 是同一机制的两个名字。

1.1为什么 LLM 需要工具

工具是给冻结在训练时刻的模型,开一扇通向实时世界、私有数据和副作用的门。

为什么需要它

没有工具时,模型只能基于训练时见过的文本作答——查不了今天的天气、读不了你的数据库、发不出一封邮件。

底层机制比文档深一层:模型生成的是 token 上的概率分布,它没有任何机制能「访问外部世界」——除非外部世界以 token 的形式出现在它的 context(上下文,即喂给模型的全部文本)里。工具调用的全部把戏,就是把「访问外部世界」变成「往 context 里追加 token」的一套协议。所以模型不是「学会了查天气」,而是「学会了发出一个查天气的请求,并在结果作为 token 回到 context 后继续往下写」。

类比 · 带边界

工具像医生的化验单——医生(模型)写下「查血常规」,但抽血、化验、出报告是检验科(harness,即包在模型外、负责真正执行的那段程序)做的;医生只读报告。

类比失效处:医生知道化验怎么做,模型则完全不知道工具内部如何实现。

1.2本质:模型只请求,不执行

模型生成一段「我要调用 X,参数 Y」的结构化文本,然后停机;执行发生在模型之外。

这是整篇教程最该先扭转的一个认识:跨过去,后面三章都顺;跨不过去,后面会反复绊住。用一个查天气的场景,把往返的 5 步显式走一遍。

  1. 用户问「旧金山现在天气怎样」。
  2. 模型不知道实时天气,于是输出一个结构化请求:调用 get_weather,location="San Francisco"——然后停机(stop_reason 变成 tool_use,生成中止)。
  3. 你的程序(harness)读到这个请求,真正去调天气 API,拿到 15°C, 多云。
  4. harness 把这个结果作为 tool_result 塞回对话。
  5. 模型读到 tool_result,生成「旧金山现在 15°C,多云」。

关键在这里:模型自始至终没碰过天气 API,它只产出了文本。第 3、4 步全是你的代码——模型既不会自己去调 API,也不会自己把结果接回来。

用户提问 模型决策 tool_use 请求 停机 ⏸ harness 执行 tool_result → 最终答案 模型在这里停下 模型内部 你的程序 (harness)
图 1.1一次工具调用的 5 个阶段。注意:只有中间那一步(朱红)发生在模型内部并触发停机;它右边的执行与回填,全是你的代码。

下面是这次往返在报文层的样子。assistant 先吐出一个 tool_use 块并把 stop_reason 标成 tool_use;harness 执行后,把结果作为 tool_result 接回去。

weather-roundtrip.json JSON
// 第 2 步:模型的输出(assistant 消息),随即停机
{
  "role": "assistant",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01A",
      "name": "get_weather",
      "input": { "location": "San Francisco" }
    }
  ],
  "stop_reason": "tool_use"
}

// 第 4 步:harness 执行后,把结果作为 user 消息接回去
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A",
      "content": "15°C, 多云"
    }
  ]
}
tool_use 第 2 步模型的请求 · stop_reason 模型在此停机 · tool_result 第 4 步 harness 的回填。
想一想

如果 harness 读到 tool_use 后,忘了把 tool_result 塞回去,会发生什么?

展开答案(先停 10 秒)

对话永远卡在这里——模型停在 tool_use,既不会自己执行工具,也不会自己往下走,它在等一个永远不来的结果。设计启示:推进对话的责任完全在 harness。

1.3一次往返的 5 个角色

把 1.2 的 5 步抽象成 5 个固定角色,每个角色对应那一步里「谁在做什么」:

  1. 工具定义——你提前声明有哪些工具(对应 1.2 之前的准备:name、description、参数 schema 都在请求里一次性给到模型)。
  2. 模型决策——基于 description 选工具、填参数(对应第 2 步的前半)。
  3. tool_use 请求——模型的输出 + 停机(对应第 2 步的后半,stop_reason = tool_use)。
  4. harness 执行——你的代码真正干活,去调 API、查库、写文件(对应第 3 步)。
  5. tool_result 回填——结果变成 context 里的 token,模型才「看得见」(对应第 4 步)。

底层机制一句话:这 5 个角色里,只有「模型决策 / tool_use 请求」发生在模型内部;其余三个都在模型外。图 1.1 底部的两条边界标签画的就是这条分界线。

想一想

用户问「1024 × 768 等于多少」,你给了模型一个 calculator 工具。模型一定会调用它吗?

展开答案(先停 10 秒)

不一定。调不调由模型基于 description 和问题自行决策;简单乘法它常常直接算出来。这说明「是否调用工具」是模型的一个概率判断,不是确定性的 if-else——后面 04 章会讲 description 如何左右这个判断。

1.4function calling 就是 tool use

「function calling」和「tool use」指的是同一套机制,只是不同厂商的叫法。

OpenAI 最早叫它 function calling,如今把 function 当作 tool 的一种 type;Anthropic 一直叫 tool use。机制完全相同:声明 → 模型请求 → 你执行 → 回填。读不同文档时别被名字绊住——同一张图 1.1,换个术语标签而已。

承接

02 章会给出两家的字段对照表,把 tools / tool_calls(OpenAI)与 tool_use / tool_result(Anthropic)逐字段对齐。

模型的世界 = context 里的 token 系统提示 对话历史 工具定义(name·description·schema) tool_result(回填进来的结果) 模型碰不到 → 你的数据库 天气 / 股价 API 文件系统 工具的真正实现
图 1.2模型只能「看见」context 里的 token。注意:右侧的真实世界——包括工具的真正实现——模型永远碰不到;它只能请求,由你的程序代它去碰。

§本章 self-check

先合上教程,把答案写在纸上或编辑器里。写完再展开对照——直接点开等于把这一节当成再读一遍。

  1. 一次工具调用往返里,模型「停机」发生在 5 个角色中的哪一步?停机之后,是谁让对话继续下去的?
  2. 「模型执行了 get_weather 工具」——这句话哪里说错了?正确的说法是什么?
  3. (设计层)你在 harness 里漏掉了「把 tool_result 塞回对话」这一步。从用户视角看,会观察到什么现象?
答案(先做完再展开)
  1. 停机发生在第 3 个角色「tool_use 请求」——模型输出请求后 stop_reason 变成 tool_use,生成中止。让对话继续下去的是 harness:它执行工具、把 tool_result 回填,再发起下一轮。模型自己不会推进。
  2. 错在「模型执行了工具」。模型只生成了一个 tool_use 请求(要调 get_weather、参数是什么),真正去调天气 API 的是 harness。正确说法:模型请求调用 get_weather,harness 执行后把结果回填给模型。
  3. 对话永远卡住:模型停在 tool_use 等结果,既不自己执行也不往下走。用户视角看到的是——发了问题后再无回复,界面像「卡住 / 转圈不出答案」,因为推进对话的那一步(回填)被漏掉了。
进阶挑战 · 刚好够不着

让模型一次请求两个工具

设计一个用户提问,让模型在同一次回复里需要调用两个不同的工具(例如同时查两个城市的天气)。想一想:这两个 tool_use 请求会怎么放?结果又怎么回填?

提示(卡住再展开)

一个 assistant 回复里可以带多个 tool_use 块——这就是「并行工具调用」,02 章会讲它的报文形状。