4667 个赞、361 条评论、等了 13 个月,换来的是一行 changelog。这行字里有个反直觉的优先级规则,以及一个它压根没解决的问题。
✿ ✿ ✿
开篇
先看三个数。
4667 —— 这是 anthropics/claude-code 仓库里 issue #6235 收到的点赞数,诉求只有一句:让 Claude Code 读 AGENTS.md。它是这个仓库历史上最多人投票的 issue。
2026-08-17 —— 这个 issue 被关闭的日期。状态标的是 completed,回复是两句话,内容是"你可以用 import 或者 symlink 绕一下"。
2026-09-18 —— 一个月零一天之后,Claude Code 2.1.277 发布,changelog 里多了一行:
Added AGENTS.md support: in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead; change it under "Project instructions" in /config (not yet on Bedrock, Vertex or Foundry)
Hacker News 上那条帖子底下最高频的一句话是 "Time to delete the symlinks"。我第一反应也是这个 —— 我自己那台机器上有十几个仓库里躺着 ln -s AGENTS.md CLAUDE.md,删起来挺爽。
然后我去把文档和 release notes 挨个读了一遍,把 symlink 又放回去了。
等了13个月的一行changelog
✿ ✿ ✿
这行字里藏着一个反直觉的规则
大多数人看到"支持 AGENTS.md 了",脑子里默认的模型是合并:两个文件都读,冲突时谁更具体听谁的。
不是这样。
实际规则是互斥:项目里只要有 CLAUDE.md,AGENTS.md 就被完全忽略,哪怕它明明躺在旁边。
为什么要做成互斥而不是合并?说实话这个选择有它的道理 —— 不这么做的话,所有已经精心调过 CLAUDE.md 的仓库,会在升级到 2.1.277 的那一刻凭空多出来一份没人审过的上下文。Anthropic 选了"不惊动任何人"这条路。
但它带来一个非常实际的后果:你以为的"顺手加一个 AGENTS.md 给同事的 Codex 用",在你自己的 Claude Code 会话里是彻底隐形的。
真正的判定链是这样的:
- 01
Claude Code 从工作目录往上走,找 CLAUDE.md、.claude/CLAUDE.md、CLAUDE.local.md这三个文件 - 02
三个里只要命中任何一个,流程结束,AGENTS.md 不看 - 03
三个全都没有,才去读 AGENTS.md
注意第 1 条的措辞。~/.claude/CLAUDE.md(你的全局配置)、组织托管的 CLAUDE.md、.claude/rules/ 下的规则文件,这些不参与这个判定 —— 它们继续跟 AGENTS.md 并排加载,互不影响。
我卡在这儿好一会儿。因为我的全局 ~/.claude/CLAUDE.md 是个几百行的大文件,我一度以为它会把所有项目的 AGENTS.md 都挡掉。没有。全局的归全局,项目的归项目,这条线画得挺干净。
互斥而不是合并的判定链
✿ ✿ ✿
四种模式,你大概率想改成第二种
/config 里现在多了一项 Project instructions。四个值:
我给自己切到了 claude-md-and-agents-md。理由很简单:我的 CLAUDE.md 里有一半内容是 Claude 专属的 —— skill 路由表、subagent 派发规则、@ 引用的上下文加载指令 —— 这些搬到 AGENTS.md 里,Codex 读到只会困惑。而另一半(build 命令、测试怎么跑、哪些目录不许碰)本来就该是所有 agent 共享的。
两个都读,各写各的,是目前最省心的分法。
这个值有个设计细节值得说一下:==仓库自己的 .claude/settings.json 设不了它==。只能写在你的用户设置里、用 --settings 传、或者由组织的托管设置强制。
为什么?因为这是"策略"不是"配置"。如果允许 checkin 进仓库的文件决定 agent 怎么加载指令,那我 clone 一个陌生仓库,它就能悄悄把我的指令加载方式改掉。这条边界画得很对。
还有一类情况是你压根看不到这个选项:
-
版本低于 2.1.277 -
会话没从 Anthropic 拉 feature flag —— 走 Bedrock、Vertex、Foundry 或者第三方 provider,或者你关了遥测 -
刚装完 / 刚升级完的第一次会话 -
你设了 disableAllHooks或allowManagedHooksOnly -
你禁用了内置的 agents-md插件
前面四条里随便中一条,/config 里就没有 Project instructions 这一项。这时候老老实实用 @AGENTS.md import。
四种模式对照
✿ ✿ ✿
先别删 symlink:三个坑
这一节是我把 symlink 放回去的原因。
坑一:从子目录启动时,import 不展开。
这是个已经被维护者复现确认的 bug(issue #78697)。场景:你在 monorepo 根目录放了一个 CLAUDE.md,里面就一行 @AGENTS.md。然后你 cd packages/api && claude。
根目录那个 CLAUDE.md 会作为 ancestor 文件被加载,它自己的正文老老实实到了上下文里 —— 但那一行 import 没有展开。
你的会话拿到的"项目指令"全文,就是指向一个从来没被加载过的文件的一行字。没有任何警告。 有人在 2.1.235 到 2.1.274 之间持续记日志,12 次注入里 12 次都没展开。
symlink 不走 import 展开这条路,读文件直接跟着链接走,所以它不受这个 bug 影响。
坑二:Windows 上的假 symlink,静默失效。
git 用 mode 120000 存 symlink。而 core.symlinks=false —— Git for Windows 安装时不主动勾就是这个默认 —— 会把它 checkout 成一个普通文本文件,内容就是链接目标那个词。
于是你的 Windows 同事拿到一个 9 个字节的 CLAUDE.md,全文内容是 AGENTS.md 这一个单词。没有 @,所以不是 import,就是一个孤零零的词。
Claude Code 会毫无怨言地加载它。所有规则全丢,git 不报警,Claude Code 也不报警。
这个坑比第一个阴险得多,因为它只在别人的机器上发作。
坑三:Edit / Write 工具拒绝写穿 symlink。
这个是有意为之(issue #66559 里维护者在 2.1.234 确认过),目的是防止一次写入被悄悄重定向到另一个文件。副作用是 Claude 想改 CLAUDE.md 的时候会先失败一次,然后自己反应过来去改 AGENTS.md。
想省掉那次失败,在 AGENTS.md 里加一行:"对 agent 指令的修改一律写进 AGENTS.md。"读不受影响。
三个坑的示意
✿ ✿ ✿
最扎心的一条:skills 还是不认
HN 那个帖子里,在一片"终于可以删 symlink 了"中间,有条评论被顶得很高:
Note that this does not include .agents/skills. Argh.
还有一条更直接:
Don't get too excited, Claude code still won't detect skills on .agents/skills.
这话说到点子上了。instruction 文件只是这件事的第一层。skills、subagents、commands、hooks —— 这些还是全部住在 .claude/ 底下。一个团队要在 Codex 和 Claude Code 之间共享一个仓库,改完 instruction 文件之后,还得一个目录一个目录地继续 symlink。
有人在帖子里贴了个 git hook 兜底,我试了,能用:
mkdir -p ~/.githooks git config --global core.hooksPath ~/.githooks cat > ~/.githooks/post-checkout <<'EOF' #!/usr/bin/env bash if [ -d .agents/skills ] && [ ! -e .claude/skills ]; then mkdir -p .claude ln -s ../.agents/skills .claude/skills fi EOF chmod +x ~/.githooks/post-checkout每次 checkout 自动补 symlink。治标,但省心。
说句公道话 —— 这次更新其实比"读个文件"要重。AGENTS.md 支持是作为 Claude Code 第一个原生 mod 实现的。mod 是 Anthropic 还没正式发布的机制,用来改 harness 本身:在模型开始干活之前,改变上下文是怎么被拼起来的。Thariq Shihipar 把这个 mod 的源码和文档都公开了。
换句话说,这次真正值得关注的不是"它读了 AGENTS.md",而是"指令加载这一层现在是可替换的代码了"。
instruction只是第一层
✿ ✿ ✿
怎么确认它真的加载了
这一步是我觉得最多人会跳过的。
/context。会话里跑一下,看 Memory files 那一段里有没有你的文件。
文件系统上的东西和 Claude 上下文里的东西是两回事。symlink 建好了不代表加载了,import 写对了不代表展开了 —— 前面那三个坑,全都能通过这一条命令看出来。
要长期留证据的话,用 InstructionsLoaded hook(2.1.69 就有了)。它会为每个 instruction 文件触发一次,带一个 load_reason:
-
session_start -
nested_traversal -
path_glob_match -
include -
compact
通过 import 进来的 AGENTS.md,load_reason 是 include,并且带一个指向那个 CLAUDE.md 的 parent_file_path。记一周日志,如果从子目录启动的会话从来没出现过 include 这条,那你就是撞上 #78697 了。
顺便提醒一句:/import 不是桥接,是一次性拷贝。拷完你手上就是两份会各自漂移的文件,而这恰恰是 AGENTS.md 当初想解决的问题。用它迁移一次,然后把拷过来的那块替换成 @AGENTS.md。
用/context验证加载
✿ ✿ ✿
扯远一点:你写进去的东西,可能在帮倒忙
写到这儿我本来想收尾了,但有组数据我觉得比这次更新本身更值得看。
苏黎世联邦理工做过一次评测:4 个 coding agent(Claude Code、Codex、Qwen Code),138 个真实任务。结论有点扎人:
- LLM 自动生成
的 context 文件:成功率 下降 0.5~2%,推理成本 上升 20~23%。8 个测试配置里有 5 个,"有自动生成的文件"比"完全没有文件"更差 - 人手写
的文件:成功率提升约 4% -
那个每个自动生成的文件都会写的 "codebase overview",没用 —— 它没让 agent 更快找到相关文件,很多时候反而多走了几步
最能说明问题的是那个反向实验:研究者把仓库里所有文档都删掉之后,LLM 生成的 context 文件突然有用了(+2.7%)。意思很清楚 —— 它平时只是在复述 agent 本来就能自己从 README 里读到的东西。
另一项效率研究给了正面的例子:只保留三类内容(编码约定、架构、项目描述)的精简 AGENTS.md,中位墙钟时间降了 28%,输出 token 降了 16%。
还有 MSR '26 对 10000 个仓库的调查,三个数:
只有 5% 的仓库有任何 context 文件;AGENTS.md 平均 142 行;一半的 AGENTS.md 创建之后再也没被提交过。
一半。写完就扔。
这组数据放在一起看有点讽刺:我们为了"让 Claude Code 读到这个文件"吵了 13 个月、写了 361 条评论、发明了三种 workaround,而文件里装的东西有一半从第一天起就在过期。
三组研究数据
我自己也中招。我的某个项目的 CLAUDE.md 里,到现在还写着一个三个月前就换掉的包管理器。Claude 每次都老老实实按那个写,我每次都手动改回来,改了大概二十次才意识到问题在文件里而不是在模型里。
✿ ✿ ✿
写在最后
如果你今天只做一件事,我建议是这个:
打开你最近在用的那个项目,跑 /context,看一眼 Memory files 里到底加载了什么。
大概率会有惊喜 —— 可能是一个你以为在生效但根本没加载的文件,也可能是一个三个月前的旧约定还在悄悄影响每一次输出。
至于 symlink 到底删不删?分情况。单包仓库、大家都从根目录启动,删了没问题,切 claude-md-and-agents-md 然后两边各写各的。monorepo、或者团队里有 Windows 机器,这事儿就先留着别动。
这个功能等了 13 个月。再多花十分钟确认它在你这儿真的生效,不亏。