工具使用与函数调用 本节摘要:Toolformer(Schick 等人, 2023)开启了自监督的工具标注:让模型自己给预训练语料标注候选 API 调用,只在「调用结果能降低下一个 token 损失」时保留标注。Berkeley 函数调用排行榜 V4(Patil 等人, 2025)则划下了 2026 年的评估标准:40% 智能体式、30% 多轮、10% 实时、10% 非实时、10% 幻觉检测。结论是——单轮函数调用已基本解决,但记忆、动态决策、长程工具链还没。本节是 Agent 工程 P0 的收官:讲透工具调用的训练信号(为什么自监督管用)、2026 年的评估靶子(BFCL V4 五类)、工程上的工具注册表(模式校验、参数强制、并行派发、沙箱),并诊断三个仍未解决的开放问题。
本节摘要:Toolformer(Schick 等人, 2023)开启了自监督的工具标注:让模型自己给预训练语料标注候选 API 调用,只在「调用结果能降低下一个 token 损失」时保留标注。Berkeley 函数调用排行榜 V4(Patil 等人, 2025)则划下了 2026 年的评估标准:40% 智能体式、30% 多轮、10% 实时、10% 非实时、10% 幻觉检测。结论是——单轮函数调用已基本解决,但记忆、动态决策、长程工具链还没。本节是 Agent 工程 P0 的收官:讲透工具调用的训练信号(为什么自监督管用)、2026 年的评估靶子(BFCL V4 五类)、工程上的工具注册表(模式校验、参数强制、并行派发、沙箱),并诊断三个仍未解决的开放问题。前五节的循环、规划、反思、搜索、精炼,最终都建立在这一节的基础上:Agent 如何安全、可靠地调用外部工具。
对应原课程:Phase 14 · Lesson 06 ·
tool-use-and-function-calling(原英文phases/14-agent-engineering/06-tool-use-and-function-calling/docs/en.md)。
阅读完本节,你应当能够:
早期的工具使用问的是:模型能不能预测出一个正确的函数调用? 现代的工具使用问的是:模型能不能跨 40 步链接工具,带着记忆,在部分可观察下,从工具失败中恢复,并且不幻觉出不存在的工具?
Toolformer 划下了基线:模型能用自监督学会何时调用工具。BFCL V4 定义了 2026 年的评估目标。两者之间的差距,正是生产 Agent 栖身的地带。
核心思想:让模型给自己的预训练语料标注候选 API 调用。对每个候选,执行它。只在「把工具结果包含进来能降低下一个 token 的损失」时,才保留这个标注。在过滤后的语料上微调。
覆盖的工具:计算器、问答系统、搜索引擎、翻译器、日历。自监督信号纯粹是「这个工具帮不帮得上预测文本」——没有人工标签。
规模结果:工具使用在规模上涌现。小模型被工具标注拖累,大模型反而受益。这就是为什么 2026 年的前沿模型把强工具使用内置,而大多数 7B 模型需要显式的工具使用微调才可靠。
BFCL 是 2026 年事实上的评估。V4 的构成:
V3 引入了基于状态的评估:在一串工具调用之后,检查 API 的实际状态(比如「文件创建了吗?」),而不是去匹配工具调用的 AST。V4 加了网页搜索、记忆、格式敏感度类别。
💡 2026 年的关键发现:单轮函数调用已接近解决。失败集中在记忆(跨轮携带上下文)、动态决策(根据前序结果选工具)、长程链(20+ 步后漂移)、幻觉检测(没有合适工具时拒绝调用)。如果你在生产里遇到工具调用问题,大概率不在这四类之外。
每个厂商都有自己的模式。细节有别,但形状相同:
name: string description: string(它做什么、何时该用) input_schema: JSON Schema(properties, required, types, enums)
Anthropic 直接用 input_schema。OpenAI 用 function.parameters。两者都接受 JSON Schema。描述是承重的——模型靠读描述来挑对工具。糟糕的工具描述是「选错工具」失败的头号根因。
不要信任任何一次工具调用。校验:
"5"。无歧义就强制,有歧义就拒。status in {"open", "closed"},模型吐了 "in_progress",带描述性错误拒掉。每一次校验失败,都应该返回一条结构化观察,让模型能用正确的形状重试。
现代厂商支持在一个助手轮里发起并行工具调用。循环是:
tool_use_id。tool_result 块回灌,按 tool_use_id 关联。工程铁律:把关联 ID 当作承重件。搞混了,就会得到「错误工具接到错误结果」的路由错误。
工具执行就是沙箱边界(详见原课程第 09 节)。短版本:每个工具都该写明读写面、网络访问、超时、内存上限。通用的 run_shell(cmd) 是危险信号;具体的 git_status() 更安全。
⚠️ 设计警示:工具描述是「选错工具」的头号根因,关联 ID 错配是并行调用的头号根因,通用 shell 是安全的头号根因。这三件事,每一件都值得在代码评审里单列检查项。
原课程 code/main.py 实现了一个生产形态的工具注册表:JSON Schema 子集校验(纯标准库);带描述、输入模式、超时、执行器的工具注册;参数强制与枚举校验;带关联 ID 的并行派发;结构化字符串的错误观察。
核心骨架如下,用伪代码展示。
class Tool: def __init__(self, name, description, input_schema, fn, timeout=10): self.name = name self.description = description # 承重:模型靠它选工具 self.input_schema = input_schema # JSON Schema 子集 self.fn = fn self.timeout = timeout REGISTRY = {} # name -> Tool def register(tool): REGISTRY[tool.name] = tool
def validate_and_coerce(args, schema): for field, spec in schema["properties"].items(): if field not in args: if field in schema.get("required", []): return None, f"缺少必填字段:{field}" continue # 类型强制:字符串 "5" -> int 5(无歧义时) if spec["type"] == "integer" and isinstance(args[field], str) \ and args[field].isdigit(): args[field] = int(args[field]) # 枚举校验 if "enum" in spec and args[field] not in spec["enum"]: return None, f"{field} 取值非法,应为 {spec['enum']}" return args, None
def dispatch_parallel(tool_calls): results = {} for call in tool_calls: # 独立的可真并行 tool = REGISTRY.get(call["name"]) if tool is None: results[call["id"]] = f"错误:未知工具 {call['name']}" continue args, err = validate_and_coerce(call["args"], tool.input_schema) if err: results[call["id"]] = f"错误:{err}" # 结构化错误观察 continue results[call["id"]] = str(tool.fn(**args)) # 按 id 关联回灌 return results
运行 python3 code/main.py 会展示一个迷你 Agent 在一轮里调三个工具,其中一个故意格式错误的调用被拒,并回灌一条模型可据以行动的描述性错误。
💡 设计要点:校验失败的路径和执行成功的路径同等重要。一次格式错的调用不该崩溃整个 Agent——它应该回灌一条「错误:缺少必填字段 X」,让模型在下一轮用对的形状重试。这就是第 01 节说的「400 错误要变成观察字符串」在工具层的落点。
每个厂商都有自己的工具模式——Anthropic、OpenAI、Gemini、Bedrock。如果你需要多厂商,用一层翻译层(OpenAI Agents SDK、Vercel AI SDK、LangChain 工具适配器)。BFCL 是参考基准——如果工具使用是你产品的核心,发布前拿它跑一遍你的 Agent。
| 厂商/框架 | 工具模式字段 | 备注 |
|---|---|---|
| Anthropic | input_schema(直接用 JSON Schema) |
描述遵从度强,tool_use_id 强制 |
| OpenAI | function.parameters(JSON Schema) |
支持并行调用,tool_call_id 关联 |
| Gemini | function_declarations |
模式作为模型配置的一部分 |
| OpenAI Agents SDK | FunctionTool 类型 + Guardrails |
自带翻译层,护栏可调工具(CRITIC 风格) |
| LangChain 工具适配器 | 统一的 BaseTool |
屏蔽厂商模式差异 |
跨厂商的关键不变量是:name + description + JSON Schema 参数 + 关联 ID。只要这四样齐了,翻译层就能在不同厂商间搬运。
本节产出一份可复用技能(原课程 outputs/skill-tool-registry.md):
skill-tool-registry.md:给定一个任务领域,生成工具目录、模式、注册表。包含描述质量自检(每个工具的描述是否告诉模型何时该用它?是否写明了边界情况?),以及一份校验清单(类型强制是否过宽?枚举是否齐全?必填字段是否明确?沙箱边界是否写清?)。Python 代码(code/main.py)是独立可运行的工具注册表,校验/强制/并行派发/错误观察四件套都是厂商无关的;把玩具执行器换成真实函数(并接上沙箱)即可投入生产。
name + description + JSON Schema 参数 + 关联 ID;跨厂商不变。run_shell 是危险信号。至此,Agent 工程的 P0 核心路径——Agent 循环、计划执行、Reflexion、思维树与 LATS、自我精炼与 CRITIC、工具使用——已经串成一条完整的闭环:从「观察-思考-行动」的基础循环,到规划-执行的解耦,到零梯度的言语自我修正,到审慎搜索,到迭代精炼,最后落到工具调用的工程地基。后续 Agent 工程的进阶内容(记忆与虚拟上下文、技能库与终身学习、HTN 与进化规划、Anthropic 工作流模式、有状态图编排、Actor 模型、多智能体编排、Agent 可观测性、失败模式与防护、Agent 工作台工程等)留待后续批次汉化。