7.4 ProviderTransform:模型差异化适配

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-17


即使有 Vercel AI SDK 的统一抽象,不同 LLM Provider 之间仍存在大量细微差异——消息格式限制、参数命名规则、Schema 支持程度、缓存机制等。ProviderTransform 模块就是处理这些差异的"适配层",确保 OpenCode 的上层逻辑可以无感知地切换 Provider。

7.4.1 消息格式标准化(normalizeMessages

不同 Provider 对消息格式有不同的限制。normalizeMessages 函数处理了这些差异:

Anthropic 的空消息限制

if (model.api.npm === "@ai-sdk/anthropic") {
  msgs = msgs
    .map((msg) => {
      if (typeof msg.content === "string") {
        if (msg.content === "") return undefined  // 移除空字符串消息
        return msg
      }
      if (!Array.isArray(msg.content)) return msg
      const filtered = msg.content.filter((part) => {
        if (part.type === "text" || part.type === "reasoning") {
          return part.text !== ""  // 移除空文本 Part
        }
        return true
      })
      if (filtered.length === 0) return undefined
      return { ...msg, content: filtered }
    })
    .filter((msg) => msg !== undefined && msg.content !== "")
}

Anthropic 的 API 会拒绝包含空内容的消息。在 Compaction 过程中,工具输出可能被清除为空字符串,这段代码确保这些空消息不会到达 API。

Claude 的 Tool Call ID 规范化

Claude API 要求 toolCallId 只包含字母、数字、下划线和短横线。其他 Provider 可能生成包含更多字符的 ID,这段代码将非法字符替换为下划线。

Mistral 的特殊要求

Mistral 有两个独特的限制:

  1. Tool Call ID 必须是恰好 9 个字母数字字符(不是"最多"而是"恰好")。

  2. 消息序列中,tool 消息不能直接跟随 user 消息——需要在中间插入一个 assistant 消息作为过渡。

交错思考链处理

某些模型(如 DeepSeek)通过特殊字段(reasoning_contentreasoning_details)返回思考链内容,而非 Vercel AI SDK 标准的 reasoning Part。这段代码在重放消息历史时,将标准格式转换回 Provider 特定的格式。

7.4.2 Prompt 缓存策略(applyCaching

Prompt 缓存可以显著降低 API 调用的延迟和成本。applyCaching 函数为支持缓存的 Provider 标记缓存点:

缓存策略的设计要点:

  1. System 消息缓存:System Prompt 在整个会话中几乎不变,缓存它们可以避免每次请求都重复处理。

  2. 最后两条消息缓存:最近的消息最可能在下一轮对话中被保留,缓存它们提高了命中率。

  3. 多 Provider 兼容:同时设置多个 Provider 的缓存选项键(anthropicbedrockcopilot 等),确保无论使用哪个 Provider,缓存配置都能被正确识别。

7.4.3 不支持的模态处理

当用户发送的附件类型不被当前模型支持时,系统会优雅降级:

这种处理避免了 API 报错——当用户给不支持图片的模型发送图片时,系统将图片替换为一条错误提示文本,让模型可以告知用户该限制。

7.4.4 输出 Token 上限管理

OpenCode 默认将所有模型的最大输出限制在 32,000 token。这是一个安全措施——虽然某些模型声称支持更长的输出,但过长的输出会导致延迟增加、成本上升和质量下降。用户可以通过环境变量 OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX 调整这个限制。

7.4.5 providerOptions 键映射

不同的 AI SDK 包使用不同的 providerOptions 键名:

当消息历史中存储的 providerOptions 键名(基于 providerID)与 SDK 期望的键名不同时,需要进行映射。例如,使用 Google Vertex Anthropic 时,providerID 可能是 "google-vertex-anthropic",但 SDK 期望的键是 "anthropic"

7.4.6 Temperature 与采样参数的模型适配

不同模型对 temperature 参数的敏感度不同:

  • Claude:不设置默认值(undefined),让 API 使用其默认值。

  • Gemini 和 GLM:设为 1.0——这些模型在 temperature=1.0 时表现良好。

  • Qwen:设为 0.55——经验值。

  • 推理模型:通常使用较高的 temperature。


下一节将分析 LLM.stream() 的完整实现——从 System Prompt 组装到流式调用,从 Provider 选项注入到错误处理。

Last updated