博客

哪个 Codex CLI 设置会赢:七层配置优先级

Codex 通过七个层级解析配置,从高到低,项目层上还有一道信任闸门。这里是顺序、每一层的用途,以及如何调试一个怎么都改不动的设置。

哪个 Codex CLI 设置会赢:七层配置优先级

Codex CLI 里“不生效”的设置,几乎总是层级问题,而不是语法问题。同一个键可以写在好几个地方,而最终生效的值由文档规定的顺序决定,而不是由你最后编辑了哪个文件决定。官方配置文档把这个顺序定义为七层,优先级从高到低,外加一道安全闸门:项目未被信任时,它会静默地关掉整个一层。这三条信息足以解释大多数配置上的意外:顺序、信任闸门,以及在改动任何东西之前先定位“获胜层”的习惯。

下面的顺序和引文来自官方的配置基础页面,字段级的背景在 Codex 手册里。构建时请对照当前页面核实。

按文档顺序排列的七个层级

Codex 明确列出了这个解析顺序,优先级从高到低。这里按原样复述,不合并任何层级:

  1. CLI 标志和--config配置覆盖项。该次调用在命令行上传入的值。
  2. 项目配置文件 .codex/config.toml,从项目根目录一直到你当前的工作目录,离得最近的文件获胜,仅限受信任的项目。
  3. Profile 文件,通过--profile profile-name对应的命令行标志选择,~/.codex/profile-name.config.toml以独立文件的形式存放在 Codex 配置目录下。
  4. 用户配置,位于~/.codex/config.toml用户主目录下的 config.toml。
  5. 云端托管的config.toml默认值,在已登录的 workspace 下会被下发。
  6. 系统配置,如果存在的话:/etc/codex/config.tomlUnix 上系统级路径下的 config.toml。
  7. 内置默认值。

那份清单里有两个细节很容易被略过。项目层不是一个文件:Codex 从仓库根目录一路走到你正在工作的目录,最近的那个文件获胜,这就是 monorepo 能让某个子目录拥有不同设置而不必复制一切的方式。而 profile 层位于用户配置之上,而不是之下,这意味着 profile 可以覆盖你个人配置里已经设好的键。

一个有用的心智模型是一叠覆盖项,而不是一组文件:每一层只贡献它实际设置的键,而第一个提到某个键的层决定该键的值。对某个键保持沉默的层对它没有任何贡献。

项目层上的信任闸门

最能解释“我的项目配置为什么被忽略”这类反馈的那句话,就在同一页面的开头:

出于安全考虑,Codex 只在你信任该项目时才加载项目的.codex/配置层。

优先级表在括号里重复了这个约束:仅限受信任的项目。页面也说明了未受信任项目的后果:Codex 会跳过项目范围内的.codex/配置层,包括项目本地的配置、hooks 和 rules;而用户配置和系统配置仍然会加载,包括用户级和全局的 hooks 与 rules。

这是安全边界,不是 bug。你克隆下来的一个仓库,不应该仅仅因为里面包含一个目录就能改变你的 agent 的行为,所以项目层在你明确同意之前是关着的。如果.codex/config.toml config.toml 里的某个设置看起来毫无效果,先检查项目的信任状态,优先于任何 TOML 调试。

策略不是一个你能覆盖的层

还有第二种机制看起来像优先级,其实不是:托管要求。文档把分工说得很直白:

要求约束行为;配置值提供默认值。

配置层决定哪个默认值适用;要求决定什么根本被允许。在受管机器上,组织可以通过requirements.toml强制执行约束,比如禁止某种审批策略或 sandbox 模式。去找一个优先级更高的配置层赢不过一条要求,因为要求并不在同一条梯子上竞争。当某个值在受管设备上拒绝改变时,要问的是有没有一条要求按住了它,而不是你还有哪个配置文件没改。

一个设置该放在哪里

顺序清楚之后,实际问题就变成放在哪。文档自己的指引很简洁:利用优先级把共享默认值放在config.toml用户配置里,并让 profile 文件只专注于那些不同的取值。

| 情形 | 该用的层 |
| --- | --- |
| 团队共享默认值 | 用户配置,~/.codex/config.toml用户主目录下的 config.toml |
| 仓库特定行为 | 仓库里的 config.toml.codex/config.toml,同时记得信任要求 |
| 环境或角色差异 | 一个 profile 文件加上--profile profile-name对应命令行标志 |
| 一次性或脚本化调用 | 命令行上的--config覆盖项 |
| 全机器范围、无策略的默认值 |/etc/codex/config.toml Unix 上的系统配置 |

键本身的语义,包括 provider 字段及其默认值,在自定义 provider 指南里。还有一条相关边界值得理清:凭证来自哪里是它自己的话题,不是一个配置层,存储模式在凭证指南里。

CI 里的取舍

持续集成让分层变得可见,因为 CI runner 恰恰是那种不能假定项目层存在的环境。一次 checkout 的信任状态不是流水线该依赖的东西,而那台机器上的用户配置可能根本不是你写的。由此有两个习惯。对每个环境不同的值,一个 profile--profile 保留一小组具名配置;对每次调用不同的值,--config命令行覆盖项把它们显式传进去。两种情况里,重要的值都在流水线里写明,而不是从某个没人审计过的层继承。在流水线里跑编码 agent 的更多模式在 CI 指南里。

如果问题里的设置是受管网络上的证书包或代理配置,那它属于企业环境的话题,而不是层级挑选;企业网络指南讲了这些。

调试一个怎么都改不动的值

从上往下过这些层,停在第一个提供该值的层:

  1. 信任状态。项目到底被信任了吗?如果没有,到此为止:项目层根本不在场。
  2. 命令行。这次调用是否带了--config配置覆盖项,或者某个设置该键的标志?
  3. 项目层。对于受信任的项目,是否存在一个比你编辑的那个更靠近工作目录的.codex/config.toml config.toml?
  4. Profile。是否有某个--profile profile 处于激活状态,而那个 profile 文件设置了同一个键?
  5. 用户配置。这个值是否在~/.codex/config.toml用户 config.toml 里,而且可能比你记得的更旧?
  6. 云端托管的默认值。是否有已登录的 workspace 在下发某个托管的config.toml默认值?
  7. 系统配置和内置默认值。最后,检查/etc/codex/config.toml Unix 上的系统配置,再看文档列出的内置默认值。

这个顺序也是验证假设最快的方式:用--config覆盖项为当前这次调用设一次值。如果行为变了,就说明键是对的,问题在放置位置;如果没变,问题在键本身或要求层。把你改过的层和获胜的层记下来;这一对是一次配置决策唯一持久的记录,也能让下一个人省掉同一趟排查。

CLI 和 IDE 共享同一套层

文档指出 CLI 和 IDE 扩展共享同一套配置层。从任一界面编辑用户配置都会同时改变两者的行为,这很方便,直到有人编辑了一个项目文件,以为它只会影响 IDE。文档里打开文件的路径很短:在 IDE 扩展里,点击右上角的齿轮图标,然后进入 Codex Settings > Open config.toml。在 ChatGPT 桌面应用里,同一个文件通过 Settings > Configuration > Open config.toml 打开,手册如此说明。同一套层,从任一工具看过去,都以相同的顺序解析。

检查清单

  1. 在调试任何项目层的值之前,先确认项目的信任状态。
  2. 用一次性的--config覆盖项复现生效值。
  3. 把共享默认值放在用户配置里,而不是散落在各个项目文件中。
  4. 把仓库特定的值放进.codex/config.toml config.toml,并记住信任闸门。
  5. 让 profile 只专注于那些不同的取值,用--profile对应标志来选择。
  6. 在 CI 里显式写明关键值,而不是依赖项目层。
  7. 当受管机器拒绝某项改动时,先查要求,再查配置层。
  8. 从上到下走一遍这七层,并把团队的放置约定一次性写下来。

七层,一道信任闸门,还有一个站在梯子之外的策略机制。大多数配置之谜都能收拢成一个问题:这里哪一层有权说话,又是哪一层在说话。本站记录的兼容服务的端点级细节在它自己的文档里,而第一次把这些层放进上下文的四工具配置在共享密钥指南里。一如既往,请对照构建时官方页面的当前内容核实顺序和引文。