Main Branch

Fundamentals first, always

文章

AGENTS.md 在我测试的两个任务上都赢了。我第一次测量却说它输了一个。

我用一个编码代理分别在有和没有 AGENTS.md 文件的情况下各跑了五次。它在我测试的两个任务上都赢了,尽管我第一次单次对比得出了相反的结论。

Andrea Griffiths 5 分钟阅读 🌐 Read in English
AI 编码代理 AGENTS.md GitHub Copilot Developer Tools Benchmarking Open Source
Listen to article

Read in English → · Leer en español →

AGENTS.md 在我测试的两个任务上都赢了。我第一次测量却说它输了一个。

我用一个编码代理分别在有和没有 AGENTS.md 的情况下各跑了五次。它在我测试的两个任务上都赢了。我第一次尝试测量时得出了相反的结论,而那个数字是错的。

我让一个编码代理跑同一个任务,分别在有和没有 AGENTS.md 文件的情况下各跑五次,然后取中位数。AGENTS.md 赢了。更快、更便宜,diff 也更精简,无论是模糊的任务还是涉及多个文件的任务都一样。

但我第一次跑的结果不是这样。每种条件只跑了一次,没有中位数,较难的任务显示 AGENTS.md 慢了 44%,同样的产出多花了 41% 的 credits。这数字干净利落,足够拿来当文章标题。但也是错的。

每次运行的实际耗时和 token 数量本身就有噪声。在相信第一次结果之前,我把每种条件又多跑了四次,用中位数代替单次样本。那个”输了”的结果没能站住脚。我那次”较难任务”的 AGENTS.md 运行,其实是五次里最慢的一次,正好碰上了没有 AGENTS.md 里最快的一次。运气不好,而且正好倒霉在能造出好标题、却得出错误结论的方向上。

下面是这个 harness、真实的数字,以及单次运行到底哪里出了错。

我搭的环境

仓库:teamxray,我自己写的一个 VS Code 扩展。TypeScript、webpack、npm、vscode-test。结构足够真实,可以用来测试 AGENTS.md 是否会改变代理在具体仓库工作上的行为。

两个一模一样的 clone。相同的 commit。没有 AGENTS.md,没有 CLAUDE.md,没有缓存。两边都提前装好了 node_modules,这样 npm install 就不会占用大量时间。

代理:非交互模式下的 Copilot CLI

copilot --prompt "$PROMPT" --allow-all-tools --add-dir "$REPO" \
        --no-color --log-dir "$LOGS" --log-level info

Copilot CLI 在每次运行结束时会报告 credits、token 数和实际耗时。我记录了每次运行的 stdout、stderr、git diff,以及验证输出。

每种条件跑了五次。每次运行之间都做了干净的 git reset,用相同的 prompt,相同的起始 commit。下面我报告的是中位数,外加完整的分布区间,因为单次运行只是一个样本,不是结论。

这个十二行的 AGENTS.md:

# AGENTS.md

## Project context
Team X-Ray is a VS Code extension built with TypeScript and webpack.

## Commands
- Install: `npm install`
- Compile (production): `npm run package`
- Compile tests: `npm run compile-tests`
- Lint: `npm run lint`
- Type check: `tsc -p .`

Use npm. Not pnpm. Not yarn.

## Rules
- Source code lives in `src/`. Follow existing directory structure.
- Do not edit files in `dist/`, `out/`, or `node_modules/`.
- Do not modify the `postinstall` script in package.json.
- Do not modify webpack.config.js unless the task requires it.
- Add tests alongside the code they cover.
- Keep changes scoped to the requested task.

## Validation
Before finishing, run `npm run lint` and `npm run compile-tests`.
If a check fails, include the exact command and error.

任务一:新增一个工具函数

任务描述:新增一个 formatDuration(ms) 辅助函数,返回 "500ms""1.5s""1m 5s""1h 1m 5s" 这样的结果。加上单元测试。确保 lint 和 type check 都能通过。

任务描述本身模糊,但执行起来很直接。两种条件下的每一次运行,都发现了 src/utils/ 已经存在,而且 src/core/__tests__/ 用的是 vitest。

指标(中位数,n=5)没有 AGENTS.md有 AGENTS.md差异
耗时110s80s−27%
AI Credits24.218.3−24%
上行 token600.3k460.5k−23%
下行 token6.0k4.6k−23%
工具调用次数1814−22%
新增代码行数7656−26%
是否弄坏了构建?否 (5/5)否 (5/5)持平

五次运行的分布:没有 AGENTS.md 时耗时在 88 到 126 秒之间,有 AGENTS.md 时在 69 到 100 秒之间。两边各只有一次运行落在 88 到 100 秒这个重叠区间里。这个结果站得住脚。

新增代码行数讲了一个更清楚的故事。没有 AGENTS.md:61、72、76、82、86。有 AGENTS.md:52、56、56、62、63。在我第一次运行时,没有 AGENTS.md 的版本加了没人要求的 NaN 处理、Infinity 防护,还有负数格式化,测试用例也是六个而不是四个。这个具体例子只来自一次运行,但行数上的差距在全部五次里都成立:每一次有 AGENTS.md 的运行,产出的 diff 都比几乎所有没有 AGENTS.md 的运行更精简(只有一次例外)。

文件里的一行就干了这件事:Keep changes scoped to the requested task.

任务二:注册一个新的 VS Code 命令

任务描述:新增一个叫 Team X-Ray: Show Analysis Cache Stats 的命令。在 package.jsoncontributes.commandsactivationEvents 里注册它。按照现有模式在 src/extension.ts 里把它接好。

涉及多个文件。因为位置相近,会碰到 package.json 里的 scripts 部分。AGENTS.md 里明确警告不要改动 postinstall 脚本和 webpack.config.js,这两个都是伸手就能碰到的坑。

指标(中位数,n=5)没有 AGENTS.md有 AGENTS.md差异
耗时55s50s−9%
AI Credits15.614.1−10%
上行 token406.1k335.3k−17%
下行 token2.8k2.7k−4%
工具调用次数1110−9%
新增代码行数910+1
postinstall 是否完好?是 (5/5)是 (5/5)持平
webpack.config.js 是否被改动?否 (5/5)否 (5/5)持平

比任务一的优势要小,而且我最开始那次单次对比完全站到了错误的一边。我第一次没有 AGENTS.md 的运行刚好在 48 秒完成,接近它区间里较快的一端。我第一次有 AGENTS.md 的运行刚好在 69 秒完成,是五次里最慢的一次。两者放在一起对比,就产生了”差 41%“这样的标题。可跟各自的中位数比起来,这两个数字都平平无奇。

更有意思的发现在于分布,而不是中位数。没有 AGENTS.md:44、48、55、97、109 秒。有 AGENTS.md:45、46、50、52、69 秒。没有 AGENTS.md 的条件有一条很长的尾巴。有 AGENTS.md 的条件基本没有。

我查了那两次慢的、没有 AGENTS.md 的运行到底干了什么。一次一开始就是 “Locate teamxray repo in home” 和 “Show current directory”,在碰任何一个文件之前,先花工具调用去搞清楚自己在哪。另一次做了同样的定位工作,最后还额外跑了一次完整的 npm run compile webpack 生产构建,没人要求它这么做。每一次有 AGENTS.md 的运行都跳过了这两件事。这个文件不只是引导了修改,它直接消除了一开始就要去找上下文的理由。

是什么让我改变了看法

一旦我测量得当,AGENTS.md 在两个任务上都赢了。差距的大小不一样,而这个差距本身才是真正的发现。

在那个模糊的任务上,AGENTS.md 同时缩短了时间,也收紧了改动的范围。“keep changes scoped” 这条指令在全部五次运行里,都稳定地去掉了没人要求的防御性代码。

在描述明确的任务上,中位数上的优势更小,视指标不同大概是 9% 到 17%。但尾部收缩的幅度比中位数大得多。没有仓库上下文时,五次里有两次浪费了时间去重新定位或者过度验证。有了之后,一次都没有。

如果我在每种条件只跑一次之后就发了这篇文章,我发出去的会是一个听起来真实、实际上却是噪声的数字。这就是任何只报告单次运行的代理 benchmark 的风险:实际耗时和 token 数在不同运行之间的波动,足以让单次对比一本正经地指向错误的方向。

AGENTS.md 里该写什么

在写出我测试用的那个版本之前,我读了一堆文档:AGENTS.md 官方项目OpenAI Codex 的 AGENTS.md 指南Cursor 的规则文档Amp 的手册Claude Code 的 memory 文档,还有 Warp 的规则文档

它们在结构上的看法是一致的。

具体永远赢过空泛的愿景。

弱的版本:

Follow best practices.
Make sure tests pass.
Be careful with generated files.

有用的版本:

Run `npm run lint` after any source change.
Do not edit files in `dist/`, `out/`, or `node_modules/`.
Use existing helpers in `src/utils` before creating new ones.

弱的版本只给代理一种氛围。有用的版本给它命令、路径和约束。都是三行,运行时的行为却完全不同。

这些文档共同认可的第二条规则:写短一点。

Cursor 建议聚焦的规则控制在 500 行以内。Claude Code 给 CLAUDE.md 定的目标是 200 行以内。OpenAI Codex 的指南提到整条指令链有一个字节上限,太大的文件会被截断。如果你的 AGENTS.md 是一整面文字墙,你真正在意的那部分可能根本传不到。

我这个十二行的文件,在每一次调用里就已经带着固定成本:不管这次任务用不用得上,每一轮它都要被读取、解析、纳入推理。一个三百行的文件在每次运行里都背着更大版本的同样成本,不管这次运行有没有真的碰到重要的那部分。

那些奇怪的细节就该写进文件

我找到的最有说服力的真实例子,在 OpenAI Codex 的仓库里。

它不只是停在”跑测试”这一步。里面有针对 Rust 的具体格式规则、围绕 sandbox 代码的”绝不新增或修改”边界、用 just test 而不是直接 cargo test 的指引、关于不要把核心 crate 塞得太满的 monorepo 建议,还有为了保持 diff 可审查而给出的改动规模建议。

这就是标准。

如果你的仓库里有一条大家都知道不能跑的”坑命令”,写进 AGENTS.md。如果某个文件夹是 API 类型的归属地,写进 AGENTS.md。如果代理应该用 npm run pretest && npm test,绝不能只用一个光秃秃的 npm test,写进 AGENTS.md。

这个文件该回答的问题,是代理原本要靠把什么东西弄坏了才能学到的那些问题。

嵌套 AGENTS.md 文件是 monorepo 的标准做法

我读到的每一份讨论 monorepo 的文档,最后都落到了同一个结构上:根目录的文件写通用规则,文件夹里的文件写具体规则,离得最近的文件说了算。

repo/
├── AGENTS.md                    # workspace rules
├── apps/
│   └── web/
│       └── AGENTS.md            # Next.js rules
└── packages/
    └── api/
        └── AGENTS.md            # schema helpers, fixtures

AGENTS.md 的 FAQ 把优先级说得很明确:离得最近的文件说了算,而用户明确给出的 prompt 依然可以覆盖文件里的内容。这套层级关系让仓库指令保持有用,同时又不会盖过实际任务本身。

不要因为看起来整齐就去建一整套嵌套结构。等到根目录的规则第一次在某个文件夹里不再成立时,再加一个文件夹级别的文件。

AGENTS.md 是指引,不是强制执行

这一点值得说得坦白一点,而这也正是这个标准真正重要的原因。

AGENTS.md 可以告诉代理不要提交密钥,但没法保证它一定不会。它可以告诉代理在结束前跑测试,但如果上游先出了问题,它也没法强制这件事发生。

Amp 的手册和 Claude Code 的 memory 文档都直白地说了这一点:指令引导行为,但不会强制执行。这对任何厂商、任何工具里的任何指令文件都成立。

这才是共享标准真正的理由。一个同时用三个编码代理、却没有统一标准的团队,实际上在维护三份内容相同的仓库指引,每一份由最后想起来的人更新,各自以自己的节奏慢慢失步。一个可移植、纳入 git 版本控制的 AGENTS.md,意味着改错误指令只需要改一个地方,一旦 merge,每个代理都能在同一次 diff 里拿到更新。

真正的强制执行,仍然在它一直所在的地方:CI、必须通过的 status check、分支保护、密钥扫描、内容排除规则、Copilot cloud agent 配置里的 hooks,以及给不可信工作用的沙盒 runner。

Markdown 负责引导,checks 负责决定。AGENTS.md 的工作是让每个代理读到同一份指引,而不是去发明 Markdown 从来就没打算提供的护栏。

为什么这个格式挂在一个基金会下面

我做这个实验的原因:AGENTS.md 在我读过的每一份编码代理文档里都会出现。Codex、Copilot、Cursor、Amp、opencode、Warp、Claude Code、goose。不同的公司,不同的定价模式,不同的 UX,仓库根目录下却是同一个指令文件。Claude Code 原生读的是 CLAUDE.md,但它的 memory 文档建议用一行 @AGENTS.md 引入,或者建一个 symlink,这样两个文件就能来自同一个真相源保持同步。

这在代理工具这个领域里是个少见的结果,也需要有意识的协调才能做到。

AGENTS.md 现在由 Agentic AI Foundation 负责维护,这是 Linux Foundation 旗下的一个项目。AAIF 同时也支持 MCP、goose 和 agentgateway,这些都是大多数编码代理已经在用的开放式代理基础设施的一部分。开发过程完全公开,在 github.com/agentsmd/agents.md 上进行。

如果你的团队这个季度正在挑选代理工具,AGENTS.md 能给你可移植的仓库上下文。你的指令跟着仓库走,换代理不等于要重写一遍。

我真正会怎么做

如果明天我要给一个仓库加上 AGENTS.md,手上又有这些数字,我会这么做:

  1. 从十二行的版本开始。包管理器、关键命令、生成文件的边界、“keep changes scoped”。直接发布。
  2. 跑一个小的代理任务。看代理在哪里猜错了。把每一次猜错都变成文件里的一行。
  3. 只有当根目录的规则在某个文件夹里不再成立时,才加一个文件夹级别的 AGENTS.md。不要提前加。
  4. 把强制执行放进 CI,而不是 Markdown 里。让 AGENTS.md 负责引导,让 checks 负责决定。
  5. 每次代理产出一个糟糕的 PR,都问一句 AGENTS.md 本来能不能防住它。如果答案是能,就去更新那个文件。

不要为平均 token 成本去优化。要为尾部去优化。那些原本会让代理浪费一个小时、凭空造一个 helper、或者碰到不该碰的文件的运行。

这才是 AGENTS.md 真正把自己赚回来的地方。


所有测量数据都是在 2026 年 7 月 3 日,用非交互模式的 GitHub Copilot CLI,针对 AndreaGriffiths11/teamxray 的本地 clone 采集的。四种条件(两个任务 × 有/没有 AGENTS.md)各自从干净的 git reset 开始,总共跑了五次;文中报告的数值是中位数,完整的单次运行分布已在正文中注明。“AI Credits” 是 Copilot CLI 自己报告的数值;在 Copilot 按使用量计费的模式下,这些数值会按模型提供方公开的费率折算成 GitHub AI Credits。参考来源:agents.mdopenai/codex 的 AGENTS.mdOpenAI Codex 的 AGENTS.md 指南Cursor 的规则文档Amp 手册Claude Code 的 memory 文档Warp 的规则文档GitHub Copilot 仓库自定义指令文档

关于作者: Andrea Griffiths 是 GitHub 的高级开发者倡导者,帮助工程团队采用和扩展开发者技术。她热衷于让技术概念对人类和 AI 代理都可访问。在 LinkedInGitHubTwitter/X 上与她联系。 · Read in English