Chapter 05
自测与跨章辨析
前四章建立了从协议到设计的完整链条。这一章不教新东西——用三层梯度的题,把它从「读过」逼成「调得出来、讲得清楚」。
这一章你要做到
- 概念层:不翻书,复述「模型只请求、harness 执行」的 5 个角色与本质。
- 原理层:重建协议字段(tool_use / tool_result / stop_reason)与 agent loop 的机制。
- 应用判别层:在具体场景里判断「该用哪一章的方案」——这是把知识迁移到真实工作的唯一检验。
所有答案集中在文末一个折叠块里。每道题先合上教程,把答案写在纸上或编辑器里,写完再展开对照——直接点开看答案,等于把这一章当成又读了一遍,「我读得很顺」的错觉正是这么来的。
·三层梯度
A概念层 · 对应 01 章
- 一次工具调用往返的 5 个角色,按发生顺序是哪 5 个?模型「停机」发生在第几个? 提示:01 章 §1.3
- 「模型执行了
get_weather工具」这句话错在哪里?正确的表述是什么? 提示:01 章 §1.2 - function calling 和 tool use 是两种不同的机制吗?一句话说清两者关系。 提示:01 章 §1.4
- 模型「看得到」和「看不到」的东西,分界线在哪里?举一个它看不到的例子。 提示:01 章 §1.1
B原理层 · 对应 02–03 章
- Anthropic 把
tool_result放在哪个 role 的消息里发回去?OpenAI 用的是哪个 role? 提示:02 章 §2.3 - OpenAI 的
tool_calls[].function.arguments是什么数据类型?拿到手要先做什么才能用? 提示:02 章 §2.3 tool_choice设成any/required,能钉死模型一定调用你指定的那个工具吗?怎样才能钉死具体工具? 提示:02 章 §2.4- agent loop 的终止由谁决定?写出
while循环「继续下一轮」的判断条件。 提示:03 章 §3.1 / §3.3 - 约束解码(constrained decoding)保证了什么、保证不了什么? 提示:02 章 §2.7
- 一个跑了 20 步的 agent,为什么第 20 步特别贵?这个成本叫什么? 提示:03 章 §3.1
C应用判别层 · 跨 01–04 章
这一层的题没有「背过就会」的答案——每一道都要先判断它落在哪些章的概念上,再做选择。括号里标了它横跨的章节。先自己定位根因,再展开答案。
- 【03 × 04】生产环境里,一个 agent 偶尔会烧掉大量 token 才返回。日志显示它反复调用同一个搜索工具,且每次返回都很大。这是 03 章的「循环 / 终止 guard」问题,还是 04 章的「工具返回值设计」问题?你会先查什么来分辨这两者?
- 【02 × 03】调试时 API 报 400,提示
tool_use与tool_result不匹配。根因更像出在 02 章的「协议配对规则」本身,还是 03 章的「循环在裁剪 / 压缩上下文时破坏了配对」?你怎么定位? - 【01 × 04】模型总是不调用你提供的 calculator 工具,宁可自己硬算、还算错。结合 01 章「调不调由模型决策」和 04 章「description 是最高杠杆」,给出你的排查顺序。
- 【04 设计 × 04 前沿】要给一个 agent 接入 8 个内部系统、几十个工具。直接为每个端点写一个 tool 定义、全塞进 context,会出什么问题?在「合并成更少的高层工具」「用 Tool Search 按需加载」「用 MCP 标准化接入」之间,你怎么权衡取舍?
- 【03 × 04 前沿】一个数据处理流水线要串起 5 个工具,中间结果体积很大。每一步都吐 JSON、过一遍模型 context,太贵。该用哪种前沿方案?结合 03 章的 re-prefill 成本,解释它到底省在哪里。
亲手画一张图
合上教程,在纸上或 Excalidraw 里画出工具调用的核心循环——只画 5 个角色,加上那条「交还控制」的边。画完翻回 01 章的图 0 / 图 1.1 对照:你画的图里,模型停机的那一步标出来了吗?「执行」和「回填」落在分界线的哪一侧?
答案(先把三层都做完再展开)
概念层
- 顺序:① 工具定义 → ② 模型决策 → ③ tool_use 请求 → ④ harness 执行 → ⑤ tool_result 回填。模型停机发生在第 ③ 个(生成 tool_use 请求后,
stop_reason变成tool_use,生成中止)。 - 错在「执行」二字。模型只生成了一个调用请求然后停机,真正执行
get_weather的是 harness(你的代码)。正确表述:模型请求调用get_weather,harness 执行后把结果回填。 - 是同一套机制。OpenAI 早期叫 function calling、现在把 function 当作 tool 的一种 type,Anthropic 叫 tool use——声明工具 → 模型请求 → 你执行 → 回填,流程完全一致。
- 分界线是「context 里的 token」:模型只能看见进入 context 的内容(系统提示、对话历史、工具定义、回填的 tool_result)。看不到的例子:你的数据库、实时天气 API、文件系统、工具的真正实现——这些它永远碰不到,只能请求 harness 代它去碰。
原理层
- Anthropic 放在
role: "user"的消息里(不是专门的 tool 角色);OpenAI 用一条role: "tool"的消息。 - 是 JSON 字符串,不是对象。要先
JSON.parse(或等价反序列化)才能访问字段。对照之下 Anthropic 的input到手已经是对象。 - 不能。
any/required只强制「调用某个工具」,不指定是哪个。要钉死具体工具,得用指定形式:Anthropic{"type":"tool","name":"x"}、OpenAI{"type":"function","function":{"name":"x"}}。 - 由 harness 决定,不是模型。继续条件:
resp.stop_reason == "tool_use"(模型还在请求工具就再转一轮;否则跳出循环)。模型自己不会可靠地判断「任务完成」。 - 只保证语法:产出的 JSON 一定符合 schema 的结构(字段、类型、枚举合法)。保证不了语义:参数形状对、值却错(valid but wrong)完全可能。
- 因为每一轮都要把不断变长的对话历史整个重新发一遍给模型(re-prefill);第 20 步要把前 19 步的全部内容再喂一次。这个随轮数累积的成本就叫 re-prefill(重新预填充)。
应用判别层
- 两者都查,但先分辨症状。「反复调用同一个工具」指向 03 章——缺少重复调用检测 /
max_iterationsguard,模型在原地打转;「每次返回都很大」指向 04 章——返回值没有分页 / 截断 /response_format,结果撑大 context、又反过来让模型更难收敛。先看日志里「同样的工具 + 同样的参数」是否重复出现(→ 03 的 guard 问题),再看单次 tool_result 的体积(→ 04 的返回值设计)。两者常常同时存在、互相放大。 - 根因几乎总在 03 层,不在 02。协议的配对规则(§2.2)是固定的、本来就对;真实事故里破坏配对的,是循环在裁剪 / 压缩上下文时丢了
tool_use却留下孤立的tool_result,或并行结果回填顺序错乱。定位方法:打印发给 API 的完整 messages,检查每个tool_use_id是否都有且仅有一个配对的tool_result,以及它们的先后顺序。 - 排查顺序:先接受 01 章的事实——「调不调工具是模型的概率判断,不是 if-else」,所以这是个可以用 prompt / 定义来影响的问题;然后直接动 04 章的最高杠杆——把 calculator 的
description写清楚「何时必须用它」(例如「任何算术运算都调用本工具,不要自行心算」),必要时配合tool_choice在该场景下强制调用。换模型是最后才考虑的。 - 全塞进 context 会让工具定义吃掉大量 token,且工具一多越容易重叠、模型越选不准(04 §4.5)。权衡:先做减法——能合并成更少的高层工具就合并(降低歧义 + 省 token);工具数量本身降不下来时,用 Tool Search Tool 让定义按需加载(2025-11,省约 85% 定义 token);如果这些系统还要被多个 agent 应用复用,则用 MCP 把它们标准化成 server(M×N → M+N),一次接入处处可用。三者不互斥,常组合使用。
- 用「模型写代码调工具」(Programmatic Tool Calling,2025-11)。它让模型写一段代码在沙箱里把 5 个工具串起来调用,中间结果留在沙箱、不进 context,只把最终结果带回模型。省的正是 03 章那笔 re-prefill 成本——「每步吐 JSON」要把每个中间结果都塞进 context、并在后续每一轮重新预填充;代码方式把这 5 步的中间数据挡在了模型 context 之外。