返回博客

OpenAI 兼容 API 中转接入教程:只改 base_url 的迁移方法与常见坑

用 OpenAI 兼容 API 中转网关接入多模型的完整流程:SDK 只改 base_url 的迁移法,stream_options 拿真实用量的正确姿势,以及 401/402/404/429/5xx 常见错误码排查。

Apiko 团队

如果你的代码已经在用官方 openai SDK 调用 GPT 系列模型,接入一个 OpenAI 兼容的 API 中转网关,理论上只需要改一行代码:把 baseURL(或 base_url)指向网关地址,再把 key 换成网关发的 sk-…。但实际接入时,流式响应、错误码、鉴权头这几个地方最容易踩坑。这篇教程按真实调用形态走一遍完整流程。

为什么"OpenAI 兼容"能做到只改一行

OpenAI 兼容,指的是网关在协议层面复刻了 OpenAI 的请求/响应结构——路径是 /chat/completions,请求体里的 model/messages/stream 字段,响应体的 choices/usage 结构,错误体的 {"error": {"message", "type", "code"}} 结构,全部保持一致。这意味着任何"说 OpenAI 协议"的工具——官方 SDK、LangChain、LlamaIndex,甚至一个裸 fetch/requests 调用——都不需要改调用逻辑,只需要换两个配置项:

  • base URL:从 https://api.openai.com/v1 换成中转网关地址
  • API Key:从 OpenAI 的 key 换成网关发的 key

要注意的是,这里说的"网关"是数据面(真正处理 /chat/completions 请求的那一层),和你注册、管理 Key、看账单的控制台站点不是同一个域名。控制台站点不代理 /v1 流量,你的所有请求应该直连网关主机。

三步接入:建 Key、换 base URL、调用模型

  1. 注册并创建 API Key——登录后进入控制台的 API Keys 页面创建一个新 Key。完整密钥只在创建那一刻显示一次,务必当场保存,后续无法再次查看明文。新账户注册即可自动获得试用额度,不需要绑卡就能先跑通流程。
  2. 把 base URL 指向网关——JavaScript SDK 用 baseURL,Python SDK 用 base_url,裸 HTTP 调用则把所有路径拼在网关地址后面。
  3. 调用任意模型——把 model 字段换成目录里的真实 id。具体有哪些模型可用、id 怎么拼,以模型目录当前展示的为准,不要凭记忆硬编。

裸 HTTP 调用长这样(把 {GATEWAY_BASE_URL} 换成你实际部署的网关地址,示例里用的是文档惯用的占位域名):

curl https://api.apiko.example/v1/chat/completions \
  -H "Authorization: Bearer $APIKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model-id",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

JavaScript(官方 openai 包,只改 baseURL 这一个参数):

import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.APIKO_API_KEY,
  baseURL: 'https://api.apiko.example/v1',
});

const completion = await client.chat.completions.create({
  model: 'your-model-id',
  messages: [{ role: 'user', content: 'Hello' }],
});

Python 同理,只需要在实例化客户端时传入 base_url:

from openai import OpenAI

client = OpenAI(
    api_key="APIKO_API_KEY",
    base_url="https://api.apiko.example/v1",
)

completion = client.chat.completions.create(
    model="your-model-id",
    messages=[{"role": "user", "content": "Hello"}],
)

三个例子里,业务逻辑(构造 messages、解析 choices[0].message)完全没变——这就是"OpenAI 兼容"这四个字真正省下来的工作量:不用为每个供应商重写一套调用代码,也不用维护多份 SDK 依赖。

流式响应:stream_options.include_usage 是关键

把请求体里的 stream 设为 true,响应会变成 server-sent events:每一行以 data: 开头,携带一个 JSON chunk,最后以 data: [DONE] 结束。这部分和标准 OpenAI 流式协议一致,大多数人踩的坑不在这里,而在怎么拿到真实 token 用量

普通的流式响应默认不会在中间 chunk 里带 usage 字段——如果你的计费或日志逻辑依赖 token 数,很容易发现流式场景下拿不到数字。解决方法是在请求体里显式加上:

{
  "model": "your-model-id",
  "messages": [{ "role": "user", "content": "Hello" }],
  "stream": true,
  "stream_options": { "include_usage": true }
}

加上这个参数后,流的最后一个 chunk会额外携带一个 usage 对象,里面是这次请求真实消耗的 prompt/completion token 数——这正是网关用来计费的同一份数字。也就是说,你在客户端拿到的 usage,和账单上扣的钱,口径是完全一致的。如果你在做用量统计或者按 token 做限流,一定要解析这最后一个 chunk,而不是自己用分词器去估算。

curl 版本的流式请求:

curl https://api.apiko.example/v1/chat/completions \
  -H "Authorization: Bearer $APIKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model-id",
    "messages": [{"role": "user", "content": "Hello"}],
    "stream": true,
    "stream_options": {"include_usage": true}
  }'

常见错误码:先查这张表再排查代码

接入过程里遇到非 200 响应,错误体结构和 OpenAI 一致,可以先按状态码定位问题,不用逐行翻代码:

| 状态码 | 常见原因 | 处理方式 | |---|---|---| | 401 | API Key 缺失或无效 | 检查 Authorization: Bearer 头是否带上了 key,确认这把 key 没有被吊销 | | 402 / 403 | 余额不足(insufficient_user_quota) | 到控制台 Billing 页面充值后重试 | | 404 | 模型或路径不存在(model_not_found) | 对照模型目录核对 model id 拼写,同时确认这把 key 有没有设置模型白名单 | | 429 | 触发限流 | 用指数退避重试,避免瞬时并发过高再次撞限流 | | 5xx | 上游故障 | 网关会自动跨渠道重试;如果最终仍然失败,这次请求不计费——预扣的额度会自动退款 |

最后一行的 5xx 处理值得展开说一下:请求发出时,网关会按估算金额做一次预扣,等上游真正返回结果后,再按实际 token 用量结算差额。如果上游最终失败(网关已经自动重试过跨渠道的备选路径),预扣金额会全额退回,你不会为一次没有拿到结果的请求付费。这个机制不需要你在客户端做任何额外处理,退款是自动发生的,可以在控制台的用量日志里核对每一笔请求的实际扣费和退款记录。

接下来

整个迁移的工作量,基本就是改一个 baseURL/base_url、换一把 key、按需加上 stream_options.include_usage。如果你想先验证流程再决定要不要长期使用,新账户注册即可获得试用额度,不需要绑卡,可以直接去注册拿一把 key 跑通这套代码;想先了解各模型的实时价格,可以看价格页——上面的单价就是账单实际使用的口径。