Project Prompt(CLAUDE.md / AGENTS.md)
CC 是四层级 + @import + rules 目录的「叠加全收」体系;Codex 是 root→cwd 单链 + 32KiB 预算的「精简拼接」体系
Project Prompt(CLAUDE.md / AGENTS.md)
结论
CC 的项目提示词是一套多层级叠加体系:Managed(组织策略)→ User(~/.claude/CLAUDE.md)→ Project(从文件系统根向下走到 cwd 的每级 CLAUDE.md / .claude/CLAUDE.md / .claude/rules/*.md)→ Local(CLAUDE.local.md),全部拼接共存、互不覆盖,支持 @path 递归 import(最大 5 层深度)、HTML 注释剥离、frontmatter paths: 条件规则、claudeMdExcludes 排除;没有硬大小上限,仅 40000 字符的「建议值」用于告警。Codex 的 AGENTS.md 则是单链最简体系:用 project_root_markers(默认 .git)定位项目根,从根到 cwd 每个目录取一个文件(AGENTS.override.md 优先于 AGENTS.md,再到 fallback 文件名),加上全局 ~/.codex/AGENTS.md,总量受 project_doc_max_bytes(默认 32KiB)硬截断;没有 import 语法,子目录嵌套语义靠 base instructions 里的「AGENTS.md spec」文字约定交给模型自觉执行。两者殊途同归的一点:项目提示词都不进 system prompt,CC 走 user message,Codex 走 contextual user message。
研究问题
- 两家的发现(discovery)规则、层级与优先级如何?
- import / 条件规则 / 排除等扩展语法支持度?
- 大小限制和截断行为?合并语义(覆盖 vs 拼接)?
各 Agent 设计与实现
Claude Code
来源:重构源码
src_2026-03-31/utils/claudemd.ts(1479 行,整个体系的核心文件)。[一手源码]
发现顺序(getMemoryFiles,claudemd.ts:790-1075)严格分四步:
- Managed:
getMemoryPath('Managed')(macOS 为/Library/Application Support/ClaudeCode/CLAUDE.md,docs/memory.md:58 [一手文档])+ managed rules 目录。最先加载,且isClaudeMdExcluded对 Managed 类型直接返回 false(claudemd.ts:547-550)——策略文件无法被用户排除。另支持 managed-settings.json 内联claudeMd键(docs/memory.md:275-289)。 - User:
~/.claude/CLAUDE.md+~/.claude/rules/*.md,仅当userSettingssource 启用(claudemd.ts:826-847);用户级文件允许无条件 external import(claudemd.ts:833 注释 "User memory can always include external files")。 - Project:从原始 cwd 向上收集目录链后反转为根→cwd 顺序处理(claudemd.ts:850-857, 878),每级尝试
CLAUDE.md、.claude/CLAUDE.md、.claude/rules/*.md。即越靠近 cwd 的文件越后出现(模型最后读到)。嵌套 worktree 有去重逻辑避免主 repo 文件被加载两次(claudemd.ts:859-884,引 issue #29599)。 - Local:每级目录的
CLAUDE.local.md,挂localSettingssource 开关(claudemd.ts:922-933)。
之后追加 AutoMem / TeamMem 入口文件(属记忆体系,见 memory 章)。--add-dir 目录的 CLAUDE.md 默认不加载,需 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1(claudemd.ts:936-977)。
@import 语法:正则 /(?:^|\s)@((?:[^\s\\]|\\ )+)/(claudemd.ts:459),支持转义空格、# 截断、相对路径按所在文件目录解析;只允许文本扩展名白名单(TEXT_FILE_EXTENSIONS,claudemd.ts:96-227,防止二进制进上下文);MAX_INCLUDE_DEPTH = 5(claudemd.ts:537),根文件 depth=0,即最多 4 跳 import(与 docs/memory.md:97 "maximum depth of four hops" 一致)。External import(指向 cwd 之外)首次需要用户批准,记录在项目配置 hasClaudeMdExternalIncludesApproved(claudemd.ts:797-801)。
内容变换:块级 HTML 注释 <!-- --> 在注入前剥离(stripHtmlComments,claudemd.ts:292;代码块内保留);.claude/rules/*.md 的 frontmatter paths: 字段使规则变为条件规则——启动时只加载无 paths 的,有 paths 的等模型读到匹配文件时以 attachment 形式注入(claudemd.ts:765-774 的 conditionalRule 过滤 + getManagedAndUserConditionalRules)。
合并语义与包装:getClaudeMds()(claudemd.ts:1153-1195)把所有文件按加载顺序拼成一个块,每个文件加来源描述("project instructions, checked into the codebase" / "user's private project instructions, not checked in" 等),头部统一冠以指令(claudemd.ts:89-90):
const MEMORY_INSTRUCTION_PROMPT =
'Codebase and user instructions are shown below. Be sure to adhere to these instructions. IMPORTANT: These instructions OVERRIDE any default behavior...'整块作为 userContext.claudeMd 进入首条 user message(context.ts:155-189)。
大小:无硬截断;MAX_MEMORY_CHARACTER_COUNT = 40000(claudemd.ts:92)仅作为 getLargeMemoryFiles 的告警阈值。文档建议单文件 200 行以内(docs/memory.md:81)。
排除:claudeMdExcludes settings 数组按绝对路径 glob 匹配,跨 settings 层合并,并做 realpath 解析处理符号链接(claudemd.ts:552-577, 581-616)。
禁用:CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 全关;--bare 跳过自动发现但保留 --add-dir 显式指定(context.ts:162-167)。
Codex CLI
来源:
codex-rs/core/src/agents_md.rs(文件头注释即官方规格说明)。[一手源码]
发现规则(agents_md.rs:1-16 头注释 + agents_md_paths,agents_md.rs:201-288):
- 从 cwd 向上找
project_root_markers(默认[".git"],可配置;空列表禁用向上遍历)确定项目根;找不到 marker 则只看 cwd。 - 从项目根向下到 cwd 的每个目录,按
AGENTS.override.md→AGENTS.md→project_doc_fallback_filenames(用户可配,如CLAUDE.md!)的顺序取第一个命中的文件(agents_md.rs:290-305),每目录最多一个。 - 全局指令:
$CODEX_HOME/AGENTS.override.md或AGENTS.md(load_global_instructions,agents_md.rs:53-81),排在项目文件之前。
大小预算:project_doc_max_bytes 默认 32KiB(config/src/config_toml.rs:68 DEFAULT_PROJECT_DOC_MAX_BYTES = 32 * 1024),是所有文件共享的总预算——逐文件扣减 remaining,超出即字节级截断并 tracing::warn(agents_md.rs:144-187);设为 0 等于禁用 AGENTS.md。
合并语义:所有条目(含 config 注入的 user_instructions)按序拼接为一条 contextual user message,渲染格式为 # AGENTS.md instructions for <目录> + <INSTRUCTIONS>...</INSTRUCTIONS> 标记对(core/src/context/user_instructions.rs:14-24)。没有 import、没有 frontmatter、没有注释剥离。
嵌套与优先级靠 prompt 约定而非代码:base instructions 的 "AGENTS.md spec" 段写明 "More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions"、"Direct system/developer/user instructions ... take precedence over AGENTS.md",并要求模型在进入 CWD 之外/之下的目录时自行检查 AGENTS.md(protocol/src/prompts/base_instructions/default.md AGENTS.md spec 段 [一手源码])。child_agents_md feature 开启时额外注入 HIERARCHICAL_AGENTS_MESSAGE 模板强化此约定(agents_md.rs:112-117;prompts/src/agents.rs:1;codex/docs/agents_md.md [一手文档])。
compaction 后重建:core/src/compact.rs:301-303 在压缩后调用 build_initial_context 重新插入 AGENTS.md + environment context,故项目提示词在压缩后不丢失。
差异矩阵
| 维度 | Claude Code | Codex CLI |
|---|---|---|
| 文件名 | CLAUDE.md / .claude/CLAUDE.md / CLAUDE.local.md / .claude/rules/*.md | AGENTS.override.md > AGENTS.md > 可配 fallback 文件名 |
| 全局层 | ~/.claude/CLAUDE.md + ~/.claude/rules/ | $CODEX_HOME/AGENTS.md(~/.codex/) |
| 组织/策略层 | Managed CLAUDE.md(无法排除)+ settings claudeMd 键 | 无对等物 |
| 目录遍历 | 根→cwd 全链 + cwd 之下子目录懒加载 | 项目根(marker 定位)→cwd 全链;子目录靠 prompt 约定模型自查 |
| 每目录文件数 | CLAUDE.md、.claude/CLAUDE.md、rules/、CLAUDE.local.md 全收 | 候选名中第一个命中者,仅一个 |
| import | @path 递归,4 跳,文本扩展名白名单,外部 import 需批准 | 无 |
| 条件加载 | rules frontmatter paths: glob,按读文件触发 | 无 |
| 大小限制 | 无硬限制;40000 字符告警阈值 | 32KiB 硬预算(总量),超出截断 |
| 合并语义 | 全部拼接(文档明示 concatenated, not overriding) | 全部拼接 + 嵌套优先级写在模型提示词里 |
| 注入通道 | 首条 user message(带 OVERRIDE 指令前缀) | contextual user message(<INSTRUCTIONS> 标记对) |
| 排除机制 | claudeMdExcludes glob | project_doc_max_bytes = 0(全关)或不放文件 |
| 互通 | 官方建议 @AGENTS.md import 或 symlink(docs/memory.md:123-143) | project_doc_fallback_filenames = ["CLAUDE.md"] 可直接读 CLAUDE.md |
最小复现
# Codex:验证 AGENTS.md 被注入(不发请求)
mkdir -p /tmp/pp-demo && cd /tmp/pp-demo && git init -q
echo "ALWAYS reply in pirate English" > AGENTS.md
codex debug prompt-input "hi" | grep -A2 "AGENTS.md instructions"
# Codex:验证 32KiB 截断
python3 -c "print('x'*40000)" > AGENTS.md
codex debug prompt-input "hi" | python3 -c "import json,sys;d=json.load(sys.stdin);print(max(len(str(i)) for i in d))"
# CC:验证层级加载(/memory 列出本会话所有已加载的 CLAUDE.md 与 rules)
claude # 进入会话后执行 /memoryHarness 接入建议(Yoda 实践)
- 检测矩阵:Yoda 的 project prompt 面板应扫描固定路径集——CC:
{每级祖先目录}/CLAUDE.md|.claude/CLAUDE.md|CLAUDE.local.md|.claude/rules/**、~/.claude/CLAUDE.md、managed 路径;Codex:项目根到 cwd 的AGENTS{,.override}.md、~/.codex/AGENTS.md。同时解析claudeMdExcludes与project_doc_max_bytes/project_doc_fallback_filenames,否则显示的清单与实际注入不符。 - 校验/lint 规则(全部源码可证):① CC
@import目标不存在或为二进制扩展名 → 报错;外部 import 未批准 → 提示首次运行会弹对话框;import 链 >4 跳 → 截断告警。② Codex 全部 AGENTS.md 字节和 >32KiB → 标红并给出会被截断的文件;③ 单文件 >200 行(CC 文档建议)→ 提示拆分到 rules/。④ 双 runtime 项目检查 CLAUDE.md 与 AGENTS.md 内容漂移(推荐 symlink 或@AGENTS.mdimport 收敛为单一来源)。 - 预览有效内容:Codex 用
codex debug prompt-input拿到逐目录条目;CC 可在 Yoda 内复刻getMemoryFiles的确定性算法(路径走查 + @import 展开 + 注释剥离),并用InstructionsLoadedhook(docs/memory.md:412)做运行时审计对账。 - 跨 runtime 写入策略:Yoda 生成项目指令时写 AGENTS.md 为主、CLAUDE.md 仅放一行
@AGENTS.md——这是两边官方都认可的互通形态,且避免 Codex 的 fallback 配置依赖。 - 踩坑:CC 的
--add-dir不自动带 CLAUDE.md(需 env var);Codex 的AGENTS.override.md会让同目录AGENTS.md完全失效(每目录只取一个),团队成员本地 override 忘删是经典事故。
失效条件
- CC 的
tengu_paper_halyard实验转正(claudemd.ts:1158-1166:跳过 Project/Local 注入、改走其他通道)——将颠覆「CLAUDE.md 全量进首条消息」的结论 - Codex 调整
project_doc_max_bytes默认值或改为逐文件预算 - CC 的 rules/
paths:语法扩展(如新增 trigger 类型) - 任一方原生支持对方的文件名(Codex 已可经 fallback 配置读 CLAUDE.md;若 CC 原生读 AGENTS.md 则互通建议要改写)
参考资料
- CC 源码:
claude-code-source-code/src_2026-03-31/utils/claudemd.ts、context.ts - CC 文档:
claude-code-docs/docs/memory.md(CLAUDE.md files / rules / managed 全节) - Codex 源码:
codex/codex-rs/core/src/agents_md.rs、core/src/context/user_instructions.rs、config/src/config_toml.rs、protocol/src/prompts/base_instructions/default.md - Codex 文档:
codex/docs/agents_md.md、codex/codex-rs/config.md