05 三档可移植性逃生口 本节摘要:前面四节讲了一套漂亮的抽象——它能把 20+ 厂商的差异吸收掉。但现实是:没有抽象能覆盖所有厂商的所有特性。某厂商的思维模式、路由偏好、特殊请求头......这些「非通用」特性如果硬塞进抽象,会让抽象膨胀到不可用。OpenCode 的解法是三档可移植性逃生口:从最可移植的「生成参数」,到按厂商分组的「providerOptions」,到最后手段的「http 透传」,逐档提供「越狱」能力。本节讲清这三档各自的适用场景与取舍——什么时候用哪一档,以及为什么不要一上来就用最强那档。 一、抽象的两难 先承认一个现实:抽象与覆盖是一对矛盾。
本节摘要:前面四节讲了一套漂亮的抽象——它能把 20+ 厂商的差异吸收掉。但现实是:没有抽象能覆盖所有厂商的所有特性。某厂商的思维模式、路由偏好、特殊请求头......这些「非通用」特性如果硬塞进抽象,会让抽象膨胀到不可用。OpenCode 的解法是三档可移植性逃生口:从最可移植的「生成参数」,到按厂商分组的「providerOptions」,到最后手段的「http 透传」,逐档提供「越狱」能力。本节讲清这三档各自的适用场景与取舍——什么时候用哪一档,以及为什么不要一上来就用最强那档。
先承认一个现实:抽象与覆盖是一对矛盾。
OpenCode 的策略是:抽象只管通用部分,厂商专属特性通过「逃生口」透传。这样抽象保持干净,需要时又能逃生。
OpenCode 提供三档逃生口,按「可移植性」从高到低排列:
第一档:生成参数(generation) ──► 最可移植,优先用 ▼ 不够用时 第二档:providerOptions ──► 按厂商分组,中等可移植 ▼ 还不够用时 第三档:http 透传 ──► 最后手段,不可移植
这是最可移植的一档,涵盖所有厂商都支持的通用生成参数:
| 参数 | 含义 |
|---|---|
| maxTokens | 最大输出 token |
| temperature | 温度(随机性) |
| topP / topK | 核采样参数 |
| penalties | 频率/ presence 惩罚 |
| seed | 随机种子(可复现) |
| stop | 停止序列 |
优先用这一档。如果你的需求能被这些通用参数满足,代码对所有厂商都可移植——换厂商不用改。
💡 默认先问「能不能用第一档解决」:遇到需求时,先查通用参数里有没有对应的。90% 的常见需求(调随机性、限长度、设停止词)都能在这里解决。
当第一档不够(你要的是某厂商的专属特性),用第二档。这一档按厂商分组,允许你传厂商专属选项:
// 概念性示例:传某厂商的专属选项 { providerOptions: { anthropic: { thinking: { ... } }, // Anthropic 的思维模式 google: { thinkingConfig: { ... } }, // Google 的思维配置 openrouter: { routing: { ... } }, // OpenRouter 的路由偏好 } }
这一档的特点:
用这一档的时机:第一档满足不了、且你要的特性是某厂商明确提供的(如思维链、路由偏好)。
当第二档也不够(你要改的是请求体、请求头、查询参数这种底层东西),用第三档——直接透传 HTTP 细节:
// 概念性示例:直接改 HTTP 请求 { http: { body: { ...额外请求体字段... }, headers: { ...额外请求头... }, query: { ...额外查询参数... }, } }
这一档的特点:
⚠️ 慎用第三档:它等于「跳出抽象直接捅 HTTP」。一旦用了,你的代码就极其脆弱——厂商 API 一改你就挂。只有在确认第一档、第二档都解决不了时才用,并要做好「这个部分不可移植」的心理准备。
把三档串成一个决策流程:
有需求 │ ▼ 第一档(生成参数)能解决吗? ├─ 能 ──► 用第一档(最可移植) │ └─ 不能 ──► 第二档(providerOptions)有对应厂商选项吗? ├─ 有 ──► 用第二档(中等可移植) │ └─ 没有 ──► 第三档(http 透传)(最后手段,慎用)
这个流程的核心原则是:能可移植就别耦合,能显式就别裸捅。每一档都是「上一档不够时才降级」。
这三档体现了 OpenCode 对抽象的成熟态度——抽象是默认,逃生是例外:
如果只有抽象没有逃生口,遇到厂商专属特性就只能「改抽象」——抽象会被各种特例污染,最终不可维护。如果只有逃生口没有抽象,代码会到处是厂商分支,也不可维护。三档共存,才兼顾了「干净」与「覆盖」。
读完第 6 章五节,你完成了对 LLM 抽象层的完整理解:
| 节 | 讲了什么 |
|---|---|
| 01 门面 + Route | 怎么配置一个部署(先 configure 再 model) |
| 02 协议/分帧/传输 | 请求怎么发、响应怎么解析(三层分离) |
| 03 LLMEvent | 解析出的统一事件流(上层对厂商无感) |
| 04 提示缓存 | 三断点省钱策略(与 Epoch 协作) |
| 05 三档逃生口 | 抽象不够用时如何兜底(生成参数/providerOptions/http) |
这套抽象是 OpenCode 能优雅支持 20+ 厂商的根本。它的核心思想是:把厂商差异下沉(到协议/分帧/传输),把统一接口上浮(到 LLMEvent),需要逃生时分档提供越狱能力。
第 6 章结束。接下来轮到 OpenWork 教程的正文填充——从第 1 章环境准备开始。