对应 Maps to:CMU 17-630 Prompt Engineering(Prof. Travis Breaux),第 5 周的第二个主题 · Prompt Augmentation: Tool Usage。原课 Schedule 对这一讲的说明(意译):工具使用从 Toolformer 到函数调用的演变,以一个为网页浏览而微调的模型为例;在工具使用的语境下介绍规划和自我批判的早期例子。原课作业 2 要求调用函数,并对比单阶段提示和多阶段任务分解。 依据说明:原课的 slides 和阅读清单不公开。Schedule 点名的几个部分——从 Toolformer 到函数调用的演变(§2、§3、§4)、为浏览而微调的模型(§7.1,WebGPT)、规划与自我批判的早期例子(§7.2–§7.3)——各占一节;§5(工具描述)和 §6(单阶段 vs 多阶段)对应作业 2。材料来自 Karpas et al.(2022,MRKL)、Parisi et al.(2022,TALM)、Schick et al.(2023,Toolformer)、Nakano et al.(2021,WebGPT)、Yao et al.(2023,ReAct)、Xu et al.(2023,ReWOO)、Gou et al.(2024,CRITIC)、Huang et al.(2024)、Gao et al.(2023,PAL)、Patil et al.(2023,Gorilla)、Qin et al.(2024,ToolLLM)、Mialon et al.(2023,综述)等;引用时按原文的说法转述结论,具体数字以原文为准。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture08/(PyTorch CPU;1 个模块 + 6 个脚本;不需要 API key;02 号脚本第一次运行会在第 5 讲的长尾模型上训练两个模型共约 20 秒,之后读缓存;每个脚本都在 5 秒以内) 预计阅读时间:4 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 说清楚模型为什么需要工具(算术、实时信息、私有数据、行动),以及”工具使用”和第 7 讲的检索是什么关系。
- 讲出工具使用的演变线:MRKL / TALM → Toolformer → WebGPT → ReAct → 原生函数调用 API。
- 写出一个工具调用的完整循环:工具描述(JSON Schema)→ 模型写调用 → 校验 → 执行 → 结果放回上下文;说清楚多轮调用的 token 成本和错误处理。
- 解释 Toolformer 的”采样—执行—过滤”,特别是过滤准则为什么能让模型学会在哪里调用。
- 写出好的工具描述和错误信息,并理解它们为什么是提示的一部分。
- 设计并评估”单阶段 vs 多阶段”的对比(原课作业 2),报告准确率、成本和轨迹。
- 区分”先规划”和”边做边看”两种执行方式,以及”有工具的核对”和”没有外部反馈的自我批判”。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| 第 4 讲:思维链、程序分解(PAL)、Self-Ask | 任务分解与错误传播(第 9 讲) |
| 第 5 讲:长尾模型、“知道自己不知道”、弃权 | 自我纠正、验证链、LLM 判官(第 10 讲) |
| 第 7 讲:检索、自适应检索 | 代码执行作为工具、软件智能体(第 11 讲) |
| 前置讲 P1:JSON 与转义;P2:消息格式、成本、重试 | 工具带来的安全问题:注入、越权(第 12 讲) |
| 智能体循环、规划、记忆(第 13、14 讲) |
0.3 本讲地图 Map
§1 为什么要工具:模型做不好的四类事
§2 一条演变线:MRKL → Toolformer → WebGPT → ReAct → 函数调用 API
§3 工具调用的协议:描述、调用、校验、执行、结果;成本与错误 ← 代码 01
§4 Toolformer:模型自己学会"在哪里调用"——在迷你模型上复现 ← 代码 02
§5 工具描述怎样写:描述和错误信息都是提示 ← 代码 04
§6 单阶段 vs 多阶段(原课作业 2) ← 代码 03
§7 浏览、规划与自我批判:WebGPT、ReAct vs ReWOO、CRITIC ← 代码 05
§8 在真实模型上;评估工具使用 ← 代码 06(可选)
§9 小结 §10 常见误区 §11 练习 §12 延伸阅读 §13 下一讲预告
0.4 记号与术语 Notation & terms
| 术语 | 本讲义里的意思 |
|---|---|
| 工具(tool) | 模型可以请求调用的一个外部函数:计算器、搜索、数据库、API…… |
| 工具描述 / 模式(schema) | 告诉模型工具做什么、参数是什么的文字和 JSON Schema |
| 调用(call) | 模型写出的”工具名 + 参数”;由执行器(我们的程序)去真正执行 |
| 结果(result / observation) | 执行器把工具的输出放回上下文,作为下一条消息 |
| 单阶段 / 多阶段 | 一次调用直接得到答案 / 分多轮、每轮可以调用工具 |
| 轨迹(trajectory / trace) | 一次多阶段执行里,每一步的模型回复和工具结果 |
| 先规划(plan-first) / 边做边看(interleaved) | 一次写出全部调用 / 每次调用后看到结果再决定下一步 |
0.5 本讲的实验装置 · The setup
- 世界:第 5 讲的 60 个国家(首都、语言、出口、领导人),外加一个虚构的人口表(每个国家 1,200–98,000 千人,由国名确定性地算出)。
- 四个工具(
tool_lab.world_tools):lookup(country, relation)查事实、population(country)查人口、calculator(expression)算术、search(query)在第 7 讲的文档库里检索。 - 文本协议:模型回复一行
CALL {"tool": ..., "args": {...}},执行器回复RESULT {...};最后模型回复ANSWER: ...。原生的函数调用 API 用结构化字段代替文字,但线路上传的是同样的东西。 - 四类问题(
make_tasks):查一个事实(1 次调用)、两国人口之差(3 次)、已知首都问语言(2 次,第二步依赖第一步)、三国人口之和(4 次)。 - 模型:迷你 Toolformer 用第 5 讲的长尾模型(真实训练);单阶段 / 多阶段 / 描述 / 规划的实验用
SimToolLLM——一个行为由旋钮决定的模拟 chat 模型(记忆随热门程度变化、心算会出错、描述含糊时会选错工具);06 号脚本可以换成真实的 Claude。
1. 为什么要工具 · Why tools
语言模型擅长”写出像样的文字”,不擅长:
- 精确计算:多位数的加减乘除、日期计算。模型是在预测下一个 token,不是在执行算法;第 4 讲的 PAL(Gao et al. 2023)让模型写程序、由解释器去算,正是一种工具使用。
- 实时与长尾的信息:今天的天气、昨天的新闻、冷门的事实(第 5、7 讲)。
- 私有的数据:用户的日程、公司的数据库。
- 行动:发邮件、下订单、改文件——模型的输出只是文字,要有程序把文字变成动作。
工具使用把这些交给外部系统:模型负责决定”调用什么、传什么参数、怎样使用结果”,程序负责真正执行。Mialon et al.(2023)把这类模型叫作”增强语言模型”(augmented language models):推理能力在模型里,知识和行动能力一部分在外部。第 7 讲的检索就是一种工具;§7 会看到,“什么时候检索”本身就是一个工具使用的决策。
2. 一条演变线 · From Toolformer to function calling
- MRKL(Karpas et al. 2022):一个”路由器”把输入分派给计算器、数据库等专家模块。早期的工具使用多是这种人工设计的路由。
- TALM(Parisi et al. 2022):在文本里插入工具调用和结果,用少量示范 + 自我训练(self-play)教模型使用工具。
- Toolformer(Schick et al. 2023):自监督地学会使用工具。在大量无标注文本里,让模型自己提出”这里可以调用某个工具”的候选,执行它们,只保留那些让后文更容易预测的调用,再用这些数据微调(§4)。
- WebGPT(Nakano et al. 2021):把 GPT-3 微调成会用一个文本浏览器(搜索、点链接、引用段落)回答问题,并给出来源(§7.1)。这是原课 Schedule 点名的”为网页浏览而微调的模型”。
- ReAct(Yao et al. 2023):不微调,只用提示让模型交替写出”思考”和”行动”,每次行动后看到观察结果再继续(§7.2)。今天大多数智能体框架的骨架。
- 原生函数调用 API(2023 年起):模型提供方把工具使用训练进模型,并在 API 里提供结构化的接口——开发者用 JSON Schema 描述工具,模型返回结构化的调用。Gorilla(Patil et al. 2023)、ToolLLM(Qin et al. 2024)等研究把规模推到上千个真实 API,也暴露了”API 幻觉”(调用不存在的接口、编造参数)等问题。2024 年的 Model Context Protocol(MCP)进一步把”工具怎样被描述和调用”标准化,让同一个工具可以接到不同的应用里。
这条线的主题是:谁来决定什么时候调用、怎样调用——从人写规则(MRKL),到模型从自己的损失里学(Toolformer),到人类示范和偏好(WebGPT),到模型在提示下即时推理(ReAct),再到训练进模型的通用能力(函数调用)。
3. 工具调用的协议 · The protocol
3.1 一次多步调用在线路上长什么样
01_protocol.py 演示 1 用一个”照剧本说话”的模型走一遍两国人口之差:
模型 → CALL {"tool": "population", "args": {"country": "pemis"}}
执行器 → RESULT {"country": "pemis", "population_thousands": 70963}
模型 → CALL {"tool": "population", "args": {"country": "dultel"}}
执行器 → RESULT {"country": "dultel", "population_thousands": 7975}
模型 → CALL {"tool": "calculator", "args": {"expression": "70963 - 7975"}}
执行器 → RESULT {"value": 62988}
模型 → ANSWER: 62988
两件事值得注意。
模型从不”自己执行”工具。 它只写出一行文字,由执行器去调用真正的函数,再把结果作为下一条消息还给它。对模型来说,工具结果和用户说的话一样,只是上下文里的文字——这也是第 12 讲间接提示注入的根源:网页、文档、邮件里的内容通过工具结果进入上下文,可能被模型当作指令。
每一轮都重发全部上下文。 四轮发给模型的 token 是 1213、1331、1446、1548:系统提示(协议 + 4 个工具的描述,约 1152 个 token)每轮都要发,历史越来越长。多步任务的输入 token 大致随步数平方增长。工具描述写得越长、步骤越多,这个代价越大;提示缓存(前置讲 P2)对这种”前缀不变、后面追加”的对话特别有效。
3.2 出错了怎么办
演示 2 让剧本里的模型故意犯四种错:调用不存在的工具、JSON 写坏了(国名没加引号)、参数不在 enum 里("capital city")、查一个不存在的国家。执行器的原则是:
- 不崩溃:错误都变成一条
RESULT {"error": ...}还给模型,计入错误次数,循环继续。 - 先校验再执行:
validate_args按 JSON Schema 检查必填字段、类型、enum、多余参数。不合法的参数一旦被执行,错误会在工具内部以更难懂的方式出现,甚至造成副作用。 - 错误信息是写给模型看的:
"argument 'relation' must be one of ['capital', 'language', 'export', 'leader']"不仅说错了,还说了正确的取值——模型据此改正。§5.2 会量化”错误信息写得好不好”的影响。
3.3 工具本身也要防御
模型写的参数是不可信的输入:它可能写错,也可能被注入的内容操纵。演示 3 的计算器如果直接 eval 模型写的表达式,__import__('os').getcwd() 就会在你的机器上执行任意代码。只检查字符也不够:9 ** 9 ** 9 只用了数字和星号,却会让程序算到天荒地老。我们的计算器先用 ast 把表达式解析成语法树,只允许数字、加减乘除和负号这几种节点——白名单要按”操作”定义,而不是按字符。更一般的原则(第 12、14 讲展开):最小权限、校验、对有副作用的工具在执行前要求人确认。
4. Toolformer:模型自己学会”在哪里调用” · Toolformer
4.1 想法
人工标注”这句话里应该在哪里查计算器”太贵。Toolformer 的做法是让模型自己试:
- 采样:在一段文字的某些位置,让模型提出候选调用,例如在 ”… the capital of X is” 之前插入
[QA("what is the capital of X?") → ...]。 - 执行:真正调用工具,拿到结果。
- 过滤:比较三种情况下后面那些 token 的损失——不调用、调用但不给结果、调用并给出结果。只有当
( 是调用并给出结果时的损失, 是另外两者中较小的那个)时才保留这个调用。直觉:这个调用必须真的让后面的文字更好预测。 4. 微调:在保留了调用的文本上微调模型。之后,模型在生成时一旦写出调用,就暂停生成、执行工具、插入结果、继续生成。
这个过滤准则的妙处在于:它用的是模型自己的损失。模型已经知道的东西,调用不能再降低多少损失,会被过滤掉;只有模型不知道的地方,调用才被保留。所以学出来的模型会在自己不知道的地方调用——这和第 5 讲的”知道自己不知道”、第 7 讲的自适应检索是同一个问题。
Toolformer 用的是 GPT-J(67 亿参数),工具有问答系统、计算器、维基百科搜索、翻译和日历;论文报告它在多项零样本任务上明显超过了不用工具的同尺寸模型,常常能和大得多的模型相比。论文也发现,使用工具的能力要到一定的模型规模才出现,太小的模型几乎不能从工具中获益。
4.2 在迷你模型上复现
02_toolformer.py 在第 5 讲的长尾模型上做同样的事。工具是一个问答工具:输入就是前面的问题,输出就是文档库里的答案;调用的格式是 a : [ ask -> 结果 ] 结果 .。60 个国家分成两半:训练国家的事实可以进入工具数据,测试国家的从不进入。
第 0 步:教它用结果。 Toolformer 不需要这一步,因为 GPT-J 本来就会照抄上下文。我们的迷你模型不会(第 5 讲 §3.5):在测试国家的问题后面插入一个随机的值作为结果,没教过的模型照抄的比例是 0%——它继续用自己的记忆回答。所以先用一批示范教它:结果有 70% 是随机的值,回答必须照抄结果。损失只算在调用标记和最后的回答上,工具返回的结果不算(结果由工具给出,模型不需要学会”预测”它)。教过之后,照抄的比例是 99.2%,连预训练里从没出现过的词也抄对了。
第 1–2 步:过滤。 给训练国家的每个事实都插入一个调用(结果是真值),算”调用让答案的 log 概率提高了多少”:
| 出现次数 | 事实数 | 平均提升(nats) | 保留(τ = 0.5) | 保留(τ = 1.0) | 保留(τ = 2.0) |
|---|---|---|---|---|---|
| 0 次 | 12 | 7.14 | 12 | 12 | 12 |
| 1 次 | 18 | 3.72 | 15 | 15 | 15 |
| 2 次 | 13 | 0.99 | 9 | 5 | 0 |
| 3–5 次 | 33 | 0.27 | 2 | 1 | 0 |
| 6–20 次 | 20 | 0.05 | 0 | 0 | 0 |
| 21 次以上 | 24 | 0.01 | 0 | 0 | 0 |
τ = 1.0 时保留了 33 个调用,几乎全在出现 0–2 次的事实上。没有任何人标注”这里该查”——过滤准则自己找到了模型的知识边界。
第 3 步:微调,然后在测试国家上看它在哪里调用:
| 出现次数 | 事实数 | 闭卷(base) | 调用率 | 准确率 |
|---|---|---|---|---|
| 0 次 | 12 | 8% | 58% | 42% |
| 1 次 | 6 | 17% | 50% | 67% |
| 2 次 | 16 | 94% | 50% | 100% |
| 3–5 次 | 31 | 100% | 13% | 94% |
| 6–20 次 | 38 | 100% | 0% | 100% |
| 21 次以上 | 17 | 100% | 0% | 100% |
和三种对照比较(120 个测试事实):
| 策略 | 准确率 | 调用次数 |
|---|---|---|
| 从不调用(base 闭卷) | 85.8% | 0 |
| 总是调用 | 96.7% | 120 |
| Toolformer(模型自己决定) | 90.8% | 22 |
| 神谕:只在 base 答错时调用 | 98.3% | 17 |
模型学会了有选择地调用:冷门事实上调用一半以上,热门事实上从不调用。它用 18% 的调用次数,拿到了”总是调用”带来的提升的 46%。它答错的 11 个事实里,8 个是该调用而没有调用——模型以为自己知道,凭记忆答错了;3 个是调用了、却抄错了。前者正是第 5 讲的问题:“知道自己不知道”是一切”何时求助”策略的前提。
开发时的第一版示范少、随机比例低,结果很能说明问题:照抄的成功率随答案这个词在预训练里出现的次数一路下降(出现 50 次以上 100%,从没出现过 0%)——模型很难准确地”写出”一个它几乎不认识的词。真实模型用子词分词,任何字符串都写得出来;但罕见的名字、长数字仍然更容易被抄错。工具给了正确答案,不等于模型会正确地转述它,“最后一公里”也要评估。
5. 工具描述怎样写 · Writing tool descriptions
5.1 描述就是提示
04_tool_descriptions.py 保持四个工具的 Python 函数完全不变,只改描述的写法:
- 含糊:“Gets info.”、“Population.”、“Math.”——参数没有说明,relation 没有列出可选值;
- 清楚:做什么、什么时候用(“Use this when you know the country’s name …”、“when you do NOT know which country …”)、每个参数是什么、relation 的 enum、“用计算器代替心算”;
- 清楚 + 例子:再加一个调用的例子。
| 描述 | 系统提示 token | 准确率 | 每题无效调用 | 每题 token | 人口题没用计算器 |
|---|---|---|---|---|---|
| 含糊 | 713 | 93.3% | 0.86 | 4339 | 32 / 60 |
| 清楚 | 1152 | 100.0% | 0.22 | 5399 | 0 / 60 |
| 清楚 + 例子 | 1227 | 100.0% | 0.22 | 5678 | 0 / 60 |
(模型是 SimToolLLM,它对含糊描述的反应——选错工具、写出不合法的 relation、不用计算器——是旋钮;这里要看的是错误的种类和它们各自对应的修法。)
含糊描述引入的三类错误,各对应一处可以写清楚的地方:
| 错误 | 修法 |
|---|---|
| 选错工具(用 lookup 查人口、拿首都名去 lookup) | 写明”什么时候用这个、什么时候用那个”,尤其是功能相近的工具 |
| 参数不合法(“capital city”、“ruler”) | 用 enum 列出可选值;给每个参数写说明和格式 |
| 不用计算器、自己心算 | 明说”算术一律用它” |
清楚的描述让系统提示长了 60%,每题的总 token 多了 24%——这是为可靠性付的钱。Anthropic 的工具使用文档把”写极其详细的描述”列为最重要的建议:说清楚工具做什么、什么时候该用、什么时候不该用、每个参数的含义、有什么限制。
5.2 错误信息也是提示
演示 2 在含糊的描述下,比较两种执行器:一种把错误说清楚(“unknown relation ‘ruler’; use one of […]”),一种只说 "error":
| 错误信息 | 准确率 | 每题无效调用 | 8 轮用完还没有答案 |
|---|---|---|---|
| 说清楚错在哪 | 93.3% | 0.86 | 1 |
| 只说 ‘error’ | 63.3% | 2.67 | 38 |
只说 “error” 时,38 道题在 8 轮里一直重复同一个错误的调用。注意第一行:描述里没有 enum,Schema 校验拦不住 “ruler”,是工具自己返回了列出合法取值的错误,模型才得以改正。写工具的人要记住:错误信息的读者是模型——要具体、可操作。
5.3 工具多了怎么办
真实系统可能有几十上百个工具。工具越多,描述越长(每轮都要付钱),功能相近的工具越容易混淆。常见的做法是工具检索:先根据问题检索出最相关的几个工具,只把它们的描述放进提示(Gorilla 和 ToolLLM 都训练了专门的 API 检索器)——这正是第 7 讲的检索用在了工具的描述上。
6. 单阶段 vs 多阶段 · Single-stage vs multi-stage(原课作业 2)
03_single_vs_multi.py 用同样的 120 道题比较三种做法(SimToolLLM:记住一个人口的概率随国家的热门程度上升;每次心算有 15% 的概率出错):
| 做法 | 查一个事实 | 两国人口之差 | 已知首都问语言 | 三国人口之和 | 平均轮数 | 平均 token |
|---|---|---|---|---|---|---|
| 单阶段(无工具) | 66.7% | 30.0% | 53.3% | 16.7% | 1.0 | 93–121 |
| 多阶段(有工具) | 100% | 100% | 100% | 100% | 2.1–5.4 | 2794–8236 |
| 多阶段(无计算器) | 100% | 83.3% | 100% | 63.3% | 2.1–4.3 | 2297–5320 |
三点:
- 步骤越多,单阶段越差:查一个事实 66.7%,三国人口之和只有 16.7%——记忆和心算的错误会累积。多阶段把每一步交给可靠的工具。
- 多阶段贵得多:每道题 2–5 轮,token 是单阶段的几十倍,延迟也成倍增加。“值不值得”取决于答错的代价和任务的步骤数。
- 一条链的可靠性取决于最弱的一环:去掉计算器,每个人口都查对了,最后一步心算仍然会错(三国之和 100% → 63.3%)。这是第 9 讲”错误传播”的预告。
演示 2 拆开单阶段的错误(60 道人口题):41 道是”记错了某个人口”,5 道是”人口都记对了、算错了”。两类错误的修法不同——前者需要查询工具或检索,后者需要计算器或程序。这也是评估多阶段系统的一条原则:不只看最终答案,还要看轨迹——每一步调用了什么、参数对不对、结果有没有被用上。
写作业 2 的报告时,建议至少报告:准确率(带区间)、每道题的模型调用次数和 token、延迟;按题目的步骤数分组;以及失败轨迹的分类(选错工具、参数错误、结果没用上、最后一步错)。
7. 浏览、规划与自我批判 · Browsing, planning and self-critique
7.1 WebGPT:为浏览而微调
WebGPT(Nakano et al. 2021)把 GPT-3 放进一个文本浏览器:模型可以发出”搜索 ……""点击第 3 个链接""引用这段话""往下翻""结束浏览”等命令,浏览器把页面的文字返回给它。它要回答 ELI5(“像对五岁孩子一样解释”)这类长问题,并给出引用。
训练分几步:先用人类在同一个浏览器里的操作做行为克隆(behavior cloning);再收集人类对两个回答的偏好,训练一个奖励模型;最后用奖励模型做拒绝采样(每个问题生成多个回答,挑奖励最高的)或强化学习。论文报告,它最好的模型的回答在相当一部分情况下比人类示范者写的回答更受评估者偏好。
WebGPT 有两个影响至今的设计:引用——回答必须附上它看过的段落,方便人类核查(第 7 讲 §6.3 的归因);以及用人类偏好训练工具使用的策略——这把第 6 讲的偏好优化带进了工具使用,也带来了第 6 讲的问题:评审者更喜欢”看起来有依据”的回答,未必是更准确的回答。
7.2 规划:先规划,还是边做边看
工具使用里最早的”规划”问题是:要不要等看到上一个结果再决定下一步?
- 边做边看(ReAct,Yao et al. 2023):模型交替写出”思考—行动”,每次行动后看到”观察”再继续。灵活,但每一步都要一轮完整的模型调用。
- 先规划(ReWOO,Xu et al. 2023):模型一次写出全部调用,用
#E1、#E2引用前面的结果;执行器依次执行、代入引用;最后一轮把全部结果交给模型。省轮数、省 token,几个独立的调用还可以并行。
05_critique_planning.py 演示 2:
| 做法 | 查一个事实 | 两国人口之差 | 已知首都问语言 | 三国人口之和 |
|---|---|---|---|---|
| 边做边看:准确率 / 轮数 / token | 100% / 2.1 / 2794 | 100% / 4.2 / 5993 | 100% / 3.2 / 4573 | 100% / 5.4 / 8236 |
| 先规划:准确率 / 轮数 / token | 100% / 2.0 / 2638 | 100% / 2.0 / 2922 | 73.3% / 2.0 / 2901 | 100% / 2.0 / 3097 |
步骤互相独立时(人口之和),先规划用 2 轮完成了边做边看要 5.4 轮的事,token 少了六成多。但”已知首都问语言”要先搜出是哪个国家、再查它的语言——先规划的模型还没看到搜索结果就得写出国名,只能凭记忆猜,准确率掉到 73.3%。**后面的步骤依赖前面的结果时,必须边做边看。**实际系统常常两者混合:先粗略规划,执行中遇到意外再重新规划(第 13、14 讲)。
7.3 自我批判:有没有外部反馈是关键
另一类早期的”自我批判”是让模型检查自己的答案。演示 1 先让模型凭记忆回答 240 个事实问题(初稿 65.4%),再用两种方式检查:
| 做法 | 检查之后 | 错 → 对 | 对 → 错 | 工具调用 |
|---|---|---|---|---|
| 自我批判(没有工具,“再想一遍”) | 65.0% | 9 | 10 | 0 |
| CRITIC(用工具核对) | 100.0% | 83 | 0 | 240 |
(模拟模型的”再想一遍”以 15% 的概率发现自己的错误、以 10% 的概率把对的改成错的——这是旋钮,用来对应下面引用的真实观察。)
没有工具时,模型用来”检查”的知识,和它用来”回答”的是同一份——它本来就不知道的事实,再想一遍也想不出来;而”请检查”这句话又暗示”可能错了”,让它动摇了本来对的答案。Huang et al.(2024)在真实模型的推理任务上发现:没有外部反馈时,自我纠正常常不能提高、甚至会降低准确率。CRITIC(Gou et al. 2024)让模型用工具(搜索、代码解释器)去核对自己的输出、再修改;外部工具提供了模型自己没有的信息,“检查”才真正有内容。第 10 讲会系统地讨论自我纠正、验证链和 LLM 判官。
8. 在真实模型上;评估工具使用 · Real models & evaluation
8.1 可选:在真实的 Claude 上
06_real_llm.py 把同样的 12 道题(四类各 3 道)交给真实的 Claude,比较单阶段、多阶段(清楚 / 含糊的描述)和先规划。真实模型不知道虚构国家的人口,所以它必须用工具——这是检验工具使用的干净设置。要看的问题:单阶段时它是说不知道还是编一个?含糊的描述下它会不会写出不合法的参数、错误信息能不能让它改正?先规划时它怎样处理”还不知道是哪个国家”?--toy 模式用 SimToolLLM 跑通同一段代码;编写环境里没有安装 anthropic SDK,真实模型的结果没有报告。默认模型上全部跑完通常不到 1 美元,也可以用 --model 换一个更便宜的模型。
8.2 评估工具使用的几个层次
| 层次 | 问题 | 指标 |
|---|---|---|
| 调用格式 | 调用能被解析吗?参数合法吗? | 无效调用率、JSON 错误率 |
| 工具选择 | 选对了工具吗?调用了不存在的工具吗? | 选择准确率、“幻觉 API”率 |
| 参数 | 参数的值对吗? | 参数准确率(需要标注) |
| 调用时机 | 该调用的时候调用了吗?不该调用的时候呢? | 调用的精确率 / 召回率(§4.2) |
| 结果使用 | 拿到的结果被正确地用上了吗? | 给定正确结果时的最终准确率 |
| 端到端 | 答对了吗?花了多少? | 准确率、调用次数、token、延迟、步数上限触发率 |
Berkeley Function Calling Leaderboard(BFCL)一类的基准专门评估前三层;端到端的评估要在你自己的任务和工具上做。
9. 小结 · Summary
- 为什么要工具:精确计算、实时和长尾的信息、私有数据、行动。模型决定调用什么,程序负责执行。
- 演变:人写路由(MRKL)→ 模型从自己的损失里学(Toolformer)→ 人类示范与偏好(WebGPT)→ 提示下即时推理(ReAct)→ 训练进模型的函数调用 API 与标准化的协议(MCP)。
- 协议:描述(JSON Schema)→ 调用 → 校验 → 执行 → 结果。每一轮都重发全部上下文(第 1 轮 1213 → 第 4 轮 1548 个 token);错误不崩溃,作为结果返回;模型写的参数是不可信的输入,白名单按操作定义。
- Toolformer:过滤准则 用模型自己的损失找到知识边界(保留的 33 个调用几乎都在出现 0–2 次的事实上);微调后的迷你模型在测试国家上用 22 次调用(总是调用要 120 次)把准确率从 85.8% 提到 90.8%;剩下的错误大多是”该调用而没有调用”。
- 工具描述和错误信息都是提示:含糊的描述 93.3% vs 清楚的 100%;错误信息只说 “error” 时准确率掉到 63.3%,38 道题陷入循环。
- 单阶段 vs 多阶段:步骤越多单阶段越差(三国之和 16.7%),多阶段可靠但贵几十倍;链的可靠性取决于最弱的一环;评估要看轨迹。
- 规划与自我批判:独立的步骤适合先规划(轮数和 token 大降),依赖前一步结果时必须边做边看(73.3% vs 100%);没有外部反馈的自我批判净效果为负(−1),用工具核对才有内容。
10. 常见误区 · Common pitfalls
| 误区 | 实际情况 |
|---|---|
| “模型会自己执行工具” | 模型只写出调用的文字;执行、校验、权限都是你的程序的责任 |
| “工具描述随便写一句就行” | 描述就是提示:含糊的描述导致选错工具、参数不合法、不用计算器 |
| “出错了就抛异常” | 错误应该作为结果返回给模型,并且写清楚怎样改正;否则模型无法恢复,或者程序直接崩溃 |
| “检查一下字符就能安全地执行模型写的代码” | 白名单要按操作定义(ast),并且最小权限、对有副作用的操作要求人确认 |
| “多阶段一定比单阶段好” | 多阶段更准但贵几十倍、慢几倍;简单任务上不划算 |
| “给模型越多工具越好” | 工具越多,描述越长、越容易混淆;用工具检索只放相关的几个 |
| “让模型先规划好全部步骤最省事” | 后面的步骤依赖前面的结果时,先规划会凭空猜测 |
| “让模型自己检查一遍就能提高准确率” | 没有外部反馈时,自我批判常常没有帮助甚至有害;要用工具或外部信息核对 |
| “有了工具,答案就一定对” | 模型可能不调用(以为自己知道)、调用了却用错结果、或者抄错 |
11. 练习 · Exercises
练习 1(写描述) 为一个 convert_currency(amount, from_currency, to_currency) 工具写一份”清楚的”描述(JSON Schema + 文字)。至少说明:做什么、什么时候用、每个参数的类型和格式、返回什么、有什么限制。
参考答案(一种可能):description:“Convert an amount of money between two currencies using today’s exchange rate. Use this whenever the user asks about a price in a different currency; do not estimate exchange rates yourself. Returns {‘amount’: float, ‘rate’: float, ‘date’: ‘YYYY-MM-DD’}. Rates are updated daily, so results may differ slightly from real-time rates.” 参数:amount(number,必须为正);from_currency、to_currency(string,三个字母的 ISO 4217 代码,例如 “USD”、“EUR”,可以用 enum 列出支持的货币)。要点:明说”不要自己估算”(对应计算器的”代替心算”);格式用例子说明;写出限制(日更新的汇率)让模型在回答里如实说明。
练习 2(计算成本) 一个多阶段任务有 6 轮,系统提示 1500 个 token,每一轮新增的历史是 120 个 token(模型的调用 + 工具结果),第一轮的用户问题是 50 个 token。(a)一共发送多少输入 token?(b)如果使用提示缓存,缓存的部分按 10% 计价,大约能省多少?
参考答案:(a)第 k 轮(k = 1…6)的输入是 1500 + 50 + 120 × (k − 1)。总和 = 6 × 1550 + 120 × (0 + 1 + … + 5) = 9300 + 1800 = 11,100 个 token。(b)每一轮里,系统提示和之前所有轮的内容都是”重复的前缀”,可以命中缓存;粗略地说只有每轮新增的 120 个 token 是新的。不缓存时 11,100 个 token 按全价;缓存后约有 (11,100 − 1,550 − 5 × 120) ≈ 8,950 个 token 按 10% 计价,相当于省下约 0.9 × 8,950 ≈ 8,000 个 token 的费用(约 73%)。实际节省取决于缓存的规则(最小长度、过期时间、写缓存的额外费用,前置讲 P2)。
练习 3(解释 Toolformer) 在 §4.2 的过滤表里,出现 21 次以上的事实平均提升只有 0.01 nats,出现 0 次的有 7.14 nats。(a)为什么?(b)如果把 τ 设成 0,会发生什么?(c)如果模型的置信度校准得很差(第 5 讲”也教它不知道的事实”那个模型),过滤会怎样出错?
参考答案:(a)提升 = log p(答案 | 调用 + 结果) − log p(答案 | 不调用)。模型已经几乎确定地知道热门事实(log p ≈ 0),调用不能再提升;冷门事实上模型的 log p 很低,看到结果后接近 0,提升很大。(b)τ = 0 时几乎所有调用都被保留(提升都 ≥ 0),模型学会”总是调用”——更贵,而且在第 0 步的照抄不完美时,还会把本来对的热门事实抄错。(c)过滤用的是模型的概率。如果模型在不知道的事实上也给出很高的概率(自信地错),“不调用”时的损失并不高——注意过滤比较的是正确答案的概率,自信地错意味着正确答案的概率很低,所以提升仍然很大,过滤本身不受影响;但第 3 步之后,模型要靠”我知不知道”的内部信号决定是否调用,一个自信地错的模型会在该调用时不调用(演示 3 里 8 个”该调用而没有调用”的错误会更多)。
练习 4(设计对比实验) 为原课作业 2 设计一个实验:比较单阶段和多阶段在你自己选的任务上的表现。写出:任务和题目来源、两种条件的提示、因变量、需要控制的变量、怎样分析失败。
参考答案(一种可能):任务:需要 1–4 步计算 / 查询的问题(例如”某城市的人口比另一城市多多少”),按步骤数分层,每层 30 道以上,答案可以自动核对。单阶段提示:只给问题,要求直接回答;多阶段:同一个问题 + 工具描述 + 协议。控制:同一个模型、同样的温度 / effort、同样的最终答案格式和判分规则;多阶段的步数上限。因变量:准确率(Wilson 区间)、每题调用次数、token、延迟、步数上限触发率。分析:按步骤数画准确率曲线;配对比较(同一道题两种条件,McNemar);失败轨迹分类(选错工具、参数错、结果没用上、最后一步错、单阶段的记错 / 算错)。
练习 5(批判) 有人说:“我们的智能体在 100 道题上准确率 95%,所以工具调用没问题。“列出至少三个你会追问的问题。
参考答案:(1)每道题平均调用了几次、多少 token、多慢?和单阶段比划算吗?(2)有多少题触发了步数上限、陷入了循环?(3)轨迹里有多少无效调用、错误恢复?在题目分布外(新的工具组合、更多步骤)会怎样?(4)该调用而没有调用的比例是多少(模型凭记忆回答、碰巧答对的题,在别的数据上可能答错)?(5)有副作用的工具(写入、发送)有没有被错误地调用?有没有人工确认?(6)工具结果里如果有恶意的文字(注入),会发生什么?
练习 6(动手) 在 tool_lab.ToolformerLab 里把 τ 改成 0.5 和 2.0,重新运行 02 号脚本(缓存键会随 τ 改变)。预期调用率和准确率会怎样变化?
参考答案:τ = 0.5 时保留的调用更多(多了出现 2 次、3–5 次的一些事实),模型会更爱调用:调用率上升,冷门事实上的召回率可能提高,但也会在一些本来知道的事实上调用(如果照抄完美,这只多花钱;如果照抄有错,可能把对的变成错的)。τ = 2.0 时只保留出现 0–1 次的事实上的调用,模型更省、更保守:调用更少,出现 2 次的那些”没把握但其实知道”的事实上不再调用。哪个更好取决于调用的成本和照抄的可靠性——实际数字要运行才知道。
练习 7(思考) WebGPT 用人类偏好训练浏览策略。结合第 6 讲,说说这可能带来什么问题,以及 WebGPT 的”引用”设计在多大程度上缓解了它。
参考答案:偏好训练会奖励评审者喜欢的东西,而评审者往往无法在有限时间里核实每一个说法——模型可能学会写出看起来有依据、读起来有说服力的回答(第 6 讲 §6.3:在评审者无法验证的地方,偏好优化学到的是”让评审者满意”)。引用让评审者可以点开来源核对,降低了核实的成本,因而让偏好信号更接近”准确”;但它没有消除问题:评审者未必真的去核对每一条引用,模型也可能挑选支持自己说法的片段(cherry-picking),或者引用和说法之间只有表面的联系(第 7 讲 §6.3 的归因问题)。更彻底的做法是让评估本身包含系统的归因检查。
12. 延伸阅读 · Further reading
演变线
- Karpas et al. (2022). MRKL Systems.
- Parisi et al. (2022). TALM: Tool Augmented Language Models.
- Schick et al. (2023). Toolformer: Language Models Can Teach Themselves to Use Tools. NeurIPS.
- Nakano et al. (2021). WebGPT: Browser-assisted Question-answering with Human Feedback.
- Yao et al. (2023). ReAct: Synergizing Reasoning and Acting in Language Models. ICLR.
- Mialon et al. (2023). Augmented Language Models: a Survey.
规模化的工具使用
- Patil et al. (2023). Gorilla: Large Language Model Connected with Massive APIs.
- Qin et al. (2024). ToolLLM: Facilitating Large Language Models to Master 16000+ Real-world APIs. ICLR.
- Shen et al. (2023). HuggingGPT.
- Anthropic (2024). Model Context Protocol(规范与文档);各家的工具使用 / 函数调用文档。
规划与自我批判
- Xu et al. (2023). ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models.
- Gou et al. (2024). CRITIC: Large Language Models Can Self-Correct with Tool-Interactive Critiquing. ICLR.
- Huang et al. (2024). Large Language Models Cannot Self-Correct Reasoning Yet. ICLR.
- Gao et al. (2023). PAL: Program-aided Language Models.
13. 下一讲预告 · Next lecture
这一讲的多阶段做法里,“拆成哪几步”是我们(或者模拟模型的规则)事先知道的。第 9 讲进入原课第 6 周:任务分解——当任务的结构不那么明显时,怎样让模型自己把一个复杂的问题拆成简单的子问题。
- 为什么分解有效:每一步更像训练时见过的问题,错误可以定位;
- Least-to-Most(Zhou et al. 2023):先拆成从易到难的子问题,再依次解决,用前面的答案解后面的问题——在长度泛化上尤其有效:我们会训练一个只见过短问题的迷你模型,看它在长问题上怎样失败、分解之后又怎样成功;
- Successive Prompting(Dua et al. 2022)和 Decomposed Prompting(Khot et al. 2023):一次问一个子问题、把子任务交给专门的”处理器”;
- 提示链(prompt chaining)与错误传播:k 步、每步准确率 p,整条链大约是 p^k——怎样测量、怎样在中间加检查。