Agent 评测中,AGENTS.md 的表现优于 Skills

第一部分:原文翻译

我们原本以为“技能”(Skills)是教会编程 Agent 掌握框架特定知识的解决方案。 但在构建了针对 Next.js 16 API 的评测(Evals)后,我们发现了一些意想不到的事情。 一个直接嵌入在 AGENTS.md 中的 8KB 压缩文档索引实现了 100% 的通过率,而“技能”方案即使在显式指令要求 Agent 使用的情况下,通过率也仅达到 79%。 如果没有这些显式指令,“技能”的表现甚至并不比完全没有文档时好。

以下是我们尝试的方法、学到的经验,以及如何为你自己的 Next.js 项目进行设置。

我们试图解决的问题

AI 编程 Agent 依赖的训练数据往往会过时。 Next.js 16 引入了诸如 'use cache'connection()forbidden() 等 API,这些并不在当前模型的训练数据中。 当 Agent 不了解这些 API 时,它们会生成错误的代码或回退到旧的模式。 反之亦然,如果你运行的是旧版本的 Next.js,模型可能会建议你项目尚不支持的新 API。 我们希望通过给 Agent 提供与版本匹配的文档访问权限来解决这个问题。

两种教授 Agent 框架知识的方法

在深入研究结果之前,先快速解释一下我们测试的两种方法:

1. Skills (技能) “技能”是一种开放标准,用于打包编程 Agent 可以使用的领域知识。 一个技能捆绑了 Agent 可以按需调用的提示词(Prompts)、工具(Tools)和文档。 其理念是,Agent 会识别出它何时需要特定框架的帮助,调用该技能,并获取相关文档。

2. AGENTS.md (被动上下文) AGENTS.md 是项目根目录下的一个 Markdown 文件,为编程 Agent 提供持久的上下文。 无论你放入 AGENTS.md 什么内容,Agent 在每一轮对话中都能获取,无需 Agent 自行决定是否加载。 Claude Code 使用 CLAUDE.md 达到同样的目的。

我们构建了一个 Next.js 文档技能和一个 AGENTS.md 文档索引,然后在我们的评测套件中运行它们,看看谁表现更好。

我们起初押注于“技能”

“技能”看起来是正确的抽象层。你将框架文档打包成一个技能,Agent 在处理 Next.js 任务时调用它,你就能得到正确的代码。 这种方式关注点分离清晰,上下文开销最小,且 Agent 只在需要时加载内容。 在 skills.sh 上甚至有一个不断增长的可复用技能目录。 我们期望 Agent 遇到 Next.js 任务时,调用技能,阅读版本匹配的文档,并生成正确的代码。 然后我们运行了评测。

技能并未被可靠地触发

在 56% 的评测案例中,技能从未被调用。 Agent 虽然拥有访问文档的权限,却没去用它。添加技能后,效果与基准线相比没有任何提升:

零提升。技能是存在的,Agent 也可以使用,但 Agent 选择了不用。 在详细的 构建/Lint/测试 细分中,技能方案在某些指标上的表现实际上比基准线还差(测试通过率 58% vs 63%),这表明在环境中引入一个未使用的技能可能会带来噪音或干扰。 这并非我们的设置所独有。Agent 无法可靠地使用可用工具是当前模型的一个已知局限。

显式指令有帮助,但措辞极其脆弱

我们尝试在 AGENTS.md 中添加显式指令,告诉 Agent 去使用该技能。 添加到 AGENTS.md 的指令示例:

在编写代码之前,首先探索项目结构, 然后调用 nextjs-doc 技能获取文档。

这将触发率提高到了 95% 以上,并将通过率提升到了 79%。

这是一个稳固的提升。但我们发现了一个意想不到的情况,即指令的措辞如何影响 Agent 的行为。 不同的措辞产生了截然不同的结果:

同样的技能,同样的文档。仅因措辞的细微变化就导致了不同的结果。 在一项评测('use cache' 指令测试)中,“先调用”的方法虽然写出了正确的 page.tsx,但完全遗漏了所需的 next.config.ts 更改。 而“先探索”的方法两者都搞定了。 这种脆弱性让我们担忧。如果微小的措辞调整会产生巨大的行为波动,那么这种方法在生产环境中就显得太脆弱了。

构建我们可以信任的评测

在得出结论之前,我们需要可信的评测。我们最初的测试套件存在提示词模棱两可、测试验证的是实现细节而非可观察行为、以及关注点集中在模型训练数据中已有的 API 等问题。 我们并没有测量我们真正关心的东西。 我们通过移除测试泄漏、解决矛盾并转向基于行为的断言来强化了评测套件。 最重要的是,我们添加了针对 不在模型训练数据中 的 Next.js 16 API 的测试。

我们强化评测套件中的 API 包括: * 用于动态渲染的 connection() * 'use cache' 指令 * cacheLife()cacheTag() * forbidden()unauthorized() * 用于 API 代理的 proxy.ts * 异步 cookies()headers() * after()updateTag()refresh()

接下来的所有结果均来自这个强化后的评测套件。 每个配置都根据相同的测试进行评判,并进行了重试以排除模型方差。

得到了回报的直觉

如果我们完全移除“决策”环节会怎样?与其希望 Agent 调用技能,不如直接在 AGENTS.md 中嵌入文档索引。 不是全部文档,而只是一个索引,告诉 Agent 去哪里找到与你项目 Next.js 版本匹配的具体文档文件。 这样 Agent 就可以按需阅读这些文件,无论你是使用最新版本还是维护旧项目,都能获得版本准确的信息。

我们在注入的内容中添加了一条关键指令:

重要: 对于任何 Next.js 任务, 优先使用 检索主导的推理 (retrieval-led reasoning),而非 预训练主导的推理 (pre-training-led reasoning)

这告诉 Agent 要参考文档,而不是依赖可能过时的训练数据。

结果让我们大吃一惊

我们在所有四种配置上运行了强化后的评测套件: (图表显示 AGENTS.md 在构建、Lint 和测试中均达到 100%)

在详细细分中,AGENTS.md 在构建、Lint 和测试方面均获得了满分。

这不是我们要的结果。“笨”方法(静态 Markdown 文件)的表现优于更复杂的基于技能的检索,即使我们微调了技能触发器也是如此。 为什么被动上下文击败了主动检索? 我们的理论归结为三个因素:

  1. 没有决策点。使用 AGENTS.md,Agent 不需要决定“我是否应该查阅这个?”
  2. 持续可用性。技能是异步加载的,且仅在调用时加载。AGENTS.md 的内容在每一轮的系统提示词中都存在。
  3. 没有顺序问题。技能会产生顺序决策(先读文档 vs 先探索项目)。被动上下文完全避免了这个问题。

解决上下文膨胀问题

AGENTS.md 中嵌入文档有导致上下文窗口膨胀的风险。我们通过压缩解决了这个问题。 最初注入的文档约为 40KB。我们将其压缩到 8KB(减少了 80%),同时保持了 100% 的通过率。 压缩格式使用管道分隔结构,将文档索引打包到最小空间:

Markdown

[Next.js Docs Index]|root: ./.next-docs
|IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning
|01-app/01-getting-started:{01-installation.mdx,02-project-structure.mdx,...}
|01-app/02-building-your-application/01-routing:{01-defining-routes.mdx,...}

完整的索引涵盖了 Next.js 文档的每个部分。 Agent 知道去哪里找文档,而无需在上下文中包含全部内容。 当它需要特定信息时,它会从 .next-docs/ 目录读取相关文件。

亲自尝试

一个命令即可为你的 Next.js 项目进行设置: npx @next/codemod@canary agents-md

该命令做三件事: 1. 检测你的 Next.js 版本。 2. 下载匹配的文档到 .next-docs/。 3. 将压缩索引注入到你的 AGENTS.md

如果你使用的 Agent 能够识别 AGENTS.md(如 Cursor 或其他工具),这种方法同样有效。

这对框架作者意味着什么

技能并非毫无用处。AGENTS.md 方法为 Agent 在所有任务中如何使用 Next.js 提供了广泛的、横向的改进。 技能对于用户显式触发的垂直、特定操作的工作流效果更好,例如“升级我的 Next.js 版本”、“迁移到 App Router”或应用框架最佳实践。 这两种方法相辅相成。 尽管如此,对于 通用框架知识,被动上下文目前的表现优于按需检索。

实用建议: 1. 不要等待技能改进。 虽然随着模型在工具使用方面变得更好,差距可能会缩小,但结果现在就很重要。 2. 激进地压缩。 你不需要在上下文中放入全部文档。指向可检索文件的索引同样有效。 3. 用评测进行测试。 构建针对不在训练数据中的 API 的评测。那是文档访问最重要的地方。 4. 为检索而设计。 构建你的文档结构,以便 Agent 可以查找和阅读特定文件,而不需要一次性获取所有内容。 5. 目标是将 Agent 从“预训练主导的推理”转变为“检索主导的推理”。


第二部分:专家评估

这篇文章揭示了当前 AI Agent 领域一个非常关键且反直觉的现象:在处理特定领域知识时,“笨拙”的静态上下文(Context)往往比“智能”的动态工具(Tools/Skills)更有效。

1. 核心洞察的价值

文章击中了当前 AI Agent 开发的一个痛点:过度依赖模型的“自主决策能力”

  • 工具调用不可靠:开发者往往假设 Agent 会像人类高级工程师一样,遇到不懂的就去查文档(调用 Skill)。但数据表明 ,Agent 在 56% 的情况下过于自信,根本不调用工具,直接基于(可能过时的)训练数据瞎编。
  • Prompt 工程的脆弱性:即使强迫 Agent 使用工具,微小的措辞差异("Must invoke" vs "Explore first")会导致巨大的结果偏差 。这说明依赖 Prompt 来修补模型行为是不稳定的。

2. 解决方案的创新性

作者提出的 AGENTS.md + 压缩索引 方案非常聪明:

  • 消除决策成本:不让 Agent 决定“是否查文档”,而是直接把“文档地图”(索引)放在它眼前 。
  • Token 经济学:通过压缩索引(从 40KB 压到 8KB),既保留了全景地图,又没有撑爆上下文窗口,让 Agent 按需去读取本地文件(Retrievable files)。

3. 对实际开发的启示

这是一个 “RAG(检索增强生成)前置化” 的典型案例。它证明了对于高频、核心的知识(如框架版本特性),直接注入上下文(Context Injection)比让模型通过多步推理去检索(Agentic Retrieval)要稳定得多。


第三部分:AI Agent 效率提升方法论

基于这篇文章的发现,我总结了一套 “上下文优先” (Context-First) 的 AI Agent 使用方法论,这将直接提升使用 Cursor、Windsurf 或 Claude Code 等工具的效率。

核心原则:不要让 Agent 猜,给它地图。

1. 建立项目的“真理来源” (Source of Truth)

不要指望 Agent 自己知道您项目用的是什么版本的库,或者您的特定编码规范。

  • 操作:在项目根目录创建 AGENTS.md(或 .cursorrules)。
  • 内容
    • 技术栈版本:明确写出 Next.js v16, React v19, Tailwind v4 等,防止模型使用过时语法 。
    • 压缩的文档索引:如果使用新出的库,不要把整个文档贴进去,而是生成一个文件结构树,告诉 Agent 哪个文件讲了什么,让它自己去读该读的文件 。

2. 强制“检索主导”模式

模型倾向于使用它“脑子”里训练过的旧知识(幻觉的来源)。您必须显式地改变它的推理模式。

Prompt 技巧:在 System Prompt 或 AGENTS.md 顶部加入以下指令(源自文章的最佳实践):

"IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any [Your Framework] tasks." (重要:对于任何 [框架名] 任务,优先使用检索主导的推理,而非预训练主导的推理。)

3. 区分“通用知识”与“特定动作”

  • 通用知识(用 AGENTS.md):框架文档、项目目录结构、核心设计原则。这些应该被动地、持久地存在于上下文中 。
  • 特定动作(用 Skills/Tools):只有那些低频的、具体的任务(如“运行数据库迁移脚本”、“执行特定测试”)才应该封装成 Tool 让 Agent 调用 。

4. 避免“过度智能”的陷阱

  • 不要试图用复杂的 Prompt 诱导 Agent 去“思考何时该用工具”。文章证明这很脆弱 。
  • 直接把信息喂给它。如果有一个文件是每次写代码都要参考的(比如 types.tsapi-schema.json),想办法将其摘要放入 Context,而不是期待 Agent 去 read_file

5. 下一步行动建议

如果正在使用 Cursor 或类似的 IDE:

  • 立即行动:检查您的 .cursorrulesAGENTS.md
  • 优化:确保里面包含了您核心依赖库的压缩索引(文件路径列表+简短描述),而不是冗长的全文。
  • 指令:加上“优先检索”的指令。

通过这套方法,将把 AI Agent 从一个“偶尔聪明的助手”变成一个“始终准确的专家”。