你让 OpenAI Codex(Codex CLI 或云端 agent)“加个用户设置页面”,代码能跑,但文件放在 src/UserSettings.tsx,而不是项目里其他页面所在的 src/pages/settings/index.tsx;命名是 UserSettings 而不是项目里一致的 SettingsPage;还用了 useState,可你的项目全用 Zustand。Codex 默认按”通用最佳实践的 React”生成,不会自动推断你仓库里既有的约定。
最快修复: 在仓库根目录放一份 AGENTS.md,把路径、命名、技术栈写清楚,然后每次 prompt 都指向一个具体的同类型文件让它照抄。截至 2026 年 6 月,Codex 每次运行都会自动读取 AGENTS.md,光这一步就能解决大部分情况。下面是完整的诊断、配置和验证方法。
先判断你属于哪一类
| 现象 | 可能原因 | 跳转 |
|---|---|---|
仓库里没有 AGENTS.md / CLAUDE.md / 规则文件 | Codex 没有可遵循的规范 | 原因 1 / Step 1 |
| 有指南但 Codex 仍用通用写法 | prompt 没指向样本文件 | 原因 2 / Step 2 |
用错了库(axios、裸 useState) | prompt 没钉死技术栈 | 原因 3 / Step 1 |
| 一半对一半错 | 项目本身不一致 | 原因 4 |
文件落在 root,不是 apps/web | Codex 选错了 workspace 根 | 原因 5 / Step 5 |
常见原因
按命中率从高到低排序。
1. 仓库里没有 AGENTS.md(或同类文件)
Codex 读取 AGENTS.md 作为项目指令;Claude Code 读 CLAUDE.md;Cursor 读 .cursor/rules/*.mdc(旧的单文件 .cursorrules 自 Cursor 0.43 起已弃用,但仍能加载)。三者都没有时,Codex 只能扫几个最近编辑的文件靠猜。
你:加个用户设置页面
Codex:(没 AGENTS.md,看了 README,里面没说目录约定)
-> 创建 src/UserSettings.tsx
你的项目:所有页面在 src/pages/<feature>/index.tsx
如何判断: 看仓库根目录。如果 AGENTS.md、CLAUDE.md、.cursor/rules/ 一个都没有,就是这个原因。
2. Codex 没读过任何同类型文件
即使有指南,如果 prompt 没指向具体的”参考样本”,Codex 还是会按训练里的”标准 React 项目”写,与你的本地约定脱节。
如何判断: 看 Codex 在生成前到底有没有读同类型文件。每次会话都会以 JSONL 记录在 ~/.codex/sessions/YYYY/MM/DD/rollout-<session-id>.jsonl,把里面的 tool call 过滤出来:
# 取最近一次会话,只看 tool call(需要 jq)
ls -t ~/.codex/sessions/**/rollout-*.jsonl | head -1 | \
xargs cat | jq 'select(.type == "tool_call")'
如果在写文件之前没有对同类型文件的 read,就是这种情况。(想实时追踪,也可以用 RUST_LOG=debug codex 2>&1 | grep file:。)
3. Prompt 没钉死技术栈
默认 prompt “实现 X 功能”等于放任 Codex 自由发挥。它会用最通用的 React + axios + useState,无视你项目的 Zustand / SWR / TanStack Query / shadcn-ui 选型。
如何判断: 对比生成代码引入的库和 package.json。如果引入了项目里没装的库(或忽视了已经装的状态管理库),说明 prompt 太宽松。
4. 项目结构本身就不一致
如果项目里一半文件用 kebab-case、一半用 PascalCase,一半在 pages/、一半在 routes/,Codex 会随机模仿其中一种,结果大约一半时间看起来”风格不对”。
如何判断: 跑 find src -name '*.tsx' | sort,看命名和位置是否统一。如果一半 PascalCase 一半 kebab-case,那是项目自身的问题,再好的 prompt 也补不全。
5. monorepo 下 Codex 选错了 workspace 根
monorepo 里,Codex 以启动时所在的目录为锚点。如果你从仓库根目录启动,它就会看到 root 的 package.json 当成入口,把文件写到 root,而不是 apps/web 或 packages/ui。
如何判断: 生成的文件出现在 root 或错误的 workspace,而不是和同类型代码在同一个 workspace。
最短修复路径
按收益排序。Step 1 + 2 通常能让 Codex 一次生成就贴近项目。
Step 1:写一份 AGENTS.md,把路径、命名、技术栈全列清楚
放在仓库根目录,Codex 每次运行都会读。想从脚手架起步的话,在 Codex 会话里跑 /init,它会根据检测到的依赖和目录结构生成初稿,你再删到只剩真正重要的规则:
# AGENTS.md
## 项目结构
- 页面:src/pages/<feature>/index.tsx
- 共享组件:src/components/<PascalName>.tsx
- API 调用:src/lib/api/<resource>.ts
- 类型定义:src/types/<resource>.ts
- monorepo 主代码在 apps/web,不要写到 root
## 命名约定
- 组件文件:PascalCase(SettingsPage.tsx)
- hook 文件:useXxx.ts
- 路由 segment:kebab-case(user-settings)
## 技术栈(不要引入替代品)
- 状态管理:Zustand(禁止 Redux / Recoil / 裸 useState 做全局)
- 数据请求:TanStack Query(禁止 axios 裸用)
- 表单:react-hook-form + zod
- 样式:Tailwind + shadcn-ui
## 工作流
- 改完跑 `pnpm typecheck && pnpm lint && pnpm test`
- 新建依赖前先问
注意别超过默认的 32 KiB 上限(~/.codex/config.toml 里的 project_doc_max_bytes),超了 Codex 会截断。规则要精炼,别写成长篇大论。
Step 2:每次 prompt 都引用一个具体的同类型文件
让 Codex 在写之前先读一个同类型的已有文件:
帮我加一个 "Notifications Settings" 页面。
请先读这两个文件,复制它们的结构和风格:
- src/pages/account-settings/index.tsx(同类型页面)
- src/components/SettingsCard.tsx(卡片组件用法)
然后按 AGENTS.md 的命名和目录约定生成新页面。
不要引入新依赖,沿用 Zustand + TanStack Query。
让它去读一个具体文件,比”参考现有风格”这种模糊指令有效得多——因为它把输出锚在你真实的代码上,而不是它训练得来的默认习惯。
Step 3:要求 plan-first,写代码前先确认结构
让 Codex 先输出计划,你确认后再让它动手:
开始写代码前请先输出一个 plan,包含:
1. 要创建 / 修改的文件路径列表
2. 每个文件的简短目的
3. 引入的新依赖(如果有)
4. 涉及哪些既有模块
等我说 "go" 再开始 edit。
如果路径错了,plan 阶段就能拦住,省得回滚。
Step 4:用 tree + naming 校验让 Codex 自查
在 AGENTS.md 末尾加一段自查指令:
# 生成新文件后执行:
tree src/pages -L 2
# 确认新页面路径符合 src/pages/<feature>/index.tsx
或者直接给一个校验脚本,并在 AGENTS.md 里要求必跑:
node scripts/verify-structure.mjs
# 输出 OK 或列出违规文件
Step 5:monorepo 锁定工作目录,并放一份嵌套 AGENTS.md
两个修复可以叠加。第一,从正确的 workspace 启动 Codex(或用参数指向它),让根目录对:
# 用 --cd(别名 -C)设置工作根。这是当前的 flag,
# 旧的 --working-dir 名称已被替换。
codex --cd apps/web "加一个用户设置页面"
# 需要跨 workspace 协作?再授权额外的可写目录:
codex --cd apps/web --add-dir ../packages/ui
# 或在 prompt 开头明确:
"工作目录是 apps/web。所有新文件必须在 apps/web/src 下,
不要碰 root 或其他 workspace。"
第二,利用嵌套指令文件。Codex 会把从 git root 到当前目录沿途的每一份 AGENTS.md 合并起来,所以在 apps/web/AGENTS.md 里写 web 专属规则,工作在该目录时就会覆盖根目录那份(同目录下若同时存在 AGENTS.override.md,以 override 为准)。把 workspace 专属的路径写进该 workspace 自己的文件里。
怎么确认修好了
重新发一遍请求,然后检查:
- 路径: 新文件落在 AGENTS.md 规定的位置(例如
src/pages/<feature>/index.tsx),而不是 root。 - 命名: 组件名 / 文件名符合你的约定(
SettingsPage而不是UserSettings)。 - 技术栈: import 和
package.json一致——Zustand / TanStack Query 进来了,axios / 裸全局useState没出现。 - 读取记录: 会话 JSONL 里能看到写文件之前对同类型文件的 read。
如果一次干净运行四项全过,说明 AGENTS.md 加载正常、prompt 也锚住了。
常见问题
AGENTS.md 能替代 CLAUDE.md 和 .cursor/rules 吗?
不能。每个工具读自己的文件:Codex 读 AGENTS.md,Claude Code 读 CLAUDE.md,Cursor 读 .cursor/rules/*.mdc。三份都放在仓库根目录并保持同步,免得某个 agent 跟着过时规则走。Codex 还会合并全局的 ~/.codex/AGENTS.md(如果你在那里放了个人默认配置)。
Codex 还是不理 AGENTS.md,为什么?
最常见的是文件太大(超过 32 KiB 的 project_doc_max_bytes 上限,尾部被截断),或者你是从 git root 之上的目录启动 Codex 的,它根本没找到文件。确认文件在根目录、且 Codex 是在仓库内部启动的。截至 2026 年 6 月,Codex 每次运行都会重建指令链,没有需要手动清的缓存。
怎么看 Codex 到底读了哪些文件?
查会话日志 ~/.codex/sessions/YYYY/MM/DD/rollout-<id>.jsonl,过滤 tool_call 条目;或者用 RUST_LOG=debug codex 运行后 grep file:。如果写文件之前没有对相关同类型文件的 read,说明你的 prompt 需要加一句明确的”先读这个文件”。
项目结构本身就不一致,怎么办? 再好的 prompt 也救不了自相矛盾的仓库。挑一套约定,写进 AGENTS.md 作为唯一标准,再把不符合的文件迁移过来(或把遗留区域标为”不要动”)。在那之前,Codex 会照搬它碰巧读到的那种写法。
设置工作目录用哪个 flag?
--cd(别名 -C),例如 codex --cd apps/web。旧的 --working-dir 名称已被替换。用 --add-dir 授权额外的可写目录。
预防建议
- 仓库根目录维护
AGENTS.md(Codex)/CLAUDE.md(Claude Code)/.cursor/rules/*.mdc(Cursor),并保持同步。 - 新增任何文件类型时,先更新 AGENTS.md,再写第一个样本,再让 AI 模仿。
- 在 prompt 模板里强制”先读一个同类型样本,再开始写”。
- 写一个
scripts/verify-structure.mjs,在 CI 阶段跑,把命名 / 目录违规的 PR 拦下。 - monorepo 项目用
codex --cd <workspace>启动,并加一份 workspace 专属的嵌套AGENTS.md,别只靠仓库根目录。 - 每季度 review AGENTS.md,把 AI 反复犯的同一种错(比如总选
useState)写成明确的禁令。