[控场AI]
· 12 分钟阅读· 6,127 字

一套规则两种用法:Codex嵌套与Claude子目录AGENTS.md

一套规则两种用法:Codex嵌套与Claude子目录AGENTS.md

解析Codex与Claude Code规则加载差异,提供monorepo下共享规则文件的完整方案。

文章针对monorepo中AI编程代理规则文件不生效的问题,对比了Codex和Claude Code的加载机制:Codex从根目录到启动目录一次性构建指令链,启动位置决定规则范围;Claude Code启动时加载上层CLAUDE.md,运行中按需加载子目录规则。解决方案是采用分层设计,用AGENTS.md承载真实规则,每个目录放置单行@AGENTS.md的CLAUDE.md实现跨工具共享。文章还介绍了通过codex debug prompt-input、/context命令和InstructionsLoaded hook验证加载内容的方法,并强调规则文件仅为指导性上下文,禁止性操作需配合权限拒绝规则或PreToolUse hook实施强制约束。

为什么你的规则文件没生效?

在 monorepo 中,apps/web/AGENTS.md 明确要求使用 Vitest 并禁止修改生成的 API 客户端。但当你从仓库根目录启动 Codex,让它修复一个 React 组件时,它还是动了 src/generated/client.ts。原因很简单:那个文件根本没进 prompt。

据 Codex 官方文档,Codex 每次运行只构建一次指令链,从项目根目录到你启动的目录,下方的规则文件被排除在外。而 Claude Code 的机制相反:它会在触及某个子目录文件时,才加载该子目录的 CLAUDE.md。

这两种加载逻辑的差异,导致同一套规则文件在两个工具下的行为完全不同。解决思路是:给每个包建立独立的规则手册,并验证每个代理实际加载了什么。

规则文件加载机制示意

第一步:根文件与包级文件的分层设计

以一个包含 React 前端和 Go 后端的 monorepo 为例,推荐的目录结构是:

acme/
  AGENTS.md            # 仓库级规则
  CLAUDE.md            # @AGENTS.md
  apps/web/
    AGENTS.md          # 前端规则
    CLAUDE.md          # @AGENTS.md
  services/api/
    AGENTS.md          # 后端规则
    CLAUDE.md          # @AGENTS.md

AGENTS.md 是 agents.md 定义的共享格式,被包括 Codex 在内的多种工具支持,承载真正的规则内容。每个 CLAUDE.md 只有一行导入语句,原因在第三步解释。

根文件覆盖全仓库通用规则,例如提交信息前缀、禁止提交密钥、在包目录内运行脚本。前端文件只写前端相关内容:使用 npx vitest run 测试、禁止编辑 src/generated/、组件采用函数式写法。后端文件只写后端规则:使用 go test ./...、已合并的迁移不可修改、错误用 fmt.Errorf 包装。

文件首行的 Rulebook: 标记是为了第四步的验证而设计的。注意控制文件体积:Anthropic 文档建议每个 CLAUDE.md 不超过 200 行,Codex 则在合并大小达到 project_doc_max_bytes(默认 32 KiB)时停止追加文件。

AGENTS.md 格式由 agents.md 社区提出,旨在为不同 AI 编程代理提供统一的规则文件接口,避免为每个工具维护独立配置。它采用 Markdown 编写,内容可包含项目约定、命令示例、目录说明等。Codex 原生支持该格式,Claude Code 从 v2.1.277 起也兼容读取,但优先级低于 CLAUDE.md。CLAUDE.md 中的 @AGENTS.md 导入语法是 Claude Code 特有的文件引用机制,解析时会将目标文件内容内联到当前上下文,且不会触发重复加载。这种设计让 AGENTS.md 成为唯一事实来源,而 CLAUDE.md 仅作为适配层。

第二步:Codex 的指令链构建逻辑

Codex 的加载顺序如下:

  1. 全局文件优先:~/.codex(或 $CODEX_HOME)下的 AGENTS.override.md,若不存在或为空则用 AGENTS.md。
  2. 从项目根目录(通常是 git 根)逐级向下到当前目录,每个目录检查 AGENTS.override.md、AGENTS.md 以及 project_doc_fallback_filenames 中列出的文件名,每个目录最多取一个文件。
  3. 文件按根目录优先拼接,越靠近当前目录的文件在 prompt 中越靠后,规则冲突时后者覆盖前者。

这意味着启动位置决定了 Codex 拿到哪些规则。做前端工作时应从 apps/web 启动:

codex --cd apps/web

此时指令链是根 AGENTS.md 加 apps/web/AGENTS.md,后端文件完全不会被读取。从根目录启动则只拿到全局和根级文件。指令链每次运行只构建一次,修改 AGENTS.md 要到下个会话才生效。

AGENTS.override.md 是替换而非追加:它会完全取代同目录的 AGENTS.md,需要手动复制仍然需要的基础规则。适合发布冻结等临时场景,删除后原文件恢复。

project_doc_max_bytes 是 Codex 配置项,用于限制指令链合并后的总字节数,默认 32 KiB。当追加文件导致总大小超过该阈值时,Codex 会停止加载后续文件,并可能在日志中提示截断。这意味着在大型 monorepo 中,若根级 AGENTS.md 过于庞大,可能挤占子目录规则的空间。建议将根文件控制在 10 KiB 以内,并利用 AGENTS.override.md 进行临时替换而非追加。该配置可在 ~/.codex/config.toml 或项目级配置中调整。

第三步:Claude Code 的按需加载机制

Claude Code 的加载分两个时机:

  • 启动时加载工作目录及其所有上级目录的 CLAUDE.md 和 CLAUDE.local.md。
  • 运行中,当 Claude 读取、写入或编辑某个子目录的文件时,才加载该子目录的 CLAUDE.md。

因此从根目录启动 Claude 也没问题:前端规则会在 Claude 打开前端文件时自动到达。若工作始终在一个包内,Anthropic 的 monorepo 指南建议直接在 apps/web 启动,这样根文件和前端文件都会在启动时加载。

Claude Code 从 v2.1.277 起也能读取 AGENTS.md,但默认仅在工作目录及上级目录中不存在 CLAUDE.md、.claude/CLAUDE.md 或 CLAUDE.local.md 时才生效。只要路径上出现任何一个 CLAUDE.md(哪怕是个人配置 CLAUDE.local.md),Claude 就会转而读取 CLAUDE.md 而忽略 AGENTS.md。

这就是每个目录都要放一个只含 @AGENTS.md 的 CLAUDE.md 的原因。@ 导入相对于所在文件解析,apps/web/CLAUDE.md 会引入 apps/web/AGENTS.md。文档明确指出该导入不会导致重复读取,且在不支持直接读取 AGENTS.md 的会话中依然有效。Claude 专属的备注可以写在导入行下方。

有一个文件 Claude 永远不会读:AGENTS.override.md。Codex 的临时覆盖不会传递到 Claude Code,如果冻结规则需要两个代理都遵守,必须写进共享的 AGENTS.md。

CLAUDE.local.md 是 Claude Code 的个人本地配置文件,通常被加入 .gitignore,用于存放开发者私有的规则或临时备注。它的加载优先级高于同目录的 CLAUDE.md,但低于 AGENTS.md 的导入链。在团队协作中,CLAUDE.local.md 不应包含项目强制规则,以免其他成员缺失关键约束。若需共享规则,应写入 CLAUDE.md 或 AGENTS.md。Claude Code 在启动时会同时检查工作目录及所有上级目录中的 CLAUDE.local.md,但其内容不会传递给其他工具。

第四步:验证每个代理实际加载了什么

验证 Codex 的方式是 codex debug prompt-input,它把模型看到的完整 prompt 输入渲染成 JSON,命令参考文档推荐用它调试指令发现。在目标目录下运行并搜索标记:

cd apps/web
codex debug prompt-input | grep -o 'Rulebook: [a-z]*'

预期输出是 Rulebook: root 和 Rulebook: web,顺序如此,且不出现 api。注意该命令的参数在不同版本间可能变化,先查看 --help。

Claude Code 则在会话中运行 /context,查看 Memory files 下的列表。该列表只显示启动时加载的文件,子目录的 CLAUDE.md 尚未出现,因为它按需加载。加载时终端会出现带路径的 Loaded 行。文档建议的测试方法是:从 shell 手动创建文件而非让 Claude 写入,然后让 Claude 读取该目录中的文件。

如需可留存的记录,可以注册 InstructionsLoaded hook。它在 CLAUDE.md 或规则文件进入上下文时触发,通过 stdin 接收 JSON 详情。下面的脚本用 jq 每次加载追加一行日志:

#!/bin/sh
# .claude/hooks/loads.sh  (chmod +x it)
jq -c '{load_reason, file_path,
        trigger_file_path, parent_file_path}' \
  >> "$CLAUDE_PROJECT_DIR/.claude/loads.log"

在根目录的 .claude/settings.json 中注册:

{
  "hooks": {
    "InstructionsLoaded": [{
      "matcher": "",
      "hooks": [{
        "type": "command",
        "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/loads.sh"
      }]
    }]
  }
}

从根目录启动 Claude 做这个测试,让它读取 apps/web 中的文件,并把 loads.log 加入 .gitignore。根据 hooks 参考文档,load_reason 为 session_start 表示启动加载,nested_traversal 表示按需加载的子目录 CLAUDE.md,trigger_file_path 指向 Claude 触及的文件。通过 @ 导入引入的文件显示为 include,parent_file_path 指向导入它的文件。读取前端文件应记录 apps/web/CLAUDE.md 为 nested_traversal,apps/web/AGENTS.md 为 include。

规则文件不是锁:补充强制约束

两个指令文件本质上都是指导性内容。Claude Code 文档直言:它把 CLAUDE.md 当作上下文而非强制配置,要阻止某个操作,应使用 PreToolUse hook 或权限拒绝规则。

因此"禁止编辑 src/generated/"应该同时出现在两个地方:规则手册中解释原因,拒绝规则中实施拦截。在根目录 .claude/settings.json 中:

{
  "permissions": {
    "deny": ["Edit(./**/generated/**/*)"]
  }
}

该规则覆盖 Claude 的内置文件工具,但 Claude 运行的脚本(如 npm run codegen)自行打开文件,仍然可以写入,这正是期望行为。相对路径模式锚定在启动 Claude 的目录,因此覆盖该点以下所有 generated/ 文件夹。项目设置不像 CLAUDE.md 那样从父目录继承,从 apps/web 启动的会话读取的是 apps/web/.claude/settings.json,需要单独复制一份。Codex 侧则可用 /status 查看会话的审批策略和可写根目录,限制在那里配置。

PreToolUse hook 是 Claude Code 提供的钩子机制,允许在工具执行前拦截并检查操作。与权限拒绝规则不同,PreToolUse hook 可以执行自定义脚本,根据文件路径、命令内容等动态决定是否放行,并可通过退出码或 JSON 输出阻止操作。例如,可编写脚本检查 Edit 工具的目标路径是否匹配 generated/ 模式,若匹配则返回非零退出码并输出拒绝原因。该机制比静态权限规则更灵活,但需注意 hook 脚本的性能开销,避免在频繁操作中引入明显延迟。

多源验证与工具生态视角

本文的机制描述交叉验证了 Codex 与 Claude Code 两个官方文档来源:Codex 的指令链是从根到当前目录一次性构建,Claude Code 采用启动时向上加载加运行中按需加载的混合模型。这个差异是社区中大量"规则不生效"报告的根因。

据 agents.md 社区和多篇开发者博客的一致观察,AGENTS.md 作为跨工具共享格式已被多数 AI 编程工具采纳,但各工具对嵌套文件的处理策略并不统一。作者本人的 Hermes IDE 项目实践中,工作树只包含 git 追踪的文件,被忽略的 CLAUDE.local.md 或 AGENTS.override.md 不会出现在新工作树中,可通过 worktree recipe 的 copy 键把这些文件复制进去。

有意思的是,加载规则随版本变化,文档中已列出多个版本分界点。每当新增规则文件、移动文件位置或升级代理版本,都应重复第四步的验证。

核心结论

跨工具共享规则文件的关键在于理解两种加载模型的差异:Codex 依赖启动位置,Claude Code 依赖文件触达时机。用 AGENTS.md 承载真实规则,用单行 CLAUDE.md 做导入适配,再用验证手段确认加载结果,最后用权限规则兜底强制约束。这套组合在 monorepo 中能显著减少"规则写了但没生效"的挫败感。

分享:

相关推荐