封面图
11 · 项目说明书 AGENTS.md:把规矩焊进 Codex 的开工流程
说个刚上手 Codex 时常见的翻车案例——把一条关键规矩塞在长篇大论里,最后谁都没听见。
去年有个 Node 项目,在根目录写了份 AGENTS.md,第一行赫然写着「本项目用 pnpm,禁用 npm」。结果 Codex 转头就来了一句 npm install。当场以为它没读到,气得把那条规矩复制了三遍、加粗、还打了感叹号。结果还是不听话。
折腾半天才发现——它读到了,只是那条规矩被埋在了第 140 行。前面塞了公司简介、产品路线图、技术选型的来龙去脉,洋洋洒洒一百多行。等 Codex 翻到「禁用 npm」那句,注意力早被前面那堆废话稀释没了。问题从来不是它不听话,是把唯一有用的那条焊进了一坨没用的里。
AGENTS.md 之于 Codex,就是 CLAUDE.md 之于 Claude Code,同一个概念换了个文件名。但 Codex 这边的发现机制、覆写规则、字节上限自成一套,有几处容易踩的坑。这篇就把它们连同「该写什么、不该写什么」一次讲清。
读完这篇,你会拿到:
- Codex 发现
AGENTS.md的完整链条(全局层 → 项目层 → 合并顺序),以及谁冲突时说了算 AGENTS.override.md这个 Claude Code 没有的「临时盖章」机制,什么时候用- 一张「该写 vs 不该写」清单,避开那个「140 行没人听」的坑
- 改文件名(
project_doc_fallback_filenames)、调字节上限(project_doc_max_bytes)两个旋钮的官方用法 - 一套照着敲就能验证「Codex 到底读没读到」的动手流程
注意:本篇只聚焦 AGENTS.md 这一个文件怎么写好、怎么被加载。和它容易混的「记忆(Memories)」机制(那是本地自动回忆层、默认关着)在 [02 核心概念] 里已经点过,记住一句话:真正必须每次生效的团队规矩,写进 AGENTS.md,别指望记忆。
01 先搞懂:AGENTS.md 是 Codex 开工前的「交接清单」
先给结论:AGENTS.md 是你写给 Codex 的一份持久指令,它每次启动、动手干活之前都先读一遍,当成这个项目的背景装进脑子。
为什么需要它?因为 Codex 每开一轮(每轮(per run)——官方术语,在 TUI 里通常是每启动一个会话算一轮)都从一张白纸开始。上回你苦口婆心交代的「用 pnpm、别碰 legacy/、测试这么跑」,这回它一概不记得。没有 AGENTS.md,你就得每次重新解释一遍。
类比:交接班的工作清单。工厂三班倒,上一班下班前会在墙上那块板写清楚——这台机器有点毛病别硬开、这批料要先质检、紧急联系人是谁。下一班接手,照着板上干就行,不用把上一班的人薅回来口述。AGENTS.md 就是 Codex 的那块交接板——而且是它每次接班前都会先扫一眼的那块。
(注意这里没有用「入职手册」——那个比喻 [02] 已经用过了。交接清单更贴切的地方在于:它强调「每一轮开工都重读」,不是「入职那天看一次」。)
什么时候该往里加内容?几个特别实在的信号:
- Codex 第二次犯同一个错 —— 这条得固化进去,别再口头纠正第三遍
- 你这轮又敲了一遍上轮敲过的那句更正
- 代码审查时发现,它本该早就知道这个代码库的某个约定
- 新队友(或者三个月后的你自己)需要同样的背景才能快速上手
最惯用的用法,[02] 里提过、这里再强调一次——把它当反馈回路:Codex 对你代码库做了错误假设,别光在对话里纠正(那是一次性的,下轮它又忘),直接让它把这条修正写进 AGENTS.md。给一个 Python 项目调了两周,那份 AGENTS.md 从空白长到二十来行,全是它踩过、被逮住、然后自己记下来的坑,现在新会话基本不犯重复错误了。
02 发现链条:Codex 是怎么找到这些文件的
AGENTS.md 不止一份,它能放在好几个地方,作用范围从大到小。Codex 启动时会把它们串成一条「指令链」。这一节就把这条链拆开——这也是跟 Claude Code 差异最大的一块,看仔细。
按官方文档,Codex 构建指令链分两步走(一轮一构建,TUI 里通常是每启动一个会话构建一次):
第一步,全局层(Global scope)。在你的 Codex 主目录(默认 ~/.codex ,除非你设了 CODEX_HOME 环境变量)里,Codex 先看有没有 AGENTS.override.md ,有就用它;没有才读 AGENTS.md 。这一层只取第一个非空文件,不会两个都读。
第二步,项目层(Project scope)。从项目根目录(通常是 Git 根)开始,一路往下走到你当前所在的目录。沿途每一个目录里,Codex 按这个顺序挑:先看 AGENTS.override.md ,再看 AGENTS.md ,最后看 project_doc_fallback_filenames 里你自定义的备选文件名。每个目录最多只收一个文件。
第三步,合并(Merge order)。Codex 把找到的这些文件从根到叶依次拼接,中间用空行隔开。越靠近你当前目录的文件,因为排在拼接结果的越后面,优先级越高、能盖过前面的。
光说有点绕,上张图:
AGENTS.md 三层发现链:机器级 ~/.codex/(override 优先)→ 项目层 Git 根逐级累加 → 拼成指令链
这张图把整条链走了一遍:先在全局层二选一,再到项目层逐级各挑一个,最后从根到叶拼起来——离你越近的,说话越晚、越管用。
这里有两个要点,都来自官方,记牢:
第一,是「拼接」不是「覆盖」。全局那份和项目那份同时生效,不存在「写了项目级,全局级就失效」。它们全都进上下文,后面的不会把前面的整个顶掉,只是冲突的条目以靠后(更近)的为准。
第二,更近的赢。比如全局 AGENTS.md 说「字符串用单引号」、项目根 AGENTS.md 说「用双引号」——项目根离当前目录更近、排得更靠后,所以双引号胜出。说白了,项目规矩能盖过你的个人偏好,这正是团队协作想要的。
类比:导航地图的三层缩放。全国地图给你大方向(全局层),城市地图给你城区路线(项目根),小区门口的指示牌告诉你最后那几步怎么走(子目录)。三层同时有效、信息互不删除;可一旦小区门口的牌子和大地图对不上,以现场最近那块牌子为准——它最贴你眼前这一步。AGENTS.md 这条链就是这套「越靠近当前位置越具体、冲突时越优先」的逻辑。
AGENTS.md 三层发现链、就近优先
这张图把三层从上到下叠了出来——全局层(~/.codex/)、仓库根、子目录依次拼接,三层同时生效;右侧那根「优先级」轴在提醒你:越靠近当前目录的那一层,排在拼接结果越靠后、冲突时越管用,这就是「就近覆盖」。
03 AGENTS.override.md:Claude Code 没有的「临时盖章」
上一节反复出现一个名字——AGENTS.override.md 。这玩意儿 Claude Code 那边没有对应物,是 Codex 独有的设计,单独拎出来讲。
先说它解决什么问题。设想你 ~/.codex/AGENTS.md 里写了一套团队共用的全局约定,平时挺好。但今天你接了个临时活,需要整段换掉这套全局指导,又不想把原文件删了(删了回头还得重写)。怎么办?
类比:在文件上盖一张「以此为准」的便签。原合同还在抽屉里,你没撕;只是临时贴了张便签条「本月按这个新条款执行」。事儿办完,便签一揭,原合同自动生效。AGENTS.override.md 就是这张便签——它在时,同级的 AGENTS.md 被整个跳过;把它删了,原来那份立刻回来。
官方给的两个典型场景:
- 全局临时覆写:在
~/.codex/AGENTS.override.md写一套临时全局指导,原~/.codex/AGENTS.md原封不动。活干完删掉 override,共享的那份就恢复了。 - 子目录特殊规则:某个子目录的团队需要一套跟外面完全不同的规矩。比如支付服务目录
services/payments/,里头放一个AGENTS.override.md:
# services/payments/AGENTS.override.md
## 支付服务规则
- 用 `make test-payments` 替代 `npm test`
- 轮换 API Key 前必须先通知安全频道
放了这个 override,这个目录里同级的 AGENTS.md 就被跳过(如果有的话),Codex 在这个目录干活时认 override 这份。
这里要拎清一个新手最容易混的点——override 跟「覆盖整条链」不是一回事:
它存在 → 同级的 AGENTS.md(和备选文件名)被跳过,这一层只认 override
说白了:override 是「这一层用我,别用旁边的 AGENTS.md」,不是「全链路只听我一个」。整条链的拼接、就近优先规则照旧。刚接触时容易理解错,以为在子目录放个 override 能把全局那份屏蔽掉,结果全局约定照样生效——后来翻官方文档才搞明白,它只在自己那一格里二选一。
还有个排查神器级的用法:万一 Codex 蹦出来一条你压根没写的奇怪指令,第一件事就是顺着目录树往上、连同 ~/.codex ,找有没有谁藏了个 AGENTS.override.md。把它改名或删掉,就回退到常规 AGENTS.md 了。官方排查清单里专门列了这条。
04 该写什么 vs 不该写什么
这一节是全篇的命门,也是开头那个「140 行没人听」的解药。AGENTS.md 写不好,九成栽在「该写的没写清,不该写的塞一堆」。
先说该写的——一句话:写「Codex 在每一轮里都该保持的事实」。翻成清单就是这五类:
| 类别 | 具体写什么 | 例子 |
|---|---|---|
| 项目概述 | 一句话说清这是个啥 | 「基于 FastAPI 的订单管理后端」 |
| 技术栈 | 语言、框架、数据库、关键工具 | 「Python 3.11 / PostgreSQL / pytest」 |
| 常用命令 | 测试、构建、检查 / lint 怎么跑 | npm run lint 、make test-payments |
| 代码约定 | 风格、命名、必须遵守的写法 | 「函数必须有类型注解」「字符串用双引号」 |
| 明确的「不要做」 | 雷区、禁改文件、必须先确认的操作 | 「禁改 migrations/ 已有文件」「新增生产依赖前先确认」 |
其中常用命令是被参考最频繁的——Codex 跑测试、提 PR 前会先来这儿翻命令,省得它瞎猜或者用错。官方那份示例 AGENTS.md 里头第一条就是命令:「提 PR 前运行 npm run lint 」。禁止清单则是防它「聪明但闯祸」的护栏:哪些目录是遗留代码只读不改、哪个操作改之前必须先问你。
再说不该写的,这才是新手翻车重灾区,也是亲身踩过的坑:
- ❌ 长篇大论的背景:公司介绍、产品愿景、技术选型的历史渊源——Codex 写代码用不上,纯占上下文,还把真正有用的规矩稀释了(参见开头那 140 行)。
- ❌ 过时信息:换了包管理器却没更新
AGENTS.md,里头还写着 npm,结果反过来误导它。 - ❌ 看代码就知道的东西:别去复述目录结构里每个文件干啥、别把 ESLint / Prettier 已经定义好的代码风格再抄一遍。Codex 自己会读代码,复述等于白占空间。
官方对「别写太大」给了硬性约束,但它的红线跟 Claude Code 不太一样——Claude Code 是「按行数」(建议 200 行内),Codex 是「按字节」:
Codex 跳过空文件,一旦拼接后的总大小达到 project_doc_max_bytes 限制(默认 32 KiB),就停止继续加文件。
注意这话有两层意思:一是默认 32 KiB 封顶,超了就截断(甚至整个文件被拦在外面);二是这个上限算的是合并后的总和,全局 + 项目 + 子目录加一块儿。所以你一份文件写太肥,可能把后面更重要的那份顶出窗口。官方建议:撞上限了,要么调高 project_doc_max_bytes ,要么把指令拆到多级子目录分散放(这正好呼应 [04 计费] 里讲的「AGENTS.md 瘦身 + 按子目录分层」省 token)。
有个好用的土办法,现在写每一条之前都先自问一句:「这条 Codex 看代码能不能自己推出来?能,就删。」 就靠这一刀,能把一份接手项目的 AGENTS.md 从一百多行砍到几十行,留下的全是它推不出来的硬约束——砍完之后,它用错包管理器的次数肉眼可见地降下去。
05 两个旋钮:改文件名、调字节上限
大多数人用默认的 AGENTS.md 就够了。但有两种情况你得动配置——这一节讲两个写在 ~/.codex/config.toml(Codex 的用户级配置文件)里的旋钮。
旋钮一:让 Codex 认你已有的文件名
设想你仓库里早就有一份 TEAM_GUIDE.md ,全队一直拿它当规范文档。你不想再造一个 AGENTS.md 重复一遍,想让 Codex 直接把 TEAM_GUIDE.md 当指令文件读。这时候就用 project_doc_fallback_filenames(备选文件名):
# ~/.codex/config.toml
project_doc_fallback_filenames = ["TEAM_GUIDE.md", ".agents.md"]
加了这条之后,Codex 在每个目录里的挑选顺序变成:AGENTS.override.md → AGENTS.md → TEAM_GUIDE.md → .agents.md ,取第一个存在(且非空)的。
旋钮二:合并内容被截断了,调高上限
上一节说过默认 32 KiB 封顶。如果你的指令确实超了、又暂时不想拆文件,可以把 project_doc_max_bytes 调大:
# ~/.codex/config.toml
project_doc_max_bytes = 65536
这就把上限抬到了 64 KiB,能塞下更多合并指导才触顶。不过得劝一句——这是治标。内容真有用,调上限;但更多时候,撞上 32 KiB 是个信号,提醒你「该精简了」或「该按子目录拆了」,而不是「该把上限往上拱」。偏好是优先拆、其次删,实在拆不动才抬上限。
两个旋钮的适用场景对照一下:
project_doc_fallback_filenames 加上它的名字
06 动手:给玩具项目配一份合格的 AGENTS.md 并验证加载
光说不练没用。下面用一个最小项目,走一遍「建文件 → 写规矩 → 验证 Codex 真读到了」的完整流程。跟着敲,几分钟搞定。
第一步:建个玩具项目并初始化 git
mkdir agents-md-demo
cd agents-md-demo
git init
预期:agents-md-demo 目录里出现一个 .git 目录。git 初始化是为了让 Codex 能把这里识别成「项目根」(它通常以 Git 根为起点往下扫),也为了这份 AGENTS.md 能进版本控制、团队共享。
第二步:手写一份精简的项目级 AGENTS.md
用你顺手的编辑器,在项目根目录新建 AGENTS.md ,贴入下面这份(注意它一共没几行——这就是好 AGENTS.md 该有的样子):
# agents-md-demo — 一个演示用的最小项目
只有用来演示 AGENTS.md 怎么写,没有真实业务逻辑。
## 常用命令
- `npm test` —— 运行测试
## 编程约定
- 所有函数必须有类型注解
- 字符串一律用双引号
## 注意事项
- 不要新增任何生产依赖,需要时先问我
预期:项目根目录下出现 AGENTS.md ,内容就是上面这几节。全文十几行——记住这个篇幅感,真实项目也别失控膨胀。
第三步:让 Codex 复述它读到的指令
官方给了一个特别直接的验证法——让 Codex 把当前生效的指令总结一遍。在项目目录里跑:
codex --ask-for-approval never "Summarize the current instructions."
预期:Codex 在动手提方案之前,会回显你刚写的那几条(命令、类型注解、双引号、别加依赖)。只要它复述出来了,就说明这份 AGENTS.md 这一轮确实被装进了它的上下文。
第四步(进阶):验证子目录覆写真的「就近优先」
想亲眼看到「越近越管用」,再做一步。在项目里建个子目录,放一份只覆盖一条命令的 override:
mkdir -p services/payments
在 services/payments/AGENTS.override.md 里写:
# services/payments/AGENTS.override.md
## 支付服务规则
- 用 `make test-payments` 替代 `npm test`
然后从这个子目录的视角启动 Codex,让它报告加载了哪些指令来源:
codex --cd services/payments --ask-for-approval never "List the instruction sources you loaded."
预期:按官方描述,Codex 会依次列出——先全局文件(如果你 ~/.codex 下有的话)、再仓库根的 AGENTS.md 、最后是支付服务这份 override;而且这一层的测试命令会以 override 的 make test-payments 为准,根目录那条 npm test 在这个子目录里被盖掉了。这就是「就近优先」的活样本。
07 小结
这一篇把 AGENTS.md 这个「Codex 的项目说明书」从发现机制到落地写法摸清了:
- 每轮开工必读的交接清单,就是 CLAUDE.md 换了个名
- 发现链条:全局 override → 全局 AGENTS.md → 项目根逐级向上拼接,更近的赢
- AGENTS.override.md:临时盖章,跳过同级 AGENTS.md,不影响其他层
- 内容清单:只写 Codex 推不出来的硬约束,别写背景、过时信息、代码能推出来的东西
- 字节上限 32 KiB(可调),撞了优先拆文件而非抬上限
- 用「Summarize the current instructions」一句话验证它读到了
你现在应该能:判断一条信息该不该进 AGENTS.md 、该放哪一层;看懂多层文件冲突时谁赢;用 AGENTS.override.md 临时换指导、又不删原文件;撞上字节上限知道是拆还是抬;用「Summarize the current instructions」一句话验证 Codex 到底读没读到。一句话——你能写出一份 Codex 真愿意听、而不是像开头那样写了 140 行没人理的 AGENTS.md 了。
下一篇 12「斜杠命令与快捷键」——这篇里已经在命令行里敲了好几次 codex ... ,那进了会话之后呢?/ 开头的斜杠命令能让你当场切模式、清上下文、看状态,再配上几个顺手的快捷键,操作效率能翻倍。下一篇就把会话里这套「快捷操作面板」一次摊开。留个小思考:这一篇让 Codex「记住规矩」靠的是写文件,那要是想在会话当场改它的行为、又不想动 AGENTS.md ,你猜该用什么?
