1. 一个仓库三套规则文件,改到怀疑人生
如果你同时用 Claude Code、Cline、Windsurf 写同一个项目,大概率经历过这种场面:.clinerules里写着「提交前跑 pnpm lint」,CLAUDE.md里写着「测试用 vitest」,Windsurf 的 rules 目录里又是另一套构建命令。改一次构建脚本,得挨个文件同步,漏掉一个,某个工具就开始按过期指令干活。
Claude Code 2.1.277 这次更新,把这件事往「一份文件喂多个工具」的方向推了一步:当项目里没有 CLAUDE.md 时,Claude Code 会去读 AGENTS.md。AGENTS.md 不是某个厂商的私有格式,它是一份跨工具约定,Codex、Gemini CLI、Cursor、Windsurf、Zed、Aider 等都在读它。对多工具协作的团队来说,这意味着你终于可以把「构建、测试、代码风格」这类与工具无关的规则抽出来,只维护一份。
这篇就围绕这次改动,把 AGENTS.md 和 CLAUDE.md 的加载顺序、覆盖规则、目录层级讲清楚,给一套可以直接复制的目录结构和配置片段,最后演示改完之后怎么验证「agent 到底读了哪份文件」。适合正在用两个以上 AI 编码工具、被规则文件同步问题折磨的开发者。
2. 先搞清楚加载顺序,再动手写文件
动手之前必须把优先级搞明白,否则你写了一份 AGENTS.md,结果项目里还躺着一个 CLAUDE.md,Claude Code 根本不会读你新写的那份。
2.1 回退关系,不是合并关系
2.1.277 官方日志 Added 部分的原文是:
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)
这句话里有三个限定,漏掉任何一个都会理解偏。第一,前提是项目中没有 CLAUDE.md,CLAUDE.md 存在时仍然以它为准,AGENTS.md 是回退项,不是把两份文件合并后一起读。第二,这条行为可以在/config的 Project instructions 下切换。第三,Bedrock、Vertex、Foundry 三个后端暂不支持,用这三个平台跑 Claude Code 的同学,这次改动对你不生效。
所以如果你打算迁移到 AGENTS.md,正确做法是把 CLAUDE.md 的内容搬过去,然后删掉或改名 CLAUDE.md,而不是两份都留着。
2.2 嵌套文件:最近的一份生效
AGENTS.md 官方文档给出了嵌套规则:把文件放进每个子包目录,agent 会自动读取目录树中离它最近的那一份,最近的优先。官方举例说,写作这份文档时 OpenAI 主仓库里有 88 个 AGENTS.md 文件。
这条规则有一个很容易搞错的推论:把packages/api/AGENTS.md放进仓库,并不会影响在packages/web/目录里干活的 agent。生效的是目录树中最近的那一份,靠外的那份不会在子目录里继续兜底。也就是说,子目录里的 AGENTS.md 是「替换」而不是「叠加」父目录的规则。
2.3 一张表看清所有层级
| 文件 / 设置 | 归属 | 什么时候被读 |
|---|---|---|
| CLAUDE.md | Claude Code 原有约定 | 存在即优先,AGENTS.md 不再被读 |
| AGENTS.md | 跨工具约定 | 仅在项目里没有 CLAUDE.md 时 |
| /config → Project instructions | 用户级开关 | 控制上面这条回退行为是否启用 |
| 企业 managed policy | 组织级 | 始终加载,优先级最高 |
从这张表能读出一个设计取向:指令文件是按作用域生效的,不是一个全局开关。同一批版本里还有几个相关改动值得并排看——2.1.271 让omitClaudeMd进入 agent frontmatter,自定义子代理可以不加载 user、project、local 三份 CLAUDE.md,但 managed policy 仍然加载;2.1.269 修复了当 CLAUDE.md 里有反对 commit 署名的规则时,attribution reminder 不再覆盖它,但 managed settings 写下的规则仍然生效。这些都是优先级的具体例证:组织设置高于项目文件。
3. 可复制的目录结构与配置片段
理论讲完,直接上能跑的东西。下面这套结构我按「根目录放通用规则、子包放专属规则」的思路设计,你可以直接抄。
3.1 推荐的目录结构
my-monorepo/ ├── AGENTS.md # 根级:全仓库通用规则 ├── packages/ │ ├── api/ │ │ ├── AGENTS.md # api 专属:Node + TypeScript 规则 │ │ └── src/ │ └── web/ │ ├── AGENTS.md # web 专属:React 规则 │ └── src/ └── pnpm-workspace.yaml根级 AGENTS.md 只放「所有包都适用」的内容,子包 AGENTS.md 放该包特有的命令和约定。注意子包文件是替换而非叠加,所以子包里需要重复写一遍它依赖的通用命令,或者干脆在子包里只写差异部分、把通用部分留在根级——但前提是 agent 的工作目录在根级时才会读到根级那份。
3.2 根级 AGENTS.md 模板
# AGENTS.md ## 项目概览 - 单仓多包:packages/api(服务端,Node + TypeScript)、packages/web(前端,React) - 本地依赖统一用 pnpm 工作区管理 ## 构建与测试 - 装依赖:pnpm install - 跑全量测试:pnpm test - 只跑单个包:pnpm turbo run test --filter <包名> - 提交前必须通过 pnpm lint,不要因为「改动很小」跳过 ## 代码风格 - TypeScript 严格模式,不用 any - 单引号,行尾不加分号 ## 需要特别小心的地方 - 涉及金额计算的改动必须补测试,边界至少覆盖 0、1 和 10001 - 不要调整 packages/api/src/auth/ 下鉴权校验的执行顺序内容上把握两条:一是可执行,写成命令、字段、路径这种可以被验证的东西;二是克制取舍,只放你这个仓库里 agent 一定会踩的坑。像「代码要优雅」「注意性能」这类没法验证的话,写进去只会稀释注意力。
3.3 子包 AGENTS.md 模板
# AGENTS.md — packages/api ## 本包专属命令 - 启动开发服务:pnpm --filter api dev - 数据库迁移:pnpm --filter api migrate ## 本包约定 - 所有对外接口必须走 src/schemas/ 下的 zod 校验 - 日志统一用 src/lib/logger.ts,不要直接 console.log3.4 如果你还想保留 CLAUDE.md
有些团队暂时不想动 CLAUDE.md,只想让 Cline、Windsurf 读 AGENTS.md。这种情况下,可以让 CLAUDE.md 只写一行指向 AGENTS.md 的说明,或者干脆把 CLAUDE.md 删掉、统一用 AGENTS.md。但记住:两份都留且内容冲突时,Claude Code 只读 CLAUDE.md,不会帮你调和。
如果你是通过 API 方式接入 Claude Code 或类似工具,Base URL、Key、Model ID 三件套要配全。以 TaoToken 为例,Base URL 填https://taotoken.net/api,Key 在控制台生成,Model ID 按你实际用的模型填。配置片段如下:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model": "claude-sonnet-4-20250514" }4. 验证 agent 到底读了哪份文件
模型声称自己读了什么并不完全可信,所以别只问它,做一个对照实验更稳。
4.1 对照实验三步法
第一步,在 AGENTS.md 里写一条只可能来自这份文件的规则,比如「任何提交信息必须以 [模块名] 开头」。第二步,让 agent 执行一个只有满足该规则才成立的动作,比如写一条提交信息或生成一个 PR 标题。第三步,保持同一仓库、同一句提示词,把 AGENTS.md 改名或移走,再执行一次,对比两次的行为差异。
行为跟着变了,说明这份文件确实在读取链路上;两次一样,说明你改的文件它根本没读。这个实验比问模型「你读了哪个文件」可靠得多,因为模型可能会根据上下文猜测,而不是真的去读了文件。
4.2 清点仓库里的指令文件
下面这个脚本用来扫描仓库里的指令文件并标出层级,自拟示意代码,非官方工具:
# list_instruction_files.py —— 自拟示意代码,非官方工具 # 用途:清点仓库里所有 AGENTS.md / CLAUDE.md,输出相对路径、目录深度和最近修改时间 from pathlib import Path import time NAMES = ("AGENTS.md", "CLAUDE.md") def scan(root: str): root_path = Path(root) rows = [] for p in root_path.rglob("*"): if p.name in NAMES and p.is_file(): rel = p.relative_to(root_path) rows.append({ "rel": str(rel).replace("\\", "/"), "depth": len(rel.parts) - 1, # 目录层级,越深离子项目越近 "mtime": time.strftime("%Y-%m-%d %H:%M", time.localtime(p.stat().st_mtime)), }) rows.sort(key=lambda r: r["rel"]) for r in rows: print(f'{r["rel"]:<50} depth={r["depth"]} mtime={r["mtime"]}') print(f"共 {len(rows)} 份指令文件") if __name__ == "__main__": scan(".") # 在仓库根目录执行代码说明:rglob负责递归收集候选文件,depth用相对路径的目录层数表示,越深越接近子项目,对应官方描述的「最近者优先」;mtime用来排查同一份规则被复制到多个目录后各自漂移的情况。这个脚本只做清点,不判断哪份文件真正生效——是否生效要靠上面的对照实验。
4.3 用 /config 确认开关状态
在 Claude Code 里输入/config,找到 Project instructions 这一项,确认 AGENTS.md 回退读取是否开启。如果你在 Bedrock、Vertex、Foundry 上跑,这一项对你不生效,需要按实际后端再确认一次。
5. 四个真实报错与排查路径
配置过程中最容易撞上的几个问题,我按报错现象、原因、解决路径整理成对照表。
5.1 改了 AGENTS.md 但行为没变
现象:你更新了 AGENTS.md 里的构建命令,但 agent 还是跑旧命令。原因大概率是项目里还有 CLAUDE.md,Claude Code 优先读了它。排查路径:用上面的脚本扫一遍仓库,确认有没有 CLAUDE.md 残留。如果有,要么删掉,要么把内容迁移到 AGENTS.md 后删除。
5.2 401 报错
现象:请求返回 401 Unauthorized。原因通常是 API Key 没配、配错,或者 Base URL 和 Key 不匹配。排查路径:检查你的配置文件里api_key字段是否填了正确的 Key,base_url是否指向https://taotoken.net/api。如果用的是环境变量,确认变量名和工具要求的一致。Key 可以在控制台的 API Keys 页面重新生成。
5.3 local proxy failed
现象:报 local proxy failed 或连接超时。原因可能是本地网络配置问题,或者 Base URL 填成了带路径的地址。排查路径:确认base_url只填到域名和/api,不要带多余路径。检查本地是否有其他进程占用了端口。如果用的是公司网络,确认没有拦截。
5.4 reading choices 报错
现象:返回结果里出现 reading choices 相关错误。原因通常是模型返回格式和工具预期不一致,可能是 Model ID 填错了。排查路径:确认model字段填的是工具支持的模型 ID,不要填成别的厂商的模型名。如果用的是 Coding Plan 或 Agent 场景,确认套餐类型和模型匹配。
5.5 OAuth 相关报错
现象:OAuth token 失效或认证失败。原因可能是 token 过期,或者配置文件里的认证方式冲突。排查路径:重新走一遍认证流程,确认配置文件里没有同时存在 OAuth 和 API Key 两种认证方式。如果用的是 Codex 的auth.json,确认里面的字段和工具版本匹配。
6. 把规则文件当成团队资产来维护
这次改动本身很小:多了一条回退读取路径,加了一个/config开关,还限定了三个后端平台不支持。但它背后是一条值得投入时间的趋势——项目级指令文件正在从一个工具的私有配置,变成多个工具共享的约定。
如果你手上堆着一批只服务单一工具的规则文件,可以从三件事开始:先把构建、测试、风格这类与工具无关的规则抽出来独立成一份 AGENTS.md;再按目录层级拆出子项目专用的部分;最后用一次对照实验确认它真的被读到了。
规则放在哪份文件里是细节,能被工具稳定读到、内容可验证,才是这套机制真正的价值。如果你还没配好 API 接入,可以先到 TaoToken API Keys 生成 Key,再对照 接入文档 把 Base URL 和 Model ID 填全。想先验证模型对话效果,可以直接用 模型对话 试一轮;如果是长期编码或 Agent 场景,Coding Plan 会更合适。