工具接口:为什么 Agent 需要结构化 I/O 本节摘要:语言模型只会产出 token,而程序会采取行动。这两者之间的鸿沟,就是「工具接口(Tool Interface)」——一份契约,让模型请求一个动作,再让 host 去执行它。2026 年的每一条技术栈——OpenAI、Anthropic、Gemini 的函数调用,MCP 的 ,A2A 的 task parts——都是同一个「四步循环」的不同编码。本节给这个循环命名,并展示跑通它所需的最小机制:描述(describe)→ 决定(decide)→ 执行(execute)→ 观察(observe),谁拥有每一步,纯工具与有副作用工具的安全分野,以及为什么「让模型直接吐 JSON」是个坏主意。
本节摘要:语言模型只会产出 token,而程序会采取行动。这两者之间的鸿沟,就是「工具接口(Tool Interface)」——一份契约,让模型请求一个动作,再让 host 去执行它。2026 年的每一条技术栈——OpenAI、Anthropic、Gemini 的函数调用,MCP 的
tools/call,A2A 的 task parts——都是同一个「四步循环」的不同编码。本节给这个循环命名,并展示跑通它所需的最小机制:描述(describe)→ 决定(decide)→ 执行(execute)→ 观察(observe),谁拥有每一步,纯工具与有副作用工具的安全分野,以及为什么「让模型直接吐 JSON」是个坏主意。
阅读完本节,你应当能够:
一个 LLM 输出的是「下一个 token 的概率分布」——这就是它全部的输出面。你问一个聊天模型「班加罗尔现在天气如何」,它能写出一串看起来合理的句子,但它拨不进天气 API。这句话可能恰好猜对,也可能滞后三天。
弥合这道鸿沟,正是工具接口的目的。host 程序(你的 Agent 运行时、Claude Desktop、ChatGPT、Cursor 或一段自定义脚本)向模型广播一份可调用工具的清单。模型在判定某个动作是必要的时候,产出一个结构化的 payload,点出工具名和参数。host 解析这个 payload,真正地运行工具,再把结果喂回去。循环往复,直到模型判定不再需要调用为止。
这份契约的第一版在 2023 年 6 月以 OpenAI 的 functions 参数问世;Anthropic 随后在 Claude 2.1 里跟进 tool_use 块;几个月后 Gemini 加入 functionDeclarations。如今每家厂商都暴露出相同的形状:输入是 JSON Schema 类型化的工具列表,输出是 JSON payload 形态的工具调用。模型上下文协议(2024 年 11 月)把它泛化,使一份工具注册表能服务所有模型;A2A(2026 年 4 月,v1.0)为 Agent 之间的委托叠加了同样的原语。
四步循环是这一切之下不变的东西。本章其余各节,都只是对它的展开。
host 用三个字段声明每个工具:
get_weather,而不是「weather thing」。模型会收到这份清单。现代厂商用各自专有的模板把这些声明序列化进系统提示,所以作为调用方,你只需面对结构化形式。
给定用户消息和可用工具,模型在三选一:
parallel_tool_calls: true(OpenAI 与 Gemini 默认开,Anthropic 需显式 opt-in)下,模型能在一轮里发出多个调用。refusal 块,而非一次调用。一个工具调用 payload 有三个稳定字段:调用 id、工具 name、一个 JSON arguments 对象。id 的存在,是为了让 host 能把之后的结果与这一次特定调用关联起来——当并行调用乱序返回时,这点至关重要。
host 收到调用,按声明的 schema 校验参数,再运行执行函数(executor)。参数非法,意味着模型幻觉了一个字段或用错了类型——这是弱模型上极常见的失败模式。生产级 host 遇到非法参数,有三条路可走:① fail fast,把错误抛回给模型;② 用受限解析器修复 JSON;③ 把校验错误塞进提示里重试模型。
执行函数本身是普通代码:Python、TypeScript、shell 命令、数据库查询。它产出一个结果——通常是字符串,但可以是任意 JSON 值或结构化内容块(MCP 里的文本、图像或资源引用)。结果必须可序列化。
# 最小工具注册表:每个工具持有四样东西 TOOL_REGISTRY = { "get_weather": { "description": "当用户询问某个城市当前天气时使用。不要用于历史数据。", "schema": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, "executor": lambda city: f"{city}: 28°C, 多云", # 确定性执行函数 "consequential": False, # 纯工具 vs 有副作用工具 }, # ... 其余工具同理 }
host 把工具结果追加进对话(作为一条带匹配 id 的 tool 角色消息),再次调用模型。此时模型在上下文里有了工具输出,可以给出最终答案或请求更多调用。循环持续,直到模型不再产出调用,或 host 触达迭代次数的安全上限。
工具按安全性分成两类:
get_weather、search_docs、get_current_time。可以放心地试探性调用。send_email、delete_file、execute_trade。必须加门控。Meta 在 2026 年提出的 Agent 安全「二选一规则(Rule of Two)」:单轮里至多同时出现三者之二——不可信输入、敏感数据、有副作用的动作。工具接口正是你落实这条规则的地方:拒绝调用、要求用户确认、或提升权限范围。完整的安全章节见第 15 节,Agent 级权限策略见第 14 章 09 节。
「要求模型用 JSON 回复」是函数调用诞生前的老套路。它在前沿模型上仍有约 5%~15% 的失败率,在更小的模型上失败率高得多。失败形态包括缺括号、尾逗号、幻觉字段、类型错误——你随后还需要一轮 JSON 修复、一次重试,或一个受限解码器(constrained decoder)。
原生函数调用更好,原因有三:
tool_use、Gemini 的 responseSchema),输出保证通过校验。第 02 节会并排走查三家厂商 API;第 04 节深入结构化输出。
循环在模型停止产出调用,或 host 触达最大轮次时终止。生产级 host 把它设在 5~20 轮之间;超过这个范围,你几乎肯定陷入了一个模型走不出的死循环。Claude Code 默认 20;OpenAI Assistants 默认 10;Cursor 的 Agent 模式 25。
替代方案——无界循环——每半年就会以「Agent 一夜烧掉 400 美元 API 费用」的事后复盘形式出现。绝不要在没有上限的情况下上线。
| 上下文 | 谁描述工具 | 谁决定 | 谁执行 |
|---|---|---|---|
| 单轮函数调用(OpenAI/Anthropic/Gemini) | 应用开发者 | LLM | 应用开发者 |
| MCP | MCP 服务端 | LLM,经 MCP 客户端 | MCP 服务端 |
| A2A | Agent Card 发布者 | 发起方 Agent | 被调方 Agent |
| Web 浏览器(函数调用 Agent) | 浏览器扩展 / WebMCP | LLM | 浏览器运行时 |
处处都是同样的四步。列名在变,结构不变。
| 维度 | 让模型吐 JSON(前函数调用) | 原生函数调用 | MCP |
|---|---|---|---|
| 合法 JSON 率 | 85%~95% | strict 模式 98%~99% | 同厂商原生调用 |
| 调用与回复分离 | 否(混在文本里) | 是(独立协议槽) | 是 |
| schema 强制 | 无 | 受限解码 | 受限解码 |
| 跨 host 复用 | N×M 重写 | 每家不同 | 一份注册表服务所有模型 |
💡 选型心法:能用原生函数调用就别让模型裸吐 JSON;能在 MCP 之上建模就别为每家 host 写定制胶水。本章其余各节都在为这两条背书。
本节产出 outputs/skill-tool-interface-reviewer.md——一个工具接口审查技能。给定一份工具定义草稿(名字 + 描述 + schema + 执行函数大纲),它按「循环适配性」逐项审计:名字是否机器稳定、描述是否是完整的用法简报、schema 是否正确使用 JSON Schema 2020-12、纯工具与有副作用工具的分类是否显式。
原课程 code/main.py 用纯标准库跑通四步循环(无 LLM):一个假的「decider」函数靠模式匹配模拟模型,执行器、schema 校验器、observe 步的脚手架都是真的。跑它能看到完整的请求/响应编排与可打印的中间状态,之后任何一节都能把这个假 decider 换成真实厂商。
加第四个工具:给 code/main.py 加一个 get_stock_price(ticker)。描述写成「当用户按 ticker 询问当前股价时使用;不要用于历史价格或市场综述。」跑脚手架,确认假 decider 会把提到 ticker 的查询路由到新工具。
打破 schema 校验器:传入一个 arguments 缺了必填字段的调用,确认 host 在执行前就拒绝。再传入一个带多余未知字段的调用,自己决定:host 该拒绝还是忽略?用安全论据为自己的选择辩护。
分类与门控:把脚手架里每个工具标为纯或有副作用。给需要门控的注册条目加一个 consequential: true 标志,改循环,使它在选中这类工具时打印「将请求用户确认」一行。这就是每个生产级 host 都需要的确认门。
手画循环:在纸上画出四步循环,把上面的厂商列表填进去(选你最熟的客户端:Claude Desktop、Cursor、ChatGPT 或自定义栈),并与第 06 节里 MCP 的变体交叉对照。
读厂商文档:从上到下读一遍 OpenAI 的函数调用指南,找出一个在请求里、却不在本节四步循环里的字段。解释它加了什么,以及为什么它「方便」而非「必需」。
id(关联结果)、name、arguments;并行调用乱序返回时 id 关键。下一节,我们把三家厂商(OpenAI、Anthropic、Gemini)的函数调用 API 并排走查,看清同一四步循环在不同协议里的编码差异。