概述
AGENTS.md 是 Codex 的项目级指令文件,用于向模型传递用户自定义的编码规范、项目说明、构建步骤等指导信息。系统会沿目录层级发现并加载这些文件,将其序列化后注入对话上下文,并在 compaction 时正确过滤和重新注入。
1. 文件发现:discover_project_doc_paths()
文件:core/src/project_doc.rs:207
1.1 发现算法
- 从当前工作目录(
config.cwd)向上遍历,寻找project_root_markers(默认为.git)标识的项目根目录 - 收集从项目根到当前工作目录路径上每个目录中的 AGENTS.md 文件
- 不会遍历超过项目根目录
- 如果没有找到项目根标记,只搜索当前工作目录
1.2 文件优先级
每个目录下按以下顺序搜索,找到第一个即停止(candidate_filenames(),第 294 行):
| 优先级 | 文件名 | 说明 |
|---|---|---|
| 1 | AGENTS.override.md |
本地覆盖,不纳入版本控制 |
| 2 | AGENTS.md |
标准项目文档,通常纳入版本控制 |
| 3 | config.project_doc_fallback_filenames 中的文件 |
额外配置的备选文件名 |
1.3 关键常量
pub const DEFAULT_PROJECT_DOC_FILENAME: &str = "AGENTS.md";
pub const LOCAL_PROJECT_DOC_FILENAME: &str = "AGENTS.override.md";
2. 文件加载:read_project_docs()
文件:core/src/project_doc.rs:149
2.1 加载流程
- 检查
project_doc_max_bytes配置,为 0 则直接返回None - 调用
discover_project_doc_paths()获取文件列表 - 按顺序逐个读取文件,用
remaining变量跟踪剩余字节预算 - 超出预算的文件会被截断并打印警告日志
- 将所有非空文件内容用
"\n\n"连接返回
2.2 拼接顺序
从项目根目录到当前工作目录,例如:
<项目根/AGENTS.md 内容>
<子目录/AGENTS.md 内容>
<当前目录/AGENTS.md 内容>
3. 内容组装:get_user_instructions()
文件:core/src/project_doc.rs:81
这是最终组装 user_instructions 的入口函数,将多种内容拼接成一个完整字符串:
config.user_instructions // 用户直接配置的指令
--- project-doc ---
<AGENTS.md 内容> // read_project_docs() 的结果
<JS REPL 指令> // 如果启用了 JsRepl feature
<插件能力摘要> // render_plugins_section()
<Skills 文档> // render_skills_section()
<层级代理说明> // 如果启用了 ChildAgentsMd feature
各部分之间用 "\n\n" 或 PROJECT_DOC_SEPARATOR("\n\n--- project-doc ---\n\n")分隔。
4. 序列化与包装
文件:core/src/instructions/user_instructions.rs
4.1 UserInstructions 结构体
pub(crate) struct UserInstructions {
pub directory: String, // 当前工作目录路径
pub text: String, // get_user_instructions() 返回的完整内容
}
4.2 序列化格式
serialize_to_text() 将内容包装为带首尾标记的格式:
# AGENTS.md instructions for /path/to/project
<INSTRUCTIONS>
...实际内容...
</INSTRUCTIONS>
4.3 转换为 ResponseItem
通过 AGENTS_MD_FRAGMENT.into_message() 转为 user role 的 ResponseItem:
impl From<UserInstructions> for ResponseItem {
fn from(ui: UserInstructions) -> Self {
AGENTS_MD_FRAGMENT.into_message(ui.serialize_to_text())
}
}
首尾标记是后续 compaction 过滤时识别该消息的关键依据。
5. 注入对话上下文
文件:core/src/codex.rs:3124
注入发生在 build_initial_context() 中,将序列化后的内容作为 user role 消息加入 contextual_user_sections:
if let Some(user_instructions) = turn_context.user_instructions.as_deref() {
contextual_user_sections.push(
UserInstructions {
text: user_instructions.to_string(),
directory: turn_context.cwd.to_string_lossy().into_owned(),
}
.serialize_to_text(),
);
}
5.1 注入时机
| 场景 | 触发条件 |
|---|---|
| 新会话启动 | InitialHistory::New,reference_context_item 初始为 None |
| 恢复会话 | InitialHistory::Resumed,重建历史后追加 |
| Compaction 之后 | reference_context_item 被重置为 None |
5.2 正常多轮对话
reference_context_item 为 Some 时走增量路径 build_settings_update_items(),不会重复注入 AGENTS.md 内容。
6. Compaction 过滤
Compaction 时需要确保 AGENTS.md 内容不被压缩模型改写,分三层处理。
6.1 第一层:按 role 丢弃 developer 消息
文件:core/src/compact_remote.rs:190
fn should_keep_compacted_history_item(item: &ResponseItem) -> bool {
match item {
ResponseItem::Message { role, .. } if role == "developer" => false,
// ...
}
}
6.2 第二层:识别并丢弃伪用户消息
文件:core/src/contextual_user_message.rs
AGENTS.md 内容以 user role 注入,通过首尾标记匹配来识别:
pub(crate) const AGENTS_MD_START_MARKER: &str = "# AGENTS.md instructions for ";
pub(crate) const AGENTS_MD_END_MARKER: &str = "</INSTRUCTIONS>";
is_contextual_user_fragment() 检查文本是否同时以开始标记开头、结束标记结尾。匹配成功则 parse_turn_item() 返回 None,该消息被 should_keep_compacted_history_item() 过滤掉。
完整的伪用户消息类型列表:
| Fragment 类型 | 开始标记 | 结束标记 |
|---|---|---|
| AGENTS.md 指令 | # AGENTS.md instructions for |
</INSTRUCTIONS> |
| 环境上下文 | <environment_context> |
</environment_context> |
| Skills | <skill> |
</skill> |
| Shell 命令 | <user_shell_command> |
</user_shell_command> |
| Turn 中止 | <turn_aborted> |
</turn_aborted> |
| Subagent 通知 | <subagent_notification> |
</subagent_notification> |
6.3 第三层:重新注入
过滤完成后 reference_context_item 被置为 None,下一个 turn 自动走 build_initial_context() 全量路径,从内存中的 user_instructions 重新注入最新内容。
7. Hierarchical Agents 层级规则
Feature Flag:Feature::ChildAgentsMd(默认关闭)
模板文件:core/hierarchical_agents_message.md
启用后在 get_user_instructions() 末尾追加层级说明,核心规则:
- AGENTS.md 管辖其所在目录及所有子目录
- 修改文件时必须遵守所有覆盖该文件的 AGENTS.md
- 深层目录的 AGENTS.md 覆盖上层的同名指令
- 用户/系统/开发者在 prompt 中直接给出的指令优先级高于任何 AGENTS.md
即使项目中没有 AGENTS.md 文件,该说明也会被追加。
8. CLAUDE.md → AGENTS.md 迁移
文件:core/src/external_agent_config.rs
处理从 Claude Code 迁移到 Codex 的场景。
8.1 检测源文件
find_repo_agents_md_source() 按顺序检查:
<repo_root>/CLAUDE.md
<repo_root>/.claude/CLAUDE.md
Home 目录级别检查 ~/.claude/CLAUDE.md。
8.2 迁移逻辑
import_agents_md() 的流程:
8.3 术语重写
rewrite_claude_terms() 执行大小写不敏感的词边界替换:
| 原始术语 | 替换为 |
|---|---|
claude.md |
AGENTS.md |
claude code / claude-code / claude_code / claudecode / claude |
Codex |
替换使用词边界检测(is_word_byte),避免误替换单词内部的匹配。
9. 配置项
文件:core/src/config/mod.rs
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
project_doc_max_bytes |
usize |
PROJECT_DOC_MAX_BYTES |
AGENTS.md 总字节数上限,超出截断 |
project_doc_fallback_filenames |
Vec<String> |
[] |
除 AGENTS.md 外额外搜索的备选文件名 |
project_root_markers |
Vec<String> |
[".git"] |
项目根目录标识 |
10. 关键文件索引
| 文件 | 职责 |
|---|---|
core/src/project_doc.rs |
文件发现、加载、内容组装 |
core/src/instructions/user_instructions.rs |
序列化与包装(加首尾标记) |
core/src/codex.rs |
注入对话上下文、全量/增量判断 |
core/src/contextual_user_message.rs |
伪用户消息标记定义与识别 |
core/src/compact_remote.rs |
Compaction 后过滤逻辑 |
core/src/external_agent_config.rs |
CLAUDE.md → AGENTS.md 迁移 |
core/hierarchical_agents_message.md |
层级规则说明模板 |
core/src/context_manager/updates.rs |
增量 diff 更新逻辑 |
core/src/context_manager/history.rs |
reference_context_item 状态管理 |
11. 完整流程图
文件系统 内存 对话上下文
───────── ──── ────────
AGENTS.override.md ─┐
AGENTS.md ──────────┤
(多层目录) │
▼
discover_project_doc_paths()
│
▼
read_project_docs() ──→ 拼接多文件内容
│
▼
get_user_instructions() ──→ 组合 config.instructions
│ + AGENTS.md
│ + plugins/skills
│ + hierarchical message
▼
存入 Session.user_instructions(会话期间不变)
│
▼
┌──────────────┴──────────────┐
│ │
全量注入 增量更新
(reference_context_item (reference_context_item
== None) == Some)
│ │
▼ ▼
build_initial_context() build_settings_update_items()
│ (不含 AGENTS.md,只含配置 diff)
▼
UserInstructions::serialize_to_text()
│
▼
包装为带标记的 user role 消息:
"# AGENTS.md instructions for ..."
"...</INSTRUCTIONS>"
│
▼
注入对话 ──→ 模型可见
║
║ Compaction 时
▼
should_keep_compacted_history_item()
│
├─ developer 消息 → 丢弃
├─ 匹配标记的 user 消息 → 丢弃
└─ 真实用户消息 → 保留
│
▼
reference_context_item = None
│
▼
下一 turn → 重新全量注入(最新内容)