01 Provider 门面模式与 Route 抽象 本节摘要:OpenCode 支持 20 多家模型厂商,每家的 API 签名、认证、部署方式都不同。如果每接一家写一套分支,代码会变成乱麻。LLM 抽象层的第一层解法是门面模式(Facade)+ Route 抽象:用门面先配置一个 Provider(端点、认证、部署),再用 Route 把「一个部署」抽象成端点+认证+分帧等字段的组合。本节讲清这套设计:门面怎么用、Route 是什么、为什么 Route 让「加一个新部署只要很少代码」。 一、问题:20+ 厂商怎么统一 先看问题的规模。
本节摘要:OpenCode 支持 20 多家模型厂商,每家的 API 签名、认证、部署方式都不同。如果每接一家写一套分支,代码会变成乱麻。LLM 抽象层的第一层解法是门面模式(Facade)+ Route 抽象:用门面先配置一个 Provider(端点、认证、部署),再用 Route 把「一个部署」抽象成端点+认证+分帧等字段的组合。本节讲清这套设计:门面怎么用、Route 是什么、为什么 Route 让「加一个新部署只要很少代码」。
先看问题的规模。OpenCode 要支持:Anthropic、OpenAI、Google、Amazon Bedrock、Azure、Cloudflare、GitHub Copilot、OpenRouter、xAI、Mistral、Cohere,以及任何 OpenAI 兼容的端点......每家都有自己的:
如果用 if 厂商 == "anthropic" ... else if 厂商 == "openai" ...,代码会膨胀到不可维护。OpenCode 的解法分两层:门面(本节)和 Route(本节)+ Protocol/Framing(下一节)。
门面模式的核心是把「配置一个 Provider」和「选择一个模型」分成两步:
第一步:配置 Provider 某Provider.configure({ 端点: ..., 认证: ..., 部署相关: ... }) ──► 得到一个「配置好的 Provider 门面」 第二步:从门面选模型 配置好的门面.model("某模型id") ──► 得到一个「可调用的模型句柄」
这种「两步走」的好处是:配置一次,可选多个模型。比如你配好了 Anthropic 门面,然后既可以用 claude-sonnet,也可以用 claude-haiku,不用重复配端点和认证。
💡 为什么叫「门面」:门面(Facade)是设计模式名——它把一堆复杂的内部细节(端点、认证、部署)藏在一个简洁的「门面」后面,你只跟门面打交道,不用关心内部。配好门面,后面选模型就是一句的事。
门面之下是 Route 抽象,这是本节真正的硬核。Route 把「一个部署(一个可调用的端点)」抽象成几个字段的组合:
Route = { protocol: 用哪种协议(anthropic-messages / openai-chat / ...), endpoint: 端点(URL、路径), auth: 认证(API Key / OAuth / AWS 签名 / ...), framing: 分帧方式(流怎么切), ...其他传输细节 }
关键洞察是:绝大多数「新部署」,其实是已有字段的重新组合。比如:
它们的字段值不同,但结构相同。所以加一个新部署,只需要 Route.make({ ...这几个字段 ... }),几行代码就够——不需要写一整个新模块。
Route 通过一个构造函数创建,大致是这样:
// 概念性示例:用一个已有协议 + 新端点 + 新认证,定义一个新部署 const 我的Route = Route.make({ protocol: 某已有协议, // 复用,不重写 endpoint: { url: "https://我的端点", path: "/v1/..." }, auth: { type: "api_key", key: ... }, framing: 某已有分帧, // 复用 });
注意三个「复用」:协议复用、分帧复用、认证复用。你要新的往往只是「端点 URL」和「认证凭据」。这就是为什么「加一个新部署只要很少代码」——你站在已有协议和分帧的肩膀上,只填差异部分。
⚠️ 别误解 Route 是「配置」:Route 不只是配置文件,它是一个真正的抽象——它把「一个部署的所有可变部分」收敛成结构化字段,让新部署变成「字段组合」而非「新代码」。这是抽象的力量。
把门面和 Route 放一起看:
门面(某Provider.configure) │ 内部组装 ▼ Route(协议 + 端点 + 认证 + 分帧) │ 选模型时 ▼ 可调用的模型句柄(.model(id))
门面是「用户视角的简洁入口」,Route 是「内部的部署抽象」。用户配门面,门面内部建 Route,Route 决定「怎么调这个部署」。这种分层让用户界面简洁(配门面),同时内部足够灵活(Route 可任意组合)。
本节讲了「怎么配置和组合部署」,但还没讲「请求体怎么构造、流式响应怎么解析」——那是 Protocol 和 Framing 的事。下一节就讲:协议(Protocol)负责构造请求体与解析流,分帧(Framing)管字节流切分,传输(Transport)管 HTTP 细节。三者分离,让支持新协议变得简单。
configure 配 Provider(端点/认证/部署),再 .model(id) 选模型;配一次可选多模型。Route.make({...}) 几行。门面和 Route 讲清了「怎么组合部署」,下一节讲部署确定后「请求怎么构造、流怎么解析」——协议、分帧、传输三件套。