Codex CLI:那些没人写过的设置

2026年10月8日 · 作者 Taylor Yang

Codex CLI 配置和参数的一线笔记:严格配置校验、AGENTS.md 静默截断、权限档案、钩子信任,以及子代理的两个烧钱坑。

关于 Codex 的建议基本都长一个样:写一份 AGENTS.md,挑个模型,设一下沙箱模式,收工。这只是地板,不是天花板。再往下压着一层几乎没人提的设置和参数,其中大部分是静默失败——不报错、不警告,只是某个配置项悄悄地不起作用。

本文所有内容都在 2026 年 10 月 8 日对照官方文档核过,每条关于本地行为的说法都在 codex-cli 0.146.0 上实测过。少数说法来自社区反馈,正文里会标出来。

这篇文章底下那层模式:Codex 的问题很少是模型的问题。它们多数是外壳(harness)的问题——某个东西被丢掉了,却没人告诉你。

如果你刚开始接触终端 agent,建议先把界面本身摸熟。Claude Code 终端快捷键指南讲的是通用的肌肉记忆,那部分在这里能直接迁移过来。

你的配置可能一直被忽略了

这一段如果只读一句,读这句。

Codex 会静默忽略它不认识的配置字段。键名拼错了、用的是后来某次发布改掉的名字、或者从老版本里搬过来一个字段,都不会有任何提示。设置不起作用,而你以为它起了作用。

这种情况有个专门的参数。--strict-config 让 Codex 遇到当前版本不认识的字段时直接报错退出。多数教程从没解释它真正的价值:这是回答「我的配置为什么没生效」唯一一个靠得住的办法。

我在这台机器上测过。本地 ~/.codex/config.toml 里有:

[skills]
max_context_tokens = 2000

带上 --strict-config:

Error loading config.toml:
/Users/yep/.codex/config.toml:496:1: unknown configuration field `skills.max_context_tokens`
    |
496 | max_context_tokens = 2000
    | ^^^^^^^^^^^^^^^^^^

不带的话,同一份配置一点输出都没有。文件被读进来了,字段被丢掉了,会话照常启动。

有意思的是,这个键确实存在于当前的官方配置参考里(“Token budget for the available-skills catalog. Defaults to 2% of the model’s context window”,显式取值上限是 10,000 tokens)。只是 0.146.0 还不知道它。这里没谁把配置写错,这就是 Codex 往前跑的时候必然出现的版本漂移——而 --strict-config 就是让你看见它的那个东西。

所以实用习惯很短:每次 CLI 升级完,跑一条严格配置命令,把它报出来的东西清掉。

codex exec --strict-config --skip-git-repo-check "reply OK"

两个已知的坑:

  1. --strict-config 会拒掉 Codex Desktop 写入的条目。 桌面端会往你的 ~/.codex/config.toml 里注入 [projects."<path>"] 块,CLI 的严格校验不认这些(issue #33181, 0.144.1)。如果 CLI 和 Desktop 共用一个配置目录,开这个开关前先看看那些块是谁写的。

  2. 文档写了的字段,严格校验照样可能不过。 文档里说远程 stdio MCP 服务器要用 experimental_environment = "remote",但稳定版 CLI 严格加载时会拒掉,你得改用 environment_id(issue #51481)。文档和运行时一旦漂开,严格模式就是专门用来暴露这个落差的。

AGENTS.md 才是重头,而且它有三种静默失败

AGENTS.md 是 Codex 的项目指令文件,相当于 Claude Code 的 CLAUDE.md。多数人只拿它写「用 pnpm」「测试放这里」这类事。它的杠杆比这个大。

坑一:超过 32 KiB 的部分直接丢,不给警告

这是我做这次调研学到的最有用的东西。有人发现 Codex 在无视他们加进去的核心规则,去翻源码,找到这么一段:

/// Maximum number of bytes of the documentation that will be embedded. Larger
/// files are *silently truncated* to this size so we do not take up too much of
/// the context window.
pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB

没有任何警告。 你写一个 40 KB 的 AGENTS.md,最后那 8 KB 永远到不了模型面前,而且你不会知道。报这个问题的人提到,Claude Code 在 CLAUDE.md 太大时启动会提醒你,Codex 不会(issue #7138, 0.63.0).

两种处理方式:

# raise the per-file read limit
project_doc_max_bytes = 131072

# or name alternates and split the rules across files
project_doc_fallback_filenames = ["TEAM_GUIDE.md", ".agents.md"]

更好的办法是让它短。AGENTS.md 的作用是防止模型乱猜,不是当发布手册用的。架构说明挪进 docs/,从根文件链过去。

坑二:嵌套 AGENTS.md 的优先级至今没定论

AGENTS.md 标准说最近的那个文件优先。Codex 既没明确实现,也没明确解释。官方追踪 issue 里记录了包括 Wix 在内的客户结构化反馈(issue #12115)。

在 monorepo 里,这意味着 packages/api/AGENTS.md 里的规则可能压根没人读。目前的绕法是在根文件里显式提醒一句——「如果工作涉及某个 package,先读它的 AGENTS.md」。脆弱,但有用(HN 讨论, 2026-04)。

坑三:AGENTS.md 可能不被当成约束来执行

用户反馈说 agent 会先应一声「我已经读过 AGENTS.md」,然后把它当成参考信息而不是必须遵守的规则,新指令一来就把旧规则丢掉(issue #41704)。这是社区观察,OpenAI 没有确认。

如果你需要硬约束真的生效,别把所有东西都塞进 AGENTS.md。实践中更靠谱的做法是另开一个 PLANS.md 专门写验收标准。有个用户备了七个全大写的 markdown 文件,Codex 后来在每次重构后主动对前后截图做 sha256 校验——直接原因就是 PLANS.md 里那句「capture before/after screenshots to prevent regressions」(HN, 2026-05)。

有个副作用值得知道:全局注入的 AGENTS.md 内容会污染聊天搜索。搜一个常见词,可能连续返回同一条 Markdown 模板行(issue #44498)。大段的格式说明别放进全局 AGENTS.md。

权限:真正的机制是档案,不是三个等级

多数人只知道 read-only / workspace-write / danger-full-access。其实还有一整套具名的权限档案系统,用来细粒度地管文件和网络策略——在受管环境里,它是唯一真能把行为锁死的机制。

内置三个档案::read-only、:workspace、:danger-full-access。自定义的写法是这样:

default_permissions = "project-edit"

[permissions.project-edit.workspace_roots]
"~/code/app" = true
"~/code/shared-lib" = true

[permissions.project-edit.filesystem]
":minimal" = "read"
[permissions.project-edit.filesystem.":workspace_roots"]
"." = "write"
".devcontainer" = "read"
"**/*.env" = "deny"

[permissions.project-edit.network]
enabled = true
[permissions.project-edit.network.domains]
"api.openai.com" = "allow"
"*.github.com" = "allow"
"tracking.example.com" = "deny"

多数人搞错的那部分

网络限制是三个开关的组合,不是两个:

network.enabledfeatures.network_proxy实际行为
false任意命令完全够不到网络
truefalse命令直连——域名规则不生效
truetrue命令走代理,规则生效;不在白名单里的域名一律拦截

写了域名规则不等于把规则打开。 你还得设 features.network_proxy = true,否则上面每一条 deny 和 allow 都是摆设。

域名语法有四种形式值得背下来:example.com 精确匹配这个主机,*.example.com 只匹配子域名,**.example.com 匹配顶级域名加所有子域名,deny 永远压过 allow。

项目信任和权限层是绑在一起的

不受信任的项目会整个跳过项目级的 .codex/ 层——项目配置、钩子、规则全不加载,而用户层和系统层照常加载(配置基础)。别把刚 clone 下来的仓库设为可信,那等于用你自己的身份执行它的配置。

项目级配置还有一份硬性禁用清单。这些键会被忽略,只在启动时给个警告:openai_base_url、model_provider、model_providers、notify、profile、profiles、otel。provider、通知和遥测设置应该放在用户级的 ~/.codex/config.toml 里(高级配置)。

最后,在受管机器上,即使你显式传了 --dangerously-bypass-approvals-and-sandbox,组织级的只读档案依然能把它盖掉,而且不会告诉你它从哪来(issue #51116)。要是某个 bypass 参数看起来没生效,先怀疑受管策略。

文档只提了一次的配置键

不是偏方技巧,就是写得很简略、几乎没人讨论的那些。

notify 把「一轮结束」接到你自己的工具上

notify = ["python3", "/path/to/notify.py"]

脚本会收到一个 JSON 参数,里面有 type(目前只有 agent-turn-complete)、thread-id、turn-id、cwd、input-messages 和 last-assistant-message。

内置 TUI 通知做不了的事,都能靠它补上:桌面弹窗、聊天 webhook、CI 更新、长任务提醒。这台机器的终端通知就是这么做的。

codex debug prompt-input 能看到模型真正看到的东西

codex debug prompt-input

它把模型可见的 prompt 输入列表渲染成 JSON。你需要知道规则到底有没有被注入、某个钩子为什么没触发、工具描述是怎么拼起来的时候,跑它比瞎猜强。0.146.0 上的 codex debug --help 确认了这条,同组的还有 models(以 JSON 输出模型目录)和 app-server。

project_root_markers 决定 Codex 在哪儿停止向上找

默认情况下,含 .git 的目录就是项目根目录,这也决定了 Codex 往上找配置和 AGENTS.md 找到哪一层就停:

project_root_markers = [".git", ".hg", ".sl"]
# empty list = never search parents; current directory is the root
project_root_markers = []

-c 的值按 TOML 解析,不是 JSON

codex --model gpt-6.1-sol                     # prefer the dedicated flag
codex --config model='"gpt-6.1-sol"'          # strings need quotes
codex --config sandbox_workspace_write.network_access=true
codex --config 'shell_environment_policy.include_only=["PATH","HOME"]'

嵌套键用点号表示(mcp_servers.context7.enabled=false)。按 TOML 解析失败的值会被当成字符串,所以引号加不加,结果不一样。

其它

--profile 和多数教程说的不一样

老教程都在讲往 config.toml 里加一个 [profiles.review] 表。那已经不是推荐写法了。

档案现在是独立文件:

codex -p review    # layers ~/.codex/review.config.toml over the base user config

0.146.0 的 codex exec --help 直接写着:Layer $CODEX_HOME/<name>.config.toml on top of the base user config。

好处是隔离:review 档案是它自己的文件,不是主配置里又一张表。做审查工作可以这么写:

# ~/.codex/review.config.toml
model_reasoning_effort = "high"
approval_policy = "never"
sandbox_mode = "read-only"

never 配 read-only 值得单独说一句:审查过程中不再为了命令审批打断你,同时沙箱照样拦住写入。这不等于「完全权限」——很多教程把这两件事混为一谈。

钩子很强大,而且有三种静默失败

钩子让你在 agent 循环里跑脚本或 MCP 工具:扫描 prompt 里有没有泄露的 API key、一轮结束时跑校验、上下文压缩前存状态、生成记忆摘要。

事件覆盖一轮之内(PreToolUse、PermissionRequest、PostToolUse、PreCompact、PostCompact、UserPromptSubmit、SubagentStop、Stop、Interrupt),外加 SessionStart、SessionEnd 和 SubagentStart。

失败一:信任绑定在哈希上

非受管的钩子必须经过 /hooks 审查并信任。记录绑定的是钩子的当前哈希——你改一行,它就变成一个新钩子,得重新信任。

这个坑在项目钩子上最让人迷惑:你反复打开 /hooks 它都不加载,于是开始怀疑自己写的脚本。更可能是项目本身不受信任,或者信任校验把它拒了。

有个 issue 把这件事测得很精确:仓库根目录放一个 SessionStart 钩子去碰一个哨兵文件;不带参数跑 codex exec 永远不创建那个哨兵,也不打印任何东西,加上 --dangerously-bypass-hook-trust 它就出现了(issue #48715, 0.157.1)。

那个参数自己的帮助文本写得很直白:“Intended only for automation that already vets hook sources.” 它是给在 Codex 之外自己审钩子的流水线用的,不是给本地图方便用的。

失败二:钩子输出会落盘

这条很容易漏。Codex 把模型可见的钩子输出限制在大约 2,500 tokens。超出之后,全文会被写进磁盘:

<temp_dir>/hook_outputs/<session_id>/<uuid>.txt

模型看到的是头尾预览加一个路径。这个机制叫 spilling。

所以:不要在钩子输出里打印密钥或敏感数据——它会落进临时目录。additionalContextLimit 可以调这个阈值(默认 2,500;设成 0 会把全文传给模型,有风险)。OpenAI 另外也提到,钩子和插件的上下文会不断累积、拉低模型表现。

失败三:后台钩子的输出可能被丢弃

每个会话最多并发跑八个后台钩子,其余排队,而且完成顺序不保证。会话结束时,没跑完的后台钩子会被取消,输出直接丢弃。 SessionEnd 始终是同步执行的。

如果你留着一个「会话结束时写日记」的钩子,把它挂在 SessionEnd 上,别挂 Stop。

目前的插件钩子

如果你在移植 Claude Code 那种插件,注意 Agent Plugins 1.0 的 extensions.com.openai.hooks 字段目前是无效的——加载器会提前返回空列表(issue #47925,在 0.160.0 上仍能复现)。只有老的 .codex-plugin/plugin.json 形式能用。

补一段历史:加入用户自定义事件钩子的那个 PR 是关掉未合并的(PR #2904),HN 那个帖子里提到团队不接外部功能 PR——得走 issue 和 RFC(HN, 2026-01)。钩子最后还是发布了,但不是很多人以为的 Claude Code 那种写法。

子代理:三个内置的、自定义文件,以及两个烧钱的坑

内置 agent 有 default(通用兜底)、worker(偏执行)和 explorer(偏读的代码库探索)。

继承这块有个细节

自定义 agent 放在 ~/.codex/agents/(个人)或 .codex/agents/(项目),一个 agent 一个文件,需要三个字段:name、description、developer_instructions。

容易漏的是推理强度的解析规则。如果自定义文件设了 model 但没设 model_reasoning_effort,它会沿用上一次解析出来的强度——来源依次是显式 spawn、然后是 [agents] 默认值、再到父会话。想要特定强度就把两个都设上。

其他会话设置——sandbox_mode、mcp_servers、skills.config——省略时会从父会话继承。

还有一条:自定义 agent 名字和内置重名时,自定义赢。 你可以自己定义一个 explorer 把内置的顶掉。

全局 [agents] 的键包括 enabled(默认 true)、max_concurrent_threads_per_session、default_subagent_model、default_subagent_reasoning_effort 和 interrupt_message。最后这个默认是 true;设成 false 会把那条模型可见的中断信息挡在 agent 上下文之外。

坑一:你的子代理不会一开始就跑在你选的模型上

用户反馈说,分叉出来的 worker 实际会先在编排器的默认模型上跑前 ~40 轮,之后才切到 default_subagent_model。切换来得太晚,编排器那头的 token 份额直接膨胀(issue #45790, 0.154.0)。

如果你要设计多 agent 工作流又盯着账单,就拿一个便宜的模型当编排器去「调研和派活」,把强模型留给真正干活的部分。

坑二:失控的子代理是真烧钱

有用户在 0.144.0-alpha.4 上跑一个 VS Code 里的简单 UI 任务,Codex 自动开了 826 个子任务(约 2.15T tokens),账单 $79,664(HN, 2026-09)。这不孤例:同一时期本地灰度还有几份报告,录到 36k tokens,而控制台显示消耗了几百万(issue #48919)。

合理的防护:在 OpenAI 计费层设硬性消费上限(不是 Codex 层);给并发数封顶;CI 里用 codex exec 而不是交互式会话。

还有一条限制

codex exec --ephemeral 和自定义子代理不兼容——临时运行不持久化 thread,自定义 agent 会带着 no thread with id 失败(issue #41474)。子代理也无法覆盖 shell_environment_policy.set,所以没法给每个子代理发不同的 API key(issue #46457)。

codex exec 才是最值得自动化的一部分

交互式的 codex 是给人用的。脚本和 CI 该用 codex exec。0.146.0 上确认过:

参数作用
--json以 JSONL 形式把事件流输出到 stdout
--output-schema <FILE>用 JSON Schema 约束最终回复
-o, --output-last-message <FILE>只把最后一条消息写进文件
--ephemeral不写会话文件
--ignore-user-config跳过 $CODEX_HOME/config.toml
--ignore-rules跳过 execpolicy 的 .rules 文件
--skip-git-repo-check允许在 Git 仓库之外运行
--dangerously-bypass-hook-trust对外审过的自动化跳过钩子信任检查

--output-schema 是最被低估的一个——它让自动化能拿到可靠的结构化输出,而不用去解析散文。

定位也变了。一位长期用户的说法是:「最难的问题好像已经不是写代码了。是编排、审批,以及知道什么时候该把控制权交回给人」(HN, 2026-06)。

JSON 输出里有个解析坑

web_search 条目带两个 id 成员——先是 exec item ID,然后是搜索调用 ID:

{"type":"item.started","item":{"id":"item_5","type":"web_search","id":"ws_0ffce7c01785eab1016abe8f507bc087d297e8f248d394573e","query":"","action":{"type":"other"}}}

重复成员名在 RFC 8259 里是允许的,但怎么处理没有规定,严格解码器会直接拒掉这一行——Go 的 encoding/json/v2 默认就拒绝重复键(issue #50053, 0.159.0;另一个项目在 0.156.1 上也撞到过同样的结构)。你要写 CI 解析器,就别假设 item.id 是唯一的。

--ignore-user-config 做的比帮助文本说的多

帮助文本说它不加载 $CODEX_HOME/config.toml。实际上它还会跳过项目级的 .codex/hooks.json 和 .codex/config.toml(包括里面的 MCP 服务器),但依然会写信任记录(issue #49333, 0.157.1)。

想要干净的 CI 基线,把 CODEX_HOME 指到一个临时目录,别依赖这个参数。

值得知道的平台差异

这些是社区反馈,不是官方确认的行为变更。用之前对一下自己的版本。

macOS —— 在 ~/Documents 下的项目里跑子代理线程切换,可能报 invalid cwd: Operation not permitted (os error 1),原因是 macOS 对 Documents 的 TCC 权限会影响到 cwd 校验。在 PyCharm 的集成终端里经常出现(issue #37229, 0.146.1)。

Linux —— --yolo 跑起来时 /proc/self/status 里仍然是 NoNewPrivs: 1,所以一条 NOPASSWD 的 sudo 规则提不了权(issue #48908, 0.157.1)。Yolo 不会让你拿到 root。

Linux + Docker —— Codex 会解析 /proc/self/mountinfo,而 Docker 网桥网络的 net:[inode] 条目会触发路径校验失败,报 “mountinfo path is not absolute”,整个文件系统沙箱跟着塌掉,连带所有 apply_patch 都用不了(issue #47987)。

Windows —— .sandbox-bin 锁死问题有一个持久修法。症状是反复出现 helper_sandbox_lock_failed。原因是一个受保护的 DACL 给 SYSTEM/Admins 授了 Modify 但没给 WRITE_DAC,再叠加上 setup marker 在配置完成前就被删掉(issue #51468、issue #50520)。

社区验证过的持久修法是改 owner,而不是改权限:

takeown /F "%USERPROFILE%\.codex\.sandbox-bin" /A

不要加 /R。改所有权让 LocalSystem 隐式拿到 WRITE_DAC,同时不动 DACL 本身。好几个人反馈它扛过了 4–5 轮重新配置加重启;icacls /grant SYSTEM:F 只能顶住一次。

Windows 上还有个代理坑:用 Proxifier 这类工具时,Codex App 需要两条规则——localhost;127.0.0.1 强制 Direct,其余全部走代理——否则本地调用会绕回代理里(V2EX 报告)。

apply_patch —— 语法上接受重复路径和倒序的 hunk,但运行时拒绝。某次 30 天的本地统计:重复路径导致 83 次失败,找不到源行导致 1,607 次失败(issue #50549)。

值得养成的两个习惯

上面这些可以收成一个想法:Codex 的问题很少是模型的问题。它们是那些悄悄失败的外壳行为——配置被丢且不警告,AGENTS.md 被截断且不警告,钩子被拒且不警告,--ignore-user-config 的作用范围比文档写的更大。

所以两个习惯能覆盖大部分:

  1. 每次升级后跑一遍 --strict-config,把它标出来的清掉。
  2. 怪模型之前先查机制。 查状态用 codex doctor,查模型真正收到什么用 codex debug prompt-input,查你那个版本里哪些参数已经稳定用 codex features list。

最后一件值得看一眼的事:在你自己机器上跑 codex features list。在 0.146.0 上,hooks、goals、multi_agent、shell_snapshot、browser_use、code_mode_host 和 remote_plugin 都是稳定且默认开启的,而 memories 是稳定但关闭的。知道哪些已经开着,比知道哪些存在有用。

这里真正省时间的大头,是把指令从你的 prompt 里搬出去,放进 agent 本来就会读的文件——可复用的 AI 提示词模板讲的是这活儿的另一半。想找这个方向上更多经过实测的工具,去逛逛省力这个分类。


参考来源

官方文档

官方文档域名 developers.openai.com 现在会重定向到 learn.chatgpt.com。

社区反馈

AGENTS.md #7138 — 静默截断 · #8759 — 全局位置默认不读 · #12115 — 嵌套 AGENTS.md · #41704 — 规则链不再被遵守 · #44498 — 注入的指令污染搜索

配置与严格模式 #33181 — 严格配置拒掉受管项目条目 · #51481 — 远程 stdio MCP 严格校验失败 · #49333 — —ignore-user-config 也会跳过项目层 · #51116 — bypass 参数被受管档案盖掉

钩子 PR #2904 — 用户自定义事件钩子 · HN — 团队不接外部功能 PR · #47925 — Agent Plugins 1.0 钩子从未加载 · #48715 — 项目钩子静默不执行

子代理与用量 #45790 — 子代理模型切换太晚 · #40299 — 子代理管理不可靠 · #41474 — —ephemeral 弄坏自定义子代理 · #46457 — 子代理环境策略 · #48919 — 用量统计对不上 · HN — $78,000 失控子代理 · HN — 基于文件的子 agent

exec 与 JSON 输出 #50053 — web_search 条目带两个 id · #50549 — apply_patch 的路径约束

平台 #37229 — macOS Documents + 子代理 EPERM · #41138 — PowerShell shell_snapshot 返回 None · #36435 — zsh KSH_OPTION_PRINT · #48908 — —yolo 仍强制 NoNewPrivs · #47987 — Docker nsfs 弄坏沙箱 · #50520 和 #51468 — Windows .sandbox-bin 锁死 · V2EX — Windows Proxifier 规则

工程实践 HN — PLANS.md 与校验和的精度 · HN — 生产环境单体仓库工作流 · HN — Codex CLI 正在原生化 · HN — 内存占用数据点