Base URL and SDK Migration2026年7月14日Flatkey

OpenAI 兼容 API 故障排查:修复 401、模型名称、流式传输和 Base URL 问题

一条实用的排查路径,帮助解决 OpenAI 兼容 API 的 401 错误、模型名称错误、流式/SSE 失败、Base URL 配置错误以及计费回读问题。

OpenAI 兼容 API 故障排查:修复 401、模型名称、流式传输和 Base URL 问题

OpenAI 兼容 API 故障排查在你不再把每一次失败请求都归结为“提供商挂了”时,会变得容易得多。大多数迁移失败都来自六个层面之一:密钥、Base URL、端点家族、模型名称、流式行为,或计费/回读。

Flatkey 帮助团队将模型访问、路由、计费、使用情况分析和运营控制集中在一个地方,但 OpenAI 兼容客户端仍然需要精确配置。请求在 SDK 中看起来可能是正确的,但仍然会失败,因为客户端指向了错误的 /v1 根路径,模型别名属于不同的端点家族,或者流被代理缓冲了。

在你更改应用代码之前,请使用这份 OpenAI 兼容 API 故障排查指南作为清晰的调试路径。先从 curl 开始,验证一个非流式请求,然后再接入 SDK,接着再逐步加入流式、工具和生产流量,一次只增加一个层面。

五分钟 OpenAI 兼容 API 故障排查路径

在检查框架代码之前,先捕获最小的、应该能工作的请求。对于 Flatkey,请使用你当前控制台中显示的 Base URL。Flatkey 公共主页当前展示的是对 https://router.flatkey.ai/v1/chat/completions 的请求,这意味着 SDK 客户端通常应将 /v1 根路径作为 Base URL,而 SDK 应追加 /chat/completions

export FLATKEY_API_KEY="sk-fk-..."
export FLATKEY_BASE_URL="https://router.flatkey.ai/v1"
export FLATKEY_MODEL="your-model-alias"

curl -sS "$FLATKEY_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"$FLATKEY_MODEL"'",
    "messages": [
      {"role": "user", "content": "Reply with exactly: ok"}
    ]
  }'

如果这条请求失败,问题就不在你的应用框架。先修复密钥、Base URL、端点家族或模型别名。如果它成功了,就把相同的值复制到 SDK 中,再继续排查。

最快的 OpenAI 兼容 API 故障排查规则很简单:在一个普通的非流式文本请求成功之前,不要测试流式、工具、JSON 模式、重试或完整的代理工作流。

将错误视为某一层的问题,而不是最终结论

使用状态码来决定下一步该改什么。

症状 可能的层 先检查什么
401invalid_api_key 或认证错误 密钥和认证头 Bearer 格式、密钥来源、复制时的空白字符、提供商密钥还是网关密钥
403 或权限被拒绝 账户、项目或策略 IP 白名单、项目成员资格、模型审批、端点权限
404model_not_found 或未知模型 模型目录和端点家族 精确模型别名、模型启用状态、/chat/completions/responses 或其他端点之间的区别
400 请求格式错误 负载结构 必需字段、不支持的参数、工具 schema、消息格式
流已连接但没有出现 token 流式路径 stream: true、SSE 解析器、缓冲代理、端点是否支持流式
请求成功但缺少 usage 回读和计费 非流式对比请求、控制台记录、最终流事件行为
429500502503504 限流、容量或上游 退避、请求量、状态页面、重试策略、回退路由

OpenAI 自己的错误指南将 401 视为认证问题,将 429 视为速率或配额问题,并将 500/503 响应视为可重试的服务器或过载情况。OpenAI 兼容网关可能会添加自己的细节,因此在升级处理时请保留响应正文和请求 ID。

在更改模型之前先修复 401

401 是最常见的 OpenAI 兼容 API 故障排查绕路,因为它看起来像模型或路由问题,而实际上通常是认证问题。

按以下顺序检查:

  1. 请求中只有一个 Authorization: Bearer ... 头。
  2. 调用 Flatkey 时使用的是 Flatkey 密钥,而不是直接的 OpenAI、Anthropic、Google 或测试密钥。
  3. 密钥中没有复制进去的引号、换行、不可见前缀或尾随空格。
  4. 密钥是从进程实际运行的环境中加载的,而不仅仅是从你的 shell 中加载的。
  5. 账户、项目、团队或 IP 策略允许该路由。

使用一个不会打印密钥的简短 shell 检查:

test -n "$FLATKEY_API_KEY" && echo "key is set"
printf '%s' "$FLATKEY_API_KEY" | wc -c

如果 curl 可用,但 SDK 返回 401,请检查环境变量名称。OpenAI 的 Python 客户端默认读取 OPENAI_API_KEY,Node 客户端默认也读取 OPENAI_API_KEY。如果你的应用仍然导出带有旧的直连提供商密钥的 OPENAI_API_KEY,那么除非你显式传入 api_keyapiKey,否则 SDK 可能会忽略你新的网关密钥。

修复 Base URL,而不要重复端点

Base URL 错误通常有两种模式:

  1. SDK 会接收完整端点,例如 https://router.flatkey.ai/v1/chat/completions,然后又再次追加 /chat/completions
  2. SDK 只接收到域名,例如 https://router.flatkey.ai,从而始终无法到达 OpenAI 兼容的 /v1 路由。

对于 Python,请传入 base_url 或设置 OPENAI_BASE_URL。官方 Python 客户端源码在未提供自定义 base URL 时,也会回退到 https://api.openai.com/v1

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["FLATKEY_API_KEY"],
    base_url=os.environ.get("FLATKEY_BASE_URL", "https://router.flatkey.ai/v1"),
)

response = client.chat.completions.create(
    model=os.environ["FLATKEY_MODEL"],
    messages=[{"role": "user", "content": "Reply with exactly: ok"}],
)

print(response.choices[0].message.content)

对于 Node,请传入 baseURL 或设置 OPENAI_BASE_URL。官方 Node 客户端将 baseURL 说明为覆盖默认 OpenAI API 根路径的配置。

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.FLATKEY_API_KEY,
  baseURL: process.env.FLATKEY_BASE_URL ?? "https://router.flatkey.ai/v1",
});

const response = await client.chat.completions.create({
  model: process.env.FLATKEY_MODEL!,
  messages: [{ role: "user", content: "Reply with exactly: ok" }],
});

console.log(response.choices[0]?.message?.content);

如果这一步 OpenAI 兼容 API 故障排查仍然失败,请在进程启动时记录解析后的 base URL。不要记录密钥。

将模型名称与端点家族区分开来

“找不到模型”可能意味着别名错误,但也可能意味着该别名被发送到了错误的端点家族。一个适用于聊天补全的模型,未必会以相同的请求体形态暴露给 Responses、Messages、图像、视频或 embeddings。

在生产中重命名模型之前,请运行以下清单:

检查项 重要原因
在当前 Flatkey 控制台中确认精确的模型别名 网关别名可能与直接供应商的市场名称不同
确认端点家族 /v1/chat/completions/v1/responses 的请求形态不同
移除可选参数 不受支持的选项可能会掩盖真正的模型问题
尝试一个简短的非流式请求 普通请求可以将路由问题与流解析问题分离
记录失败的请求体和时间戳 支持和审计复核需要精确的模型、路由和错误信息

OpenAI 的外部模型文档也采用同样的思路用于自定义端点:提供端点 URL,指定模型 slug,并运行一次验证调用。请把你的网关配置也按同样方式对待。不要让每个服务都传入原始模型字符串,而是在代码中维护一份经过批准的小型模型映射表。

在非流式工作后再调试流式传输

流式传输应该是第二阶段测试。OpenAI Chat Completions 参考文档返回的要么是 JSON chat completion 对象,要么是 chat completion chunk 对象的流式序列。启用 stream 时,Responses API 也支持 text/event-stream

使用直接的流探测:

curl -N "$FLATKEY_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"$FLATKEY_MODEL"'",
    "stream": true,
    "messages": [
      {"role": "user", "content": "Count from one to five slowly."}
    ]
  }'

如果非流式请求可用而流式不可用,请检查流路径:

  • 确认响应使用兼容 SSE 的 content type。
  • 禁用会在返回前缓冲整个响应的 API 客户端中间件。
  • 为该路由关闭反向代理缓冲。
  • 检查你的前端解析器是否期望 Chat Completions chunk,而你的路由返回的是 Responses events。
  • 与现有的 Flatkey 流式检查清单对比,链接为 /blog/openai-compatible-streaming-sse-test

这一步 OpenAI 兼容 API 故障排查在无服务器和自动化工具中尤其重要。有些封装会返回成功的 HTTP 状态,却隐藏了在流关闭前调用方实际上没有收到任何 token 的事实。

只有在基础请求干净后再添加 tools

工具调用会增加另一层失败点。网关、路由或所选模型可能接受普通聊天消息,但拒绝工具 schema、tool_choice、并行工具调用,或严格结构化输出设置。

使用三步请求梯子:

  1. 使用相同模型的纯文本请求。
  2. 在相同请求中加入一个很小的函数 schema。
  3. 完整的生产工具 schema。

如果请求 1 成功而请求 2 失败,那么你排查的就不再是认证或 base URL 了。你正在排查模型能力、端点家族或 schema 支持。移除可选字段,缩短描述,并确认所选模型路由是否支持你需要的工具行为。

证明用量和计费回读

不要把 OpenAI 兼容 API 故障排查停在“响应返回了文本”这一步。对于生产迁移,你还需要证明这次请求是可被财务和运营团队审阅到的。

在成功完成一次烟雾测试后,请捕获:

证据 它证明了什么
请求时间戳和路由 哪个网关路径接收了流量
模型别名 请求的是哪个已配置模型
响应状态和请求 ID 支持团队可以追踪什么
usage 对象或 token 数 应用是否能记录成本驱动因素
仪表盘或计费回读 财务是否可以核对支出
降级或重试事件(如有) 路由策略是否改变了路径

Flatkey 的定位是围绕单一密钥、清晰定价、统一计费,以及一个用于密钥、用量和路由的仪表盘。对于迁移来说,在把真实流量切过去之前,最好将工程侧的烟雾测试与控制台中的用量回读检查配对进行。

适合生产环境的故障排查工作流

当 OpenAI 兼容 API 迁移失败时,请按以下顺序处理:

  1. 使用当前控制台 base URL、一个密钥和一个已批准的模型别名,运行一次非流式 curl 请求。
  2. 在更改 payload 之前,先修复任何 401 或 403 问题。
  3. 在更改 SDK 版本之前,先修复 base URL 的拼接方式。
  4. 在更改重试策略之前,先修复模型别名和端点家族。
  5. 为 SDK 显式添加 api_keyapiKey,以及 base_urlbaseURL
  6. 再添加流式传输,并验证客户端能收到增量事件。
  7. 每次只添加一个功能:工具调用或结构化输出。
  8. 检查用量和计费回读。
  9. 将可工作的值迁移到一个可回滚的配置中。

这个顺序可以避免 OpenAI 兼容 API 故障排查变成猜谜游戏。每一步都要么验证了一层,要么给你留下一个更小的故障需要修复。

Flatkey 何时能帮上忙

当根本问题是运营上的分散时,Flatkey 会很有用:提供方密钥太多、模型访问不一致、用量难以审计,以及计费路径分散。统一网关并不能消除对端点家族、模型别名、流式传输、工具和计费回读的测试需求,但它能让团队在一个地方标准化这些检查。

如果你正在迁移应用,请将本指南与 Flatkey 的 OpenAI 兼容迁移指南 /blog/openai-compatible-api-migration 以及烟雾测试清单 /blog/ai-api-smoke-test-checklist 配合使用。

当你准备好用 Flatkey 密钥测试该工作流时,请从 /sign-up 开始,并把第一次烟雾测试控制在足够便于手工检查的规模。

常见问题

为什么当密钥已经设置时,我的 OpenAI 兼容 API 仍然返回 401?

程序可能读取的是与你修改的不同环境变量,或者这个密钥属于错误的提供方。请检查解析后的变量名、Authorization: Bearer 头、复制时是否带入了空白字符,以及任何账户或 IP 策略。

SDK 的 base URL 应该包含 /chat/completions 吗?

通常不需要。把 /v1 base URL 交给 SDK,然后让 SDK 自动追加端点。传入完整端点经常会造成路径重复。

为什么模型在不使用流式传输时可以工作,但在 stream: true 时失败?

base 路由可能是正确的,但流式路径可能被缓冲中间件、SSE 解析器不匹配,或不支持流式传输的路由/模型组合所阻塞。在调试前端代码之前,先用 curl -N 进行测试。

为什么在模型名称有效时仍会出现“model not found”?

该别名可能在一个端点家族中有效,而在另一个家族中无效;或者网关暴露的别名与直接提供方不同。请同时确认当前控制台中的别名和端点家族。

在发送生产流量之前我应该测试什么?

测试一次非流式请求、一次 SDK 请求、一次流式请求、如果应用使用工具则测试一次具有代表性的工具调用、一次失败路径,以及一条计费/回读记录。然后为之前的提供方路由保留一个可回滚配置。

OpenAI 兼容 API 故障排查不是为了记住每个提供方的所有错误,而是为了证明从密钥到 base URL、从 base URL 到端点家族、从端点家族到模型别名,以及从成功响应到用量记录的整条路径。当这些层次都清晰时,通过 Flatkey 切换流量就会变成一次受控迁移,而不是深夜里的调试会话。