兼容实际承诺的内容
兼容 OpenAI 的 API 接受 OpenAI SDK 发送的相同 HTTP 请求。你更改 base URL 和密钥,客户端即可继续工作。该承诺位于协议层:路径 /v1/chat/completions,Authorization 标头中的 bearer token,包含 model 和 messages 的 JSON 正文,以及包含 choices 和 usage 的响应。流式输出遵循相同的服务器发送事件模式。
它不承诺相同的模型、相同的行为、相同的限制或相同的接口集。原始 API 背后的公司决定其提供什么,每个其他提供商独立决定复制什么。将兼容性视为避免重写客户端代码的方法,而不是保证其他内容不变。
差异对比
该表列出了跨提供商变化的领域以及此 API 的情况。右列是你在评估任何服务时进行自我测试的提示。
| 领域 | 此 API | 在任何提供商处检查什么 |
|---|---|---|
| 模型 ID | 一个 ID:uncensored | 你的旧模型名称是被接受、映射还是拒绝 |
| 接口 | /v1/chat/completions,/v1/responses,/v1/models | 你的代码调用的每个接口是否存在 |
| 模态 | 仅文本 | 图像、音频和视频支持(如果需要) |
| 嵌入、微调 | 不提供 | 这些调用将转向何处 |
| 上下文窗口 | 100,000 token,包含提示词和补全 | 限制是仅覆盖输入还是两者都覆盖 |
| 并发和速率 | 完整密钥:同时 8 个,每分钟 300 个。试用密钥:同时 2 个,每分钟 60 个 | 如何测量限制以及如何报告拒绝 |
| 函数调用、JSON 模式 | 支持 | 边缘情况,如流式工具调用 |
| 内容行为 | 针对合法的成人使用进行调优,无内容拒绝 | 改变模型返回内容的审核层 |
| 计费 | 预付额度,按 token 计费,无订阅 | 最小值、过期、订阅层级 |
该 API 的定价行为是 $0.15 每 1M 输入 token 和 $0.50 每 1M 输出 token。对于任何其他服务,请阅读其当前定价页面,而不要依赖记忆或第三方摘要。
模型 ID 和行为
最大的实际差异是模型本身。使用 OpenAI 时,你可以从命名模型菜单中选择。这里你发送 "model": "uncensored" 并获得由独立提供商运行的单一开放权重模型。它不是任何模型供应商的官方 API,也不是将请求转发到多个供应商的路由器。
这有两个后果。首先,你无法通过更改字符串来 A/B 测试模型大小;你只能运行一个模型,并围绕它调整提示词和采样。其次,为其他模型编写的提示词可能需要进行小幅修改,因为指令遵循、格式习惯和拒绝行为均有所不同。该模型针对合法的成人使用进行调优,无内容拒绝,因此,对于更严格模型编写的带有长篇幅安全前言的提示词通常可以缩短。
一个硬性限制不变:禁止涉及未成年人的性内容。
不存在的接口和功能
在整个 OpenAI 接口面上,兼容性很少是完整的。这里的差异是故意的:该服务仅处理文本,不处理其他内容。
- 无图像、音频或视频生成。
- 无嵌入接口,因此检索管道需要单独的嵌入提供商。
- 无微调。
存在的内容涵盖大多数聊天应用程序:POST /v1/chat/completions,POST /v1/responses,流式输出,使用 tools 和 tool_choice 的工具调用,以及通过 response_format 的 JSON 模式。采样控制如 temperature、top_p、stop、seed 和惩罚被传递到模型。
如果你的应用混合了多种功能,通常的做法是保留原始提供商用于嵌入和图像,并将你希望无审查的文本生成发送到新的基础 URL。成本与权衡分析将详细说明这种拆分。
限制、错误和标头
限制是迁移中令人惊讶的地方,因为它们决定你的代码在负载下应如何行为。此 API 对完整密钥设置 8 个并发请求和每分钟 300 个请求,对试用密钥设置 2 和 60,最大正文为 8 MB。超过限制的请求返回 HTTP 429。错误代码告诉你触发了哪个限制:rate_limited 表示每分钟请求,concurrency 表示同时请求过多。临时过载也返回 429 并添加 Retry-After: 5。
阅读代码和标头,不要只看状态码。立即重试 concurrency 错误会使情况恶化,短暂等待即可解决。流式请求会占用其槽位直到流结束,因此并发计算的是打开的流,而不仅仅是正在进行的调用。
响应包括 X-Request-Id、X-Balance-USD、X-RateLimit-Limit-Requests 和 X-RateLimit-Limit-Concurrency。余额标头是提供商特定的额外内容:OpenAI SDK 代码忽略它,但你可以读取它来跟踪支出。余额也可通过 GET /v1/balance 和 OpenAI 风格的 GET /v1/dashboard/billing/credit_grants 获得。
迁移检查清单
当在提供商之间移动应用程序时,每次都使用相同的简短列表。它可以在用户之前捕获大多数意外情况。
- 同时替换 base URL、密钥和模型 ID,并在配置文件中搜索隐藏的旧模型名称。
- 删除对新侧不存在的接口的调用,并将它们路由到具有这些接口的提供商。
- 运行你的提示集并比较输出。期望来自不同模型的不同措辞和格式。
- 测试通过你的用户和 API 之间的每个代理和负载均衡器的流式输出。
- 为 429 添加重试逻辑,读取错误代码和 Retry-After 标头。
- 记录每个请求的 token 使用情况,以便成本意外在几天内显现,而不是在月底。
快速兼容性测试
不要信任功能列表,包括此列表。运行你的应用程序进行的调用并比较结果。一个练习你依赖部分的简短脚本只需几分钟:
from openai import OpenAI
client = OpenAI(
base_url="https://api.apicompatible.com/v1",
api_key="YOUR_API_KEY",
)
# 1. Plain completion
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Reply with the word ready."}],
temperature=0,
seed=7,
)
print("plain:", r.choices[0].message.content)
# 2. JSON mode
j = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Return a JSON object with keys name and age for a fictional person."}],
response_format={"type": "json_object"},
)
print("json:", j.choices[0].message.content)
# 3. Streaming
chunks = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Count from one to five."}],
stream=True,
)
print("stream:", "".join(c.choices[0].delta.content or "" for c in chunks if c.choices))
如果你的代码使用函数,请添加一个函数调用用例;如果你的提示词较长,请添加一个接近真实提示词大小的长上下文用例。每个参数的详细信息在 文档 中,API 密钥页面 显示了如何领取 $0.50 试用额度,这对于此类检查已绰绰有余。
