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 步显式走一遍。
- 用户问「旧金山现在天气怎样」。
- 模型不知道实时天气,于是输出一个结构化请求:调用
get_weather,location="San Francisco"——然后停机(stop_reason变成tool_use,生成中止)。 - 你的程序(harness)读到这个请求,真正去调天气 API,拿到
15°C, 多云。 - harness 把这个结果作为
tool_result塞回对话。 - 模型读到
tool_result,生成「旧金山现在 15°C,多云」。
关键在这里:模型自始至终没碰过天气 API,它只产出了文本。第 3、4 步全是你的代码——模型既不会自己去调 API,也不会自己把结果接回来。
下面是这次往返在报文层的样子。assistant 先吐出一个 tool_use 块并把 stop_reason 标成 tool_use;harness 执行后,把结果作为 tool_result 接回去。
// 第 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, 多云"
}
]
}
如果 harness 读到 tool_use 后,忘了把 tool_result 塞回去,会发生什么?
展开答案(先停 10 秒)
对话永远卡在这里——模型停在 tool_use,既不会自己执行工具,也不会自己往下走,它在等一个永远不来的结果。设计启示:推进对话的责任完全在 harness。
1.3一次往返的 5 个角色
把 1.2 的 5 步抽象成 5 个固定角色,每个角色对应那一步里「谁在做什么」:
- 工具定义——你提前声明有哪些工具(对应 1.2 之前的准备:name、description、参数 schema 都在请求里一次性给到模型)。
- 模型决策——基于 description 选工具、填参数(对应第 2 步的前半)。
- tool_use 请求——模型的输出 + 停机(对应第 2 步的后半,
stop_reason=tool_use)。 - harness 执行——你的代码真正干活,去调 API、查库、写文件(对应第 3 步)。
- 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)逐字段对齐。
§本章 self-check
先合上教程,把答案写在纸上或编辑器里。写完再展开对照——直接点开等于把这一节当成再读一遍。
- 一次工具调用往返里,模型「停机」发生在 5 个角色中的哪一步?停机之后,是谁让对话继续下去的?
- 「模型执行了 get_weather 工具」——这句话哪里说错了?正确的说法是什么?
- (设计层)你在 harness 里漏掉了「把 tool_result 塞回对话」这一步。从用户视角看,会观察到什么现象?
答案(先做完再展开)
- 停机发生在第 3 个角色「tool_use 请求」——模型输出请求后
stop_reason变成tool_use,生成中止。让对话继续下去的是 harness:它执行工具、把tool_result回填,再发起下一轮。模型自己不会推进。 - 错在「模型执行了工具」。模型只生成了一个
tool_use请求(要调get_weather、参数是什么),真正去调天气 API 的是 harness。正确说法:模型请求调用get_weather,harness 执行后把结果回填给模型。 - 对话永远卡住:模型停在
tool_use等结果,既不自己执行也不往下走。用户视角看到的是——发了问题后再无回复,界面像「卡住 / 转圈不出答案」,因为推进对话的那一步(回填)被漏掉了。
让模型一次请求两个工具
设计一个用户提问,让模型在同一次回复里需要调用两个不同的工具(例如同时查两个城市的天气)。想一想:这两个 tool_use 请求会怎么放?结果又怎么回填?
提示(卡住再展开)
一个 assistant 回复里可以带多个 tool_use 块——这就是「并行工具调用」,02 章会讲它的报文形状。