引子:AGENTS.md 是什么

在仓库根目录放一个 Markdown 文件,告诉 AI Coding Agent 这个项目是什么、怎么构建、有哪些规矩。这个做法现在有了一个事实标准的名字——AGENTS.md

它的角色是:README.md 给人类读者看,AGENTS.md 给 AI 读者看。前者聚焦快速开始与贡献指南,后者聚焦构建命令、编码约束、验证闭环、踩坑清单,覆盖任何会影响 Agent 产出正确性的上下文。

这个概念最初由 Anthropic 的 Claude Code 以 CLAUDE.md 形式普及。随后各家工具一度各走各路:Cursor 用 .cursorrules,Copilot 用 .github/copilot-instructions.md,Gemini CLI 用 GEMINI.md,Cline 用 .clinerules,Sourcegraph AMP 提议 AGENT.md(单数),OpenAI Codex 提议 AGENTS.md(复数)。最终以 OpenAI 的 AGENTS.md 胜出——AMP 主动把 agent.md 域名重定向到 agents.md,Linux Foundation 下属的 Agentic AI Foundation 托管标准,Cursor、Kiro、灵码、Qoder、Copilot 等主流工具先后支持。到 2026 年初,GitHub 上已有超过 6 万个开源项目使用这个格式。Claude Code 仍然读 CLAUDE.md,但内容完全通用,一条软链就能兼容:ln -s AGENTS.md CLAUDE.md

历史脉络和段落顺序不是 AGENTS.md 真正值得讨论的地方。真正值得讨论的问题只有一个:为什么一份几百行的 Markdown,能显著改变 AI Coding 的产出质量? 答案藏在一条设计原则里:地图,而非手册。

核心设计原则:地图,而非手册

OpenAI 在 Harness Engineering 实践中把 AGENTS.md 的第一原则命名为「Map, not Manual」。Anthropic 的官方博客在 Skill 设计中也重复了同一条原则:渐进式披露(progressive disclosure)。两家把不相容的工具做到相容点上,背后的机制是同一个:大模型的注意力是有限资源,上下文窗口越大,注意力被稀释得越严重。

一份 5000 行、事无巨细的 AGENTS.md,和一份 200 行、只标注导航的 AGENTS.md,对 Agent 的实际帮助往往是后者更大。前者让模型淹没在细节里,关键约束和普通说明被同等对待;后者只告诉模型「要做 X 就去看 Y 文档,要写 Controller 就去看 Z 目录」,模型按需跳转,注意力花在真正当前任务相关的那一小块。

这条原则推导出了一个判断标准:

  • 不知道就会写出错误代码的信息,写进 AGENTS.md
  • 不知道只会写出不够好代码的信息,写进详细文档,AGENTS.md 里放链接

这条标准把「该写什么」和「不该写什么」一刀切干净。分层架构的依赖方向、异常处理的强制约定、响应体由框架统一包装,违反就直接跑不通或留下隐患,必须进 AGENTS.md。某个 Service 内部的调用顺序、某个组件 prop 的推荐写法,错了可以改,属于详细文档的领域。

一组先行的场景

在展开具体实践之前,先看一组没有 AGENTS.md 时 AI Coding 的典型失败模式。它们解释了为什么 AGENTS.md 不是可有可无的装饰,而是工程化使用 AI Coding 工具的基础设施。

上下文割裂。前后端分属两个 Git 仓库时,AI 工具一次只能看到一半。改一个前后端联动的功能,比如后端新增接口、前端调用这个接口,需要在两个窗口之间来回切换。每次切换,模型就丢掉上一轮的上下文。

私域组件不可见。训练数据里没有的闭源组件(内部组件库、私有 SDK、商业框架),AI 既不认识也查不到文档。写出来的代码要么编译不过,要么 prop 传错,要么漏掉必要配置。

规矩活在人脑里。每个项目都有自己的潜规则:异常必须通过 BusinessException 抛出、响应体禁止手动构造、Controller 不得直接注入 Repository。这些规矩写在团队成员的默契里,不写下来 AI 就反复违反,每次纠正完下次还犯。

验证闭环断裂。AI 改完代码不知道怎么构建、怎么启动、怎么自测。本地环境配置、启动命令、验证方式散落在聊天记录和个人脚本里。AI 只能把代码改完就停下,剩下的人来接手。这意味着 Agent 无法夜间自主执行:连项目都启动不起来,谈不上自主闭环。

这四种失败模式背后是同一个根因:项目的知识与约束存在于人脑,不存在于 AI 能读到的地方AGENTS.md 就是把这些隐性知识外化成可读文本的那个载体。

五类实践:让 AGENTS.md 真正起作用

AGENTS.md 自己一份是不够的,它需要周边基础设施配合。下面五类实践来自在多个真实项目(Spring Boot + React 的全栈管控系统、C++ 内核引擎、产品基线、文档系统)里收敛出的共同经验。

实践一:仓库聚合,消除上下文割裂

前后端分仓的项目,最直接的改造是把前端仓库放进后端的子目录下,或者进一步重构为 monorepo。两种方案的代价不同,收益方向一致:让 AI 在一个窗口里同时看到 Controller 定义和对应的前端调用。

脚本聚合是成本较低的折中:一个 setup-repos.sh 把前端仓库 clone 到 frontend/ 下,.gitignore 里把 frontend/ 屏蔽掉,不影响后端 CI/CD,不用 AI 工具的同事完全无感。monorepo 是更干净的选择:前后端代码、用户手册、参考项目、构建脚本、文档在同一棵目录树下。

1
2
3
4
5
6
7
project-root/
├── server/ # 后端
├── web/ # 前端
├── user-guide/ # 用户手册(Markdown,可由 AI 基于代码生成)
├── reference-projects/ # 参考项目(git submodule)
├── scripts/ # 构建、启动、检查脚本
└── docs/ # 架构文档、设计文档

把用户手册也放进来有个副作用:AI 改完功能代码后可以顺手把对应的用户文档也更新掉。文档和代码在同一次 commit 里共进退,文档滞后问题自然消失。

实践二:环境配置归一,让 AI 能启动项目

AI 无法验证产出的根本障碍往往不在于 AI 不会验证,而在于验证所需的环境配置散落在每个人的 .bashrc、IDE JVM 参数、临时 export 里。AGENTS.md 只要无法指向一个明确的配置文件,Agent 就没法自主启动项目。

可行的约定是:所有本地环境变量统一写在 ~/.<project>_env,纯 KEY=VALUE 格式,启动脚本自动 source。放在 ~ 下而不是项目目录的理由是避免误提交到 Git。AGENTS.md 明确写清楚优先级:

1
2
3
### 数据库连接
1. 先查 ~/.<project>_env(启动脚本自动 source,文件不存在则跳过)
2. 若文件不存在,回退到 application.yml 中的缺省值

配套一键启动脚本:

1
2
3
./scripts/start-server.sh                # 构建 + 启动 + 健康检查
./scripts/start-server.sh --quick # 服务健康则秒返回
./scripts/start-server.sh --skip-build # 跳过构建直接重启

JDK 检测、优雅关闭旧进程、健康检查轮询这些细节被封装在脚本里,AGENTS.md 只暴露命令。Agent 的认知负担被压到最低:不需要理解 nvm、fnm、JAVA_HOME 的关系,只需要知道「要启动后端就跑这个脚本」。

实践三:验证闭环,改完代码不算完

验证闭环是 AGENTS.md 实践里最容易被低估的一环。它直接决定了 Agent 的产出是「看起来做完」还是「真的跑通了」。

后端的验证主力是 curl。一个可靠的 curl 验证规范有几条硬约束:每个 curl 独立执行,不串联;响应写到 /tmp/ 下的临时文件,后续用 python3 独立解析;token 获取模板化,登录写文件、提取、后续请求携带。这套啰嗦是有原因的。Agent 在 shell 里执行命令时容易踩到兼容性坑,例如 zsh 下管道加方括号 glob 会炸,curl | python3 -c "print(data['key'])" 直接报错。临时文件中转多一步,稳定性高一个量级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Step 1: 登录,结果写文件
curl -s -X POST http://localhost:8080/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' > /tmp/login.json

# Step 2: 提取 token(独立命令)
python3 -c "import json; print(json.load(open('/tmp/login.json'))['data']['token'])" > /tmp/token.txt

# Step 3: 业务接口调用
TOKEN=$(cat /tmp/token.txt)
curl -s -X POST http://localhost:8080/providers/list \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"page":0,"size":10}' > /tmp/result.json

前端的验证则要靠 Agent Browser。curl 看不到页面渲染、交互、布局问题,调试前端疑难杂症时需要让 Agent 自己打开浏览器、操作页面、截屏对比(Qoder 的 agent-browser、Claude Code 接入的 computer use 都提供这个能力)。这比让 Agent 凭空猜 CSS 问题要可靠得多。

验证闭环的完整形态是:lint 和格式检查在每次代码变更后自动触发,启动脚本把应用真正拉起来,curl 或 Agent Browser 跑接口/页面验证,Spec 的 Design 文档里明确写入验证方案。最后这一点尤其关键,它告诉 Agent「写完代码不算完,自测过功能才算完」。这套端到端验证是夜间自主执行能跑通的前提。睡前设计好 Spec 提交给 Agent,第二天早上验收结果,是只有在闭环完整时才成立的工作模式。

实践四:自动化检查,给规则装上执行力

AGENTS.md 里写的规则,如果没有自动化检查,AI 和人都会违反。规则的优先级天然是:能自动化检查的 > 写在 AGENTS.md 中的 > 口头约定的

典型例子是分层依赖检查。项目里定义严格的层级约束:

1
2
3
4
5
6
L0 - entity/          → 只允许依赖 common
L1 - repository/ → 只允许依赖 entity, common
L2 - core/ → 横切关注点,不允许依赖业务包
L3 - config/ → 允许依赖 core, service
L4 - service/ → 业务核心层
L5 - controller/ → 只允许依赖 service, core, common

光把这段贴进 AGENTS.md 是不够的。需要一个 shell 脚本扫描所有 Java 文件的 import 语句,按包路径判断层级,检测是否违反依赖方向。违规时输出的错误信息必须按 WHAT / WHY / HOW 三段式组织:

1
2
3
✗ service/gateway/impl/SomeService.java 导入了 entity.SomeEntity
原因: gateway 客户端实现禁止直接依赖业务 Entity,须通过 DTO 传递数据
修复: 在编排层完成 Entity→DTO 转换,客户端只接收 DTO

这三段不只是给人看的,更是给 AI 看的。Agent 读到这条错误信息后可以直接按 HOW 的指引去修,不需要额外上下文。集成到 make lint-arch,一条命令完成检查。改完代码 Agent 自己跑一遍检查,形成「改 → 检 → 修」的闭环。

通过 Makefile 把质量检查命令收敛成统一入口:

1
2
3
4
5
lint-arch:    ./scripts/lint-deps.sh      # 分层依赖检查
lint-format: mvn spotless:check # 格式检查
format: mvn spotless:apply # 格式修复
build: mvn package -DskipTests # 构建
test: mvn test # 测试

Agent 不需要记住每个检查命令的具体写法,AGENTS.md 只暴露 make lint-archmake lint-format

实践五:引入参考项目源码,替代永远滞后的文档

这一条是反直觉但效果最显著的一条。AI 不认识的东西——闭源私域组件、内部网关内核、公有云项目、竞品架构——靠写使用文档补上,成本高、覆盖不全、还总是滞后于实现。换一个思路:不写文档,直接把源码引进来。

1
2
3
4
5
6
7
reference-projects/
├── higress/ # 开源网关内核源码
├── nacos/ # 开源注册配置中心源码
├── teamix-pro/ # 闭源组件库源码(TypeScript)
├── apigo/ # 公有云 AI 网关后端(Go)
├── apigw/ # 公有云 AI 网关前端(React)
└── himarket/ # 开源 AI 开放平台(Spring Boot)

每个参考项目通过 git submodule 引入,配合 ignore = all 避免影响 CI/CD。本地按需拉取:

1
2
git submodule update --init                                # 首次拉取全部
git submodule update --init reference-projects/teamix-pro # 只拉取单个

源码永远不会过时,它本身就是最准确的文档。 AI 不会写私域组件时,直接读源码里的 TypeScript 定义和实现;要对接网关内核时,直接查看路由和插件的实际代码。代价是仓库体积变大和 submodule 的管理负担,换来的是 Agent 写代码的准确性。对于训练数据中不存在的内部项目,这个交易划算。

仅有源码还不够。每个参考项目配一份架构说明文档(docs/design-docs/ref-*.md),作为参考项目的地图:

文档 说明
ref-higress.md 网关内核:路由模型、插件机制、CRD 结构
ref-nacos.md Nacos:配置中心对接、服务发现集成
ref-teamix-pro.md Teamix Pro:ProTable/ProForm 使用模式、TS 类型速查
ref-apigo.md 公有云后端:目录结构、分层架构、核心模块
ref-apigw.md 公有云前端:页面结构、组件体系、路由设计
ref-himarket.md HiMarket:多模块结构、领域模型

ref 文档告诉 Agent「这个参考项目的整体结构和关键模块在哪」,reference-projects 提供细节。这些 ref 文档本身也可以由 AI 基于参考项目源码生成,又一个「代码生成文档」的闭环。

引入这么多参考仓库会不会让 Agent 无所适从?实测下来不会。AGENTS.md 的项目结构树标注了每个目录的用途,ref 文档提供架构概览,参考优先级规则明确「什么时候该看哪个项目」。现在的大模型知道什么时候该去参考项目里翻实现,什么时候该在本项目里改动,不会因为仓库里多了几个参考项目就迷失方向。

AGENTS.md 的骨架

把上面五类实践沉淀成一份 AGENTS.md,大致的章节结构是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# AGENTS.md

## 1. 项目概述
一段话:项目定位、技术栈、仓库结构。
前 10 行必须让 AI 建立项目心智模型。

## 2. 快速命令
构建、启动、格式化、质量检查的命令速查表。
环境变量配置说明(env 文件位置、启动脚本自动 source)。

## 3. 后端架构
包结构树(ASCII)+ 每个包的用途注释。
核心子系统简要说明 + 详细文档链接。

## 4. 前端架构
技术栈、路由方案、API 层约定、组件库规范。
详细文档链接。

## 5. 关键约定
5-10 条硬性编码规则(违反会直接导致问题的)。
每条规则附详细文档链接。

## 6. 本地开发与验证流程
「改 → 构建 → 启动 → 验证」完整闭环。
curl 验证模板、Token 获取、日志路径。

## 7. 质量检查
lint、format、build、test 命令矩阵。

## 8. 参考项目约定
参考项目列表 + 优先级规则。

## 9. 文档导航
所有详细文档的索引表。

控制在 200 行以内。超过这个范围说明有细节该拆到 docs/ 下的专题文档里。

怎么开始写

从零写一份 AGENTS.md 的门槛比想象中低。

第一种起步方式:大多数 AI Coding 工具提供 /init 或等价命令(Claude Code 的 /init、Qoder 的 qoder init),扫描项目结构生成初始版本。自动生成的版本覆盖项目概述和基本构建命令,作为骨架够用。

第二种起步方式:用更完整的 Skill 起步。Qoder 社区开源的 harness-creator 会一并生成 AGENTS.md、分层架构约束的 lint 脚本、Makefile、验证脚本、参考文档,相当于把本文提到的实践打包成一键产出。

起步之后的迭代是 bad case 驱动 的:

  • 某次 AI 用了错误的命名风格 → 思考「如果 AGENTS.md 里多一条 XX 规则,是不是就不会犯」→ 补一条
  • 某次 AI 在错误的层级引入了依赖 → 是全局规则,加到 AGENTS.md;或者补一个 lint 脚本自动拦
  • 某次 AI 在某个模块用错了某个 prop → 是模块细节,补到对应的 docs/ 文档里

判断改哪里的标准始终是一条:全局规则进 AGENTS.md,模块细节进 docs。如果所有细节规则都往 AGENTS.md 里怼,上下文会膨胀到重要规则被淹没。

谁来读这些文件

推广 AGENTS.md 时,明确标注每个文件的读者有助于降低团队的理解成本:

文件 读者 说明
README.md 项目介绍、快速开始,人类入口
AGENTS.md AI 为主,人可浏览 AI 工具自动读取的项目指令
docs/*.md AI 为主,人可参考 各模块的开发手册
scripts/*.sh 人和 AI 共用 构建、启动、部署脚本
setup-repos.sh 人执行 一键环境搭建

README 和 AGENTS.md 是互补的。README 聚焦快速开始和贡献指南,AGENTS.md 聚焦构建命令、编码规范、验证流程。内容可能有少量重叠(比如项目概述),侧重点不同,不需要合并。脚本是人和 AI 共用的部分,因此脚本本身的可读性和健壮性格外重要,其中的每一行注释都可能成为 Agent 决策的依据。

为什么是 AGENTS.md

工具不统一的现状下,AGENTS.md 作为核心入口的价值来自几个方面:

  • 足够通用:被大多数主流工具识别,一份文件覆盖多数场景
  • 零配置成本:工具打开项目自动读取,不需要安装插件或配置 hook
  • 维护负担低:不用为每种工具维护一份规则文件
  • 兼容性好:不识别 AGENTS.md 的 Claude Code 可以通过 ln -s AGENTS.md CLAUDE.md 接入

和特定工具绑定的 rules、hook、Skill 作为补充,核心规则全部收敛到 AGENTS.md 一个入口。换工具时迁移成本趋近于零。

作为知识沉淀的副作用

AGENTS.md 的初衷是给 AI 看,它的维护过程本身是一次团队知识梳理。过去散落在 Wiki、聊天记录、口头约定里的编码规约,现在被结构化地写进了一份 Markdown。新人入职读完 AGENTS.md,就能拿到过去需要摸索很久的「潜规则」。给 AI 写 AGENTS.md 的过程,客观上也是给人写了一份项目使用手册。

这和 Code as Documentation 的思路一脉相承:隐性知识外化成可执行、可校验的文本,才能跨越时间和人员传承。AGENTS.md 加上 lint 脚本、启动脚本、验证规范,本质是在构建一个反馈回路:Agent 读 AGENTS.md 理解项目,写代码,自动检查,启动验证,根据结果修正。人类的角色从回路中的每一步执行者,变成回路本身的设计者。

参考资料