这阵子折腾 Codex CLI 的人不少,但很多人的真实需求其实不是连官方云服务,而是把手头已有的第三方 API 接进去——要么是公司内部搭好的统一接口网关,要么是云厂商提供的兼容接口,要么是自建推理服务对外暴露的 endpoint。官方文档对新接入方不太友好,config.toml 里几个字段藏得深,Key 放错地方、路径写错、模型标识对不上,都是日常。更烦的是 401、404 这种错误,表面看都是"请求没成功",实际根因差了十万八千里。
这篇文章干脆把我自己配置和排障的全过程拆开讲。config.toml 的几种写法、Key 到底放在哪、401 和 404 分别怎么定位,都会覆盖。配置片段直接抄,思路讲清楚,看完你至少能少踩一半的坑。
1. 配置结构:config.toml 和 Key 为什么要分开
先搞清楚 Codex CLI 的配置目录长什么样。以默认安装为例,它把配置放在用户主目录下的.codex目录里,核心文件就两个:一个config.toml,一个auth.json。很多人的第一反应是"这不都是配置文件吗,写一起不就行了",但这两者的职责完全不同,拆开是有明确理由的。
config.toml存的是不敏感的运行参数:模型名、用哪个 provider、base_url 指向哪、超时、日志级别等。这类信息即使被同步到工作电脑、提交进团队仓库,也不会有泄露风险。而auth.json存的是真正的凭证,也就是 API Key 或登录态 token,属于高度敏感信息,权限默认是只读给当前用户,也不应该被任何版本管理工具捕获。
~/.codex/ ├── config.toml # 模型、provider、base_url 等非敏感配置 ├── auth.json # 密钥/登录态,权限通常为 600 └── logs/ # 运行时日志这个"配置和密钥分离"的设计几乎是现代 CLI 工具的共识。好处有两个:第一,密钥不会误入仓库;第二,你可以放心地把 config.toml 分享给同事,对方只需要自己配 Key。如果你刚接触 Codex CLI,建议先养成一个习惯——凡是sk-开头的东西,默认只放进环境变量或 auth.json,永远不要手滑写进 config.toml。
另一个容易忽略的点是,Codex CLI 支持通过环境变量CODEX_HOME指定配置目录,也支持在启动命令里显式传--config。我见过有人改了半天 config.toml 没生效,结果发现当前 shell 里CODEX_HOME指向了另一个目录,改错了地方。先执行echo $CODEX_HOME和codex --version确认你正在操作的是哪一份配置,这是排障的第一步。
| 文件/变量 | 用途 | 是否可提交仓库 |
|---|---|---|
config.toml | provider、模型、路径、运行参数 | 可以 |
auth.json | API Key、登录 token | 不行 |
CODEX_HOME | 覆盖默认配置目录 | 按需 |
| 环境变量 | 注入 Key 或开关调试 | 看情况 |
2. 实操:在 config.toml 里接入第三方 API 的配置写法
接第三方 API,本质上就是告诉 Codex CLI:"别去默认的官方地址了,去我给的地址,用我给的模型标识。"所以核心动作是改两个东西:一是model_provider,二是model。
2.1 最简配置:一个自定义 provider
假设你拿到的第三方服务信息是这样的:
- 接口地址:
https://api.example.com/v1 - 模型标识:
my-model-name - 给你的 Key:
sk-xxxxx
那 config.toml 可以这样写:
model = "my-model-name" model_provider = "my-provider" [model_providers.my-provider] name = "My Provider" base_url = "https://api.example.com/v1" env_key = "MY_API_KEY" wire_api = "chat" requires_openai_auth = false字段逐个说明。
base_url是请求的根地址,Codex CLI 会根据wire_api自动拼后面的路径。如果base_url写到/v1,wire_api设成chat,那实际请求路径就是https://api.example.com/v1/chat/completions。所以你要确认服务商给你的 endpoint 已经包含了 API 版本前缀,别自己多写或少写一层。
env_key是本次接入最关键的一个字段,它告诉 Codex CLI:去哪个环境变量里取 API Key。上面例子中,你需要先在 shell 里导出这个变量,然后 Codex 会自动把它放到请求的 Authorization 头里。Key 放在哪一节详细讲,这里先记住:config.toml里填的只是环境变量的名字,不是 Key 本身。
wire_api有两种取值:chat和responses。绝大多数第三方兼容服务只实现了/chat/completions,所以设chat最稳。如果你的服务商明确说支持新的 Responses API,才设responses。设错了最常见的报错就是 404,因为请求路径压根不对。
requires_openai_auth是一个很容易踩的开关。它的意思是"这个 provider 是否需要 Codex 的官方登录态"。接第三方接口时,务必显式写成false。如果不写,某些版本会沿用默认行为,往请求里附加一个官方登录 token,第三方服务一验签就直接 401。
2.2 用 profile 管理多个第三方服务
如果你手里不止一个第三方 API,甚至同时还要保留官方服务的配置,别来回改 config.toml,用 profile 就够了。Codex CLI 的配置支持profiles区块,每一个 profile 是一套完整的"哪家服务、哪个模型、哪个 Key"的组合。
[profiles.work] model = "my-model-name" model_provider = "work-provider" [profiles.work.model_providers.work-provider] name = "Work Provider" base_url = "https://gateway.example.com/v1" env_key = "WORK_API_KEY" wire_api = "chat" requires_openai_auth = false [profiles.personal] model = "another-model-name" model_provider = "personal-provider" [profiles.personal.model_providers.personal-provider] name = "Personal Provider" base_url = "https://api.personal-example.com/v1" env_key = "PERSONAL_API_KEY" wire_api = "chat" requires_openai_auth = false用的时候加一个--profile参数:
codex --profile work codex --profile personal这样切换不同接口服务只需要改启动参数,不用动文件,也不怕改错回不去。我个人的建议是:只要接了两个以上 provider,就立刻切到 profile 方案。否则你迟早会因为来回改配置搞混某次请求到底走了哪家服务。
2.3 base_url 的常见坑:多一层少一层
关于base_url,我见过太多因为路径层级出问题的案例。这里把规则讲透:
- 如果服务商的文档写的是
https://api.example.com/v1/chat/completions,那你的base_url要填https://api.example.com/v1,因为wire_api = "chat"会自动拼上/chat/completions。 - 如果服务商的文档写的是
https://api.example.com(根地址),但实际接口在根路径下直接就是/chat/completions,没有/v1这一层,那base_url可能得填https://api.example.com,但这种情况极少见,大多数兼容服务都有/v1前缀。 - 如果服务商提供的是"部署式"接口,路径里还可能带上具体的部署名,比如
https://api.example.com/v1/deployments/my-model/chat/completions,这种路径很难直接套进 Codex 的base_url自动拼路径逻辑里。能不用这种特殊路径就不用,非用不可的话,优先考虑在网关侧把路径转发成标准/v1/chat/completions形态。
一句话总结:base_url写到"API 版本根路径"这一层,不要带具体的接口名,也不要落下/v1。
3. Key 放哪里:三种方式与优先级
刚才配置里出现了env_key,这只是告诉 Codex 去哪里找 Key,Key 本身怎么填进去,还有讲究。我按推荐的优先级把方法列出来。
3.1 环境变量:最推荐的方式
既然 config.toml 里指定了env_key = "MY_API_KEY",那你只需要在启动 Codex 之前把这个环境变量导出就行:
export MY_API_KEY="sk-xxxxx" codex --profile work这里有个很容易忽略的细节:环境变量是跟着当前 shell 生命周期的。你今天在终端里 export 了,关掉终端窗口再打开,变量就没了。所以至少要把它写进 shell 的启动文件里,比如:
# 追加到 ~/.zshrc 或 ~/.bashrc export MY_API_KEY="sk-xxxxx"更讲究一点的做法是用 direnv 之类的工具按目录注入环境变量,避免把 Key 写死在全局启动文件里。不过对大多数人来说,写进~/.zshrc已经够用了,只要保证你启动 Codex 的终端环境里能echo $MY_API_KEY看到值就行。
调试的时候可以用一行代码快速确认环境变量是否生效:
[ -n "$MY_API_KEY" ] && echo "Key is set" || echo "Key is missing"如果显示 missing,后面的 401 排障就是白费劲,问题根本不在服务商,而是 Codex 压根没拿到 Key。
3.2 auth.json:官方服务的默认位置
auth.json通常是执行官方登录流程后自动生成的,里面存的是登录态 token 或官方 API Key。如果你只是接第三方 API,严格来说不需要依赖这个文件,用环境变量注入就够干净了。
但有一个例外场景:某些版本允许直接在这个文件里按配置项写入 Key。我建议你不要手动猜字段名去改它,因为不同版本结构不一样,改坏了 Codex 可能直接起不来。正确做法是让程序自己写:优先跑官方的登录/设置命令,让它把凭证塞进 auth.json。如果第三方服务商提供了专门针对 Codex CLI 的接入说明,以服务商给的命令为准。
无论哪种方式,只要 auth.json 里出现了明文 token,就记住两个底线:一是不要把它提交进任何仓库,二是文件权限最好保持 600(只有当前用户可读写)。检查权限的命令:
ls -l ~/.codex/auth.json如果不是-rw-------,建议立刻chmod 600 ~/.codex/auth.json。
3.3 Key 的优先级和"从哪来"的判断
当环境变量和 auth.json 同时存在时,Codex CLI 到底用哪一个,不同版本可能有差异。但排障时可以按最朴素的原则来推:先看 config.toml 里那个 provider 有没有指定env_key。指定了,说明它优先从环境变量取;没指定,才会走 auth.json 或其他默认鉴权路径。
我自己遇到过一次很诡异的 401:config.toml 里明明写了 A 服务的env_key,环境变量也导出了,但请求还是打到官方服务上去了。后来发现是 shell 启动文件里老早 export 过一个OPENAI_API_KEY,而 Codex 在某个环节把OPENAI_API_KEY当成了兜底 Key。所以排障第一步永远是"确认实际生效的 Key 是哪一份",别想当然。
还有一点:不要把 Key 直接当成参数写进 shell 历史里。比如codex --api-key sk-xxxxx这种命令,执行完 Key 会留在.zsh_history文件里,哪天文件被同步走,等于明文泄露。用环境变量的方式就能避开这个问题。
4. 401/404 排障:从现象到根因的完整排查路径
HTTP 401 和 404 是所有接入第三方 API 时遇到最多的错误。很多人一看到这两个状态码就懵,其实它们的含义完全不同,定位路径也不一样。
4.1 401 Unauthorized:先确认 Key 到底有没有被正确送到
401 的意思是"服务端知道你是谁,但不想理你",说白了就是鉴权失败。触发原因列表如下:
| 常见根因 | 排查方法 |
|---|---|
环境变量没设置或名字和env_key不一致 | echo $MY_API_KEY确认有值 |
| config.toml 里误填了 Key 本身而不是变量名 | 检查env_key是否是FOO_BAR格式 |
| Key 已经过期或被服务商吊销 | 到服务商控制台查看 Key 状态 |
requires_openai_auth未设置成 false,导致附加了错误鉴权 | 显式写false,重启后重试 |
| 请求实际发到了别的服务,而不是你配置的服务 | 看日志确认实际请求的 Host 路径 |
| 某些服务商要求 Header 里必须附带额外字段 | 参考服务商文档补充配置 |
最有效的定位方法不是反复改配置猜,而是直接用 curl 模拟一次请求,绕开 Codex 单独验证第三方接口本身是否正常:
curl -i https://api.example.com/v1/chat/completions \ -H "Authorization: Bearer $MY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "my-model-name", "messages": [{"role": "user", "content": "hi"}] }'重点看三件事:
- 返回状态码是不是 200。如果 curl 都返回 401,说明配置层的可能性不大,问题在 Key 本身或服务商侧。
- 返回体里的错误信息。很多服务商会附带
message字段,告诉你"key expired"还是"invalid key",比状态码有用得多。 - curl 到
api.example.com都通,但 Codex 报错,那说明 Codex 请求的地址、Header、路径或模型名与实际请求不一致,回到配置上找差异。
有个技巧是给 curl 加-v参数看握手细节,重点看Authorization头是不是你预期的那串 Key 前缀。很多第三方的响应头里还会带WWW-Authenticate提示,也能辅助判断。
4.2 404 Not Found:路径或模型标识不对
404 的意思是"你的请求地址不存在"。接第三方 API 时,404 基本都不是网络问题,而是"请求拼出来的路径在服务商那边没有对应资源"。常见根因如下:
| 常见根因 | 排查方法 |
|---|---|
base_url缺少/v1前缀 | 检查服务商文档里的 endpoint 层级 |
base_url多写了/chat/completions | 只保留到版本根路径 |
wire_api设错 | 第三方只支持 chat 却设成 responses,路径会变成/responses,必 404 |
| 模型标识不对 | 服务商叫gpt-xx,你配置里写成了别家名字 |
| 服务商要求模型名带部署前缀 | 按服务商提供的精确标识填写,别凭记忆写 |
我在实际项目里遇到的 404,十次里有七次是wire_api设错或模型标识多空格、大小写不一致。有一位朋友排查了整整一个下午,最后发现模型名末尾多了一个不可见空格。所以碰到 404,先复制服务商文档里的模型标识,再用引号包起来看长度,别手敲。
还有一个隐蔽场景:某些服务商把兼容接口放在/openai/v1这种前缀下,而不是标准的/v1。这时base_url要写完整,比如https://api.example.com/openai/v1。这种信息通常只在文档最不起眼的地方写,遇到 404 先翻 endpoint 说明。
4.3 让排障更快的两个动作:开日志和抓实际请求
如果 curl 能通,但 Codex 还是 404,那就必须看清楚 Codex 实际发出的请求长什么样。Codex CLI 支持调试模式,建议启动时加上--debug或设置环境变量打开详细日志,具体以你安装版本的帮助输出为准。日志会记录它使用的 base_url、模型名、鉴权 header 是否存在,以及收到的响应头。
codex --profile work --debug看到实际请求路径后,再和 curl 通的那个路径逐字符比对。只要路径、模型名、鉴权三者完全一致,不可能一个通一个 404。不一致的话,差异点就是根因。
4.4 401/404 速查定位表
| 状态码 | 核心信号 | 优先检查方向 | 次要检查方向 |
|---|---|---|---|
| 401 | 鉴权失败 | env_key 是否设置、Key 是否过期 | requires_openai_auth 是否显式 false |
| 404 | 路径/资源不存在 | base_url 层级、wire_api 类型 | 模型标识、部署前缀、空格 |
| 429 | 限流 | Key 配额或请求频率 | 服务商套餐 |
| 500 | 服务端异常 | 服务商服务状态 | 请求体格式是否异常 |
这张表我建议存一下。大多数第三方 API 接入问题,最后都可以归到"路径对不对、Key 对不对、模型名对不对"这三件事上,没有任何玄学。
5. 我的配置习惯与几条实用经验
写到最后,分享几条长期折腾下来的个人习惯,不一定写进文档,但确实能帮你省时间。
第一,永远先 curl 通再动 Codex。拿到任何第三方服务的 endpoint 和 Key,第一件事是拿 curl 发一条最简单的消息。如果 curl 都过不了,那不管 config.toml 怎么改都是白搭。这个动作能把"服务商问题"和"Codex 配置问题"快速切分开。
第二,把 config.toml 当成会被反复改的草稿,养成备份习惯。我有一次升级 Codex CLI 之后,配置格式被新版本不再兼容,直接启动报错。当时幸好留了一份旧配置备份,对照着才把新格式填对。所以每次大改配置前复制一份到一个固定的备份目录,花不了几秒,关键时刻很值。
第三,环境变量名要有区分度。不要所有第三方服务都用API_KEY这种名字,否则你同时接两三个服务时,环境变量互相污染,查都查不出来。我习惯用服务名_API_KEY这种风格,比如WORK_API_KEY、PERSONAL_API_KEY。config.toml 里的env_key和 shell 里 export 的名字必须严格一致,大小写也不能差。
第四,关注版本变化。Codex CLI 的配置 schema 在快速迭代,wire_api、requires_openai_auth这些字段都是后来才出现的。网上很多旧教程里少了这些字段,抄过来不一定能跑。最稳的做法是每次安装新版本后,先跑一下codex --help或codex config --help看看当前版本的配置项,再对照填。
最后再补一个具体的实用技巧:如果你在局域网环境里访问第三方 API,注意确认 DNS 解析和网络连通性。有时候 401 或 404 是因为请求压根没到达目标服务,中途被拦截了,返回了一个形似错误页的响应。这种时候curl -v的握手阶段信息能帮你先判断网络链路是否畅通,再回来查配置。我自己排障时,永远是先 curl,再看日志,最后才改 config.toml。这个顺序基本不会错。