一句话摘要:函数调用是 LLM 应用层最关键的结构化契约,但 OpenAI/Anthropic/Google/MCP 四类协议在 JSON Schema 子集、tool_use block 结构、并行语义、流式 partial 解析上仍有显著差异;本文以"应用开发者"视角给出 ToolIR 中间表示 + 协议适配器 + 流式解析器 + Tool Gateway 的完整工程范式,并给出 14 条可立刻落地的工程清单。
一、问题的提出:函数调用为什么是 LLM 应用的核心契约
2026 年是 LLM 应用从"Prompt Engineering + RAG"走向"Agent + Tools"的转折点。如果说 2024 年 RAG 工程定义了"应用层如何从知识库召回上下文",那么 2026 年函数调用(Function Calling)协议工程定义了"应用层如何让模型结构化地调用外部世界"。一条函数调用的准确率、一次工具调用链的并行度、一组多 provider 工具描述的语义保真度,已经成为 LLM 应用能否上线、能否规模化、能否多云部署的核心瓶颈。
但应用开发者面对的现实是:没有统一的协议。OpenAI 的 Tools API 用 tools[].function.{name,description,parameters} 的扁平 JSON Schema;Anthropic 的 Tool Use 把工具描述嵌入 system 或 user 消息后通过 tool_use block 返回;Google Gemini 的 Function Calling 同时支持"函数声明"与"代码执行"两种模式;MCP(Model Context Protocol)2025 年由 Anthropic 推出后已成为事实标准,但它的 resources / tools / prompts 三元组与上面三类 API 仍然存在字段命名、错误语义、协议握手的多重差异。一个 LLM 应用如果想支持"GPT-5 + Claude 4.5 + Gemini 2.5 + 本地 MCP server"四类后端,必须维护四套工具描述、四套响应解析器、四套流式协议适配——这就是 LLM 应用层的"协议碎片化"问题。
本文以"应用开发者"视角给出系统性的工程范式:ToolIR 中间表示 + 协议适配器 + 流式解析器 + Tool Gateway 四层架构,并用 14 条工程清单给出可立刻落地的设计决策。写作目标读者是 LLM 应用架构师、AI 平台工程师、以及负责多模型策略的技术负责人——你不需要从头实现这四层,但需要理解每一层的关键设计权衡,避免在应用层做出"短期省事、长期无法多云"的决策。
二、形式化:四类函数调用协议的语义对齐矩阵
我们把"函数调用协议"形式化为一个五元组 (M,T,C,R,S):M 是消息协议(message envelope),T 是工具描述(tool description schema),C 是调用请求(call request payload),R 是返回结果(return result envelope),S 是流式语义(streaming semantic)。下表给出 2026 年 9 月的实测对齐矩阵:
| 协议 | M 消息载体 | T 工具描述 | C 调用请求 | R 返回结构 | S 流式语义 |
|---|
| OpenAI Tools (2024-08+) | messages[] + tools[] 顶层 | JSON Schema 子集(不支持 oneOf/anyOf 的复杂组合) | tool_calls[].function.{name,arguments} (string-encoded JSON) | role=tool + tool_call_id | SSE + tool_calls.delta 增量 arguments |
| Anthropic Tool Use (2024-10+) | messages[] 含 tool_use / tool_result block | input_schema (JSON Schema 完整支持) | tool_use.id + tool_use.input (object) | tool_result.tool_use_id + content | SSE + input_json_delta 增量 JSON |
| Google Gemini Function Calling | contents[] + tools[] 顶层 | OpenAPI 3.0 子集(parameters 用 JSON Schema) | functionCall.{name,args} | functionResponse.{name,response} | SSE + partial function call args |
| MCP (Model Context Protocol 2025-03+) | JSON-RPC 2.0 over stdio/SSE/HTTP | JSON Schema 完整 + outputSchema | tools/call JSON-RPC method | content[] typed array (text/image/resource) | SSE notifications + progress |
关键观察:
- JSON Schema 子集差异:OpenAI 在 2024-08 之前严格不支持
oneOf/anyOf,现在部分支持但行为不一致;Anthropic 完整支持;Google 走 OpenAPI 3.0 subset,限制更多。
- 字符串 vs 对象编码:OpenAI 的
arguments 是 JSON string(解析前必须 json.loads),Anthropic 的 input 是 object(直接 args.foo)——这是应用层最容易出错的地方。
- 流式 partial 协议:OpenAI 增量字符串(需自己拼 JSON),Anthropic 增量 JSON patch(结构更友好),Gemini 部分支持,MCP 通过 progress 通知。
- 错误语义:OpenAI 把工具错误编码为普通
role=tool content,Anthropic 用 is_error: true 块,Gemini 用 functionResponse.response.error 字段,MCP 用 JSON-RPC error 字段。
把这五元组的每个维度差异都理解清楚,是设计 ToolIR 的前置条件。
JSON Schema 子集差异(最隐蔽的兼容性问题):
- OpenAI:支持
type/properties/required/enum/description,对 $ref 支持有限,additionalProperties: false 必须显式声明。
- Anthropic:完整支持 JSON Schema Draft 7 + 引用 + 组合关键字 + format 校验。
- Google:OpenAPI 3.0 subset,
description 是必填的(不填会被拒),enum 必须显式列举所有可能值。
- MCP:完整 JSON Schema Draft 2020-12 +
outputSchema(描述返回值的 schema,应用层可以做 response validation)。
协议特定的 metadata 字段:
- OpenAI 有
strict: true(强制 JSON Schema 严格校验,2024-08 后稳定)和 parallel_tool_calls: false(强制单次只调一个工具)。
- Anthropic 有
cache_control: {type: "ephemeral"}(在 tool description block 上可以打 cache 标记)和 disable_parallel_tool_use: true。
- Google 有
codeExecution 配置(允许模型生成并执行 Python 代码,本质是另一种工具)。
- MCP 有
_meta 字段(任意 provider-specific metadata)和 annotations(hint 给 UI 层做展示)。
调用请求的 wire format 差异:
{
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Beijing\",\"unit\":\"celsius\"}"
}
}]
}
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "get_weather",
"input": {"city": "Beijing", "unit": "celsius"}
}
{
"functionCall": {
"name": "get_weather",
"args": {"city": "Beijing", "unit": "celsius"}
}
}
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {"city": "Beijing", "unit": "celsius"}
}
}
应用层若直接用这四种 wire format 写胶水代码,每多接一个 provider 就多一份 if/else 分支。正确的做法是在 SDK 内部完成 wire ↔ ToolIR 双向转换,业务代码只看到 ToolIR。
ToolIR(Tool Intermediate Representation)是应用层的"协议中立的工具描述语言",我们建议以下五元组结构:
interface ToolIR {
id: string;
name: string;
description: string;
parameters: JSONSchema;
outputSchema?: JSONSchema;
behavior: {
readonly?: boolean;
destructive?: boolean;
idempotent?: boolean;
open_world?: boolean;
requires_approval?: 'always' | 'never' | 'if_destructive';
};
constraints: {
timeout_ms: number;
max_retries: number;
rate_limit?: {rpm: number; tpm?: number};
cost_estimate_usd?: number;
};
provider_hints?: Record<string, Record<string, unknown>>;
protocol_meta?: Record<string, unknown>;
}
ToolIR 与已有 schema 库的关系:ToolIR 不是要替代 Pydantic/Zod,而是"在 Pydantic/Zod 之上加一层 provider-neutral 语义"。应用层的典型用法是:先用 Pydantic/Zod 定义参数 schema(享受 IDE 自动补全、运行时校验、序列化),再通过 pydantic_to_tool_ir(model) 或 zod_to_tool_ir(schema) 转换为 ToolIR,最后 adapter 把 ToolIR 序列化为 provider wire。ToolIR 在这两者之间起"协议中立的中介"作用——它是 OpenTelemetry GenAI semantic conventions 在工具描述层的具体化,也是 MCP 协议 server 端的标准输入格式。这一层抽象的最大价值不是减少代码量,而是让"换 provider"从 2 周重构降为 2 天配置——而 LLM 应用的 provider landscape 在 2026 年仍处于快速演化期(每季度都有新模型发布、新协议推出),可替换性就是竞争力。
ToolIR 的版本管理:ToolIR 必须带 schema_version 字段("1.0"),每次不向后兼容的改动都升级版本号。应用层在 Tool Registry 里维护 (tool_name, schema_version) → ToolDefinition 的多版本映射,避免某次 ToolIR 升级把历史 conversation log 都打挂。这是企业级 LLM 应用"灰度发布工具定义"的基础——可以先让 10% 的流量走新版本 ToolIR,观测通过后再全量。多版本共存的工程机制:(a) Tool Registry 的 get(tool_name, schema_version=default) 显式接受版本参数;(b) Conversation log 在 metadata 里记录每个 tool_call 用的是哪个 schema_version;(c) 旧版本的 ToolIR 在 Deprecation Date 之后才下线,且下线前 N 周通过 SDK 警告所有调用方。这种版本治理模式和数据库 schema migration 是同构的,工程团队可以直接复用现有 DevOps 流程。
设计决策背后的关键考量:
id 独立于 name:name 是 wire format 层的 snake_case,id 是应用层的稳定句柄。当你想给工具改个名字(比如 get_weather → fetch_weather),所有引用过这个工具的 conversation log、cache key、trace span 都不需要迁移。
behavior 字段独立:OpenAI 的 strict、Anthropic 的 cache_control、Google 的 codeExecution 在协议层是异构的,但在应用层它们都映射到"这个工具的运行时行为约束"——这是统一的语义。
parameters 用完整 JSON Schema:OpenAI/Anthropic/Google/MCP 都承诺 JSON Schema 兼容(即使子集不同),我们用 Draft 2020-12 作为 IR 规范,adapter 负责降级到 provider-specific subset。
constraints.timeout_ms 是必须的:函数调用协议本身的语义不含超时,超时是应用层约束。统一在 IR 里表达,避免每个 adapter 自己实现 timeout。
provider_hints 与 protocol_meta 分离:hints 是 adapter-to-adapter 提示(如"OpenAI 这个工具描述特别长,触发 truncation 阈值"),meta 是 protocol-specific 元数据(如 cache_control)。两者在应用层都不应该被业务代码直接访问。
五、协议适配器与双向语义保真
Adapter 层负责 ToolIR ↔ provider wire format 的双向转换。核心难点是"语义保真":不是 1:1 字段映射,而是保证 provider 在自己协议下能表达 IR 的全部语义。
下行(ToolIR → provider wire):
abstract class ToolAdapter {
abstract toWire(ir: ToolIR): ProviderToolWire;
abstract fromWire(wire: ProviderToolCall): ToolCallIR;
abstract toResultIR(wire: ProviderToolResult): ToolResultIR;
abstract fromResultIR(ir: ToolResultIR): ProviderToolResultWire;
abstract streamParser(): ToolStreamParser;
}
class OpenAIToolsAdapter extends ToolAdapter {
toWire(ir: ToolIR): OpenAIToolWire {
const subSchema = downgradeToOpenAISubset(ir.parameters);
if (ir.behavior.idempotent && ir.constraints.max_retries > 0) {
return {type: 'function', function: {name: ir.name, description: ir.description, parameters: subSchema, strict: true}};
}
return {type: 'function', function: {name: ir.name, description: ir.description, parameters: subSchema}};
}
}
上行(provider wire → ToolIR)的关键陷阱:
- OpenAI arguments string 解析失败:模型有时会输出
{"city": "Beijing} 这种截断的 JSON,应用层必须做 partial JSON 修复(见 §七流式解析)。
- Anthropic tool_use block 与 text block 同 message:一次响应可能既有
text 又 tool_use,且 tool_use 是数组,应用层必须遍历所有 blocks 不能 first()。
- Google Gemini 的
functionCall 嵌入 parts[]:与 text/functionResponse/inlineData 共享 parts 数组,应用层必须按 parts[i].functionCall 判别。
- MCP 的
content[] typed array:可能含 text / image / audio / resource / embedded_resource 五种类型,应用层必须支持多模态返回。
语义保真测试(adapter 层的 QA):每个 adapter 都要跑一个 "round-trip 测试集",把同一组 ToolIR 经过 toWire/fromWire 转换后语义不变(即 name/parameters/result 都一致),且关键约束(timeout_ms / readonly / destructive)不丢失。
六、并行调用、依赖图与控制流:Function Calling 的拓扑编排
2026 年 LLM 应用的核心效率提升来自并行函数调用:模型一次性发出多个 tool_call(如同时查天气、查股票、查日历),应用层并行执行后一次性回填——相比串行调用,端到端延迟从 N × latency 降到 max(latency)。
协议层差异:
- OpenAI:默认并行,可通过
parallel_tool_calls: false 关闭。
- Anthropic:默认并行,可通过
disable_parallel_tool_use: true 关闭;tool_use block 是数组,多个并行。
- Google:默认并行,函数声明无显式 parallel 开关。
- MCP:单次
tools/call 是一个 RPC,但应用层可以并发发多个 RPC。
应用层的"工具依赖图"(Tool Dependency Graph):
当工具之间存在依赖关系时(如"先查 user_id 再查 user_orders"),盲目并行执行会导致数据竞争。正确的做法是在 ToolIR 里声明依赖:
interface ToolIR {
dependencies?: {
tools?: string[];
data_sources?: string[];
auth_scopes?: string[];
};
}
应用层用依赖图做拓扑排序,可并行的 batch 一起发(用 Promise.all 或 asyncio.gather),有依赖的等依赖完成再发下一批。这本质上是把 DAG 调度引入 LLM 应用——和 Airflow / Prefect 的任务编排是同构的。
控制流进阶:模型可能输出"先调 A,根据 A 的结果决定调 B 还是 C"的条件分支,这种条件工具链应用层通过 tool result 反向 prompt 实现,让模型在拿到 tool result 后再做决策。这是 LLM 应用层"agent loop"的核心机制,详见 id=657(Agent 上下文工程)和 id=649(端到端延迟预算工程)。
并行执行的资源竞争与一致性:当多个并行 tool_call 访问同一资源时(如多个写操作、共享的计数器),应用层必须做"读写隔离 + 版本号"机制。具体做法:(a) 每个 tool_call 携带 idempotency_key(基于 conversation_id + tool_name + args hash),下游 API 自动去重;(b) 写操作类工具必须返回新的 version/etag,后续依赖此资源的 tool_call 必须带 expected_version,应用层在调用前校验版本一致性;(c) 并行的读操作可以不加锁,但读后写必须按依赖图顺序串行。这个设计与传统分布式系统的事务隔离级别(RC/RR/Serializable)是同构的,应用层可以复用熟悉的一致性协议设计经验。失败补偿:并行 tool_call 部分失败时(如 5 个并行里 2 个 4xx),应用层不能简单 retry 全部——成功的 3 个已经执行了副作用,必须先做"补偿事务"(compensating transaction),再 retry 失败的 2 个。这是 LLM 应用"事务一致性"的核心难题,也是 agent loop 复杂度的根源。
七、流式工具调用:partial JSON 解析与渐进式 UI 渲染
2026 年 LLM 应用 UX 的一个分水岭是流式工具调用:模型一边生成 tool_call 的 arguments JSON,应用层一边解析、渲染部分结果。这种"渐进式 UI"对延迟敏感的 LLM 应用(如 IDE 插件、客服 Copilot)是核心体验。
partial JSON 解析器设计:
OpenAI 的 stream 协议每个 chunk 推送 tool_calls.delta.function.arguments(增量字符串),应用层必须累积字符串后尝试 partial parse。partial parse 算法的核心是"截断到最后一个完整的 {} 闭合边界",然后反序列化已经完整的部分:
class PartialJSONParser {
private buffer = '';
private lastValidPrefix = '';
feed(chunk: string): PartialResult {
this.buffer += chunk;
const trimmed = this.trimToValidJSON(this.buffer);
if (trimmed !== this.lastValidPrefix) {
try {
const parsed = JSON.parse(trimmed);
this.lastValidPrefix = trimmed;
return {state: 'complete', value: parsed, raw: trimmed};
} catch (e) {
return {state: 'partial', value: this.tryPartialParse(trimmed), raw: trimmed};
}
}
return {state: 'pending', value: undefined, raw: this.lastValidPrefix};
}
private trimToValidJSON(s: string): string {
let depth = 0;
let inString = false;
let escape = false;
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (escape) { escape = false; continue; }
if (c === '\\') { escape = true; continue; }
if (c === '"') { inString = !inString; continue; }
if (inString) continue;
if (c === '{') depth++;
if (c === '}') {
depth--;
if (depth === 0) return s.slice(0, i + 1);
}
}
return s.slice(0, s.lastIndexOf('{'));
}
}
Anthropic 的 input_json_delta 优势:直接给结构化的 partial JSON,应用层不需要自己做 trim。但代价是必须监听 content_block_stop 才知道完整。
UI 层的渐进式渲染:在 IDE 插件里,应用层每拿到 partial JSON 就把已知的字段(如 path、line)高亮到编辑器;在客服 Copilot 里,已知的字段(如 order_id)先填表,未知的(如 reason)显示 loading skeleton。这种 UX 比"等所有参数到齐再渲染"快 2-4 倍感知延迟。
MCP 的流式协议:通过 SSE notifications 推送 progress 事件(带 progressToken + progress + total),应用层做进度条 UI。MCP 还支持 cancellation(通过 notifications/cancelled),应用层可以主动取消正在执行的工具。
流式协议的可观测性差异:OpenAI 的 delta 流只给字符串碎片,应用层必须自己拼装完整 JSON 才能上 span,导致 trace 里看不到"模型在生成第几个 token 时触发了哪个 tool_call";Anthropic 的 input_json_delta 流给结构化 partial JSON,应用层可以在 span 里记录"当前已解析到 input.city = Beijing";Gemini 的 partial 协议最弱,delta 粒度不可控;MCP 的 progress notification 粒度最细,应用层可以在工具执行阶段也打点("已查询 5/10 个 database row")。这个差异直接影响生产环境的调试效率——当用户报告"我的 tool_call 看起来很奇怪"时,能拿到 partial state 的团队比只能拿到最终 string 的团队 debug 快 3-5 倍。这是选择 LLM provider 时被严重低估的"工程体验"维度。
流式工具调用的 error recovery:partial JSON 解析失败的常见原因是模型输出了截断的 JSON(如 {"city": "Beijing 缺少闭合)。应用层的 fallback 策略:(a) 在最后一次 stream 结束后,对 buffer 做"自动补全"(根据 JSON Schema 推断缺失字段的默认值);(b) 如果补全后仍不合法,返回 tool_result.error = "incomplete_arguments" 给模型,让模型重新生成;(c) 不要 silent retry(直接调下游 API)——silent retry 会让对话历史里看不到错误,破坏 trace 完整性。这是 LLM 应用层最容易踩的"silent failure"陷阱之一,应用层必须把"参数不合法"显式建模为可观测的事件,而不是吞掉。
八、安全纵深:从 prompt injection 到 schema validation 的多层防御
函数调用协议的安全风险远高于普通文本生成:模型一旦有能力调用外部 API,prompt injection 就从"输出攻击"升级为"行动攻击"——攻击者可以让模型调转账 API、调删除 API、调数据外泄 API。
七层防御体系:
- Tool IR 层(设计期):每个工具显式声明
destructive / requires_approval / open_world 属性。destructive 工具必须经过二次确认(人类在 UI 上点"确认"按钮)才能执行。
- Schema 校验层(输入期):每个 tool_call 的 arguments 必须用 JSON Schema 严格校验,任何 schema 不符合的 call 立即拒绝(不传给下游)。这一步可以拦截 30-50% 的恶意构造。
- Argument sanity check 层(语义期):在 schema 校验之外,定义"argument 合理性检查"(如金额 ≤ 单日上限、文件路径必须在白名单内、URL 必须在公司域名内)。这种"业务级白名单"比通用 schema 校验更有效。
- Sandbox 执行层(运行时):工具执行必须在沙盒里(无网络/受限文件系统/受限环境变量),即使工具代码被注入也只影响沙盒。
- Provider-side guardrail 层(API 期):OpenAI 的
strict: true + Anthropic 的 prompt caching + Google 的 safety filters 都是 provider 提供的"原生护栏",但不能作为唯一防线——provider 护栏失效时应用层必须有 fallback。
- Audit log 层(全链路):每次工具调用必须记录"哪个 session、哪个 user、哪个 prompt、哪个 model、哪个 tool、什么参数、什么结果",存到不可变存储(WORM)。这是事后追溯和合规审计的基础。
- Rate limit + anomaly detection 层(行为期):每个用户的工具调用频率、调用模式必须监控,异常调用模式(如突然大批量数据外泄)触发自动熔断。
MCP 协议的安全增强:MCP 2025-06 引入 OAuth 2.1 资源服务器模式,应用层可以基于 OAuth scope 决定哪些用户能用哪些 MCP 工具。但应用层仍需在 SDK 内做"tool-level authz",不能依赖 MCP server 自己声明的权限。
第 1-5 条:Tool Gateway(应用层入口)
- 部署一个统一的 Tool Gateway(可以是 in-process SDK、可以是 sidecar、可以是独立服务),所有 LLM 调用必须经过 Gateway,禁止业务代码直接调 provider API。
- Gateway 内置 provider adapter registry(OpenAI/Anthropic/Google/MCP),用 ToolIR 描述所有工具,业务代码只 import ToolIR,不 import provider wire format。
- Gateway 内置工具依赖图(Tool Dependency Graph)引擎,自动并行无依赖的 tool_call,串行有依赖的。
- Gateway 内置 partial JSON 解析器,支持 OpenAI/Anthropic/Google/MCP 四种流式协议,向上层暴露统一的
partial_result 流。
- Gateway 内置七层防御体系(§八),尤其 destructive 工具的二次确认必须强制在 Gateway 实现,不能依赖 UI 层(UI 可被绕过)。
第 6-10 条:SDK(开发者接口)
6. 提供 define_tool(ToolIR) 装饰器/函数式 API,让业务开发者用 1 行代码定义一个工具,自动生成四套 provider wire。
7. 提供 execute_tool_call(call, ctx) 统一执行接口,ctx 包含 user_id、session_id、auth_scopes 等。
8. SDK 内置 Round-trip 语义保真测试套件(每个 adapter 跑 100 个测试用例,确保 ToolIR ↔ wire 双向不丢字段)。
9. SDK 内置 Pydantic/Zod schema 互转工具,让开发者用熟悉的 schema 库定义工具参数,自动生成 JSON Schema + ToolIR。
10. SDK 必须支持"懒加载 tool description"——当工具很多时(如 100+ 个 MCP server),不能把全部 description 塞进 system prompt,要按需动态选择。
第 11-14 条:Observability(可观测性)
11. 每次 tool_call 必须产生一个 trace span,包含 ToolIR.id、provider、latency、token cost、arguments schema 校验结果、错误码等。
12. 必须记录 tool_call 的 partial state stream(partial JSON 解析的中间状态),用于调试"为什么模型生成了一个不完整的 JSON"。
13. 集成 Langfuse / Helicone / Phoenix / LangSmith 之一作为 trace 后端,trace 必须包含 tool definition、tool call、tool result 三段完整链路。
14. 设置三个核心 SLO:(a) tool_call 成功率 ≥ 99.5%、(b) tool_call P95 latency ≤ 2s、(c) destructive tool 二次确认率 = 100%。任何一个 SLO 跌破触发 page。
结语:函数调用协议工程是 LLM 应用从"Prompt + RAG"走向"Agent + Tools"的必经之路,也是 2026 年最被低估的应用层基础设施。没有 ToolIR 的应用层是短命的——每一次新 provider 接入都是一次痛苦的 adapter 重写,每一次新工具添加都是一份 wire format 复制粘贴,每一次新的 prompt injection 攻击都是一次架构级风险。ToolIR + 协议适配器 + 流式解析器 + Tool Gateway 四层架构是规模化 LLM 应用的"工业地基",也是多模型策略、跨云部署、企业级合规的工程前提。
给架构师的额外 5 条决策指引:
- 不要从零写 SDK,先用开源:LangChain/LlamaIndex 的 tool calling 抽象虽然粗糙但已支持四类 provider,可以作为起点二次封装;Vercel AI SDK 的 provider-neutral 设计对前端友好;Cloudflare Agents SDK 把 Tool Gateway 内置到边缘运行时。选 SDK 的核心标准是"是否暴露 ToolIR 等价物让你扩展"——封闭 SDK(如某些 copilot 平台)会让你后续无法满足自定义需求。
- provider 锁定风险评估:每个 provider 都有"vendor lock-in"的隐藏成本——OpenAI 的 strict mode 锁定 JSON Schema 写法,Anthropic 的 prompt caching 锁定 message block 结构,Google 的 codeExecution 锁定 Python 执行环境。2026 年最务实的策略是"primary + secondary"双 provider,主用便宜且效果好的(如 GPT-5),备用 SLA 高的(如 Claude 4.5),Tool Gateway 负责 fallback 决策。这比 100% 押注单一 provider 健康得多。
- 不要在应用层重新实现 OAuth:很多团队试图在 Tool Gateway 里实现"用户授权某工具"的流程,结果做出来的既不安全也难维护。正确做法是直接复用 MCP 2025-06 引入的 OAuth 2.1 资源服务器模式——MCP server 端声明它需要哪些 scope,Tool Gateway 在调用时透传用户 token,scope 检查由 MCP server 自己完成。这是把"工具授权"的工程复杂度下放到协议层的范例。
- Tool 定义的生命周期管理:工具不是静态的——会被弃用(deprecation)、会被版本化、会有兼容性问题。应用层必须建立 Tool Registry(类似 npm registry),记录每个 tool 的 version、status(active/deprecated/removed)、migration guide。Registry 是 LLM 应用从"能跑"走向"能治理"的关键一步——没有 Registry 的应用在工具数量超过 50 个后基本无法维护。
- Tool IR 与 prompt 工程的协同:很多团队把 Tool Description 当作"提示词的一部分"随便写,结果模型调工具准确率只有 60-70%。正确做法是把 Tool Description 当作"prompt artifact"严格管理:(a) 每个工具的 description 必须由 prompt 工程师 + 领域专家联合评审;(b) 工具的 description 进入 prompt A/B 平台,用真实 query 数据做对比实验;(c) 工具 description 的修改要走 PR review + CI 验证(用 golden set 跑成功率回归)。这是 LLM 应用"工程化 prompt 管理"的必经之路,与 devops 中 "infrastructure as code" 是同构的工程哲学。今天就做这三件事:先把所有工具定义迁到 ToolIR;再部署一个 Tool Gateway;最后接 OpenTelemetry 把所有 tool_call trace 打通。这三件事做完,你的 LLM 应用就具备了"多模型 + 多云 + 多模态 + 多租户"的可扩展性。
参考文献
- OpenAI. Function Calling Guide. https://platform.openai.com/docs/guides/function-calling. 2024-08.
- Anthropic. Tool Use with Claude. https://docs.anthropic.com/en/docs/tool-use. 2024-10.
- Google. Gemini Function Calling Documentation. https://ai.google.dev/gemini-api/docs/function-calling. 2024.
- Anthropic. Model Context Protocol Specification. https://modelcontextprotocol.io/specification/2025-06-18. 2025.
- OpenAI. Structured Outputs Guide. https://platform.openai.com/docs/guides/structured-outputs. 2024-08.
- Anthropic. Prompt Caching for Tool Descriptions. https://docs.anthropic.com/en/docs/prompt-caching. 2024.
- Google. OpenAPI 3.0 Specification for Gemini Function Calling. https://ai.google.dev/gemini-api/docs/structured-output. 2024.
- Pydantic. JSON Schema Generation from Python Types. https://docs.pydantic.dev/latest/concepts/json_schema/. 2024.
- Zod. TypeScript-first Schema Validation. https://zod.dev/. 2024.
- JSON-RPC 2.0 Specification. https://www.jsonrpc.org/specification. 2010.
- W3C. JSON Schema Draft 2020-12. https://json-schema.org/draft/2020-12/schema. 2020.
- OpenTelemetry. GenAI Semantic Conventions for Tool Calls. https://opentelemetry.io/docs/specs/semconv/gen-ai/. 2024.
- Langfuse. Tool Call Tracing Documentation. https://langfuse.com/docs/observability/features/tool-calls. 2024.
- Phoenix (Arize). Tool Call Evaluation Framework. https://docs.arize.com/phoenix. 2024.