
春节刚过那阵子我把 codex 装在一台开发机上想着先跑两天看看手感。结果第一天就有点难受它每次动文件之前都要停下来问我一句模型也总是走默认那个最快但不太靠谱的档位遇到要改多个文件的重构任务回答经常半途而废。我当时的第一反应是“这工具不行”后来才发现问题根本不在工具本身而在我从没认真打开过~/.codex/config.toml。绝大多数人第一次用 codex都是敲一句codex就开干配置文件一直是安装时生成的那份空白模板。等用久了才发现模型、审批、沙箱、MCP、项目级指令这些东西全要靠 config.toml 来捏。这篇就聊 codex 的 config.toml 配置。我不会把文档里的字段表抄一遍那种东西查文档更快我更想讲清楚每个配置项背后的设计意图、它在实际任务里怎么影响模型的行为以及哪些字段一旦配错会出现“看起来没报错但就是不对劲”的诡异现象。适合刚装好 codex 想把它调到顺手的中级用户也适合要给团队统一下发配置、但又不想把权限放得太开的负责人。看完至少能做到三件事知道配置从哪来、知道每个关键字段在管什么、知道出问题先从哪里查。1. config.toml 到底在管什么从一次“配置没生效”说起我第一次真正意识到 config.toml 的分量是因为一次很典型的误判。当时我想让 codex 在做批量重命名的时候不要每次都弹确认于是照着记忆在配置文件里写了一行approval_policy never保存重启结果它还是弹。我以为是被缓存了清了目录重装还是弹。折腾了小半个小时才反应过来我改的是全局配置但当前目录下有一个项目级的配置把它覆盖掉了。这个坑后面会单独讲这里先把它抛出来是因为它恰好说明了 config.toml 的定位它不是一份“建议”而是 codex 启动时逐层读取、逐层覆盖的运行时声明。用一句话概括config.toml 是 codex 的声明式行为配置文件。它用 TOML 格式写成决定了四类事情。第一类是模型相关包括默认走哪个模型、推理强度开多大、上下文窗口按多少算。第二类是执行安全也就是它能不能自己动文件、能不能执行命令、执行命令之前要不要问你、在什么目录范围内活动。第三类是扩展能力比如挂载哪些 MCP 服务器、能不能读项目里的说明文档。第四类是交互体验包括界面的一些开关、通知方式、文件打开器之类。这四类事情如果全靠命令行参数传会非常痛苦。想象一下每次启动都要敲一长串--model、--approval、--sandbox输错一个字符就是另一种行为而且没法在团队里复现。配置文件的价值就在这里把“我希望这个工具怎么工作”固化成一个可以被版本管理、可以被 diff、可以被复制粘贴的东西。你换一台机器把配置拷过去行为就是一致的。这也是为什么我后来坚持把配置分成“全局个人偏好”和“项目内约定”两层来维护——个人习惯不该污染项目项目约束也不该被个人习惯覆盖。还有一个容易被忽略的点config.toml 里的很多字段是有“安全默认值”的。比如审批策略默认是偏保守的沙箱默认是限制写范围的。这些默认值在单人玩票时很舒服但一旦你开始让 codex 做真正的工程任务比如批量重构、跑测试、修 lint保守默认会把你拖进无休止的确认弹窗里。反过来如果你为了省事一刀切把权限开到最大又会在某次误操作里付出代价。所以配置这件事的本质不是“抄一份能跑的”而是理解每一项在权衡什么然后按你的任务类型分档。1.1 TOML 语法里几个高频写错的地方TOML 看起来简单但它有几条规则特别容易让从 JSON、YAML 过来的人翻车而 codex 对配置解析失败的处理往往不够显眼写错了可能只是“部分配置没生效”你甚至不会收到明确报错。第一个是表table的位置。TOML 里[section]之后的所有键都属于这个表直到遇到下一个表头。很多人会写成这样先写一堆顶层键中间插一个[profiles.fast]然后又想写顶层键结果那些键被归到了 profile 里面。正确做法是把所有顶层键写在最前面所有表头放在后面。第二个是布尔值必须小写。true和false是全小写写成True、TRUE都会解析失败。这个在从 Python 习惯迁移过来时尤其容易犯。第三个是字符串里的反斜杠。Windows 路径如果直接写C:\Users\name反斜杠会被当成转义符。要么用正斜杠要么用单引号包起来要么老老实实双写。第四个是数组和表的混用。像args [--foo, bar]这种字符串数组很直观但如果某个字段期待的是“表数组”你写成普通数组就会静默失效。我的建议很朴素改完配置之后别急着跑任务先用最小代价验证一下。比如启动 codex 让它报一下当前模型或者跑一个只读的小任务看看审批行为对不对。花三十秒验证比事后花半小时排查划算得多。1.2 配置文件真正解决的三个痛点把视角拉高一点config.toml 之所以值得花时间研究是因为它同时解决了三个不同层面的问题而这三个问题各自对应一类配置字段。一致性问题。同一个仓库今天你跑、明天同事跑、下周换台机器跑如果配置散落在各自的命令行历史里行为一定不一致。把所有约定写进项目里的配置文件代码审查的时候连“这个工具怎么跑”一起审了出问题好定位。安全边界问题。codex 这类工具最大的风险不是它笨而是它太听话。你让它“清理一下临时文件”它可能真的去删。审批策略和沙箱配置就是那道闸门写得越明确你越敢放手让它干重活。我在生产仓库里从来不会把沙箱关掉最多是把工作区写权限打开网络访问单独控制。能力扩展问题。默认的 codex 只能读写当前工作区、执行本地命令。接上 MCP 服务器之后它能查文档、连数据库、调内部接口。这些扩展全靠配置文件声明而且不同项目需要的扩展完全不同正好适合放在项目级配置里。理解了这三层你就不会把 config.toml 当成一个“调参文件”而会把它当成项目的运行说明书。后面所有字段的取舍基本都能回到这三层里找答案。2. 配置文件的位置、加载顺序与覆盖规则搞不清加载顺序是 config.toml 相关问题的第一大来源没有之一。我见过太多人改了配置没反应最后发现是改错了文件或者被更高优先级的文件压住了。这一节把这件事讲透。codex 的配置大致有三个来源层级。最底层是全局配置放在用户主目录下的.codex/config.toml管的是“我这台机器上所有项目通用的偏好”比如默认模型、默认审批策略、界面开关。中间层是项目级配置放在项目根目录下的.codex/config.toml管的是“这个仓库特有的约定”比如沙箱要不要开网络、挂哪些 MCP、项目说明文档多大。最上层是profile也就是在全局配置里定义好的若干套预设启动时用参数指定套用哪一套。覆盖规则是“后者覆盖前者”而且是按键覆盖不是整份替换。这一点很重要项目级配置里只需要写你想改的那几个键其余键继续沿用全局的值。很多人以为项目配置是一份完整配置于是复制粘贴了一整份过来结果全局那边后来改了模型项目里还停在老模型上两边越走越远。提示覆盖是按键粒度不是文件粒度。项目配置里只写差异项能显著降低后续维护成本。除了这三层还有命令行参数它是最高优先级的临时覆盖只对本次运行生效。适合做一次性实验比如临时换个模型试试效果别把实验性参数写进配置文件。2.1 验证配置到底有没有生效光看文件内容没用得让工具自己告诉你它读到了什么。我常用的验证手法有三种。第一种是观察行为。审批策略是不是真的放开了跑一个会写文件的小任务就知道沙箱有没有限制写范围让它往工作区外面写一个临时文件试试看是被拒绝还是成功。行为是最诚实的证据比任何日志都直接。第二种是故意制造冲突。在全局里设一个值在项目里设另一个值看最终表现是哪个。这个方法有点笨但极其可靠尤其是在你不确定某个字段到底属于哪一层的时候。第三种是逐项注释法。当你怀疑某个字段有问题把它注释掉再跑一遍。如果行为变了说明这个字段确实在起作用如果没变那要么它没生效要么它被覆盖了。二分排查比盯着文件瞎猜快得多。我建议养成一个习惯每次改完配置先跑一个“冒烟任务”比如让它读一个文件然后总结观察它是否按预期弹确认、是否能正常写。三十秒的事能挡掉后面百分之八十的困惑。2.2 项目级配置该不该进版本库这个问题我被问过很多次。我的答案是该进但要分层。项目级配置里那些跟“这个仓库怎么工作”相关的部分比如沙箱范围、MCP 挂载、说明文档路径应该提交进版本库让所有协作者行为一致。而那些跟个人机器相关的部分比如本地某个工具的绝对路径、个人偏好的界面设置应该留在全局配置里不要进仓库。实际操作上我会在项目里放一份“团队配置”只包含安全和协作相关的键然后在 README 里写清楚个人覆盖该怎么做。这样既保证了底线一致又不会把每个人的习惯硬塞给所有人。最怕的是有人把带个人路径的配置提交上去别人拉下来一跑路径不存在工具直接罢工或者静默降级。还有一个小细节项目级配置的路径是相对于项目根目录的如果你在子目录里启动它向上找根目录的行为不一定符合你的直觉。稳妥做法是始终从项目根目录启动或者在配置里把相对路径写成明确的相对根路径。3. 核心配置项逐条拆解模型、审批、沙箱这一节是全文的重头戏。我把最影响实际体验的字段分成三组来讲模型组决定“它有多聪明”审批组决定“它有多大胆”沙箱组决定“它能在哪活动”。这三组互相牵制任何一组单独调都会失衡。3.1 模型与推理强度别只看名字模型选择是最直观的一项model字段写什么启动就用什么。但真正影响输出质量的往往不是模型名而是推理强度这个伴随参数。推理强度高模型在动手之前会想得更久适合复杂的重构、跨文件的逻辑推理推理强度低响应快适合格式化、改注释、写样板代码这类机械活。我的经验是按任务类型分档而不是按心情分档。日常写业务代码用中等强度跑批量修改和代码审查用高强度写文档和注释用低强度。因为高强度虽然质量好但延迟明显你如果拿它去改一个变量名会觉得这工具怎么这么磨叽。还有一个容易踩的坑是上下文窗口。有些模型的实际可用窗口和标称不一样配置文件里如果显式写了窗口大小写得比实际小会浪费能力写得比实际大会导致中途截断表现为“它好像忘了我前面说的话”。如果你不确定就别显式写让工具用默认值。我吃过一次亏为了让长文件分析更完整手动把窗口调大结果模型在中间就开始丢上下文排查了半天才想到是这个字段。模型名写错的情况也要提一句。写错之后不一定会报错有些实现会静默回退到默认模型你以为是新模型在跑其实还是老模型。表现就是“怎么感觉没变化”。所以换模型之后一定要用一两个有区分度的任务验证一下比如问它一个只有新模型才知道的细节或者观察输出风格差异。3.2 审批策略三档的实际差别审批策略是 config.toml 里最值得花时间理解的一个字段因为它直接决定了你和工具之间的协作节奏。它大致分三档我用“过马路”来类比。第一档是每一步都问。工具想执行任何有副作用的操作都先停下来等你点头。最安全但最烦。适合刚上手、或者在核心仓库里做敏感操作时用。这一档的价值在于让你看清楚它到底想干什么用几天之后你会对它的行为模式有直觉。第二档是失败时才问。工具先自己尝试遇到权限问题、命令失败、需要越界操作时才回来问你。这是我觉得最舒服的一档适合日常开发。它给了工具一定的自主空间又不至于在真正卡住时闷头乱试。第三档是从不问。全自动风险自负。我只会把它用在完全隔离的沙箱环境、或者一次性脚本任务里。在真实仓库上用这一档本质上是在赌它不会误解你的意图而这个赌注通常不值。配置的时候要注意审批策略和沙箱是两套独立的机制。审批管的是“要不要问你”沙箱管的是“物理上能不能做到”。有些人把审批放开到从不问以为就畅通无阻了结果被沙箱拦住表现为“它说要做但什么也没发生”。这两者要一起看。3.3 沙箱模式读写范围与网络访问沙箱是安全边界它的核心是回答两个问题能写哪些目录能不能联网。写范围通常有三档完全只读、只写工作区、全盘可写。只读适合代码审查和问答工具只能看不能动你完全不用担心它误删东西。只写工作区是日常默认它能改项目里的文件但动不了系统目录和你的家目录。全盘可写基本只在容器或一次性环境里用本机上我从来不这么配。工作区写模式下面还有细项比较关键的是网络访问。默认通常是关的因为联网意味着它可以下载依赖、访问外部接口也意味着数据可能外流。如果你的任务需要装包、拉依赖就得单独把这个开关打开。我的做法是默认关需要时开任务结束再关回去。把它写在项目配置里而不是全局可以避免在别的项目里意外联网。还有一个细节是临时目录和缓存的处理。有些构建工具会往系统临时目录写东西沙箱如果限制得死构建会莫名其妙失败报错还很不直观。遇到这种情况与其怀疑工具不如先看看是不是写权限被限制了把必要的临时路径加进允许列表。场景审批策略沙箱写范围网络代码审查、问答每步都问或失败才问只读关日常编码失败才问仅工作区关装依赖、跑构建失败才问仅工作区按需开一次性批处理从不问仅工作区关隔离容器内实验从不问全盘按需这张表不是教条但它能帮你快速判断自己当前配置是不是“过松”或“过紧”。过松的表现是你开始担心它乱来过紧的表现是你一直在点确认。3.4 环境变量与 shell 策略codex 执行命令时会继承环境。这里有个隐患你的 shell 环境里可能有各种令牌、密钥、私有仓库地址。如果不对环境做过滤模型在排查问题时可能把这些内容读进上下文甚至写进日志。配置里通常有控制环境传递的开关可以指定“只透传哪些变量”或者“屏蔽哪些模式”。我的做法是白名单透传只允许构建必需的那几个变量过去其余一律不带。这样即使模型执行了env之类的命令也看不到敏感内容。另外shell 的启动方式也值得注意。有些实现默认用非登录 shell这意味着你.bashrc里配的路径和别名不会生效命令找不到就成了常见报错。遇到“明明终端里能跑工具里说 command not found”先怀疑这一点而不是怀疑工具坏了。4. profiles把“日常编码”和“危险操作”分开放如果你只有一个配置文件、一套参数那你迟早会陷入两难要么配置太保守干活时一直被打断要么太激进某次误操作让你心跳加速。profiles 就是解这个矛盾的工具。它允许你在同一份配置里定义多套命名预设启动时按名字切换。我把它理解为“工作模式”。就像相机有光圈优先、快门优先、手动模式不是哪个更高级而是对应不同拍摄场景。codex 的 profile 也一样写业务代码是一套做代码审查是一套跑批量迁移又是另一套。4.1 profile 的语法结构与常见写法错误profile 在 TOML 里是嵌套表基本形态是在配置里定义若干个命名分组每个分组下面挂具体的键。切换的时候在启动命令后面带上名字即可。这里有两个高频错误。第一是把 profile 当成完整配置在里面重复写全局已经有的键导致以后改全局不生效。正确做法是 profile 里只写跟默认不同的部分靠覆盖机制继承。第二是表头嵌套写错比如把键写在了 profile 表外面结果它变成了全局设置看起来“profile 没起作用”其实是根本没进 profile。还有一个隐蔽的坑profile 名里带空格或者特殊字符。虽然 TOML 允许带引号的键名但你在命令行里传的时候容易出问题。建议一律用小写字母加连字符别玩花活。4.2 我常用的三套 profile 设计第一套叫“日常”。审批用失败才问沙箱只写工作区网络关闭推理强度中等。这套是我一天里用得最多的写功能、改 bug、补测试都靠它。特点是几乎不打断我但也不会让我担心。第二套叫“审查”。审批用每步都问沙箱只读网络关闭推理强度高。这套用来做代码审查和方案评估。只读是关键因为它保证工具不会“顺手帮你改一下”你拿到的是纯粹的分析意见。推理强度高是因为审查需要理解上下文关系快而浅的回答没什么价值。第三套叫“批处理”。审批用从不问沙箱只写工作区网络按需推理强度低。这套专门用来跑机械性的批量任务比如统一改导入路径、批量补注释、格式化。特点是快而且因为任务足够机械全自动的风险可控。这三套覆盖了我九成以上的使用场景。剩下那一成比如临时要连一个内部服务我会临时用命令行参数覆盖不专门建 profile避免 profile 越堆越多最后自己都记不清。4.3 profile 与项目配置打架怎么办这是个真实会发生的冲突。项目配置里写了沙箱不开网络你的“批处理”profile 里写了开网络谁赢结论是具体程度更高的赢也就是 profile 里的显式设置通常会覆盖项目里的同名键。但不同实现的具体规则可能有差异所以我采取的策略是不让它们重叠项目配置只管安全底线相关的键profile 只管效率相关的键两边尽量不碰同一个字段。如果实在要重叠我的做法是在 profile 里显式写清楚“这一项我要覆盖项目设置”并且在项目 README 里注明。这样至少出问题时能快速定位是哪一层在起作用。最怕的是两边都写了、都没注释最后行为诡异排查起来像解谜。5. MCP 服务器与项目级指令的接入config.toml 的另一大块能力是扩展。默认的 codex 只能看当前工作区和执行本地命令接上 MCP 之后它可以访问外部信息源。这一节讲怎么接、接的时候注意什么。5.2 项目说明文档的加载控制codex 有一个很重要的机制它会读取项目里的说明文档把项目约定、编码规范、常用命令这些信息带进上下文。这个机制的效果非常明显——同样一句“加个接口”有项目说明和没项目说明产出的代码风格能差出一个档次。配置里可以控制这个文档的最大读取字节数。设得太小文档被截断模型只看到前半部分后面的约定全丢设得太大每次请求都塞一堆内容浪费上下文窗口。我的经验值是把项目说明控制在一页到两页之间只写“必须知道的”比如目录结构、命名约定、测试怎么跑、不许做什么。那些可以现查的细节不要写进去。这里有个容易被忽略的点说明文档是每次请求都会带的不是只在第一次带。所以它的体积直接影响成本和延迟。我见过有人把整个架构文档贴进去结果工具变慢、回答变浅还以为是模型不行。5.3 让模型“守规矩”的写法项目说明文档的写法有很大讲究。写得好模型像老员工写得差模型像刚入职的实习生。我总结了几条。写禁令比写建议有效。“不要修改生成的代码目录”“不要动数据库迁移文件”这类明确禁令模型遵守得很好。而“尽量保持代码风格一致”这种模糊建议基本等于没说。给可执行的命令。与其说“记得跑测试”不如直接写“改完之后执行 npm test失败就先修测试”。模型对具体命令的响应远好于抽象要求。列出禁区清单。把不能碰的目录、不能改的配置、不能删的文件列成清单。这比任何安全配置都直接因为它是模型自己能读懂的约束。别写太长。超过两页模型对后半部分的注意力会下降。真有必要写长的拆成多个文件按需引用。6. 我踩过的几个典型坑与排查链路这一节讲讲真实的翻车现场。我把排查过程完整写出来因为比结论更有用的是思路。6.1 配置写错却毫无提示最坑的一种情况是 TOML 语法错误但工具照常启动只是部分配置失效。表现是“我明明配了怎么没生效”。我的排查链路是这样的先确认文件路径对不对用绝对路径打开看一眼再把可疑段落整段注释掉看行为有没有变化然后逐行恢复定位到具体哪一行。十有八九是布尔值大小写、字符串转义、表头位置这三类问题。后来我养成了一个习惯改配置之后先跑一个只读任务观察审批行为是否符合预期。如果它该问的时候不问或者不该问的时候乱问说明审批相关配置没生效立刻回头查。6.2 沙箱开着却说没有写权限有一次我让 codex 改一个文件它回复说没有权限写入。我第一反应是沙箱限制太严准备去放开。结果检查发现我要改的那个文件在项目目录之外是一个被软链进来的共享目录。沙箱的工作区写范围是按真实路径算的软链指向的外部目录不在范围内自然写不了。这个坑的教训是先确认目标路径到底在哪再调沙箱。用readlink -f之类的命令把真实路径打出来比盲目放宽权限安全得多。放宽权限是最后手段不是第一手段。6.3 模型换了但感觉没变前面提过一次这里展开说。换模型之后如果感觉输出风格、能力没变化大概率是模型名写错导致回退或者 profile 覆盖了全局设置。排查顺序是先看当前生效的 profile 是哪个再看那个 profile 里有没有写 model最后看模型名拼写。三层都确认没问题再用一个有区分度的任务验证。6.4 团队配置互相覆盖多人协作时最头疼的是配置冲突。有人为了自己方便在项目配置里加了个人路径有人把审批策略改成了从不问并提交还有人加了一个只有自己机器上才有的 MCP 服务器。结果别人拉下来要么启动报错要么行为诡异。我的解决办法是把项目配置拆成两份一份是“基线配置”只含安全底线进版本库一份是“本地覆盖”写个人偏好加进忽略列表。然后在 README 里写清楚哪份该改、哪份不该动。这个约定建立起来之后配置相关的扯皮少了非常多。7. 一套可以直接抄的配置模板与调优思路讲完原理和坑给点能直接用的东西。下面这份模板是我目前的主力配置你可以按需删改。注意所有路径和模型名都要换成你自己环境里的别照抄。7.1 最小可用模板先给一个精简版适合刚上手、不想被一堆字段吓到的人。核心就三件事模型、审批、沙箱。# 全局配置 ~/.codex/config.toml model 你的默认模型名 approval_policy on-failure sandbox_mode workspace-write [sandbox_workspace_write] network_access false这三行半基本能覆盖日常编码。审批失败才问沙箱只写工作区网络默认关。跑几天你会对它的行为有直觉再往下加东西。7.2 带 profile 的完整模板等你开始觉得“有些任务想更激进一点”就可以上 profile 了。# 顶层默认值 model 你的默认模型名 approval_policy on-failure sandbox_mode workspace-write model_reasoning_effort medium [sandbox_workspace_write] network_access false # 审查模式只读 高强度推理 [profiles.review] approval_policy untrusted sandbox_mode read-only model_reasoning_effort high # 批处理模式全自动 低强度推理 [profiles.batch] approval_policy never sandbox_mode workspace-write model_reasoning_effort low # 装依赖模式需要联网 [profiles.install] approval_policy on-failure sandbox_mode workspace-write [profiles.install.sandbox_workspace_write] network_access true项目级配置则尽量薄只写差异# 项目内 .codex/config.toml [sandbox_workspace_write] network_access false # 如果需要接内部服务在这里挂 MCP注意项目级配置里不要写模型名。模型属于个人偏好写进项目会让所有人被迫用同一个模型换模型还得改仓库。7.3 调优顺序从安全到效率最后说说我建议的调优路径。很多人一上来就想把体验拉满结果配了一堆自己都不理解的字段出问题完全没法定位。我推荐按这个顺序来。第一步先把安全底线定下来。沙箱写范围、网络开关、环境变量白名单这三样先配好并且用只读任务验证一遍。这一步做完你才敢放手让它干活。第二步调审批节奏。从每步都问开始用两三天观察它都在什么时候想干什么。等你对它的行为模式有数了再放宽到失败才问。这个顺序不能反反过来你会失去对它的直觉。第三步按任务分 profile。等你发现自己频繁地临时改参数就说明该建 profile 了。把最常用的两三种场景固化下来其余的继续用命令行临时覆盖。第四步最后才碰模型和推理强度。这两项对体验影响大但也最容易让人误判。先用默认值跑一段时间建立基线再针对性调整。别一上来就追求“最强模型 最高强度”那样你会分不清是模型强还是任务简单。我在实际配置上最大的体会是配置文件是给自己看的不是给别人看的。网上流传的那些“最强配置”大部分是针对特定任务的特定取舍直接抄过来往往水土不服。真正好用的配置是你自己一行一行改出来、并且每行都能说出为什么的配置。我现在的配置文件里还有几行注释写着“这条是某次误删之后加的”每次看到都提醒我当初为什么这么设。这种带着记忆的配置比任何模板都可靠。