← 返回资讯

OpenAI 兼容平台迁移:换一家 API 要改什么、要测什么

2026-08-07

一个同事跟我说,换平台很简单,官方文档写得明明白白:改 base_url、换 key、换模型 ID,三处,五分钟。他确实五分钟就跑通了 hello world,然后花了两周才把这条链路放上生产——中间修了流式结束标志不一致、函数调用参数被吞、结构化输出合法率掉了一截、重试逻辑因为错误体结构变了而形同虚设。

「改三处就能切」这句话没错,但它只覆盖能跑通,不覆盖能上线。真正的工作量不在改配置,在验收。这篇把两件事分开讲:前半段是那三处配置到底怎么改、各家写法差在哪;后半段是一份可以直接照着打勾的迁移验收清单。


一、先认清一件事:不是所有平台都是「改三处」

在动手之前先确认目标平台的接入范式,这个判断比比价更早。以本系列已核实的几家为例:

平台接入范式base_url / endpoint模型 ID 形式
CerebrasOpenAI 兼容https://api.cerebras.ai/v1扁平名 gpt-oss-120b
Together AIOpenAI 兼容https://api.together.ai/v1两段式 MiniMaxAI/MiniMax-M3
Fireworks AIOpenAI Anthropic 双兼容https://api.fireworks.ai/inference/v1三段式 accounts/fireworks/models/deepseek-v3p1
Nebius Token FactoryOpenAI 兼容https://api.tokenfactory.nebius.com/v1/两段式带日期版本 deepseek-ai/DeepSeek-R1-0528
Replicate自有 predictions API(非 OpenAI 兼容https://api.replicate.com/v1/predictionsversion + input 结构

前四家的迁移成本是「改三处配置」;Replicate 不是——它有自己的 predictions API,请求体是 versioninput,默认走异步(提交任务,再靠轮询或 webhook 拿结果),要同步就得加 Prefer: wait 请求头。这不是改配置,是改调用范式。如果你的排期是按「改 base_url」估的,碰上这类平台会直接崩盘。

所以迁移评估的第一个问题不是「多少钱」,而是「它是不是 OpenAI 兼容端点」。这个问题答错,后面所有排期都不作数。关于兼容层本身的边界,可以先看 OpenAI 兼容到底兼容了什么


二、改配置的三处:写法差异比想象中大

「三处」是指 base_url、api_key、模型 ID。听起来平淡,但这三处每一处都有具体的坑。

base_url:路径写法各不相同,必须逐字照抄

看上面那张表的第三列,五家的路径没有一家长得一样:

  • Cerebras 是干净的 https://api.cerebras.ai/v1
  • Together 是 https://api.together.ai/v1,同样是标准的 /v1 结尾。
  • Fireworks 是 https://api.fireworks.ai/inference/v1——比多数平台多一段 inference。这一段最容易被漏掉,因为人的手指会习惯性地打出 域名/v1。漏了以后的表现很有欺骗性:返回的是 404,而不是鉴权错误。很多人看到 404 的第一反应是「模型名写错了吧」,于是拿着模型 ID 反复核对,方向从一开始就错了。
  • Nebius Token Factory 官方写法是 https://api.tokenfactory.nebius.com/v1/带末尾斜杠

最后这一条值得多说一句:不同 HTTP 客户端在拼接路径时对末尾斜杠的处理不完全一致,有的会规范化掉,有的会拼出双斜杠。官方文档怎么写你就怎么写,别自作主张「顺手规整一下」。同理,也别凭记忆给一个域名补 /v1——你记住的那个 /v1 可能来自另一家。

规则很简单:base_url 从官方文档复制粘贴,一个字符都不改。 这是整个迁移里最不值得动脑的一步,也是最容易因为动脑而出事的一步。base_url 本身的构成和常见误区,另有一篇专门讲:base_url 怎么填

api_key:环境变量名不通用

每家的环境变量名是自己定的:Cerebras 文档用 CEREBRAS_API_KEY,Together 用 TOGETHER_API_KEY,Fireworks 用 FIREWORKS_API_KEY,Nebius 用 NEBIUS_API_KEY,Replicate 用 REPLICATE_API_TOKEN(注意它叫 TOKEN 不叫 KEY)。

鉴权方式这几家都是 HTTP 头里的 Authorization: Bearer <你的密钥>,这部分是一致的。但如果你的代码里写死了某一家的环境变量名,迁移时就会出现「key 明明配了却读不到」的低级故障。迁移前把密钥读取收敛成一个配置项,别散落在各处。

模型 ID:换平台就是换命名空间

这一处后面单独讲,因为它的坑最深。


三、先 curl 打通,再配 SDK

我的建议是:任何一次平台迁移,都先用 curl 把完整地址打通,再回头去配 SDK 的 base_url。

顺序反过来的话,你面对的是一个复合故障源。SDK 会替你拼路径、加请求头、序列化请求体、处理重试。当它报错时,你分不清是 base_url 写错了、是 key 没读到、是模型 ID 不对,还是 SDK 版本的兼容问题。

而 curl 是把完整 URL 一次性打出去的——路径是什么就是什么,没有中间层。所以:

curl https://api.fireworks.ai/inference/v1/chat/completions \
  -H "Authorization: Bearer $FIREWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"accounts/fireworks/models/deepseek-v3p1","messages":[{"role":"user","content":"ping"}]}'

这一步通了,你就同时确认了四件事:域名可达、路径正确、key 有效、模型 ID 存在。然后倒推:把完整 URL 去掉末尾的 /chat/completions,剩下的就是要填进 SDK 的 base_url。这个倒推动作听着笨,但它把「我以为的 base_url」换成了「已经被验证过的 base_url」。

排查价值还在于分层。如果 curl 通了而 SDK 不通,问题一定在 SDK 这一层——版本、代理、超时、请求头覆盖,范围一下子缩小到几行代码。如果 curl 就不通,那就压根不用去看 SDK。OpenAI SDK 侧的具体配置可以参考 用 OpenAI SDK 接第三方平台


四、模型 ID 的两个坑

模型 ID 是三处配置里最容易低估的一处。它不是一个「名字」,而是一个带命名空间的标识符,各家的层级结构完全不同。

坑一:少写组织前缀,报错会把你带偏

Together 的模型 ID 是两段式的「发布方/模型名」,比如 MiniMaxAI/MiniMax-M3。只填后半截会失败——这不奇怪,奇怪的是报错看起来像权限问题

这个细节值多少钱,取决于你在错误方向上花了多久。看到疑似权限的报错,正常人的排查路径是:检查 key 有没有过期、账号有没有开通这个模型、组织权限是不是没配、要不要去控制台申请访问。这一圈走下来可能就是半天,而真正的原因只是模型 ID 少了个前缀。

Fireworks 更极端,是三段式:accounts/fireworks/models/deepseek-v3p1。公共模型走 accounts/fireworks/... 这个路径,自部署模型的路径不同。Nebius 则是两段式再带一个日期版本后缀:deepseek-ai/DeepSeek-R1-0528——注意最后那串数字也是 ID 的一部分,不是文档里的注释。

所以碰到疑似权限的报错,先把模型 ID 跟官方文档逐字符比一遍,再去查权限。这个顺序能省下大量时间。模型 ID 的命名规律另有一篇细讲:模型 ID 的命名规则

坑二:不要假设大小写不敏感

看看这几个真实写法:MiniMaxAI/MiniMax-M3accounts/fireworks/models/deepseek-v3p1deepseek-ai/DeepSeek-R1-0528。同一个「deepseek」在不同平台的写法里,大小写规则并不统一——有的全小写,有的驼峰。

标识符是否大小写敏感由平台自己决定,你无从假设。稳妥做法只有一个:从官方文档复制粘贴,包括大小写、连字符、斜杠、日期后缀,一律照抄。 不要手打,不要「顺手统一成小写」,也不要在代码里对模型名做任何规范化处理。

顺带一提,Fireworks 同时提供 OpenAI 兼容端点和 Anthropic SDK 兼容端点。这意味着如果你的存量代码是按 Anthropic SDK 写的,也有一条低成本迁移路径,不必先改写成 OpenAI 范式再迁——这是另外几家没有的选项。


五、核心:迁移验收清单

下面这份清单是这篇文章的主体。它列的是「该测哪些」,不是「哪家不支持什么」。 各平台的能力矩阵在变,别人的结论对你的场景不一定成立,唯一可靠的是你自己跑一遍。

A. 连通性

  • curl 打完整地址,非流式请求返回 200
  • SDK 用倒推出来的 base_url 跑通同一个请求
  • 密钥从环境变量读取(确认变量名已按新平台改过),不是硬编码
  • 生产网络环境下也跑一遍——本机能通不代表出口 IP、代理、DNS 都没问题

B. 参数兼容(只验你实际用到的)

不要去做「全参数矩阵测试」,那是浪费。把你线上请求体里真正出现过的字段列出来,逐一验证:

  • 采样类参数(温度、top_p 之类):传上去接不接受,接受了行为是否合理
  • 最大输出长度类参数:字段名有没有变、上限行为是什么
  • 停止词、惩罚项、随机种子等:你用到了就测,没用到就跳过
  • 重点:传了一个平台不认识的参数会怎样——是静默忽略,还是直接报错?这两种行为的运维含义完全不同。静默忽略意味着你的温度可能根本没生效,而线上不会有任何告警。

C. 流式

流式是迁移事故的重灾区,因为它有很多隐性约定:

  • 事件格式:分片的结构与字段是否与旧平台一致
  • 结束标志:流怎么算结束、有没有终止事件、结束时是否附带用量统计
  • 首包与分片节奏:你的前端如果依赖分片粒度做渲染,节奏变了会有观感差异
  • 客户端主动中断:断开连接后服务端是否正确停止,你的计费与日志怎么记
  • 流中途出错:错误是以事件形式出现在流里,还是连接直接断开?你的错误处理接不接得住

D. 函数调用 / 工具调用

  • 工具定义的 schema 格式是否被接受
  • 返回的调用参数是不是合法 JSON,能否被你现有的解析代码直接吃下
  • 多轮工具调用的消息拼装顺序是否还成立
  • 并行调用多个工具时的返回结构
  • 流式 + 工具调用同时开启时的分片形态(这是最容易崩的组合)

E. 结构化输出

这一项必须跑一批样本,不能只跑一条。单条成功说明不了任何问题。

  • 用你线上真实的 schema,跑几十到上百条真实输入
  • 统计合法率:解析成功的比例是多少,失败的是哪一类(字段缺失、类型不符、多了解释性文字、JSON 被截断)
  • 跟旧平台的合法率做对比,而不是跟「100%」做对比
  • 确认失败样本的兜底路径还能工作(重试、降级、人工队列)

F. 长上下文接近上限时的行为

短请求都能过,长请求才见真章:

  • 输入接近上下文上限时,是清晰报错还是静默截断?静默截断是最危险的——结果看起来正常,内容却少了一截
  • 输出被长度限制截断时,返回体里有没有明确标记,你的代码认不认这个标记
  • 上下文窗口的计算口径(输入输出是否共享同一个预算)
  • 超长请求的超时行为:是先返回错误,还是挂到你的客户端超时

G. 错误码与错误体结构

你的重试逻辑依赖这个,它一变,整套容错就失效了。

  • 各类错误(鉴权失败、参数非法、模型不存在、限流、服务端错误)分别返回什么状态码
  • 错误体的 JSON 结构:字段名叫什么、错误类型放在哪一层
  • 你的重试判断如果是按错误体里某个字段做的,新平台还有没有这个字段
  • 限流类错误的响应里有没有可用的退避提示信息,你的退避策略要不要跟着调
  • 最隐蔽的一种:旧平台返回 429 的场景,新平台可能返回 5xx,或者反过来。分类错了,重试就会打在不该重试的地方

限流与故障切换的整体策略,可以配合 多上游故障切换 一起设计。

H. 计费口径

这一项没法靠测试得出结论,必须去官网和控制台确认,而且不要假设跟前一家相同

  • 输入、输出、以及其他计费维度分别怎么计
  • 失败的请求哪些计费、哪些不计费——尤其是「已经产生了部分输出然后出错」和「客户端主动中断流式」这两种
  • 缓存类机制(如果有)怎么计费、命中判定条件是什么
  • 计价单位本身:有的平台在同一张页面上,输入按「每百万 token」标价,输出按「每千 token」标价。单位不统一时,先换算到同一口径再比,否则会差出三个数量级
  • 用量数据在哪里能查、延迟多久出账、能否按 key 或按项目拆分

不知道确切的限速和配额时,稳妥的起步方式是:低并发起步,先观测再加压。上线首日按远低于预期的并发跑,把错误码分布、延迟分布、单位成本先测出来;准备好指数退避重试;用一小批真实流量跑出你自己的每千次调用成本,再决定要不要放量。这比拿着一份别人的参数表去猜靠谱得多。


六、不只测「能不能跑」,还要测「结果是否等价」

上面八组测完,你得到的结论是「这条链路能工作」。但迁移真正的风险不在链路,在输出分布变了而你没发现

换平台往往意味着换模型,即使模型名字看起来一样,服务侧的配置也不一定相同。做法是:

拿同一批输入,在旧平台和新平台各跑一遍,把两边的输出并排放着看。

样本要从真实流量里抽,覆盖你的主要场景和边角场景。然后看三层:

  1. 格式层:下游解析代码还能不能吃下。JSON 字段齐不齐、markdown 结构变没变、有没有多出「好的,以下是……」这类前缀。这一层是硬失败,最容易发现。
  2. 业务规则层:你如果有基于输出内容的判断逻辑——比如按关键词路由、按分类结果分流、按置信度卡阈值——这些规则在新输出上还成不成立。这一层是软失败,不报错但结果错。
  3. 风格与长度层:输出普遍变长或变短,会直接影响你的输出成本和前端展示。这一层不算错,但会让账单和体验偏离预期。

对比的时候不要只看「哪个更好」,先看「差异在哪」。差异清单本身就是你的上线风险清单。

顺带说一句成本:输出长度的变化会直接改变你的单位成本,所以旧平台的成本模型不能直接搬到新平台——哪怕单价一样,token 消耗量也未必一样。上线后用真实流量重新测一遍自己的单位成本,是迁移的必做项。


七、回滚预案与灰度

迁移最忌讳的是「切换日」——某天把配置一改,全量流量过去,然后祈祷。

更稳的做法是:

迁移期同时保留新旧两条通道。 平台选择做成运行时可切换的配置项,而不是编译期常量。这样出问题时,回滚是改一个配置、重启一次,而不是回滚一次发布。

按比例灰度。 先切一小部分流量过去,观察一段完整的业务周期(要覆盖你的高峰时段,低谷时段跑得好说明不了什么)。指标至少看四个:错误率、错误码分布、延迟分布(看分位数,不要只看均值)、单位成本。

定义好回滚触发条件,写在纸上。 「错误率超过多少」「结构化输出合法率跌破多少」「P99 延迟超过多少」——事先定好数字,比事发时凭感觉拍板要可靠。人在故障现场是很容易自我说服「再等等看」的。

旧通道别急着拆。 至少留到新通道跑满一个完整的业务周期、并且经历过一次高峰之后。把旧平台的 key 提前注销,等于亲手拆掉自己的降落伞。


八、上线前自查清单

  1. 目标平台是不是 OpenAI 兼容端点?如果不是(比如 Replicate 那种自有 predictions API),排期要按「改调用范式」重估,不是「改三处配置」。
  2. base_url 是从官方文档复制粘贴的吗?inference 这类多出来的路径段有没有漏、末尾斜杠有没有按原文保留?
  3. 模型 ID 是逐字符照抄的吗?组织前缀、日期后缀、大小写有没有动过?
  4. 先用 curl 打通完整地址,再倒推 base_url 配进 SDK——这个顺序执行了吗?
  5. 你实际用到的每一个请求参数,是否都单独验证过?平台不认识的参数是报错还是静默忽略,确认了吗?
  6. 流式的结束标志、中断行为、错误在流中的表现,测过吗?函数调用与结构化输出跑的是一批样本还是只有一条?
  7. 错误码与错误体结构对齐了吗?你的重试逻辑依赖的那个字段,新平台还在吗?
  8. 计费口径去官网确认过了吗(尤其是失败请求与缓存的计费方式)?灰度方案、回滚触发条件、旧通道保留期限,都定下来了吗?

本文涉及的 base_url、模型 ID 与环境变量名均来自各平台官方文档的公开写法。价格、限速、免费额度与各平台的功能支持范围会随时调整,请以官方文档和控制台的当前信息为准。

相关阅读