跳转到主要内容

OpenAI base_url 怎么改:/v1、优先级与排错

base_url 参数优先于 OPENAI_BASE_URL,都没设才用 api.openai.com/v1;base 要写到服务商要求的那一级,SDK 只在后面接接口路径。

LaoZhang AI Team发布于17 分钟阅读
文章目录
OpenAI base_url 生效顺序:base_url 参数优先,其次 OPENAI_BASE_URL,都没设时用 api.openai.com/v1

把 OpenAI 格式的代码改发到 Azure、Gemini、中转网关或自建服务,真正要弄清的只有三件事:哪个设置生效,base 写到哪一级,目标服务有没有实现你调用的接口。

下面的行为以 2026 年 9 月 26 日的最新版官方 SDK 为准:openai-python 3.19.2 与 openai-node 7.23.0,用一个只记录请求路径的本地回显服务逐项跑过。

  • 生效顺序:创建客户端时传入的 base_url(Node 里叫 baseURL)优先,其次是环境变量 OPENAI_BASE_URL,都没有才用默认的 https://api.openai.com/v1。旧教程里的 OPENAI_API_BASE,当前 SDK 已经不读。
  • 写到哪一级:SDK 把 chat/completions、responses 这类接口路径直接接在 base 后面,所以 base 要包含服务商要求的全部前缀。多数 OpenAI 兼容服务是 /v1,Azure 是 /openai/v1/,Gemini 是 /v1beta/openai/。不要把完整的接口地址当 base 填。
  • 兼容有边界:改地址只改变请求发往哪里,不会让对方多出一个接口。不少服务只实现了 Chat Completions,client.responses.create 发过去会得到 404。

哪个设置生效:参数、环境变量和默认值

常规情况很简单:显式参数赢过环境变量,环境变量赢过默认值。容易出事的是几种边缘写法,两个 SDK 的处理并不一致:

配置情况Python 实际使用的 baseNode 实际使用的 base
只设 OPENAI_BASE_URL该变量的值该变量的值
参数和 OPENAI_BASE_URL 都设了参数参数
都没设https://api.openai.com/v1https://api.openai.com/v1
OPENAI_BASE_URL 设成空字符串空值,发请求时报 APIConnectionError: Connection error.当作没设,回到 https://api.openai.com/v1
只设旧变量 OPENAI_API_BASE被忽略,仍发往 api.openai.com被忽略,仍发往 api.openai.com
设了 OPENAI_BASE_URL,同时指定数据驻留 euhttps://eu.api.openai.com/v1https://eu.api.openai.com/v1
传入 baseURL: null不适用不再读环境变量,用默认地址

空字符串这一行最值得留意。.env 里留一行 OPENAI_BASE_URL= 作占位、或者 CI 里变量存在但没填值,在 Python 下会直接连接失败,问题还算明显;在 Node 下请求会悄悄回到 api.openai.com,第三方服务的 key 随之发给 OpenAI,结果多半是一个看不出原因的鉴权失败。

另外几条会影响判断的规则:

  • 单次请求改地址:Python 的 client.with_options(base_url=...)、Node 的 client.withOptions({ baseURL }) 只改这一次调用,客户端本身的 base 不变。适合同一个程序里少量请求发往另一个服务。
  • 数据驻留与 base_url 互斥:openai-python 的 data_residency(可选 us、eu、ae)会把地址换成 https://eu.api.openai.com/v1 这类区域地址,而且优先于 OPENAI_BASE_URL。它和 provider 都不能与 base_url 同时传,一起传会直接报错。要用 OpenAI 区域地址,用这个参数,不要自己拼 base;项目能否使用某个区域,SDK 里看不出来,以 OpenAI 账户侧为准。
  • 框架可能有自己的变量:有的第三方框架可能仍会自己读 OPENAI_API_BASE,再传给 SDK。用框架时,看它的文档确认读的是哪个变量,别假设它和官方 SDK 一致。

两种写法的最小示例:

python
import os
from openai import OpenAI

# 写法一:显式参数,优先级最高
client = OpenAI(
    base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
    api_key=os.environ["GEMINI_API_KEY"],
)

# 写法二:只靠环境变量
#   export OPENAI_BASE_URL="https://api.example.com/v1"
#   export OPENAI_API_KEY="该服务签发的 key"
client = OpenAI()
javascript
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
  apiKey: process.env.GEMINI_API_KEY,
});

/v1 写到哪一级:SDK 怎么拼地址

SDK 的做法是“base + 接口相对路径”。openai-python 会先给 base 补上末尾斜杠,再把 chat/completions 接上去;Node 结果相同。所以 base 里必须已经带上服务商要求的每一段前缀,SDK 不会替你补 /v1,也不会识别出你多写了什么。

以 client.chat.completions.create(...) 为例,服务端实际收到的路径如下:

base 的写法服务端收到的路径结果
https://api.example.com/v1/v1/chat/completions正确
https://api.example.com/v1//v1/chat/completions正确,末尾斜杠有没有都行,不会出现双斜杠
https://api.example.com/chat/completions少了 /v1,接口挂在 /v1 下的服务会返回 404
https://api.example.com/v1/chat/completions/v1/chat/completions/chat/completions路径重复,返回 404

调用 client.responses.create(...) 时,路径是 {base}/responses,规则相同。

base 的四种写法与服务端实际收到的路径:带 /v1 正确,只写主机或填完整接口地址会返回 404

常见目标该怎么填:

目标base 填什么同时要换的需要知道的限制
OpenAI 官方不用填,默认 https://api.openai.com/v1无无
OpenAI 数据驻留不填 base,改用 data_residency无与 base_url 不能同时使用
Azure OpenAI(v1 API)https://你的资源名.openai.azure.com/openai/v1/,也接受 services.ai.azure.com 形式的资源地址key 用该资源的 key,或通过 api_key=token_provider 用 Entra ID;model 填部署名不再需要 api-version;微软说明 v1 GA 目前只覆盖部分推理和管理能力
Gemini APIhttps://generativelanguage.googleapis.com/v1beta/openai/Gemini API key;Gemini 模型 IDGoogle 标注对 OpenAI 库的支持仍是 beta;文档没有列出 Responses
中转网关或第三方服务服务商文档给出的“OpenAI SDK base”该服务的 key 与模型 ID支持哪些接口各家不同,先看文档
自建或本机服务该服务文档给出的 OpenAI 兼容地址按服务要求Cursor 连不上本机地址,见后文

Azure 一行依据微软 Learn 的 v1 API 页面(2026 年 5 月 13 日版),Gemini 一行依据 Google 的 OpenAI 兼容文档(2026 年 9 月 2 日更新)。在 Azure 上,只设 OPENAI_BASE_URL 和 OPENAI_API_KEY 两个环境变量,然后直接用 OpenAI(),同样可行;旧的 AzureOpenAI(azure_endpoint=..., api_version=...) 写法仍然存在,但走 v1 API 时用不着。

同一家服务,不同 SDK 的 base 层级也可能不同。以 laozhang.ai 为例,它的文档写明 OpenAI SDK 的 base 是 https://api.laozhang.ai/v1,而 Google Gen AI SDK 和 Anthropic SDK 要填主机根 https://api.laozhang.ai。这就是“看文档填,不凭习惯加 /v1”的原因。

中文里说的“代理”,可能是两件事

“走代理调 OpenAI”在中文语境里有两种意思,配置位置完全不同:

  • 中转服务:一个实现了 OpenAI 格式接口的第三方服务,请求的目的地就是它。它的地址填进 base_url,key 也用它签发的。
  • 网络代理:只是出网经过的一跳,请求的目的地没变。它不该写进 base_url,而是配在 SDK 的 HTTP 客户端上。

把网络代理地址填进 base_url,SDK 会把它当成 API 服务,直接向代理服务器请求 /chat/completions 这类路径,而代理本身并不提供这些接口。两者可以同时存在:

python
from openai import OpenAI, DefaultHttpx2Client

client = OpenAI(
    base_url="https://api.example.com/v1",  # 请求发往哪个 API
    http_client=DefaultHttpx2Client(proxy="http://proxy.example.com:8080"),  # 出网经过哪个代理
)

DefaultHttpx2Client 是 openai-python 3.19.2 README 里的写法;较早的版本用的是 DefaultHttpxClient,按你安装的版本对照它自带的 README。Node 通过 fetchOptions 传入代理:

javascript
import OpenAI from 'openai';
import { fetch, ProxyAgent } from 'undici';

const client = new OpenAI({
  baseURL: 'https://api.example.com/v1',
  fetch,
  fetchOptions: { dispatcher: new ProxyAgent('http://proxy.example.com:8080') },
});

Chat Completions 能通,Responses 不一定

“OpenAI 兼容”通常指兼容 Chat Completions 这一个接口,不等于实现了 OpenAI API 的全部。改了 base_url 之后,SDK 依旧会把 client.responses.create 发到 {base}/responses,对方没有这个接口就是 404。

几个有据可查的情况:

  • Gemini:Google 的兼容文档列出了 Chat Completions(含流式、函数调用、用 reasoning_effort 控制思考)、图片、音视频、结构化输出、嵌入、批处理和模型列表,没有列出 Responses。OpenAI 社区里有开发者在 2025 年 11 月 6 日报告,用 Gemini 的兼容地址调用 responses.create 得到 404。文档没写不代表永远不行,但在它出现在文档里之前,别把生产代码押在上面。
  • Azure OpenAI v1:微软明确说 v1 GA 首批只支持部分能力,用哪个接口先查 Azure 的支持列表。
  • 中转网关:以文档为准,而且可能按模型区分。laozhang.ai 的公告写明 GPT-6 Astra、Sol、Luna 可以走 POST /v1/chat/completions 和 POST /v1/responses;这条说明只针对这几个模型,不能推到它的全部模型。它的注册需要 Gmail 并经过白名单审核。

实际做法:先用 chat.completions.create 打通地址和 key,再单独试 responses.create。需要 Responses 独有的能力而对方没有提供时,要么退回 Chat Completions,要么改用该服务的原生 SDK。如果你正在把 Assistants 代码迁到新接口,同时打算换服务商,先确认对方支持 Responses;迁移本身见 OpenAI Assistants API 迁移到 Responses API:生产切换指南。

确认请求实际发往哪里

配置写对了不等于请求发对了。下面四种方法由浅到深,任意一种都能把“我以为”变成“我看到”。

1. 打印生效的 base。 在创建客户端的同一处打印 client.base_url(Python)或 client.baseURL(Node)。环境变量、空字符串、数据驻留谁生效,这一行就能看出来。

2. 指向回显服务,看真实路径。 下面这个脚本只在本机 IPv6 回环地址上监听,把收到的请求路径原样返回,不会转发到任何服务商,也不需要真实 key:

python
# echo_server.py:只记录收到的请求路径,不转发到任何服务商
import json
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer

class Echo(BaseHTTPRequestHandler):
    def do_POST(self):
        self.rfile.read(int(self.headers.get("Content-Length", 0)))
        print(self.command, self.path, flush=True)
        body = json.dumps({
            "id": "echo", "object": "chat.completion", "created": 0, "model": "echo",
            "choices": [{"index": 0, "finish_reason": "stop",
                         "message": {"role": "assistant", "content": self.path}}],
        }).encode()
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, *args):
        pass

class LoopbackV6(HTTPServer):
    address_family = socket.AF_INET6

# 只监听 ::1,局域网里的其他机器访问不到
LoopbackV6(("::1", 8765), Echo).serve_forever()

另开一个终端,把你要验证的 base 换成回显地址,保留原来的路径部分:

python
from openai import OpenAI

client = OpenAI(api_key="sk-test", base_url="http://[::1]:8765/v1")
print("生效的 base:", client.base_url)
r = client.chat.completions.create(model="any", messages=[{"role": "user", "content": "hi"}])
print("服务端收到的路径:", r.choices[0].message.content)

正常会输出 /v1/chat/completions。把 base_url 换成你配置里的写法(去掉 /v1、改成环境变量、贴完整接口地址),就能直接看到前面表格里的每一种结果。Node 用 baseURL: 'http://[::1]:8765/v1' 做同样的事。回显服务返回的是固定格式,只适合验证地址,不能验证服务商的真实行为。

3. 打开 SDK 日志。 Python 设 OPENAI_LOG=info 或 OPENAI_LOG=debug;Node 可以设 OPENAI_LOG 环境变量,或在客户端上传 logLevel: 'debug',后者优先。

4. 去服务商后台看有没有这条请求。 这是判断“请求到底有没有到”的最终依据。服务商的调用日志里完全没有记录,说明请求根本没到那里,这时改 key、改模型名都没用,问题在地址这一层。

按症状找该改的那一层

现象先看什么常见原因怎么改
404,服务端收到的是 /chat/completions请求路径base 少了 /v1 等前缀按服务商文档补全 base
404,路径里出现 /chat/completions/chat/completions请求路径把完整接口地址当成了 basebase 截到接口名之前
Chat Completions 能用,responses.create 返回 404调用的接口对方没实现 Responses改用 Chat Completions,或换支持 Responses 的服务
401 或 Incorrect API key provided,错误来自 OpenAI生效的 base第三方 key 被发到了 api.openai.com:Node 下 OPENAI_BASE_URL 为空、只设了旧变量 OPENAI_API_BASE、Cursor 里地址被重置打印生效的 base,改用 OPENAI_BASE_URL 或显式参数
401,错误来自目标服务key 的归属key 不属于这个服务或这个 Azure 资源换成该服务签发的 key
模型不存在一类的错误model 字段用了 OpenAI 的模型名;Azure 要填部署名换成目标服务的模型 ID 或部署名
APIConnectionError: Connection error.base 是否为空、主机能否连通Python 下 OPENAI_BASE_URL 为空字符串;主机名写错;网络或代理问题删掉空变量,再查网络
429无需再查地址请求已经到达目标服务,是限流按服务商的配额规则处理

按症状排查 base_url 问题:404、401、模型不存在、APIConnectionError 与 429 各自对应的原因和改法

连接类错误的特点是请求没有拿到任何 HTTP 响应,和 401、404 这种“服务端已经回话”的错误要分开处理,详见 OpenAI API 连接错误:先验证路由再修复 APIConnectionError。Azure 上遇到 429,地址已经没问题,排查方向见 Azure OpenAI TPM 限流排查:用量没满,为什么仍报 429?。

Cursor 里的 "Override OpenAI Base URL"

Cursor 的这个开关和 SDK 的 base_url 是一个意思,但路由规则由 Cursor 决定,而且官方帮助页没有写它的具体行为。以下内容来自 2026 年 8 月至 9 月 Cursor 员工在官方论坛上的回复,属于当时的实现,后续可能变化。

先确认开关真的生效。 在 Cursor Settings > Models 里粘贴 OpenAI API key,和打开 “Use OpenAI API key” 开关是两个独立步骤;Override OpenAI Base URL 只在这个开关打开时起作用。开启后新建一个对话再试(2026 年 8 月 26 日回复)。

开关会预填官方地址。 打开 Override 时,输入框默认填入 https://api.openai.com/v1;关掉再重新打开,会被重置回这个默认值。这时第三方 key 被发给 OpenAI,报 Incorrect API key provided,而服务商后台看不到任何 401,因为请求从没到过那里。改完地址后按回车或点击输入框外保存,关掉设置再打开,确认填的仍是你的地址(2026 年 9 月 10 日回复)。

作用范围比想象的大。 开启后,除 Claude 和 Gemini 以外的模型都会用这组 key 和地址,包括内置模型选择器里的 OpenAI 模型,以及跑在 Cursor 自己设施上的 Composer、Grok;后两者带上你的 key 会报 “This model does not support custom API keys”。目前做不到让自定义模型走你的地址、同时让 Cursor 模型走 Cursor,只能在两者之间切换开关。按模型分别路由的需求在跟进中,没有时间表(2026 年 8 月 21 日、9 月 15 日、9 月 23 日回复)。

与内置模型重名的名字会被截走。 自定义模型名如果和 Cursor 内置模型的保留名称相同(员工举的例子有 kimi-k3、kimi-latest),Cursor 会把它当成自己托管的模型,不发往你的地址。换成服务商那边不与之冲突的真实模型 ID 即可(2026 年 8 月 19 日、9 月 10 日回复)。

本机地址用不了。 所有请求都要先经过 Cursor 的服务器构建提示词,所以 Override 需要公网可访问的 HTTPS 地址,本机回环地址和局域网地址都连不上。员工给出的变通办法是用 ngrok 或 Cloudflare Tunnel 把本机服务暴露成公网 HTTPS(2026 年 2 月 22 日、4 月 2 日、5 月 22 日回复)。隧道打通后,本机模型服务就成了任何拿到地址的人都能调用的公网接口,开放前先在前面加一层鉴权。

截至 2026 年 9 月 26 日,Cursor 帮助页还写明三点:自己的 key 只用于聊天模型,Tab 补全仍用 Cursor 的模型;使用自己的 key 时,Cursor 的零数据保留政策不适用;团队版和企业版里,用自己 key 发出的请求同样按 Cursor Token 费率计费,每百万 token $0.25(含输入、输出和缓存 token)。另外,用自己的 key 时,上下文长度和速率限制按服务商那边的规则走。

常见问题

OpenAI API 默认的接口地址是什么?

两个官方 SDK 的默认 base 都是 https://api.openai.com/v1,Chat Completions 的完整地址是 https://api.openai.com/v1/chat/completions。直接调用 OpenAI 时不需要设置 base_url;只有使用区域数据驻留时,才通过 data_residency 换成 us、eu、ae 对应的区域地址。

“OpenAI 兼容接口”是什么意思?

指服务商按 OpenAI API 的请求和响应格式实现了部分接口,你可以用官方 SDK 只改 base_url、key 和模型名来调用它。实现到什么程度由服务商决定,最常见的是只有 Chat Completions,Responses、文件、批处理等是否可用要逐项看文档。

旧代码里的 openai.api_base 和 OPENAI_API_BASE 还能用吗?

那是 1.0 之前的 openai-python 的写法。在 openai-python 3.19.2 和 openai-node 7.23.0 里,单独设置 OPENAI_API_BASE 不会产生任何效果,请求仍然发往 api.openai.com。迁移时改成 OPENAI_BASE_URL 环境变量,或在创建客户端时传 base_url。

Codex 也是改 OPENAI_BASE_URL 吗?

Codex 有自己的配置方式:只是给内置 OpenAI 通道换地址,用 config.toml 里的 openai_base_url;换成另一家服务商,要声明独立的 custom provider。两条路线的区别和 401、404 的定位见 Codex 自定义 API 配置:Key、Base URL 与 Provider 怎么选。