把 Codex CLI 指向另一个供应商,只需对 ~/.codex/config.toml 做一点小改动:一行选定供应商,一个表定义它,三个字段通常就能补全。但有一个硬约束决定该供应商到底能不能用,而它从配置文件的形态上并不明显:wire 协议设置只接受一个值,responses,因此你指向 Codex 的端点必须说 Responses API 的形态。只实现了 Chat Completions 的供应商不是配置错了,而是形态不兼容——那是另一个问题,有另一种修法。
以下内容都遵循官方Codex 手册,它是这里所引示例、字段名和默认值的来源。在上线配置之前,请对照当前页面核实。
最小配置,按官方发布的样子
手册的示例一次定义了三个供应商。归结为最基本的模式,配置是这样:
model = "gpt-6.1-sol"
model_provider = "proxy"
[model_providers.proxy]
name = "OpenAI using LLM proxy"
base_url = "http://proxy.example.com"
env_key = "OPENAI_API_KEY"最上面两行选定模型和供应商 ID。随后那个表定义该 ID:显示名称、Codex 调用的 base URL,以及提供密钥的环境变量。同一个官方示例中的两处变化,对理解这个模型很重要:
- 本地 Ollama 条目使用
base_url = "http://localhost:11434/v1",而完全没有env_key,因为没有密钥需要提供。 - Mistral 条目使用
base_url = "https://api.mistral.ai/v1",配合env_key = "MISTRAL_API_KEY"。
注意这些 base URL:Ollama 示例带一个 /v1 后缀,代理示例则没有。这不是需要取平均值的矛盾;它反映的是每个端点如何挂载自己的 API。故障排查一节会详细讲。
字段表,逐字段说明
手册逐个记录了自定义供应商的字段。核心几行重述如下,默认值标注为官方默认值:
| Field | Type | Meaning |
| --- | --- | --- |
| name | string | 自定义供应商的显示名称 |
| base_url | string | 模型供应商的 API base URL |
| env_key | string | 提供供应商 API key 的环境变量 |
| wire_api | responses | 供应商使用的协议;responses 是唯一受支持的值,省略时也是默认值 |
| query_params | map | 追加到供应商请求的额外查询参数 |
| http_headers | map | 添加到供应商请求的静态 HTTP 头 |
| env_http_headers | map | 存在时从环境变量填充的 HTTP 头 |
| request_max_retries | number | 对供应商发起 HTTP 请求的重试次数(官方默认值:4) |
| stream_max_retries | number | SSE 流式中断的重试次数(官方默认值:5) |
| stream_idle_timeout_ms | number | SSE 流的空闲超时,单位为毫秒(官方默认值:300000) |
| supports_standalone_web_search | boolean | 声明支持兼容的独立 web search 端点(官方默认值:false) |
在写第一行配置之前,有两行值得强调。wire_api 将在下一节讲述,因为它决定兼容性。而 stream_idle_timeout_ms 的单位是毫秒,所以默认值是三十万毫秒;照手册的写法写,并且把换算做一次,而不是粘贴一个看起来像秒的数字。
硬约束:wire_api 只接受 responses
这就是全文存在所要传达的那句话,引自手册的字段表:
responses 是唯一受支持的值,省略时也是默认值。没有 chat 这样的值可以切换。如果你的端点只实现 Chat Completions 形态,正确的结论不是“调整配置”,而是“这个端点不说 Codex 要求的形态”。修法在端点一侧:跑一个暴露 Responses 形态的转换层,或者选一个原生实现它的供应商。两种形态之间的差异,以及如何测试某个端点真正实现的是哪一种,见API 形态指南;更完整的兼容性清单见集成清单。
这也是为什么那么多“兼容”端点与其他工具配合良好,却在 Codex 上失败:与 Chat Completions 兼容,和与 Responses 形态兼容是两个不同的主张,而配置文件无法弥合这个差距。
保留 ID:不要把供应商命名为 openai
自定义供应商 ID 不能复用内置的。手册列出三个保留 ID:openai、ollama 和 lmstudio。如果你需要为内置 OpenAI 供应商指定不同的 base URL,手册指向一个专门的设置 openai_base_url,而不是用覆盖表,因为内置供应商 ID 不能被覆盖。至于其他情况,选一个不同的 ID,例如 proxy,或者像官方示例对本地 Ollama 实例那样用 local_ollama。
这是一条小规则,却有一个大失败模式:配置里名为 openai 的供应商不会做出名字暗示的事,由此产生的困惑通常表现为一次莫名其妙的“端点错了”的调试过程。
把密钥和请求头配置好,而不把秘密写进文件
配置文件描述密钥,但不包含它。env_key 指明提供 API key 的那个环境变量,Codex 在请求时读取它。这带来一个值得围绕它设计的实际后果:轮换密钥意味着改环境变量并重启,而不是编辑一个可能已经被提交到某处的配置文件。Codex 相关的凭据存储与登录模式见凭据指南,而把密钥挡在会被复制、记录或截图的文件之外的更广泛纪律,见密钥卫生指南。
请求头也沿同一条界线划分。http_headers 存放可以放心写下来的静态头值。env_http_headers 存放值来自环境变量的头名称,用于那些实质上是伪装凭据的请求头。如果某个头携带 token,它属于第二个 map,而不是第一个。
按顺序排查
当自定义供应商“连不上”时,有用的做法是把这句话变成可判定的检查项。按它们实际是真正原因的频率排序:
- 端点说的是 Responses 形态吗?这是压倒性最常见的成因,而且它在配置里看不出来。在归咎 Codex 之前,先用一个 Responses 形态的请求直接测试该端点。
- base URL 路径对吗?针对各自的目标遵循官方示例,并确认你的端点实际使用的挂载路径。Ollama 示例带有
/v1;代理示例没有。不要不加核对就把某个示例的后缀搬到另一个示例上。 - 环境变量在启动 Codex 的那个 shell 里导出了吗?在另一个终端里设置的变量,或者写在未重启的服务定义里的变量,对这个进程来说并不存在。
- 供应商 ID 与保留名称冲突了吗?
openai、ollama和lmstudio都已被占用。 - 超时和重试适合你的网络吗?官方默认值是 4 次请求重试、5 次流重试,以及 300000 毫秒的流空闲超时。如果你的网络慢或不稳定,要有意识地调整,而不是一味堆叠重试。
- 路上有企业 TLS 代理吗?证书信任问题会产生看起来像配置错误的连接失败;证书与代理配置见企业网络指南。
一个边界说明:supports_standalone_web_search
表里最后一个字段很容易被过度解读。手册说该设置用于声明支持兼容的独立 web search 端点,默认值为 false,并指出独立搜索仍在开发中、默认关闭;仅有供应商兼容并不能启用它。把该能力设为 true 也不能启用搜索:供应商必须支持兼容端点,所选模型和运行时也必须支持独立搜索,同时配置好的 web search 模式和管理限制仍然生效。
实践中:不要把该标志当作让 web search 生效的开关。把它当作关于你端点的一项诚实声明,除非你已验证端点一侧,否则保持 false。
清单
- 首先确认端点说的是 Responses 形态。
- 选一个不属于保留名称的供应商 ID。
- 为供应商写
name、base_url和env_key。 - 在启动 Codex 的 shell 里导出密钥变量。
- 把静态头放进
http_headers,把携带秘密的头放进env_http_headers。 - 对照你这类端点对应的官方示例,确认 base URL 路径。
- 通过 Codex 发一个真实请求,如果有错误,按形态、路径、凭据的顺序去解读它。
- 记住官方重试默认值(4、5 和 300000 毫秒),这样偏离它们才是有意为之;并通过改环境变量来轮换密钥。
自定义供应商就是一张附带严格形态要求的小表。形态对了,其余都是记账;形态错了,再怎么调重试也没用。本网站所记录 API 的平台侧细节在其 文档 中,而这里引用的每个字段名和默认值都应在构建时对照当时的官方手册重新核对。