Codex 是 OpenAI 推出的编程智能体客户端,默认通过 OpenAI 官方 API 连接模型能力。实际项目里,很多团队希望让 Codex 接入 DeepSeek、智谱 GLM、阿里云 DashScope、讯飞星火等 OpenAI 兼容服务,或者通过统一网关管理多个模型 Key,实现不同场景下的模型切换和成本控制。这个过程的本质不是“破解”,而是配置一层兼容接入:让 Codex 的请求地址、模型名、协议方式和 API Key 都指向目标服务。网上常说的“免费接入”“低成本算力”大多是指利用各平台的官方免费额度或按量计费,而不是真正意义上的零成本。
实际调试中,最常遇到的不是“环境装不上”,而是模型名不匹配、协议不匹配、上下文超限、余额不足、本地转发服务没有处理/responses路由等细节问题。这篇文章会从 Codex 调用 API 的链路讲起,先回答“它到底在请求什么”,再给出一套最小可复现的第三方 API 接入方法,最后把高频报错整理成排查指南,方便按故障层逐段定位。
1. 先搞清 Codex 调用 API 的完整链路
1.1 Codex 不是模型,而是连接模型的客户端
Codex 是一个命令行或桌面工具,负责接收用户的自然语言任务、调用工具、执行代码、整理回答。真正的“智能”来自它背后连接的模型 API,而不是 Codex 自己内置某个大模型。因此 Codex 的配置里必然存在几个关键信息:API 地址、模型名称、认证 Key、协议类型。理解了这层关系,后续所有配置和排错都会变得清晰。
默认情况下,Codex 连接 OpenAI 官方 API,所以开箱即用的配置是官方地址和官方模型。当你希望使用其他模型服务时,只需要替换这组信息。替换之后,Codex 依然负责交互和工具调度,但回答和推理来自你指定的上游模型。这个行为并不特殊,就像一个聊天客户端可以配置不同的 IM 协议一样。
1.2 OpenAI 兼容协议与 Responses API
OpenAI 的模型 API 有两类常见调用风格:
- Chat Completions 风格,路径通常是
/v1/chat/completions。 - Responses 风格,路径通常是
/v1/responses。
Chat Completions 是更早、更普遍的协议,大量第三方平台都实现了兼容接口。Responses 是较新的协议,抽象了「输入、推理、输出、工具调用」等整段交互。Codex 新版本倾向于使用 Responses 协议,但很多第三方服务并不支持它。因此 Codex 的配置里有一个关键字段,通常叫wire_api,用来告诉客户端“应该用哪种协议访问上游”。
可以这样理解:
- 如果上游只提供
/v1/chat/completions,那么wire_api应该设置为chat。 - 如果上游提供
/v1/responses,那么wire_api可以设置为responses。 - 如果本地有一个统一接入层,把
/v1/responses转换成上游/v1/chat/completions,也可以让 Codex 保持responses协议。
协议不匹配是新手最容易踩的坑。Codex 请求了一个不存在的路径,或者上游只支持另一种格式,都会出现 404、模型不支持、请求格式错误等异常。
1.3 本地转发服务在整个链路中的位置
没有本地转发服务时,调用链是:
Codex -> 上游 API有本地转发服务时,调用链变成:
Codex -> 本地统一接入层 -> 上游 API本地统一接入层通常是一个运行在你电脑或内网服务器上的 HTTP 服务,监听某个端口,例如http://127.0.0.1:8000/v1。它接收 Codex 发来的请求,再把请求转发给真实的上游 API。为什么要加这一层?
主要原因有三个:
- 多供应商切换:Codex 同时只能配置一个 base_url 和一个 model,想切模型需要改配置。统一接入层可以按请求参数或简单规则把请求分发给不同上游。
- 参数改写:不同上游的模型名、上下文长度、辅助参数不同,可以在这里统一改写。
- 日志和审计:团队使用时代码会经过本地服务,便于记录每条请求的模型、耗时、费用和错误。
值得强调的是,本地统一接入层不等同于任何网络代理工具,它只是一个普通的 HTTP 转发服务,属于工程上常见的 API 网关思维。下面在环境准备和实现部分,会给出一个最小可运行版本。
2. 环境准备:安装 Codex、准备 Key 并先验证上游
2.1 安装 Codex CLI 的常见方式
Codex 的安装方式会随版本变化,实际以官方文档为准。常见方式有两种:
- 通过 npm 安装:
npm install -g @openai/codex- 通过官方安装脚本:
curl -fsSL https://codex.openai.com/install.sh | bash安装后确认版本:
codex --version如果命令不存在,需要重新打开终端,或确认 npm 的全局 bin 目录已经加入 PATH。
这里还有一个建议:不要在系统全局环境混装多版本 Node.js 和 Codex,否则后续升级和定位问题会比较混乱。如果只是个人学习,使用官方推荐安装方式即可。如果是团队环境,建议统一版本,避免一部分人用旧版配置、一部分人用新版导致行为不一致。
2.2 确认配置目录和版本信息
Codex 的配置文件一般在用户目录下的.codex文件夹中。常见路径是:
~/.codex/config.toml在 Linux/macOS 下,可以用:
ls -la ~/.codex在 Windows 下,通常位于用户目录的.codex目录。如果你不确定配置文件有没有生效,可以先执行:
codex --help查看当前版本支持的配置参数。不同版本的 config.toml 字段可能有差异,不要直接复制网上旧教程里的全部内容。
2.3 准备 API Key,并先用 curl 验证上游连通性
在配置 Codex 之前,先把上游 API 单独验证一遍。这一步可以避免把问题混在一起。
假设你要接入 DeepSeek,先设置环境变量:
export DEEPSEEK_API_KEY="sk-你的密钥"然后请求模型列表接口:
curl https://api.deepseek.com/models \ -H "Authorization: Bearer $DEEPSEEK_API_KEY"如果返回正常的 JSON 数组,说明地址和 Key 正确。接着验证一次最小对话请求:
curl https://api.deepseek.com/chat/completions \ -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-chat", "messages": [{"role": "user", "content": "hello"}], "stream": false }'这里暴露了两个信息:
- 上游地址是否包含
/v1前缀。 - 上游支持的是
/chat/completions还是/responses。
不同平台的 base_url 设计不同。有的写https://api.deepseek.com,Codex 会自动补路径;有的必须写https://api.deepseek.com/v1。建议先用手动 curl 测试出可用的完整地址,再把这个地址填进 Codex 配置。
2.4 学习环境与生产环境的 Key 管理差异
学习环境调试时,直接 export 环境变量比较方便,但不要因此养成把 Key 写进 config.toml 的习惯。生产中最好使用密钥管理服务、容器环境变量或 CI 系统的 secret 能力。尤其是多人协作时,config.toml 一旦提交到 Git,Key 就会出现在历史记录里,即使后面删除也很难彻底清干净。
3. 最小接入:用环境变量和 config.toml 对接第三方模型
3.1 环境变量方式最快,但要注意作用域
很多 OpenAI 兼容客户端都会读取以下环境变量:
export OPENAI_BASE_URL="https://api.deepseek.com" export OPENAI_API_KEY="sk-你的密钥" export OPENAI_MODEL="deepseek-chat"然后运行:
codex这种方式最直接,但要注意:
- 环境变量只在当前终端进程内生效,新开窗口需要重新 export。
- 如果 Codex 是桌面版,它可能不读取终端环境变量,而是读取图形界面配置。
- 某些第三方服务使用的模型名不是
deepseek-chat,而是平台自定义的名称,比如deepseek-v4-pro、deepseek-v4-flash,必须和平台文档对齐。
推荐做法是:先用环境变量方式验证能不能跑通,再决定是否改成 config.toml。
3.2 用 config.toml 管理模型供应商
config.toml 的好处是配置持久化,并且可以同时定义多个供应商。一个简化示例:
model = "deepseek-chat" model_provider = "deepseek" [model_providers.deepseek] name = "DeepSeek" base_url = "https://api.deepseek.com" env_key = "DEEPSEEK_API_KEY" wire_api = "chat"字段含义:
model:Codex 默认使用的模型名。model_provider:选择使用哪个供应商配置。base_url:上游 API 地址。env_key:从哪个环境变量读取 API Key。这里不直接写 Key,而是写环境变量名。wire_api:chat或responses,决定 Codex 调用上游时使用哪种协议。
如果上游是兼容 Responses 协议的服务,可以把wire_api改成responses。如果你的统一接入层同时支持两种协议,也要根据实际转发能力选择。
3.3 常见上游服务的 base_url 与模型名对照
不同平台的兼容地址和模型名差异很大。下面表格用于说明思路,实际以各平台文档为准:
| 平台 | 兼容入口示例 | 典型模型名 | 说明 |
|---|---|---|---|
| DeepSeek | https://api.deepseek.com | deepseek-chat、deepseek-reasoner | 支持 Chat Completions 兼容接口 |
| 智谱 | https://open.bigmodel.cn/api/paas/v4 | glm-4-plus、glm-4.5 | 地址通常自带/v4路径 |
| 阿里云 DashScope | https://dashscope.aliyuncs.com/compatible-mode/v1 | qwen-plus、qwen-max | 兼容模式地址含/compatible-mode/v1 |
| 讯飞星火 | 以官方文档为准 | 如4.0Ultra、generalv3.5 | 部分版本走 OpenAI 兼容接口 |
配置时最容易出的问题有两个。第一个是地址重复带/v1,比如 base_url 已经写了https://api.deepseek.com/v1,Codex 又补一个/chat/completions,拼出来变成/v1/v1/chat/completions。第二个是模型名用了官方文档之外的名字,比如第三方平台把模型重新命名为gpt-5.6-sol、deepseek-v4-pro,如果你没有先在平台确认,就会得到 not supported 报错。
3.4 验证 Codex 是否真正使用了第三方模型
完成配置后,进入 Codex 交互界面,输入一个简单的编码问题,例如:
写一个 Python 函数,判断一个字符串是否为回文。如果它能正常返回结果,说明链路已经通了。但为了确认它真的走的是第三方 API,而不是因为某个缓存或旧配置用了官方 Key,可以做两件事:
- 在上游控制台查看请求日志和消耗 token 记录。
- 如果用了本地统一接入层,查看本地日志中记录的
model字段。
不要只看到 Codex 能回答就认为配置成功,还要确认请求确实发到了预期地址。
4. 本地统一接入层:一个最小可运行的 FastAPI 转发服务
4.1 为什么要把多家 API 收敛到同一个入口
当只有一个人调试时,直接改 config.toml 就够了。但团队使用或需要统一审计时,不同人各自管理 Key 和配置会非常混乱。一个本地统一接入层可以做到:
- 请求入口固定为
http://127.0.0.1:8000/v1。 - 上游地址、Key、模型名集中在服务端配置。
- 通过日志看到每个请求的来源、模型、耗时和错误。
- 可以在后续加入限流、鉴权、缓存和费用统计。
这里说的统一接入层就是一个普通 HTTP 服务,不要把它理解为任何“绕过限制”的工具。它只是把 Codex 客户端和上游 API 之间的调用关系整理得更清晰。
4.2 最小转发服务的完整代码
下面用 Python 的 FastAPI 写一个最小转发服务。它同时暴露两个路由:/v1/chat/completions和/v1/responses。请求进来后,会把 JSON Body 透传给上游,并把上游响应返回给 Codex。
import os import httpx from fastapi import FastAPI, Request from fastapi.responses import JSONResponse, StreamingResponse app = FastAPI() UPSTREAM_BASE = os.getenv("UPSTREAM_BASE", "https://api.deepseek.com") UPSTREAM_KEY = os.getenv("UPSTREAM_KEY", "") UPSTREAM_MODEL = os.getenv("UPSTREAM_MODEL", "deepseek-chat") REQUEST_TIMEOUT = float(os.getenv("REQUEST_TIMEOUT", "600")) async def forward_to_upstream(request: Request, path: str): body = await request.json() if not body.get("model"): body["model"] = UPSTREAM_MODEL headers = { "Authorization": f"Bearer {UPSTREAM_KEY}", "Content-Type": "application/json", } timeout = httpx.Timeout(REQUEST_TIMEOUT) async with httpx.AsyncClient(timeout=timeout) as client: req = client.build_request( "POST", UPSTREAM_BASE.rstrip("/") + path, headers=headers, json=body, ) upstream_resp = await client.send(req, stream=True) if upstream_resp.status_code >= 400: error_body = (await upstream_resp.aread()).decode("utf-8", errors="ignore") await upstream_resp.aclose() return JSONResponse( status_code=upstream_resp.status_code, content={"error": error_body}, ) media_type = upstream_resp.headers.get( "content-type", "text/event-stream" ) return StreamingResponse( upstream_resp.aiter_bytes(), status_code=upstream_resp.status_code, media_type=media_type, ) @app.post("/v1/chat/completions") async def chat_completions(request: Request): return await forward_to_upstream(request, "/chat/completions") @app.post("/v1/responses") async def responses(request: Request): return await forward_to_upstream(request, "/responses") @app.get("/v1/models") async def list_models(): return {"data": [{"id": UPSTREAM_MODEL}]}这份代码的关键点有三个:
- 它透传了 Codex 的完整请求体,不会自作主张修改消息结构。
- 它使用流式转发,避免上游按 SSE 流式返回时,本地服务一次性加载完整响应导致卡顿。
- 它保留了上游的
content-type,这样 Codex 能正确识别返回的是普通 JSON 还是 SSE 流。
运行方式:
pip install fastapi uvicorn httpx export UPSTREAM_BASE="https://api.deepseek.com" export UPSTREAM_KEY="sk-你的密钥" export UPSTREAM_MODEL="deepseek-chat" uvicorn proxy:app --host 127.0.0.1 --port 8000然后用 curl 验证:
curl http://127.0.0.1:8000/v1/models如果返回包含模型名的 JSON,说明服务已启动。
4.3 Codex 的 wire_api 与转发路由如何匹配
Codex 最终请求哪个路径,取决于wire_api:
wire_api = "chat"时,Codex 请求/v1/chat/completions。wire_api = "responses"时,Codex 请求/v1/responses。
所以本地服务至少要实现 Codex 实际会调用的那个路由。很多报错里出现local proxy failed while handling codex endpoint /responses,本质就是本地服务收到了/responses请求,但处理逻辑异常或没有转发到正确的上游路径。
使用上面这份 FastAPI 代码时,如果希望 Codex 走 Chat Completions 协议,则配置:
model = "deepseek-chat" model_provider = "local" [model_providers.local] name = "Local Gateway" base_url = "http://127.0.0.1:8000/v1" env_key = "LOCAL_API_KEY" wire_api = "chat"如果上游服务支持 Responses 协议,则把wire_api改成responses,Codex 就会请求/v1/responses。
4.4 用 cc-switch 管理多套 Codex 配置的思路
社区里出现频率较高的cc-switch是一类配置管理工具。它做的事情并不神秘:帮助用户在不同模型供应商配置之间切换,本质是生成或覆盖 Codex 的 config.toml,或者切换环境变量。你可以在工具界面里保存多套配置:
- Codex 使用 DeepSeek
- Codex 使用智谱
- Codex 使用本地统一接入层
- Codex 使用官方 API
切换时,工具会替换当前生效的配置。如果你看到报错cc switch local proxy failed while handling codex endpoint /responses,说明 cc-switch 配置的本地服务没有正确响应/responses请求。排查方向是:
- 本地服务是否在运行。
- 端口和 base_url 是否一致。
- 本地服务是否实现了
/v1/responses路由。 - 上游地址是否填写正确。
这类工具适合个人开发和团队内部使用,但不建议在生产环境引入过多不透明配置层。生产环境更应该用明确的配置文件、统一网关和日志系统。
5. 常见报错排查:从错误信息定位故障层
Codex 接入第三方 API 的报错千奇百怪,但都可以按故障层来分类:客户端参数问题、上游 API 问题、网络或转发服务问题、账户权限或余额问题。下面按高频报错逐条说明。
5.1 模型名不存在的两种典型表达
常见报错片段:
the supported api model names are deepseek-v4-pro or deepseek-v4-flash或
the 'gpt-5.6-sol' model is not supported when using codex with a...这两种报错都指向模型名配置错误。第一个报错说明平台只支持特定的模型名,你填入的名字不在列表里。第二个报错说明客户端发送的gpt-5.6-sol模型名不是上游平台支持的名称,或者 Codex 内置的模型开关与该名称不兼容。
处理方式:
- 先通过上游平台查看模型列表。
- 如果用本地转发服务,可以直接请求
/v1/models查看平台支持哪些模型。 - 在 config.toml 里把
model改成平台支持的名称。 - 如果平台支持多种模型,但不同的模型需要不同的参数格式,还要检查插件或请求参数里是否把模型名称写死。
不要凭印象写模型名。很多第三方平台会在文档里给出“模型别名”,例如deepseek-v4-pro、deepseek-v4-flash,直接在请求里传deepseek-chat反而会被拒绝。
5.2 thinking_budget 参数报错并不一定是模型问题
常见报错:
api error: 400 the thinking_budget parameter must be a positive integerthinking_budget是控制模型思考预算的参数。Codex 对支持推理的模型会自动传入类似参数,但不同平台解析方式不同。出现这个报错时,通常有三种可能:
- 平台要求该参数必须为正整数,而客户端传了其他类型或非法值。
- 上游模型不支持推理参数,但 Codex 仍然发送了该字段。
- 本地转发服务对请求体做了改写,导致参数格式被破坏。
排查建议:
- 查看 Codex 或本地服务日志,确认实际发送的请求体。
- 如果当前模型不需要推理能力,尝试在 Codex 配置中关闭或降低推理相关设置。
- 如果是本地统一接入层,可以在转发前移除或修正
thinking_budget字段。 - 如果使用的是中转平台,换一个支持该参数的模型,或向平台确认参数规范。
这个报错的难点在于它发生在请求解析阶段,而不一定是模型没有余额或不可用。先抓请求体,再判断是客户端填写错误还是上游限制。
5.3 上下文超限:需要先区分客户端还是服务端限制
常见报错:
api error: 400 this model's maximum context length is 1048576 tokens...这类报错解释起来其实不复杂:请求中的 prompt、历史消息、工具调用结果加起来超过了模型上下文窗口。1048576是模型允许的最大 token 数,不代表平台出错。
处理方向:
- 减少单次请求携带的消息数量。
- 关闭或缩短历史记录,不要让 Codex 每次都携带完整上下文。
- 在 Codex 中使用“新会话”,而不是在一个超长会话里持续追问。
- 在本地转发服务中做 context 压缩或裁剪,但要注意不要影响正确性。
这里要特别注意:很多模型上下文窗口是“总窗口”,而输出 token 也会占用一部分空间。即使你感觉输入不多,再加上系统提示、工具返回、历史代码片段,可能已经接近上限。
5.4 连接中断和 402 余额不足怎么处理
常见报错:
api error: connection lost mid-response. the response above may be incomplete这种通常发生在流式输出过程中,Codex 已经收到一部分内容,但连接突然断开。原因可能是:
- 上游服务超时。
- 本地转发服务的超时时间设置太短。
- 网络不稳定,或代理/网关重启。
- 上游服务在处理长输出时负载过高。
处理方式:
- 拉长请求超时时间,在 FastAPI 示例里通过
REQUEST_TIMEOUT控制。 - 查看本地服务日志,看连接在哪一阶段断开。
- 尝试关闭流式,直接返回完整响应,验证是不是 SSE 转发的问题。
- 如果频繁出现,说明上游服务稳定性不足,考虑切换更稳定的供应商或模型。
另一个常见报错:
api error: 402 insufficient balance这说明上游账户余额不足或欠费。处理方式是到对应平台充值,或者切换到一个仍有免费额度或额度充足的模型。有些平台会返回402而不是401,容易被误判为权限问题。遇到4xx时,先区分状态码:
401:认证失败,API Key 错误或无效。402:余额不足。403:权限不足,或者被网关拒绝。404:路径不存在。429:请求频率超限。
5.5 403 和本地转发失败怎么查
报错片段:
transport failure for /api/agentpreset.list: http 403这个报错里的/api/agentpreset.list不是上游模型接口,而是 Codex 客户端或桌面版内部请求的接口。如果它返回 403,通常不是模型配置问题,而是:
- 登录态失效。
- 当前使用的 API Key 没有调用某个客户端内部功能的权限。
- 本地安全软件或网关规则拦截了请求。
排查顺序:
- 查看完整日志,确认是哪个进程发起的请求。
- 重新登录或重新生成 API Key。
- 检查客户端版本和配置,看是否缺少某个权限位。
- 如果是团队环境,联系管理员检查网关策略。
另一类报错:
cc switch local proxy failed while handling codex endpoint /responses这里需要先看懂结构:cc switch是配置切换工具,local proxy是它配置的本地转发服务。报错发生时,Codex 访问本地转发服务的/responses路由,但本地转发服务没有正常处理。
排查步骤:
- 确认本地转发服务进程是否还在运行。
- 手动执行:
curl http://127.0.0.1:8000/v1/models如果连接不上,说明服务没有启动或端口不对。
- 检查本地转发服务是否实现了
/v1/responses路由。如果只实现了/v1/chat/completions,而 Codex 配置用的是wire_api = "responses",必然失败。 - 确认上游地址是否可达。可以在本地服务所在机器上直接 curl 上游地址。
很多本地转发问题都出在“配置文件写了、服务没启动”或“服务启动了、路由不匹配”,先按这个顺序排查可以省下大量时间。
6. 学习环境与生产环境的最佳实践
6.1 两类环境的配置差异
学习环境追求快速验证,可以用环境变量、本地 config.toml、免费或低成本模型。生产环境追求稳定、可审计和可回滚,不能把个人 Key 直接写死在配置里,也不能让每个人各自维护一套不同版本的 config.toml。
学习环境建议: