直接
/init生成的 CLAUDE.md,可能比没有更糟。但一份精心设计的 context file,能让 AI 编程助手的效率提升数倍。
一、一个反直觉的结论
苏黎世联邦理工学院 SRI 实验室(Software Reliability Lab)的团队做了一项大规模实验(论文:Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents?,arXiv:2602.11988):在 SWE-bench Lite(业界评估 AI 修 bug 能力的标准 benchmark)和自建的 AgentBench(138 个任务、12 个有 context file 的 repo)上,分别测试 Claude Code、Codex、Qwen Code 三套 agent,对比三种配置——没有 context file、用 /init 自动生成、开发者手写。
结果出人意料:
- LLM 自动生成的:平均降低 3% 成功率,但增加 20% 推理成本(多跑 2-4 步)
- 开发者手写的:只有微弱的正向效果(约 4%),但同样会增加成本
- 不论用什么模型生成、用什么 prompt 模板,结论都差不多
换句话说:如果你直接 /init 出来一份 CLAUDE.md 然后就放着不管,可能比没有更糟。
二、为什么会这样?不是 agent 不听话,是它太听话
论文做了追踪分析,发现了几个有意思的现象:
- 有 context file 时,agent 跑更多测试(
pytest使用增加) - 探索更多文件(
grep、read操作增加) - 推理 token 多 22%
- 但找到第一个相关文件的步数并没有减少
为什么写多了反而坏事?因为 LLM 每次交互有 Context Window(上下文窗口) 限制——比如 Claude 3.5 Sonnet 大约能处理 200K token。AGENTS.md 的每一行都在跟代码、错误信息、对话历史抢这个配额。写进去的内容如果没帮助,就等于挤掉了真正有用的上下文。
更有意思的是指令依从度数据:被写进 AGENTS.md 的工具,使用次数会从几乎为零跳到平均每次任务 1.6-2.5 次。有开发者算过,提到的工具被使用概率高 160 倍。
agent 确实会遵守 AGENTS.md 里的指令——问题是你写进去的「不必要指令」反而让任务变复杂了。
还有一个讽刺的发现:研究团队把 repo 里所有 .md、docs/、示例代码都删掉之后,LLM 自动生成的 context file 才开始发挥正面效果。意思是这些自动生成的内容很大程度上只是在重复已有文档而已。
三、该写什么?一个简单的判断标准
Phil Schmid 根据研究归纳成三块:
- WHAT(是什么):技术栈、项目结构、各部分做什么。对于 Monorepo 来说,这部分特别重要(因为 agent 不容易自己摸出来)
- WHY(为什么):项目目的、关键组件存在的理由。帮 agent 理解意图而不只是结构
- HOW(怎么做):怎么构建、测试、验证变更。特别是非预期的工具链——比如这个项目用
uv而不是pip、用bun而不是npm
一个更精确的判断标准:如果这件事 agent 自己读代码就能发现,那就不用写。
值得写的,反过来都是 agent 没办法从代码推出来的——非预期的工具链选择、历史包袱、团队共识、外部约束、踩过的坑。
四、不该写什么?大多数人塞进去的其实没帮助
自动生成的 AGENTS.md 最常包含但最没用的几类:
重述 README 和已有文档的内容 前面提到的研究已经证明,大部分自动生成的内容只是在「复述已有文档」,等于白写。
大段架构概览 / 目录列表
100% 的 LLM 自动生成的 AGENTS.md 都会包含「我们的架构分三层、handler 处理 X、service 处理 Y……」,但数据显示这对 agent 找对文件的速度没有任何改善。agent 一个 ls 就能拼出结构,不需要你告诉它。
看得到的技术栈细节
package.json、pyproject.toml 已经写了,agent 看得到。「我们用 React」是废话,「我们用 React 18 但 hooks 走自己的 wrapper」才有价值。
代码风格规定 「用 4 空格缩进」、「camelCase 命名」这种规则交给 linter / formatter 处理,比塞进 AGENTS.md 便宜、确定、不吃上下文配额。前沿模型大概只能可靠遵守 150-200 条独立指令——这是 Claude Code 等工具的系统提示词结构决定的,超出这个数量模型会开始"选择性遗忘"——别拿来写 linter 做得更好的事。
空泛的标语 「写 clean code」、「good naming」、「performance matters」——模型训练数据里一堆,写了等于没写,还占依从度配额。
详细 API 文档 用链接带到外部文档就好,不要把整个 API 表贴进来。
只在特定场景才用到的指令 「部署时要先 X」、「跑 e2e 测试前要 Y」这种任务指令应该分到专门的子文件,用条件区块或 import 带进来,不要塞在主文件让每次会话都加载。
研究最后的结论很精准:「人写的 context file 应该只描述最低限度的需求」。多写不会加分,反而扣分。
五、作者本人的澄清
论文火了之后,作者 Thibaud Gloaguen 在 LinkedIn 上亲自发帖澄清,点名一些大 V 把研究结论讲歪了。他重申:
我们证明的是 LLM 自动生成的 AGENTS.md(例如 Claude Code 的
/init)没有提升任务完成率,而且让 agent 行为变得更「吵」。但简洁的、人写的 AGENTS.md 是可以帮到 coding agent 的。重点在于:如果你决定要在每次交互前都加上某些指令,先问自己这些指令对你大多数任务真的需要吗?如果不是,看情况再加可能更好。
也就是说,真正的反派是 /init 和「塞越多越好」的心态,不是 AGENTS.md 本身。
几个值得补充的反面意见:
- 研究选的 repo 样本可能有偏差:选的大多是「近期的小型 LLM 周边项目」,对于有大量领域知识的中大型项目,人写的高质量 AGENTS.md 价值会大很多
- 只测了测试通过率,没测代码质量:如果 agent 把测试弄绿了但完全忽略项目规范和架构惯例,这篇研究是看不出来的
- Vercel 的案例反而证实有效:在「教 agent 训练数据里没有的最新框架知识」这个场景下,context file 效果非常显著
- 写 AGENTS.md 的副作用是逼你思考:有开发者说「维护 CLAUDE.md 三个月了,效果比想象中明显,但不是因为真的给了什么 context——而是写的过程逼你把脑袋里的隐性知识讲清楚」
六、GitHub 从 2500+ repos 学到了什么
GitHub 观察了跨 Claude Code、Codex、Copilot 的 2500 多个 agents.md,归纳出 5 个原则:
- 可执行指令放前面——
npm test、pytest -v这种 agent 会反复查的东西,放开头最容易被找到 - 代码示例 > 长篇解释——一个示范你 codebase 风格的真实片段,胜过几段抽象描述
- 明确的边界——三层:永远要做、要先问、绝对不要做。最常见也最有用的一条:「绝对不要 commit secrets」
- 具体的技术栈细节——不要写「React 项目」,要写「React 18 + TypeScript + Vite + Tailwind CSS」并标版本
- 6 个核心领域:指令、测试、项目结构、代码风格、git 工作流、边界
七、为什么 Claude 会忽略你的 CLAUDE.md?
HumanLayer 的 Dex Horthy 写了一篇关键观察:Claude Code 的系统提示词会把 CLAUDE.md 包在一个 <system_reminder> 里,并明确告诉模型「以下内容可能跟你的任务相关,也可能不相关」。
这就是为什么当你的 CLAUDE.md 越写越长,agent 会越来越常忽略某些段落——它真的会主动过滤。
解法是用条件区块——把场景特定的指令包起来,只在相关场景才加载。各工具的实现方式不同:
Claude Code:<important if> 标签
<important if="writing or modifying tests">
- Use createTestApp() helper for integration tests
- Mock database with dbMock
</important>
Cursor:.mdc 文件的 globs 匹配
Cursor 用独立的 .mdc 文件(放在 .cursor/rules/ 目录下),通过 YAML 前置信息控制条件加载:
---
description: "测试相关规范"
globs:
- "src/**/*.test.ts"
- "tests/**/*.ts"
alwaysApply: false
---
- Use createTestApp() helper for integration tests
- Mock database with dbMock
当上下文中出现匹配 globs 的文件时,规则自动激活。四种触发模式:
| 模式 | alwaysApply | globs | 行为 |
|---|---|---|---|
| Always | true | — | 始终加载 |
| Auto Attached | false | 已设置 | 文件匹配 glob 时自动加载 |
| Agent Requested | false | 未设置 | AI 根据 description 自行判断 |
| Manual | false | 未设置 | 用户在 Chat 中 @ruleName 手动触发 |
Codex CLI:不支持条件语法
Codex 的 AGENTS.md 是纯 Markdown,没有条件加载机制。替代方案是利用层级合并——在子目录放一份精简的 AGENTS.md,agent 在该目录工作时自动加载。
核心东西(技术栈、目录结构、项目身份)还是要常驻,但测试、部署这种只在特定场景才用得到的,加上条件就比较不会被当成可选的。
HumanLayer 自己的 CLAUDE.md 只有 60 行。
八、大项目怎么办?分层,不要写一份大文件
当你的 AGENTS.md 超过 150-200 行(大约对应 150-200 条独立指令),社群普遍建议改用模块化:
- 每个子目录一份 AGENTS.md:Codex CLI、Cursor、Copilot 都支持层级合并——agent 在
packages/api/工作时,会把根目录、中间层、和packages/api/AGENTS.md都读进来。OpenAI 自己的 Codex repo 就用了 88 个 AGENTS.md - import 语法:Claude Code 官方支持
@path/to/file引用语法,把领域特定的指引拆出去,主文件只放路标。被引用的文件可以是任意位置的 Markdown,支持相对路径、绝对路径、甚至@~/.claude/my-rules.md引用个人配置,递归引用最大深度 5 层。注意:反引号包裹的@不会被解析为导入(如@anthropic-ai/claude-code)
一个典型的目录结构和引用方式:
project-root/
CLAUDE.md # 主文件,用 @ 导入子文件
docs/
git-workflow.md # Git 工作流规范
api-conventions.md # API 设计约定
agent_docs/ # 领域特定指引(目录名随意)
database.md # 数据库操作规范
testing.md # 测试策略
CLAUDE.md 中这样写:
# 项目概览
See @README for project overview.
# 领域规范
- 数据库操作: @agent_docs/database.md
- 测试策略: @agent_docs/testing.md
- Git 工作流: @docs/git-workflow.md
# 个人偏好
- @~/.claude/my-project-instructions.md
注意:这是 Claude Code 的官方功能,Codex CLI 不支持
@导入语法。Codex 的替代方案是利用层级合并——在子目录放独立的 AGENTS.md,agent 在该目录工作时自动加载。
- 跨工具用 symlink:写一份 AGENTS.md 当作唯一来源,CLAUDE.md、.cursorrules 都用 symlink 指过去,不要维护平行版本。创建 symlink 的命令:
- macOS / Linux:
ln -s AGENTS.md CLAUDE.md - Windows(管理员 PowerShell):
New-Item -ItemType SymbolicLink -Path CLAUDE.md -Target AGENTS.md
兼容性提醒:symlink 只适用于格式相同的文件。Cursor 的
.mdc文件有 YAML 前置信息,跟标准 AGENTS.md 格式不同,不能直接 symlink 复用。对于 Cursor,建议维护一份纯 Markdown 的 AGENTS.md 作为源文件,然后为.mdc编写一个轻量转换脚本,或者直接接受两套文件的少量重复。 - macOS / Linux:
九、真正的红线要靠 hooks,不能只靠 CLAUDE.md
有个常被忽略的数字:根据社区开发者的实测反馈,Claude Code 只有约 60% 的时间能遵守 80% 的 CLAUDE.md 规则。在长会话中情况更糟——上下文压缩后,规则要么被精简要么直接消失。意思是「绝对不能做的事」如果只写在 CLAUDE.md 里,会有相当概率 agent 还是会做。所以成熟的做法是把规则分两层:
- CLAUDE.md(建议性):写惯例、偏好、流程——约 60% 的依从率够用了
- hooks(强制性):真正的硬规则写在
.claude/settings.json的 hooks 里。例如PreToolUse回 exit code 2 是唯一能真的拦下 agent 动作的方式。「不能 push 到 main」、「edit 后一定要 lint」这种事用 hook 写,不是放在 CLAUDE.md 里许愿
举个例子,这是阻止 agent 直接 push 到 main 分支的 hook 配置:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'git push.*main'; then echo '❌ 禁止直接 push 到 main,请创建 PR' >&2; exit 2; fi"
}
]
}
]
}
}
exit 2 是关键——非零退出码会让 Claude Code 终止这次工具调用,并把你的错误信息展示给 agent。
Hooks 事件类型全景
Claude Code 的 hooks 系统支持 4 个生命周期事件:
| 事件 | 触发时机 | 能否阻断 | 典型用途 |
|---|---|---|---|
PreToolUse | 工具调用参数创建后、执行前 | ✅ exit 2 阻断 | 禁止 push main、强制 lint |
PostToolUse | 工具成功执行后 | ⚠️ 可反馈但无法回滚 | 格式化、日志记录 |
Notification | 发送通知时(等待用户输入等) | ❌ | 自定义通知(如发 Slack) |
Stop | Claude 准备结束响应时 | ✅ exit 2 阻止停止 | 检查是否遗漏测试 |
matcher 支持精确匹配工具名(Bash、Edit、Write、Read、Grep、Glob、WebFetch、WebSearch 等)和正则表达式(如 "Write|Edit|MultiEdit" 匹配所有文件修改操作,"mcp__.*__write.*" 匹配 MCP 写操作)。省略 matcher 则对该事件的所有工具生效。
一个 settings.json 可以配置多个 hook,所有匹配的 hook 会并行执行。配置文件优先级:~/.claude/settings.json(用户级)< .claude/settings.json(项目级)< .claude/settings.local.json(本地项目级,不提交)。
有个很精准的框架:
写 CLAUDE.md 的时候,你不是在写一段 prompt,你是在写一个系统的运行时变量。它每次会话都会跟任务、时间、其他上下文互动,不是静态文字。把它从 100 行砍到 80 行不是排版,是在重构。
十、不同工具的 Context 机制差异
Claude Code、Codex、Cursor、TRAE、CodeBuddy 都支持 AGENTS.md(或等价物),但能力差异很大:
| 维度 | Claude Code | Codex | Cursor | TRAE SOLO | CodeBuddy |
|---|---|---|---|---|---|
| Context File | CLAUDE.md | AGENTS.md | .cursorrules + .cursor/rules/*.mdc | ❌ 无项目级文件 | CODEBUDDY.md |
| AGENTS.md 兼容 | ✅ 原生 | ✅ 原生 | ❌ 不支持 | ✅ 支持(桌面版) | ✅ 支持 |
| 条件语法 | <important if="..."> | ❌ 无 | .mdc 的 alwaysApply + globs | ❌ 无 | ❌ 无 |
| Hooks | ✅ 完整 | ❌ 无 | Plugin 层面 | ❌ 无 | ✅ 完全兼容 Claude Code |
| 强制拦截 | exit 2 阻断 | ❌ 无 | 有限 | ❌ 无 | exit 2 阻断 |
| 权限系统 | 基础(靠 hooks) | 沙箱模式 | .mdc 规则 | 全局规则 | allow/ask/deny 三级 |
| 记忆功能 | ❌ 无 | ❌ 无 | ❌ 无 | ❌ 无 | ✅ 跨会话持久化 |
| MCP | ✅ | ✅ | ✅ | ✅ | ✅ |
| Skill 系统 | ❌ 无 | ❌ 无 | ❌ 无 | ✅ 按需加载 | ✅ |
| 斜杠命令 | ✅ 18 个内置命令 | ❌ 无 | ❌ 无 | ✅ | ✅ |
| 子代理 | ✅ Subagents | ❌ 无 | ✅ Parallel Agents | ✅ SOLO Coder | ✅ |
| IDE 形态 | CLI only | CLI only | VS Code fork | 独立应用 | IDE + CLI |
选择建议
| 你的需求 | 推荐工具 | 原因 |
|---|---|---|
| 需要强制规则(如禁止 push) | Claude Code / CodeBuddy | 完整的 hooks 系统 |
| 追求极简、快速上手 | Codex | 零配置,开箱即用 |
| IDE 深度集成、可视化 | Cursor / TRAE SOLO | 完整的 IDE 体验 |
| 跨会话记忆、团队共享 | CodeBuddy | 原生 memory + teamMemory |
| 多设备协同、移动办公 | TRAE SOLO | 三端联动,云端执行 |
| 国内网络、中文优化 | CodeBuddy / TRAE SOLO | 腾讯云/字节跳动基础设施 |
十一、趋势观察:AGENTS.md 生态正在标准化
Claude Code 的 CLAUDE.md、OpenAI Codex 的 AGENTS.md、CodeBuddy 的 CODEBUDDY.md——虽然文件名不同,但核心思想高度一致:给 AI 一个"项目记忆植入"。
这个生态正在经历三个层面的标准化:
1. 文件格式趋同
所有工具都选择了 Markdown 作为载体,因为:
- 对人可读(工程师愿意写)
- 对 AI 可解析(不需要专用格式)
- 可版本控制(跟代码一起维护)
Trae 和 CodeBuddy 甚至明确支持跨工具复用 AGENTS.md,这意味着"写一份,到处用"正在成为现实。
2. Hooks 成为"硬规则"的事实标准
Claude Code 首创了 PreToolUse hooks,CodeBuddy 完全兼容了这一规范。这是比"写进文档"更可靠的约束方式——exit 2 能真正阻断 agent 的动作,而不是靠"请遵守"这种软性请求。
未来,任何严肃的企业级 AI 编程工具,几乎必然需要 hooks 级别的强制能力。
3. 从"静态文档"到"动态上下文"
早期的 AGENTS.md 是"写死"的——每次会话全量加载,写多了反而坏事。
新一代工具在解决这个问题:
- TRAE SOLO 的 Skill:按需加载,AI 先判断相关性再加载完整内容
- CodeBuddy 的 Memory:跨会话持久化,自动选择相关记忆注入上下文
- MCP 协议:动态检索外部上下文,替代静态文档
这意味着 AGENTS.md 不会消失,但它的角色会从"唯一的上下文来源"变成"上下文生态的一部分"——跟记忆、技能、MCP 检索协同工作。
一个预测
未来 1-2 年内,"项目级 AI 上下文"将形成三层架构:
- 记忆层(Memory):跨会话持久化,自动维护
- 规则层(Rules/Hooks):强制约束,保障安全
- 检索层(MCP/RAG):动态获取,按需加载
AGENTS.md 会留在规则层,但它的内容会大幅精简——只写"不写就会错的东西",其他的交给记忆和检索。
这个趋势其实已经在发生——现有工具的能力已经可以映射到这三层:
| 层 | 正在实现的工具 | 具体能力 |
|---|---|---|
| 记忆层 | CodeBuddy | memory + teamMemory 跨会话持久化 |
| 规则层 | Claude Code / CodeBuddy | Hooks(PreToolUse/PostToolUse/Stop)+ AGENTS.md |
| 检索层 | 所有支持 MCP 的工具 | 通过 MCP 协议动态检索外部上下文 |
| 按需加载 | TRAE SOLO / CodeBuddy | Skill 系统根据任务相关性按需注入 |
对普通开发者来说,现在就可以做的准备:把 AGENTS.md 精简到只保留"不写就会错的东西",把场景特定的指引拆到子文件或 MCP 服务,把硬规则迁移到 hooks。
十二、总结
这几篇研究加起来,其实在不同层面讲同一件事:
- 默认保持精简——目标 300 行以内,能用 60 行更好。每一行都会吃掉每次会话的上下文,要值得
- 重点写「不写就会错的东西」——用
uv不是pip、用bun不是npm,这种非预期的工具链比结构概览有用得多 - 写偏好比写事实有价值——技术栈和结构 agent 自己摸得到,但「测试完再给我网址」、「用 Exa 不要用内置 search」这种行为偏好它摸不到
- 采用渐进式披露——主文件放路标,细节分到子文件。对复杂项目甚至要走到三层架构搭配 MCP 检索
- 不要
/init出来就放着——数据显示自动生成的会降低成功率,要人工修改 - 真正的红线用 hooks,不要许愿——CLAUDE.md 是建议,hooks 才是强制
- 把文档当基础设施,不是 README——文档过期 agent 就会出错,要纳入维护周期
最有启发的一点是:写 CLAUDE.md 的本质,是在跟一个「每次都失忆但会完全相信文档」的 agent 工作。你不是在写给未来会加入团队的工程师看,而是在设计一个 agent 在开始工作前的记忆植入。
这个视角换过来,什么该写、什么不该写,自然就清楚了。
参考资料
- How LLM-Generated Context Files Affect Coding Agents - LogicStar, ETH Zurich
- How to Write a Great agents.md - Lessons from Over 2,500 Repositories - GitHub Blog
- Stop Claude from Ignoring Your CLAUDE.md - HumanLayer
- Writing a Good CLAUDE.md - HumanLayer
- Codified Context 论文 - 三层架构 + MCP 检索
- Claude Code Hooks 官方文档
- OpenAI Codex CLI 官方文档
- Cursor Rules 官方文档
- TRAE SOLO 官方文档
- CodeBuddy CLI 官方文档