Skip to content

不同的 LLM Provider 对 Tool Schema 的支持不完全一致,你会怎么处理这种差异?OpenClaw 是怎么做 Schema 适配的? ​

  • 标签:AI、OpenClaw、大模型应用开发、AI应用开发

🏛 业务架构师 ​

不同 LLM Provider 对 Tool Schema 支持差异(可选参数 vs 必填、嵌套对象深度、anyOf/oneOf、枚举、附加 properties 是否允许等)是跨模型 Agent 的现实痛点——同一工具在 Claude 上正常、换某开源模型就报 schema 不合法或调用格式走样。我的处理策略:

1. 能力探测与降级映射(适配层统一消化):框架维护各 Provider 的能力矩阵,把通用 Schema 自动转换为各模型可接受的子集——不支持深层嵌套就拍平参数;不支持可选字段就给默认值;不支持 oneOf 就拆成多个工具。业务侧只写一份标准 Schema。

2. 工具描述冗余补偿:Schema 表达力降级后(比如没法声明"二选一"),把约束写进工具的文字描述里("要么传 order_id 要么传 phone,二选一")——模型读描述的能力是跨厂商稳定的。

3. 输出侧校验与重试:模型返回的调用参数一律过 JSON Schema 校验,不合规自动带着明确错误信息重试一次——推理模型的一次纠错成功率很高。

4. 回归测试矩阵:工具集×模型矩阵做自动化冒烟——换模型、加工具都要跑一遍真实调用。

OpenClaw 的做法正是在模型接入层做 Schema 适配器,主流程面向统一中间格式。业务提醒:多模型策略的隐性成本就在这些适配细节里,立项时要预留这块工程量;反过来,把工具层建在 MCP 标准上,能把一部分适配责任转移给生态工具链。

🏗 系统架构师 ​

Provider 的 Tool Schema 差异是真实的适配层问题,我的处理方案分层,最后看 OpenClaw 的做法:

差异面:参数格式(OpenAI 的 function/tool call vs Anthropic 的 tool_use 内容块)、Schema 严格度(有的要求 additionalProperties:false,有的不支持 oneOf/anyOf 深嵌套)、命名限制(字符集、长度上限)、能力差异(并行调用支持、流式工具调用增量、结构化输出保证级别)。

我的四层处理:

  • 内部统一 IR:系统内只维护一份规范化的工具定义(中间表示),到出口按 Provider 方言序列化——经典适配器模式,N 个 Provider 也只有 N 个序列化器,不是 N×M 份拷贝;
  • 能力探测与降级:启动时探测 Provider 能力(max schema 复杂度、并行调用),不支持的构造自动降级(oneOf 拆成多个工具、长描述截断);
  • 校验兜底:无论 Provider 是否严格校验,出口一律本地 Schema 校验(never trust),失败走修复重试;
  • 回归测试矩阵:每个工具定义 × 主流 Provider 的兼容性用例集,Provider 升版跑回归。

OpenClaw 的做法:统一的工具注册内部模型 + 各 Provider 适配器做 schema 编译(内部模型 → 目标方言),含命名规范化、嵌套展开、严格模式补齐;能力差异在运行时探测处理。

架构本质:这是"数据库方言适配"问题在 LLM 领域的翻版——方言在边界翻译,核心域模型保持统一。

💻 全栈开发专家 ​

Provider 差异是 Agent 框架的现实问题:OpenAI strict mode、Anthropic 不支持某些 oneOf、Gemini 的 enum 语义、开源模型对复杂 schema 的遵循度参差。我的处理方案:

1. 内部统一中间表示(IR):工具定义在框架内用统一模型(name/description/params 的内部 schema),不直接用任何厂商格式。适配层负责双向转换:

typescript
interface ToolIR { name: string; description: string;
  inputSchema: JsonSchemaObject;  // 内部规范:JSON Schema draft 07 子集
  annotations?: { destructive?: boolean } }
// 每个 Provider 一个 Converter:ToolIR → OpenAI tools[] / Anthropic tool / Gemini functionDeclarations

2. 能力探测与降级(初始化时探测,运行时按能力路由):

  • 不支持 oneOf/anyOf → 预展开成 enum 描述或拆成多个工具;
  • 不支持 strict schema → 放宽 + 运行时强校验兜底(解析失败把错误喂回模型重试);
  • 不支持并行工具调用 → 串行执行队列模拟;
  • 文本协议兜底(最差的模型也能用):把工具 schema 写进 system prompt,要求模型输出 JSON 块,正则提取——ReAct 文本协议作为最后防线。

3. 测试矩阵:同一工具集在所有目标 Provider 上跑「工具选择与参数生成」回归用例(我们维护 30 条),模型/供应商升级先跑矩阵——Anthropic 某次 API 升级改变了 tool_use id 格式,就是这套矩阵提前拦下的。

经验:参数校验永远不要信 Provider,运行时 JSON Schema 校验是唯一防线;description 是跨 Provider 的一致性关键,写得具体比格式技巧重要。