Codex 不遵守项目约定:用 AGENTS.md 修复

Codex 写 `getUserById`,但你的项目用 `findUserById`——每条约定都要在 AGENTS.md 里配「规则 + 一个 canonical example 文件」,再确认它真的加载了。

Codex 生成的代码能跑,但”感觉”不像你的项目。命名用 get* 但你们用 find*;错误处理 throw string 但你们 throw 类型化的 AppError;测试用 expect(x).toBe(y) 但你们的风格是 assert.strictEqual(actual, expected)。Review 起来很别扭——不是错了,而是形状错了。

最快的修法:把每条出问题的约定写进 AGENTS.md,一行规则,再配一个已经遵守该约定的真实文件指针,然后跑 /status 确认文件真的加载了。Codex 抄的是 example 文件,规则只是个标签。指向 src/services/user/getUserById.ts 比任何文字描述都管用。

约定从几个文件 sample 里看不出来,必须在 session 一开始就显式化——以「规则 + 具体例子」的形式让 Codex 能读到。截至 2026 年 6 月,Codex(OpenAI 的编码 agent)会自动读 AGENTS.md:它把全局的 ~/.codex/AGENTS.md,加上从 git 根目录一路向下到你正在编辑的目录里的每一份 AGENTS.md,按”根在前”拼接,总量上限 32 KiB(project_doc_max_bytes)。AGENTS.md 是合同,指向 canonical example 文件就是落实。

你属于哪一类

动手改之前,先花 20 秒做个分诊:

你看到的现象最可能的原因跳到
没人说得出规则写在哪个文件约定根本没写下来原因 1
规则在 STYLE.md / CONTRIBUTING.md(不是 AGENTS.mdCodex 没读到那份文档原因 2
输出更像某个流行博客/Storybook 模式,不像你的输给了训练数据的默认原因 3
AGENTS.md 有规则但下面没代码/例子光规则没例子原因 4
grep 显示 src/ 里两种拼法都有repo 自相矛盾原因 5
Codex 的文件 lint 不过,但规则在 .eslintrcCodex 跳过了 lint原因 6

一个能覆盖大部分情况的快速检查:在 session 里跑 /status 看当前用的模型和配置,再让 Codex “按顺序列出你加载的所有 instruction 文件”。如果你的 style 文档不在这个列表里,你就在原因 1 或 2——先把文件路径修对,再去动 prompt。

常见原因

按命中率从高到低:

1. 约定根本没写下来

团队上季度在群里说定 find* 表示可能返回 null、get* 表示必须存在或 throw。Codex 看不到群——也没人把这条写进文件。它就按训练里先看到的那个挑。

如何判断:问团队”这条约定写在哪儿”——没人指得出来文件,Codex 也找不到。

2. 约定写了但 Codex 没读

STYLE.md 存在,但 Codex 只自动加载 AGENTS.md 文件(全局的 ~/.codex/AGENTS.md,加上从 git 根目录向下到你正在编辑文件的每个目录里各一份)。叫 STYLE.mdCONTRIBUTING.mddocs/conventions.md 的文档,除非有东西明确指向它,否则不会进入 context。截至 2026 年 6 月,Codex 仍把 README.md 当成普通源文件、而不是 instruction——只有 AGENTS.md 链(以及它的 AGENTS.override.md 同级文件)会被自动注入。

如何判断:跑 /status,再让 Codex “列出你加载的 instruction 文件”。如果 STYLE.md 不在列表里,就是从没被读到。

怎么修:要么把内容并进 AGENTS.md(或用 /init slash 命令生成一份脚手架再粘进去),要么保留 STYLE.md,在 prompt 里用 /mention STYLE.md 把它挂到当前这一轮。

3. Codex 用了常见模式而不是你的

团队用 kebab-case 的 testid(data-testid="user-card-name"),Codex 生成 camelCase——因为它训练用的公开样例里 camelCase 远比 kebab-case 多。你的约定输给了统计学上的默认值。

如何判断:新代码更像某个流行博客或框架文档的模式,而不像你现有代码——testid / lint 规则 / 测试断言 / 命名这几类最常见。

4. AGENTS.md 写了规则但没给例子

“用团队的错误模式”——什么模式?Codex 需要一个具体例子来抄——一行规则不够。

如何判断:AGENTS.md 某节有标题但下面没代码块也没文件指针。光规则没例子的章节经常被忽略。

5. 项目里约定本身就不一致

一半代码用 findUserById,一半用 getUserById(迁移没做完)。Codex 抽到哪个就用哪个,没明确 canonical 就是随机输出。

如何判断grep -c "findUserById" src/ && grep -c "getUserById" src/ 都非 0——Codex 没办法挑对,因为根本没”胜方”。

6. 约定靠 linter 强制但 Codex 没跑 lint

约定确实被编码了——.eslintrcnaming-convention: camelCase——但 Codex 写完代码没跑 lint。问题到 CI 才暴露。

如何判断:Codex patch 后跑 pnpm lint path/to/new-file.ts——有报错就是它绕过了 lint 编码的约定。

最短修复路径

按收益从高到低,前 3 步覆盖最常见情况。

Step 1:每条约定 = 规则 + canonical example

别只写规则,配一个 Codex 能读的真实文件:

## 命名约定

- `find*` 表示可空查找(返回 `T | null`)。
  例子:`src/services/user/findUserById.ts`
- `get*` 表示必有查找(缺则 throw)。
  例子:`src/services/user/getUserById.ts`

## 错误处理

所有 throw 必须 extend `src/lib/errors.ts``AppError`
例子:`src/api/billing/handlers.ts:42`

## 测试风格

用 vitest + `assert`**不**`expect`)。
canonical:`src/services/user/user.test.ts`

Codex 实际抄的是 example 文件,规则只是导航。

Step 2:task prompt 里指向 example

加 `getOrgById` 查找。
完全照 `src/services/user/getUserById.ts` 的样子:
- 函数形状一样
- 错误 throw 风格一样
- 测试布局一样(见 `getUserById.test.ts`)

文件 + 测试一起生成。

指向一个真实文件比任何文字描述都有效。

Step 3:形状错了拒收 + 重 prompt 时点 canonical

Codex 形状错了:

你的输出用了 `expect(x).toBe(y)`——那是 jest 风格。
本 repo 用 `assert.strictEqual(actual, expected)`。
canonical 见 `src/services/user/user.test.ts`。

按 canonical 风格重生成测试。

每次拒收都是给当前 session 训练,1-2 次纠正后剩下的就稳了。

Step 4:可视化标记列成必需产出

data-testid、ARIA、自定义 decorator 这种,列成必需输出:

新组件必须包含:
- 所有可交互元素上 `data-testid="<kebab-name>"`
- icon-only 按钮加 `aria-label`
- `<Component>.stories.tsx` Storybook story 文件

缺任何一项都拒收。

Step 5:把 lint 加进 verifier

Lint 编码了很多 Codex 绕过的约定,把它写进”完成”:

写完后跑:
  pnpm eslint <new-files> --max-warnings 0

任意 warn/error 都修,修完再说完成。

ESLint config 严格点(naming-conventionimport/order、自定义规则),很多约定就自动强制了。你也可以把 lint 步骤直接写进 AGENTS.md,让每个 session 都知道这是”完成”的一部分:

## 完成的定义
- `pnpm eslint <changed-files> --max-warnings 0` 通过
- `pnpm vitest run <changed-files>` 通过

Step 6:Monorepo 每包一份 AGENTS.md

不同包可以有不同约定,不要强用一份根规则。每包加一份:

AGENTS.md                 → 全 repo 通用规则(根)
apps/web/AGENTS.md        → Next.js App Router 约定
packages/ui/AGENTS.md     → 组件库约定
packages/db/AGENTS.md     → Prisma + repository 模式约定

Codex 把从 git 根目录向下到被编辑文件的整条链按”根在前”拼接。离被编辑文件越近的文件在拼接后的 prompt 里越靠后,所以遇到冲突的规则时最近的那份胜出。截至 2026 年 6 月有两个坑:

  • 拼接后的总量上限是 32 KiBproject_doc_max_bytes)。臃肿的根文件会把你的分包规则挤出去——根文件要保持精简。
  • 某一级如果存在 ~/.codex/AGENTS.override.md(或目录里的 AGENTS.override.md),它会取代该级正常的 AGENTS.md。某个包突然不守自己的规则时,先查是不是有个游离的 override 文件。

怎么确认修好了

  1. 确认文件加载了。在 session 里跑 /status,再让 Codex “按顺序列出你加载的所有 instruction 文件”。你的 AGENTS.md(以及任何嵌套的)应当出现在里面。想要一份留底记录,就用 codex -c log_dir=./.codex-log 启动,然后在 ./.codex-log/codex-tui.log 里 grep 这些文件路径。
  2. 重跑那个出错的任务。让 Codex 再生成一次之前写错的那类文件(比如一个新的 get* 查找加它的测试)。
  3. 和 canonical 对比。新文件应当在函数形状、错误风格、测试布局上都和你指向的 example 文件一致。
  4. 跑 verifierpnpm eslint <new-file> --max-warnings 0 和你的测试命令都应当通过,没有约定相关的 warning。

如果第 1 步显示文件加载了、但输出形状还是错的,那问题就出在规则本身——它几乎肯定缺一个具体的 example 文件(原因 4)。补上指针再重跑。

常见问答

Codex 会读 CLAUDE.md.cursorrules 吗? Codex 自动加载的是 AGENTS.md 链。AGENTS.md 现在是一个开放标准(由 Linux 基金会下的 Agentic AI Foundation 维护),Codex、Cursor、Gemini CLI、Windsurf、Copilot 都支持,所以单用一份 AGENTS.md 是最通用的选择。如果你的 repo 里还留着 CLAUDE.md,最干净的做法是写一行 AGENTS.md 说”遵守 CLAUDE.md 里的约定”,并在任务里 /mention CLAUDE.md,而不是指望 Codex 自己去找。

为什么 Codex 第一个文件守规则、下一个就漂了? 通常两个原因:规则没配例子(模型没东西可抄——原因 4),或者 32 KiB 溢出,大的根 AGENTS.md 把分包文件挤掉了。用 /status 查加载顺序,把根文件瘦下来。

规则加了 Codex 还是不理,怎么办? 先确认它真的加载了(上面第 1 步)。最常见的疏漏是规则写在 STYLE.md/CONTRIBUTING.md 而不是 AGENTS.md,所以从没被注入。并进 AGENTS.md,或用 /mention 挂上去。

AGENTS.md 要提交进 repo 吗? 要——把每个项目的 AGENTS.md 提交进去,整个团队(以及 CI 上的 agent)才能共享。机器相关或个人偏好放进你的全局 ~/.codex/AGENTS.md,那份不进版本库。

Codex 在不同包之间自相矛盾。 这是 override 或优先级规则在起作用。离被编辑文件最近的那份胜出;游离的 AGENTS.override.md 会取代该级正常的文件。在目录树里搜一下 override 文件和冲突规则。

预防建议

  • AGENTS.md 每条约定都配一个具体 example 文件——光规则的章节会被忽略
  • 项目内不一致先解决(钦定一个胜方),再指望 Codex 跟约定
  • 能用 ESLint/Biome/Prettier 编码的尽量编码——让 linter 强制 AGENTS.md 描述的内容
  • Monorepo 每个 package 一份 AGENTS.md,最近的那份生效;根文件保持小,别撑爆 32 KiB 上限
  • 形状错了上线,当成文档缺口而不是 Codex 失败——补规则 + example
  • 每季度让 Codex 按项目风格写一段,看输出有没有漂移——漂了就刷新 doc

相关阅读

标签: #Codex #Coding Agent #排查 #排查 #约定