Yoda
参考Agent 设计指南上下文

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)严格分四步:

  1. ManagedgetMemoryPath('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)。
  2. User~/.claude/CLAUDE.md + ~/.claude/rules/*.md,仅当 userSettings source 启用(claudemd.ts:826-847);用户级文件允许无条件 external import(claudemd.ts:833 注释 "User memory can always include external files")。
  3. 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)。
  4. Local:每级目录的 CLAUDE.local.md,挂 localSettings source 开关(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 messagecontext.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):

  1. 从 cwd 向上找 project_root_markers(默认 [".git"],可配置;空列表禁用向上遍历)确定项目根;找不到 marker 则只看 cwd。
  2. 项目根向下到 cwd 的每个目录,按 AGENTS.override.mdAGENTS.mdproject_doc_fallback_filenames(用户可配,如 CLAUDE.md!)的顺序取第一个命中的文件(agents_md.rs:290-305),每目录最多一个。
  3. 全局指令:$CODEX_HOME/AGENTS.override.mdAGENTS.mdload_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:1codex/docs/agents_md.md [一手文档])。

compaction 后重建core/src/compact.rs:301-303 在压缩后调用 build_initial_context 重新插入 AGENTS.md + environment context,故项目提示词在压缩后不丢失。

差异矩阵

维度Claude CodeCodex CLI
文件名CLAUDE.md / .claude/CLAUDE.md / CLAUDE.local.md / .claude/rules/*.mdAGENTS.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 globproject_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   # 进入会话后执行 /memory

Harness 接入建议(Yoda 实践)

  1. 检测矩阵: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。同时解析 claudeMdExcludesproject_doc_max_bytes/project_doc_fallback_filenames,否则显示的清单与实际注入不符。
  2. 校验/lint 规则(全部源码可证):① CC @import 目标不存在或为二进制扩展名 → 报错;外部 import 未批准 → 提示首次运行会弹对话框;import 链 >4 跳 → 截断告警。② Codex 全部 AGENTS.md 字节和 >32KiB → 标红并给出会被截断的文件;③ 单文件 >200 行(CC 文档建议)→ 提示拆分到 rules/。④ 双 runtime 项目检查 CLAUDE.md 与 AGENTS.md 内容漂移(推荐 symlink 或 @AGENTS.md import 收敛为单一来源)。
  3. 预览有效内容:Codex 用 codex debug prompt-input 拿到逐目录条目;CC 可在 Yoda 内复刻 getMemoryFiles 的确定性算法(路径走查 + @import 展开 + 注释剥离),并用 InstructionsLoaded hook(docs/memory.md:412)做运行时审计对账。
  4. 跨 runtime 写入策略:Yoda 生成项目指令时写 AGENTS.md 为主、CLAUDE.md 仅放一行 @AGENTS.md——这是两边官方都认可的互通形态,且避免 Codex 的 fallback 配置依赖。
  5. 踩坑: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.tscontext.ts
  • CC 文档:claude-code-docs/docs/memory.md(CLAUDE.md files / rules / managed 全节)
  • Codex 源码:codex/codex-rs/core/src/agents_md.rscore/src/context/user_instructions.rsconfig/src/config_toml.rsprotocol/src/prompts/base_instructions/default.md
  • Codex 文档:codex/docs/agents_md.mdcodex/codex-rs/config.md

On this page