
1. 为什么本地配置总是“差一点点”三份配置文件的边界1.1 Codex CLI 本地定制的真实需求最近一段时间我一直在折腾 Codex CLI 的本地化配置起因很简单官方默认的模型和供应商链路在某些场景下不够灵活团队里有人想换成内部网关有人想接入第三方兼容模型服务还有人希望在项目目录里塞一份规则文件让 Agent 按照团队规范来干活。绕了一圈之后发现问题基本都集中在三样东西上TOML 配置文件、AGENTS.md 规则文件以及它们之间的优先级关系。如果你已经用 Codex 跑过几个真实任务大概率会遇到类似的情况改完config.toml里的模型参数重启后不生效在项目根目录写了 AGENTS.mdAgent 却像没看到一样用第三方切换工具换了供应商自己手工加的自定义字段直接被抹掉。这些现象背后不是玄学而是 Codex 的配置体系里有一套固定的加载顺序和覆盖规则只不过官方文档没有把“文件之间打架”这件事讲透。这篇文章不打算复述官方 README而是把我在本地反复试出来的配置边界、优先级规则和踩坑链路完整记录下来。适合正在用 Codex CLI 做本地开发、想接入自定义模型或自定义 Agent 行为的读者也适合那些被“配置不生效”折磨到崩溃的人。1.2 TOML、AGENTS.md、第三方切换工具各管哪一段先给三样东西定位这样后面不会乱config.toml管的是“用哪个模型、连哪个端点、带什么密钥、开什么功能”。它是 Codex CLI 的主体配置相当于汽车的发动机参数。AGENTS.md 管的是“Agent 在这个目录里应该遵守什么规则、优先看什么文件、按什么流程干活”。它更接近驾驶手册不控制引擎但影响驾驶行为。第三方切换工具热词里提到的 CC Switch 一类本质上是帮你快速改写config.toml的工具不是独立配置层。它最省事也最容易产生“覆盖”问题。很多人的认知误区在于以为 AGENTS.md 可以替代 TOML 完成模型切换或者以为切换工具只是临时叠加配置。实际上模型和端点的最终裁决权永远在 TOML 及其对应的环境变量手里AGENTS.md 只是行为层。你可以在 AGENTS.md 里写“请使用 DeepSeek”但如果 TOML 里没配 DeepSeek 的供应商和端点Agent 根本不知道去哪找这个模型。换句话说本地自定义 Agent 的完整拼图是先用 TOML 把模型链路打通再用 AGENTS.md 把行为规则固化最后才轮到切换工具做快速变更。下面按这个顺序逐一拆解。2. TOML 配置拆解模型、供应商与行为参数的完整字段地图2.1 顶层参数model、model_provider 与行为开关Codex CLI 的config.toml路径在不同平台不太一样macOS 和 Linux 通常在~/.codex/config.tomlWindows 在%USERPROFILE%\.codex\config.toml。如果你设置了环境变量CODEX_HOME那配置目录就跟着它走。一份最基础的 TOML 长这样model gpt-5 model_provider openai organization_id verbose 0 approval_policy on-request sandbox_mode workspace-write autoupdate false这里有几个顶层字段新手容易忽略model_provider必须和model配合使用。Codex 判断请求往哪发不是只靠model而是看model_provider是否指向一个已定义的供应商。如果你只改了model没改model_provider请求还是走默认供应商。approval_policy控制命令执行权限never表示所有命令都要手动确认on-request表示 Agent 请求时再确认on-failure表示失败时才确认。本地调试阶段我建议先用on-request避免 Agent 在你不注意的时候跑危险命令。sandbox_mode控制文件系统访问范围。workspace-write允许写当前工作区read-only只读danger-full-access不限制。自定义模型链路调试阶段先用workspace-write配合 Git 分支保护自己别一上来就danger-full-access。还有一个容易被忽略的字段是verbose。默认是 0调成 1 可以看到 HTTP 请求的基本流向调成 2 能看到请求头、请求体和更细的调试日志。排查“配置不生效”时verbose 1是最快的定位手段后面专门讲。2.2 自定义供应商model_providers 的 base_url、wire_api 与密钥Codex 允许你在 TOML 里声明自己的供应商这是接入非官方模型服务的关键。标准写法是通过[model_providers.xxx]段来声明一个自定义供应商[model_providers.deepseek] name DeepSeek Compatibility base_url https://api.deepseek.com env_key DEEPSEEK_API_KEY wire_api chat解释一下字段base_url供应商 API 的入口地址。注意有些服务商要求加上/v1路径有些不用具体以服务商文档为准。这个字段决定 Codex 把请求发到哪也是后面各种报错的根源之一。env_key环境变量名Codex 会从环境变量里读取该供应商的密钥。比如env_key DEEPSEEK_API_KEY那你就得在 shell 里export DEEPSEEK_API_KEYsk-xxx。这一步经常有人漏导致请求发出后返回 401。wire_api协议类型只有两个选项responses和chat。responses对应 OpenAI 最新的 Responses API 风格chat对应传统的 Chat Completions 风格。绝大多数第三方兼容服务商只实现了chat如果你不写这个字段Codex 默认走responses很可能直接报错或得不到预期输出。当你在model_providers里定义好供应商后顶层配置要这样引用model deepseek-chat model_provider deepseek注意model这个名字不是随便填的它必须是你所接服务商实际支持的模型 ID。比如 DeepSeek 官方目前有deepseek-chat和deepseek-reasoner你就填这两个之一而不是填 OpenAI 的模型名。还有一个进阶字段requires_openai_auth默认是 true。如果你接入的是 OpenAI 的兼容端点但不想走 OpenAI 官方认证可以显式设为 false。但这个字段用得不多只有在自建网关时才会遇到。2.3 一份可直接跑的 DeepSeek 兼容配置示例把上面的知识点拼起来一份能直接跑通的 DeepSeek 接入配置如下# ~/.codex/config.toml model deepseek-chat model_provider deepseek verbose 1 approval_policy on-request sandbox_mode workspace-write [model_providers.deepseek] name DeepSeek Compatibility base_url https://api.deepseek.com env_key DEEPSEEK_API_KEY wire_api chat然后在 shell 里设置环境变量export DEEPSEEK_API_KEYsk-你的密钥跑codex之前先确认两个点一是环境变量已加载二是 TOML 里没有残留的旧供应商段。很多人配置了半天不生效就是旧配置里还有一行model_provider openai在顶层压着。有一点必须提醒wire_api chat的模型在 Codex 里使用工具调用和长上下文时表现可能不如官方 models。因为 Codex 的工具调用逻辑是围绕 Responses API 设计的转到 Chat Completions 后第三方服务商对tools参数的支持程度参差不齐。实测下来DeepSeek 这类头部服务商的工具调用还算稳定但一些小型兼容端点会在工具调用时返回 400 或截断输出。遇到这类情况优先检查服务商是否完整支持tools、tool_choice参数而不是急着换模型。3. AGENTS.md决定 Agent 行为的是规则文件而不是提示词3.1 AGENTS.md 的递归加载机制与生效范围如果你用过 Claude Code会知道CLAUDE.md的作用。Codex 这边对应的是 AGENTS.md机制类似但有一些细节差异。AGENTS.md 的加载不是只读一份根目录文件而是分层的。Codex 会从当前工作目录向上查找或者按会话涉及的文件路径就近加载。具体来说~/.codex/AGENTS.md全局规则所有项目都会读到适合放通用代码风格、通用安全红线。项目根目录的AGENTS.md项目级规则适合放仓库结构、构建命令、测试规范。子目录下的AGENTS.md目录级规则只对进入该目录及子孙目录的任务生效适合放模块专属约定。如果你在项目根目录执行 Codex它默认会同时加载全局和项目级 AGENTS.md。如果你在packages/utils/子目录下执行则会加载全局、项目根目录和该子目录三层的规则文件后者的优先级更高。这个特性叫做“渐进式披露”意思是 Agent 只有在涉及某个目录时才会看到该目录的规则避免一开始就把所有项目的规矩全塞进上下文。这种设计有一个实际的好处大型 monorepo 里每个子包可以拥有独立的 AGENTS.md互不干扰。比如packages/api/AGENTS.md里写“所有数据库查询必须走 repository 层”而packages/frontend/AGENTS.md里写“组件必须通过 eslint 检查”。Codex 在对应目录下干活时会自动切换规则视角。3.2 项目级规则怎么写才不会被 Agent 无视写 AGENTS.md 最容易犯的错是“写成一篇散文”。Agent 读取规则文件后会把内容作为系统上下文的一部分但上下文窗口有限规则文件太长太大真正关键的指令会被稀释。我自己的实践是控制在 50 行以内只写“必须做”和“禁止做”两类事项。用绝对指令式开头例如“不要修改 lock 文件”“在提交前必须运行 pnpm test”比“建议在提交前测试”有效得多。把项目路径、命令这类事实写清楚。例如“构建产物在dist/目录入口文件是src/main.ts”这能显著减少 Agent 瞎猜的几率。用 引用把大文档拉进来。AGENTS.md 支持path/to/file语法把某个具体文件的内容嵌入为参考上下文比如docs/api-conventions.md。这比把整段规范复制进 AGENTS.md 更干净。另外AGENTS.md 里写命令时要考虑环境差异。比如你写“运行source .env加载密钥”Agent 可能会尝试执行但这个文件在项目里不存在。更靠谱的做法是写“从环境变量DATABASE_URL读取数据库连接串”并确保 TOML 对应的环境变量已设置。3.3 版本升级后 AGENTS.md 还能不能用的疑虑网络热词里有“当前还能使用的项目 agents.md”这其实反映了一个普遍焦虑Codex 版本迭代快老规则文件会不会失效我的经验是AGENTS.md 的加载机制在主流版本里一直是稳定能力但存在几个版本差异点旧版 CLI 可能忽略子目录的 AGENTS.md只读根目录的。如果你把规则放在子目录且发现不生效先升级 CLI 再看。新版本对 AGENTS.md 的 Markdown 结构支持更严格比如要求##标题级别清晰。文件里如果全是乱糟糟的文本某些版本可能直接跳过。自 0.2x 版本之后Codex 支持在 AGENTS.md 里用环境变量占位比如${WORKSPACE_ROOT}但旧版本不会解析这种写法。所以我建议把 AGENTS.md 当作“跟随项目走”的配置而不是“跟随 Codex 版本走”的配置。换新机器、升级 CLI 后先跑一个简单任务验证规则是否被读取再开始大批量使用。验证方法很简单在 AGENTS.md 里写一句“回答任何问题前先输出 PROJECT_RULES_LOADED”然后问 Codex 一个简单问题看它有没有输出这句标记。没有输出就说明规则文件没被加载。4. 优先级陷阱cc switch 会覆盖 TOML这句话到底指什么4.1 从加载顺序看 TOML 与第三方切换工具的关系先说结论CC Switch 这类工具不是“叠加配置”而是“覆写配置”。很多人在网上搜到“cc switch 会覆盖 toml”但不知道覆盖的具体机制。我实际跟踪了一下过程是这样的CC Switch 会把你在界面上选的供应商信息模型、端点、密钥环境变量写入~/.codex/config.toml或者切换它自己管理的配置模版。这个写入动作是整体替换不是 merge。也就是说如果你之前在 TOML 里手工加过[model_providers.custom]、改过sandbox_mode、加过approval_policy切换后这些字段可能被保留也可能被重置取决于切换工具生成的模板内容。更隐蔽的是CC Switch 的本地网关模式会修改base_url指向本地端口同时可能引入自己的中转逻辑。此时 Codex 请求不是直接发往服务商而是先到本地网关再由网关转发。所以“cc switch 会覆盖 toml”这句话的正确理解是切换工具的写入逻辑以它自己的模板为准它不会智能保留你的自定义字段。如果你在 TOML 里手工配置了深度定制的供应商参数再用 CC Switch 切换一下大概率要重新回来检查一遍。防止被覆盖的办法有几种重要配置先备份cp ~/.codex/config.toml ~/.codex/config.toml.bak切换完再 diff。把不希望在切换中丢失的全局行为字段比如sandbox_mode、approval_policy尽量写在环境变量或AGENTS.md里而不是只依赖 TOML。使用切换工具后主动检查config.toml内容不要默认它一定是对的。4.2 判断当前哪些配置在生效的两种手段你可能会问我怎么知道此刻 Codex 到底用的是哪一份配置有两个可靠手段。第一种codex --verbose。启动后它会打印当前加载的配置路径和请求目标。如果日志里显示config file: /home/xxx/.codex/config.toml那就是主配置如果显示请求发往http://127.0.0.1:xxxx说明本地网关在介入。这个信息能直接判断你的流量到底走了哪条链路。第二种主动修改验证。在 TOML 里临时改一个不影响功能的字段比如把verbose 0改成verbose 1保存后重新启动 Codex如果日志输出变详细了说明你编辑的文件就是实际生效的文件。如果没变化说明当前生效的是另一份配置——你需要检查CODEX_HOME环境变量、是否有系统级配置覆盖了用户级。还有一个很容易被忽略的点环境变量会覆盖 TOML 中的同名参数。如果你在 shell 里设置了CODEX_MODEL或OPENAI_MODEL这类环境变量它的优先级高于 TOML 的同名字段。我在调试时经常遇到一种情况明明 TOML 里写了model deepseek-chat但日志里请求的还是gpt-5最后发现是 shell 启动脚本里残留了export OPENAI_MODEL...。5. 接入第三方兼容模型时最常踩的三个坑5.1 wire_api 不匹配导致的 /responses 报错热词里有一条很典型的报错cc switch local proxy failed while handling codex endpoint /responses。我遇到过的场景是CC Switch 把 Codex 指向了本地网关本地网关只实现了 Chat Completions 接口但 Codex 默认使用 Responses API 发送请求。当 Codex 试图 POST/responses时网关无法理解这个请求直接返回失败。排查链路是这样的先确认请求路径。用codex --verbose查看日志看请求到底是打到/responses还是/chat/completions。如果日志显示/responses而网关只支持/chat/completions这就是 wire_api 不一致的问题。回到 TOML在model_providers段补上wire_api chat。这一步能把 Codex 的请求从 Responses API 切换到 Chat Completions API。重启 Codex 后再看日志确认请求路径已经变成/chat/completions。有些第三方服务商会给自己做一个统一入口不管你是/responses还是/chat/completions都转发到后端大模型这时候不用改 wire_api。但绝大多数兼容服务商并没有完整实现 Responses API所以接入前先做一次“接口探测”最稳妥用 curl 直接打/responses和/chat/completions看看哪个返回正常。# 先用 chat completions 探测 curl -X POST https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $DEEPSEEK_API_KEY \ -d {model:deepseek-chat,messages:[{role:user,content:hi}]} # 再试 responses 接口 curl -X POST https://api.deepseek.com/responses \ -H Content-Type: application/json \ -H Authorization: Bearer $DEEPSEEK_API_KEY \ -d {model:deepseek-chat,input:hi}如果第二个请求返回 404 或 400说明该服务商不支持 Responses APIwire_api必须用chat。5.2 模型名不被上游支持的处理gpt-5.6-sol 报错案例另一个常见报错是the gpt-5.6-sol model is not supported when using codex with a ...这个报错我第一眼看到时也愣了一下。它通常出现在切换工具或自定义配置里填了一个上游根本不存在的模型名比如gpt-5.6-sol这种看起来像内部代号的名字。报错本身并不复杂但暴露了一个常见操作误区很多人以为 Codex 的model字段可以随便填比如为了绕过某些限制填一个“听起来更强”的模型名。实际上Codex 的model字段必须对应当前供应商实际支持的模型 ID。OpenAI 官方端点不认识gpt-5.6-sol自然返回 not supported。正确处理方式是先确认你选的供应商支持哪些模型去服务商文档查真实模型 ID。用 OpenAI 官方端点时确认 Codex 版本接的是 GPT-5 系列还是其他系列不同版本 CLI 对模型的支持范围不同。如果你是从某个现成配置或教程里复制来的模型名务必查一下那个教程的时代背景。模型 ID 更新很快半年前的正确写法现在可能已经废弃。我个人排查这类报错的习惯是把报错信息原样复制到供应商的接口文档里搜如果文档里查不到gpt-5.6-sol这个 ID那说明模型名本身就是问题根源如果文档查得到再看是不是wire_api或base_url配错。5.3 本地代理失败与端点健康检查回来说那条cc switch local proxy failed while handling codex endpoint /responses的报错。除了 wire_api 不匹配还有一种更基础的原因本地网关进程根本没有正常监听端口。Codex 把请求发到127.0.0.1:某个端口但那个端口后面是空的请求自然失败。这时排查顺序是看进程是否存在。Linux/macOS 用lsof -nP -iTCP:端口 -sTCP:LISTENWindows 用netstat -ano | findstr 端口确认端口上有进程在监听。直接对本地端点做健康检查。假设配置的base_url是http://127.0.0.1:8080用 curl 打http://127.0.0.1:8080/v1/models或它提供的健康检查路径看是否有正常响应。检查本地网关日志。绝大多数本地网关节点的日志里会直接写明转发失败的原因比如“上游连接超时”“证书校验失败”“上游 API key 缺失”。这一步往往能省下大量猜配置的时间。确认证书问题。如果本地网关使用自签名证书而 Codex 或系统的 CA 证书库里没有安装该证书TLS 握手会失败。这种情况可以临时把base_url的https改成http仅限本地调试环境或者把自签名证书加入信任列表。踩了这些坑之后我才意识到本地自定义 Agent 的稳定性很多时候不是模型本身决定的而是端点链路通不通、协议匹配不匹配这些“基础设施问题”决定的。先把这些排查干净再谈 Prompt 优化和规则配置才有效。6. 调试与兜底把 verbose、日志和环境变量用起来6.1 verbose 输出应该怎么看verbose是调试 Codex 配置最直接的开关但很多初级用户不知道应该看日志里的哪几行。我的建议是重点关注三类信息配置文件路径日志开头部分会打印config file: ...确认你改的文件就是它。模型与供应商解析日志里会显示model: xxx和provider: xxx看这两行是否和你 TOML 里的一致。请求 URL日志中间会有POST https://...字样看到实际请求的目标地址。如果地址不是预期的服务商端点说明base_url或切换工具在捣鬼。当你开了verbose 2日志会包含调试级别的请求体输出。这时注意不要把包含密钥的请求头发到公开工单或群里先自己做脱敏处理再分享排查信息。有时候verbose打开后日志量太大反而不容易找到关键信息。我的做法是分两步先用verbose 1看基本信息定位到大致环节后再决定要不要升到 2。6.2 配置文件被覆盖后的恢复习惯CC Switch 或手动误改导致config.toml被覆盖是本地配置最常见的“事故”。我现在的习惯是在~/.codex/下保留一份config.toml.template内容是经过验证的基础配置。被覆盖时直接cp config.toml.template config.toml恢复。每次调整配置前用 Git 管理~/.codex目录。git init之后每次修改前先 commit切工具或实验配置时随时可以回滚。把密钥放在环境变量文件里例如~/.codex/.env不要让密钥写进 TOML 或模板。CC Switch 切换配置时会覆写 TOML但不会动你的环境变量文件这样密钥不至于丢失。还有一个小技巧当你不确定某个切换工具会不会破坏配置时先用cp把当前配置复制到临时文件把切换后生成的 TOML 和临时文件做diff。一眼就能看出它到底改了什么diff ~/.codex/config.toml ~/.codex/config.toml.bak这个命令救过我很多次尤其是在切换工具版本升级后行为变化的情况。最后再分享一个实操经验本地自定义 Agent 的配置不是一次到位的而是一个持续演进的过程。先把 TOML 的模型链路跑通再叠 AGENTS.md 的行为规则最后才考虑用切换工具做多供应商快速切换。顺序反了你会在“配置被覆盖”和“规则不生效”之间反复横跳浪费大量时间。保持配置文件的版本管理习惯用 verbose 日志做定位手段第三方模型的接入稳定率会高很多。