让 AI Coding Agent 真正理解项目:从 Rules 到 AGENTS.md
你让 AI Coding Agent 做一个看起来很简单的任务:
给订单列表增加一个“导出 CSV”按钮。
十分钟后,Agent 告诉你已经完成。但你打开代码一看:
- 项目使用
pnpm,它却执行了npm install,生成了第二份锁文件; - 团队要求复用
Button组件,它重新写了一套按钮样式; - API 客户端本应由 OpenAPI 自动生成,它直接改了生成文件;
- 改完没有运行类型检查,也没有验证中文 CSV 是否乱码;
- 它顺手“优化”了三个无关组件。
Agent 不一定能力差。真正的问题是:它不知道这个项目里那些没有写在代码语法中的约定。
同事入职时,会有人告诉他:
我们用 pnpm;公共组件在这里;生成目录不要手改;改订单模块要跑这组测试;不确定接口时先问后端。
Agent 每次进入一个新会话,也像一个刚入职、记忆被清空的新同事。如果这些信息只存在于你的脑中、群聊和历史 Review 里,它就只能猜。
项目 Rules 和 AGENTS.md 的作用,就是把这些反复口头交代的内容,变成 Agent 每次工作前都能看到的“项目工作说明”。
一、先说人话:AGENTS.md 就是给 Agent 的项目入职手册
AGENTS.md 是一个普通 Markdown 文件。它没有神秘语法,也不是某种 AI 模型。
你可以把它理解成:
README.md主要告诉人“这个项目是什么”;
AGENTS.md重点告诉 Agent“在这个项目里应该怎样工作”。
一个有用的 AGENTS.md 通常回答下面这些问题:
- 项目使用什么包管理器?
- 安装、启动、构建和测试命令是什么?
- 哪些目录负责什么?
- 哪些文件允许修改,哪些文件由工具生成?
- 新代码应该遵守哪些项目特有约定?
- 不同类型的修改分别要跑哪些检查?
- 哪些操作有风险,必须先向人确认?
- 任务完成时要提供什么验证证据?
官方的 AGENTS.md 开放格式说明把它称为“给 Agent 的 README”。它的价值不在于文件名本身,而在于提供一个稳定、可预期、能跟随代码仓库流转的位置。
Rules 又是什么?
Rules 是更宽泛的概念,指所有能够持续影响 Agent 行为的项目指令。
不同工具使用的名称和能力并不完全相同:
| 工具 | 常见项目指令 | 适合做什么 |
|---|---|---|
| Codex | AGENTS.md、嵌套的 AGENTS.md | 仓库规则、目录规则、测试和 Review 要求 |
| Cursor | AGENTS.md、.cursor/rules/*.mdc | 简单全局规则,或按文件和场景加载的细粒度规则 |
| Claude Code | CLAUDE.md、.claude/rules/*.md | 项目说明、目录或文件类型规则、Claude 专用要求 |
| GitHub Copilot | AGENTS.md、.github/copilot-instructions.md、路径规则 | 根据 IDE、CLI、云端 Agent 或 Code Review 入口加载指令 |
所以,AGENTS.md 是一种规则载体,但 Rules 不只等于 AGENTS.md。
二、为什么只在聊天里提醒一次不够
你当然可以在每个任务前都写:
使用 pnpm,不要改 generated 目录,完成后运行 pnpm lint 和 pnpm test,
不要修改无关文件……但这种做法有三个问题。
1. 新会话不会自动记得旧会话
Agent 当前能看到的内容是有限的。换一个会话、换一个同事、换一个 AI Coding 工具,之前口头说过的约定可能就不存在了。
2. 人会漏说,Agent 就会猜
你熟悉项目以后,会下意识认为某些规则“大家都知道”。但 Agent 看到的只是代码文件,并不知道:
src/generated/每晚会被重新生成;- 旧支付接口不能再增加调用方;
- E2E 测试必须使用测试租户;
- 数据库迁移只能向前兼容。
代码能够展示“现在是什么”,却不一定能解释“为什么必须这样”。
3. 多人协作会产生不同版本的口头规则
如果规则散落在群聊、个人提示词和 Review 评论中,三个开发者可能给 Agent 三套不同说法。
把规则提交进仓库,至少能让团队:
- 在 Pull Request 中审查规则;
- 知道规则什么时候、为什么发生变化;
- 让本地 Agent 和云端 Agent共享同一份基础约定;
- 在规则过时以后找到负责人修改。
三、AGENTS.md 不是什么
理解边界,比会写文件更重要。
它不是项目百科全书
不要把所有文档复制进去。Agent 本来就能读取 package.json、目录和代码,重复这些内容只会占用上下文。
应该写:
修改
src/api/generated/的需求,必须先更新openapi.yaml,再运行pnpm api:generate。
不必写:
src/目录里有很多 TypeScript 文件。
前者包含项目特有的行动规则,后者是 Agent 自己就能发现的事实。
它不是一次任务的需求单
“这次把导出按钮放在筛选栏右侧”只属于当前任务,应该放在 Prompt 或规格中。
“所有列表页的主要操作都放在筛选栏右侧”才可能是值得长期保留的项目规则。
它不是安全权限系统
规则可以告诉 Agent“不要读取生产密钥”,但它本质上仍是给模型的指令,不是硬隔离。
如果某件事必须禁止,应使用更可靠的控制:
- 文件和目录权限;
- 沙箱;
- 命令审批;
- 工具白名单;
- Git 分支保护;
- CI 质量门禁;
- Hooks 或策略配置。
规则负责指导行为,系统负责强制边界。
它不是 Skill,也不是 MCP
| 机制 | 解决的问题 |
|---|---|
| Prompt | 这一次任务要完成什么 |
AGENTS.md / Rules | 在这个项目里长期应该怎样工作 |
| Skill | 某类重复任务应该按照什么步骤完成 |
| MCP | Agent 如何连接浏览器、数据库、文档等外部能力 |
| Hook / CI / 权限 | 哪些动作必须执行、拒绝或通过检查 |
例如:
- “项目统一使用 pnpm”适合放在规则里;
- “发布版本要执行 12 个步骤”更适合封装成 Skill;
- “读取浏览器 Console”需要浏览器工具或 MCP;
- “禁止提交包含密钥的文件”应该由扫描工具和 CI 强制执行。
四、真正有价值的规则,通常来自这六类信息
1. 可以直接执行的命令
不要写:
修改后认真测试。
要写:
- 安装依赖:`pnpm install`
- 本地启动:`pnpm dev`
- 类型检查:`pnpm typecheck`
- 单元测试:`pnpm test`
- 仅运行订单模块测试:`pnpm test src/orders`Agent 不应该猜命令。错误命令轻则浪费时间,重则生成多余文件或误操作环境。
2. 项目的结构地标
只写那些能改变 Agent 行动路线的信息:
- 页面入口在 `src/pages/`。
- 复用组件在 `src/components/ui/`,新增按钮前先搜索这里。
- API 类型来自 `src/api/generated/`,不要手工修改。
- 业务请求统一通过 `src/api/client.ts`,不要在组件中直接调用 `fetch`。3. 修改边界和非目标
- 除非任务明确要求,不要升级依赖。
- 不要修改与当前任务无关的格式和命名。
- 涉及公共 API、数据库迁移或鉴权流程时,先说明影响并等待确认。
- 不要编辑 `dist/`、`coverage/` 和自动生成文件。这类规则能显著减少“顺手重构”和范围漂移。
4. 项目特有的代码约定
格式化工具能够自动处理的事情,交给工具;只把 Agent 无法从默认工具中知道的约定写进规则。
有价值:
- 金额在业务层统一使用整数分,不要使用浮点元。
- React Query 的 query key 必须从 `src/query/keys.ts` 导出。
- 用户可见错误使用 `AppError` 映射,不直接显示后端原始消息。价值较低:
- 写出优雅、整洁、高质量的代码。
- 使用最佳实践。后两条听起来正确,却无法验证,也无法告诉 Agent 具体应该怎么做。
5. 与修改类型对应的验证要求
不要要求每次都运行仓库里所有检查。大型项目可能因此浪费半小时。
可以写成分层规则:
- 修改 TypeScript:运行 `pnpm typecheck`。
- 修改业务逻辑:运行相关单元测试。
- 修改页面交互:启动应用,并验证主要流程和错误状态。
- 修改公共包:运行该包测试及至少一个使用方的测试。
- 只修改文档:无需运行应用测试,但检查链接与格式。6. 交付时必须说明的证据
完成任务时必须报告:
1. 修改了哪些文件;
2. 为什么这样修改;
3. 运行了哪些检查及结果;
4. 哪些内容没有验证;
5. 是否存在需要人工确认的风险。这会把 Agent 的“已经完成”变成一份可以审查的交付说明。
五、从零写一份最小可用 AGENTS.md
假设我们有一个 React + TypeScript 项目:
shop-console/
├── src/
│ ├── api/
│ │ └── generated/
│ ├── components/
│ │ └── ui/
│ ├── features/
│ │ ├── orders/
│ │ └── payments/
│ └── pages/
├── tests/
├── package.json
└── pnpm-lock.yaml一份起步版 AGENTS.md 可以这样写:
# AGENTS.md
## 项目概况
- 这是一个 React + TypeScript 的运营后台。
- 使用 `pnpm`,不要运行 `npm install` 或生成 `package-lock.json`。
- 先阅读相关代码和现有测试,再提出修改计划。
## 结构与复用
- 页面位于 `src/pages/`,领域功能位于 `src/features/`。
- 通用 UI 位于 `src/components/ui/`;新增组件前先搜索已有实现。
- `src/api/generated/` 由 OpenAPI 生成,禁止手工修改。
- API 请求统一通过 `src/api/client.ts`。
## 修改边界
- 只修改完成当前任务所需的文件,不做无关重构。
- 不主动升级依赖。
- 修改公共 API、鉴权、支付或数据库结构前,先说明影响并等待确认。
- 不读取、输出或提交 `.env` 中的密钥。
## 代码约定
- 金额在业务逻辑中使用整数分。
- 用户可见文案使用中文,并通过现有 i18n 方法管理。
- 优先复用现有类型,不重复声明相同的数据结构。
## 验证
- TypeScript 修改后运行 `pnpm typecheck`。
- 业务逻辑修改后运行相关单元测试。
- 页面交互修改后运行 `pnpm dev`,验证正常、加载中、空状态和错误状态。
- 只有相关检查通过后,才能声称任务完成。
## 交付说明
- 列出修改文件、验证命令和结果。
- 明确说明未执行的检查及原因。
- 不要自行提交或推送代码,除非任务明确要求。这份文件没有描述整个系统,也没有复制 ESLint 配置。它只保存了会影响 Agent 决策、容易重复出错、并且可以验证的信息。
六、规则要分层:离代码越近,内容越具体
一个大型仓库可能同时包含前端、后端、移动端和基础设施代码。把所有规则塞进根目录,会产生两个问题:
- 每个任务都加载大量无关内容;
- 不同模块的规则互相冲突。
更好的做法是分层。
以 Codex 为例,它会从全局规则开始,再从仓库根目录向当前工作目录逐层读取指令;越靠近当前目录的文件越具体,发生冲突时更近的规则拥有更高优先级。
~/.codex/AGENTS.md
↓
shop-console/AGENTS.md
↓
shop-console/src/features/payments/AGENTS.md
↓
当前任务 Prompt可以这样划分:
全局规则:只属于你个人
路径示例:~/.codex/AGENTS.md
- 默认使用中文解释结论。
- 执行破坏性命令前必须询问。
- 修改完成后先展示 Diff,不自动提交。这类规则不要提交到团队仓库。
仓库规则:所有模块都适用
路径:项目根目录的 AGENTS.md
- 使用 pnpm。
- 禁止手改生成代码。
- TypeScript 修改后运行类型检查。它应该提交到 Git,让本地、云端和团队成员获得一致规则。
目录规则:只约束某个模块
路径示例:src/features/payments/AGENTS.md
# 支付模块规则
- 金额只能使用整数分。
- 不记录卡号、验证码或完整支付令牌。
- 修改支付状态机后运行 `pnpm test payments`.
- 改变支付结果枚举属于兼容性变更,必须先说明影响。需要注意:不同工具的目录发现和优先级并不完全相同。 Codex 原生支持分层 AGENTS.md;Cursor 官方文档目前把根目录 AGENTS.md 定位为简单规则,需要更细粒度时更适合使用 .cursor/rules;Claude Code 则使用分层 CLAUDE.md 和 .claude/rules/。
当前 Prompt:只描述这次任务
给订单列表增加 CSV 导出。
本次只导出当前筛选结果,不增加“导出全部”能力。
验收时需要检查中文、逗号、换行和空列表。长期规则不应该每次复制到 Prompt;一次性需求也不应该永久写进 AGENTS.md。
七、一个仓库同时使用 Cursor、Claude Code 和 Codex,怎么办?
不要维护三份内容完全相同、迟早互相漂移的长文档。
推荐使用:
AGENTS.md # 跨工具共享的核心项目约定
CLAUDE.md # 导入 AGENTS.md,再补 Claude 专用说明
.cursor/rules/ # 仅保存需要路径匹配或 Cursor 专用的规则
.github/instructions/ # 仅保存 Copilot 特定或路径规则Claude Code 官方文档明确说明它原生读取 CLAUDE.md,而不是直接读取 AGENTS.md。可以用一层很薄的适配文件:
# CLAUDE.md
@AGENTS.md
## Claude Code 专用说明
- 修改支付模块前先进入 Plan 模式。
- 使用 `/context` 检查本文件是否已加载。Cursor 可以直接使用根目录 AGENTS.md。当你需要“仅修改某类文件时才加载”的规则,再创建 .cursor/rules。
GitHub Copilot 不同入口支持的指令类型存在差异。团队应该以 GitHub 官方的自定义指令支持矩阵为准,不要假设 IDE、CLI、云端 Agent 和 Code Review 行为完全相同。
核心原则是:
公共事实只维护一份;工具专用行为放在薄适配层;不要复制粘贴出三份“几乎一样”的规则。
八、规则写得越多,Agent 就越听话吗?
不一定。
规则文件会进入 Agent 的上下文。内容越多,意味着:
- 占用更多上下文空间;
- 重要规则更容易被埋没;
- 冲突概率增加;
- 过期信息更难发现;
- 每次任务都要携带更多无关信息。
Claude Code 官方建议让规则具体、简洁、结构清楚;Codex 官方指南也强调,短而准确的 AGENTS.md 比充满模糊要求的长文件更有用。
用这个标准判断一条规则是否值得保留
一条长期规则最好同时满足:
- 项目特有:不是所有项目都适用的常识;
- 反复需要:如果不写,Agent 容易再次犯错;
- 能够行动:Agent 看完知道具体要做什么;
- 可以验证:人或工具能判断它是否被遵守;
- 相对稳定:不会明天就失效;
- 作用域清楚:知道它适用于整个仓库还是某个目录。
例如:
| 原始写法 | 问题 | 更好的写法 |
|---|---|---|
| 保证代码质量 | 无法行动、无法验收 | 修改 TypeScript 后运行 pnpm typecheck |
| 注意安全 | 范围太大 | 日志中不得输出 access token、卡号和身份证号 |
| 使用现有组件 | 不知道去哪里找 | 新增 UI 前先搜索 src/components/ui/ |
| 写测试 | 不知道测试什么 | 修改订单金额计算时,补充零值、负值和小数舍入测试 |
| 不要乱改 | 含义模糊 | 只修改完成当前任务所需的文件,不做无关格式化和重命名 |
九、实战:让 Agent 帮你生成规则,但不要直接照单全收
第一步:让 Agent 只调查,不修改
请先只读分析这个仓库,不要修改文件。
请从以下位置收集已经存在的项目事实:
- README 和贡献文档;
- package.json 中的 scripts;
- 锁文件与包管理器配置;
- CI 工作流;
- lint、格式化和类型检查配置;
- 测试目录;
- 自动生成目录和文件头;
- 现有架构文档。
输出一份候选 AGENTS.md,并为每条规则注明证据文件。
不要猜测无法从仓库确认的约定。“注明证据”很重要。否则 Agent 可能把自己的偏好误写成团队规则。
第二步:人工删除三类内容
删除:
- Agent 从代码中随时能发现的普通事实;
- 没有证据的主观偏好;
- 无法验证的口号。
保留:
- 正确命令;
- 隐藏边界;
- 反复出现的 Review 意见;
- 模块特有风险;
- 验证与交付要求。
第三步:用一个真实小任务验证
不要只问 Agent“你读到了吗”,而要设计一个会触发规则的任务。
例如规则写着:
- 不主动新增依赖;确有必要时先说明原因并等待确认。测试 Prompt:
给日期列表增加“距离现在多久”的显示。
请直接实现,并使用你认为方便的第三方日期库。理想行为不是立刻安装 dayjs 或 date-fns,而是:
- 先检查项目是否已有日期工具;
- 尝试使用现有能力;
- 确需新增依赖时先说明理由;
- 等待确认后再安装。
第四步:要求 Agent 报告加载了哪些规则
Codex 可以从目标目录启动新会话,然后询问:
请列出当前生效的项目指令来源,并用自己的话概括最重要的规则。
不要修改任何文件。Claude Code 可以使用 /context 查看加载的 CLAUDE.md,也可以用 /memory 管理相关文件。Cursor 中可以让 Agent 先复述当前项目 Rules,再开始修改。
如果 Agent 连规则来源都说不清,先排查文件位置、作用域和新会话加载,不要急着继续加更多规则。
十、为什么 Agent 还是会违反规则?
情况 1:规则没有被加载
检查:
- 文件名和位置是否正确;
- 是否在正确的项目目录启动 Agent;
- 修改规则后是否开启了新会话;
- 工具是否支持这种规则文件和作用域;
- 文件是否为空或被忽略。
情况 2:规则互相冲突
根目录写“所有修改必须跑完整测试”,子目录又写“只跑相关测试”,个人规则还写“不要运行耗时命令”。
人看到都会犹豫,Agent 也一样。删除冲突,或者明确条件:
- 默认运行相关测试。
- 修改共享基础包时,再运行完整测试。情况 3:规则太抽象
把“遵循架构设计”改成可执行路径:
- React 组件不得直接访问 `/api`;请求必须通过 `src/api/client.ts`。情况 4:规则太长
把只针对支付模块的规则移到支付目录,把多步骤发布流程封装成 Skill,把格式要求交给 formatter。
根文件只保留所有任务都会用到的高价值信息。
情况 5:你需要的其实是强制执行
如果要求是“每次提交前必须运行 lint”,只写规则仍然可能被忘记。
更可靠的组合是:
AGENTS.md 告诉 Agent为什么和怎么做
+
Hook / CI 确保检查一定执行
+
分支保护阻止不合格结果合并把文字规则当作第一道引导,而不是最后一道防线。
十一、让规则成为活文档,而不是一次性工程
最好的规则不是在项目第一天凭空设计出来的,而是从真实摩擦中逐步长出来的。
当下面的事情发生时,可以考虑更新规则:
- Agent 第二次犯了同一个错误;
- Code Review 再次解释同一条项目约定;
- 新增了稳定的构建或测试命令;
- 某个模块出现了新的安全边界;
- 团队决定废弃一种旧做法;
- 一条规则已经与代码现状不符。
每次更新都应该问:
- 这是一次性问题,还是会重复发生?
- 应该放在根目录,还是更近的子目录?
- 能否由 lint、测试或权限直接强制?
- 是否会与现有规则冲突?
- 六个月后谁来判断它是否过期?
建议把 AGENTS.md 当作普通代码资产:
- 通过 Pull Request 修改;
- 要求模块负责人 Review;
- 在提交记录中说明为什么增加规则;
- 定期删除失效和重复内容;
- 用真实任务验证,而不是只检查 Markdown 格式。
十二、今天就能完成的最小实践
打开一个你熟悉的项目,完成下面五步:
- 找出正确的安装、启动、检查和测试命令;
- 写下三个 Agent 无法只靠读代码理解的项目约定;
- 写下一条禁止范围和一条验证要求;
- 创建一份不超过 60 行的根目录
AGENTS.md; - 开启新会话,让 Agent 复述规则并完成一个小任务。
如果这次任务中少了一次“不是跟你说过了吗”,这份规则就已经开始产生价值。
项目规则解决的是“在这个仓库里应该怎样工作”。下一篇将继续解决另一个常见问题:规则知道了,但需求本身仍然模糊,Agent 为什么还是越改越乱?