关于 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"
两个已知的坑:
-
--strict-config会拒掉 Codex Desktop 写入的条目。 桌面端会往你的~/.codex/config.toml里注入[projects."<path>"]块,CLI 的严格校验不认这些(issue #33181, 0.144.1)。如果 CLI 和 Desktop 共用一个配置目录,开这个开关前先看看那些块是谁写的。 -
文档写了的字段,严格校验照样可能不过。 文档里说远程 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.enabled | features.network_proxy | 实际行为 |
|---|---|---|
| false | 任意 | 命令完全够不到网络 |
| true | false | 命令直连——域名规则不生效 |
| true | true | 命令走代理,规则生效;不在白名单里的域名一律拦截 |
写了域名规则不等于把规则打开。 你还得设 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 解析失败的值会被当成字符串,所以引号加不加,结果不一样。
其它
[history] persistence = "none"关掉本地会话记录;history.max_bytes给文件封顶,超了丢最老的条目。hide_agent_reasoning = true不输出推理内容,CI 日志会干净很多。tui.alternate_screen = "never"保留终端的滚动回溯。tool_output_token_limit设历史里单条工具结果的 token 预算——大仓库里调试时有用。file_opener可以填cursor、windsurf或vscode-insiders,把main.py:42变成可点击链接。
--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 的作用范围比文档写的更大。
所以两个习惯能覆盖大部分:
- 每次升级后跑一遍
--strict-config,把它标出来的清掉。 - 怪模型之前先查机制。 查状态用
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 提示词模板讲的是这活儿的另一半。想找这个方向上更多经过实测的工具,去逛逛省力这个分类。
参考来源
官方文档
- 配置基础 —— 配置优先级、项目信任
- 高级配置 —— 项目级禁用键、
project_root_markers、notify、history、TUI - 配置参考 —— 键的语义和默认值
- Hooks —— 生命周期事件、信任模型、spilling、并发
- 子代理 —— 自定义 agent 的结构与继承
- 权限 —— 权限档案、那个三开关网络矩阵
- 构建 skills —— 本地 skill 加载与启用
- openai/codex README —— 安装和登录
官方文档域名
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 — 内存占用数据点