☰
AGENTS.md:给AI Coding Agent的架构说明书,根治多语言项目翻车
2026/10/17 10:41:39 网站建设 项目流程

这两年 AI Coding Agent 火得一塌糊涂,但大家慢慢发现一个问题:Agent 写点函数、做个 CRUD 还行,一旦碰上那种积累了五六年的多语言多框架老项目,它经常"好心办坏事"。我见过太多翻车案例——它在完全不了解项目架构的前提下,直接绕过封装层去改底层数据,或者在一个本该走事件驱动的服务里硬塞了一个同步调用,更别提把分层架构改成"屎山"的骚操作。问题的根子不在模型能力,而在 Agent 缺少一份真正能读懂的"架构说明书"。

AGENTS.md 就是为这件事而生的。它本质上是给 AI Coding Agent 看的架构契约,和 README 给人类看、注释给代码局部做解释不同,AGENTS.md 要回答的是"这个项目整体怎么组织、什么能碰、什么不能碰、改动应该往哪放"。这篇文章我会从原理讲到落地,结合多语言多框架仓库的实际案例,把 AGENTS.md 从一份"希望 Agent 能看懂"的文档,变成一套可执行、可维护、可验证的项目规范。不管你是个人维护开源库,还是团队里管着一个多模块的复杂系统,这份指南都值得你照着做一遍。

1. AI Coding Agent 为什么总是"局部正确,整体崩坏"

1.1 Agent 的工作模式决定了它天生缺少"上帝视角"

先说透一件事:AI Coding Agent 和人类工程师的工作方式有本质差别。人进到一个项目里,哪怕没人带着,也会先翻 README、看目录结构、找架构文档,在脑子里建立一张"项目地图",然后才动手写代码。Agent 不是这样,它拿到的是一个被截断的上下文窗口,里面装着文件树、当前打开的文件内容,以及你 prompt 里给的少量指引。

在这种模式下,Agent 最擅长的是"局部推理":看到某个函数,它能根据上下文猜出函数意图;看到某个接口,它能按惯例补出实现。但一旦问题需要全局判断——"这个改动会不会破坏模块边界""这个依赖方向是不是违反了分层原则"——它就抓瞎了。因为没有全局信息,它只能在局部找一个"看起来最合理"的方案,而这个方案往往和整体架构冲突。

举个我实际见过的例子。一个 Java 后端项目,标准的三层架构:Controller 层调用 Service 层,Service 层调用 Repository 层,Repository 层操作数据库。业务方让 Agent 加一个"导出用户列表"的接口。Agent 没有看架构约束,直接在 Controller 里注入了一个 JdbcTemplate,写了一段 SQL 把数据查出来返回了。从功能角度说,这个接口完全能用;从架构角度说,它直接让数据访问层穿透到了 web 层,后续所有想复用 Service 层业务校验的调用方都会被带偏。

1.2 缺少架构上下文时的典型翻车场景

根据我观察到的案例,Agent 在缺少架构信息时最常踩这几类坑:

  • 越层调用:Web 层直接操作数据库、绕过 Service 层调用 Repository,这在分层架构里是重灾区。Agent 分不清职责边界,因为代码里没有明确告诉它"哪一层允许依赖哪一层"。
  • 循环依赖:在模块较多的项目里,Agent 为了复用某个方法,直接在 A 模块里 import B 模块,而 B 模块本来就在 import A 模块。人看一眼模块依赖图就能避免的事,Agent 在单文件上下文里根本察觉不到。
  • 破坏聚合边界:DDD 项目里聚合根是数据的唯一入口,Agent 却可能绕过聚合根直接修改聚合内部的实体状态,因为这样"看起来改动更小"。它不知道这种做法会破坏业务不变式。
  • 选错技术栈实现:一个项目里既用了同步 HTTP 调用,又引入了消息队列,Agent 在不了解"哪个场景走同步、哪个场景走异步"的约定时,往往凭自己的偏好选一个,结果和整体设计相悖。
  • 忽略副作用约定:有的项目要求所有更新操作必须经过审计日志,Agent 没有这个上下文,直接改数据,上线后审计记录缺失。

这些坑的共同点是:代码本身没有语法错误,测试也能过,但从架构角度看就是错的。传统的错误 Agent 可以通过编译器和单元测试拦住,架构错误没有任何自动化工具能拦——除非你提前把架构规则写清楚,并且让 Agent 在动手前就看到它。

1.3 为什么 README 和注释解决不了这个问题

很多人会问:我们项目里有 README,也有详细的注释,Agent 不会看吗?答案是:看了,但没用。

README 是写给人类看的,它的目标读者是有推理能力和背景知识的工程师。你写"本项目采用微服务架构,各服务通过 RabbitMQ 通信",人类能自动脑补出完整的通信模型,但 Agent 不会。它只会把这句话当作一个普通段落,权重和你写"这是一个电商项目"差不多。更关键的是,README 通常缺少 Agent 做决策时最需要的两类信息:一是"什么不能做"的负面约束,二是"代码应该放在哪"的映射规则。

注释的问题更明显,它只存在于局部文件里。Agent 在改 A 文件时,看不到 B 文件里关于模块边界的注释。就算它把整个文件树都扫了一遍,也不可能读完整个项目所有的注释。你需要的是一个独立的、集中的、Agent 在开始工作前必然读取的文档——这就是 AGENTS.md 的位置。

2. AGENTS.md 到底是什么,以及它和 CLAUDE.md、.cursorrules 的关系

2.1 一份能落地的 AGENTS.md 包含哪些内容

AGENTS.md 的灵感其实来自 Anthropic 的 CLAUDE.md 和社区的 .cursorrules,但和它们有一个关键区别:AGENTS.md 是工具无关的。CLAUDE.md 是 Claude Code 专属,.cursorrules 是 Cursor 专属,而 AGENTS.md 是一个被多个 Agent 工具广泛支持的通用约定。只要你的 Agent 工具支持读取项目上下文,它就能识别 AGENTS.md。这意味着你不需要为每个工具单独维护一份规范。

那 AGENTS.md 里到底该写什么?我按优先级排一下:

内容块说明优先级
项目概览这个项目干什么的、服务对象是谁必须有
技术栈清单语言、框架、关键库及版本约束必须有
快速启动命令本地运行、测试、构建、lint 的具体命令必须有
目录结构与责任边界每个目录/模块干什么、允许依赖谁必须有
架构原则分层规则、数据流方向、事务边界必须有
禁止事项明确列出"不要做的事"和原因强烈建议
变更流程加功能/修 bug 时应该按什么顺序操作建议
代码风格约定命名、格式化、注释语言等可选

注意,这里的每一项都不是简单的描述,而是要能被 Agent 当成"决策依据"。什么叫决策依据?就是当 Agent 在多个方案之间犹豫时,它读到你的规范,能直接排除错误选项。

我举个实际例子,"目录结构与责任边界"这条,差的写法是:"src 目录是源代码,test 目录是测试代码。"好一点的写法是:"src/services 是业务逻辑层,只能被 src/controllers 和 src/workers 调用,禁止被 src/models 反向依赖;所有数据库操作必须经过 src/repositories,禁止在 services 里直接写 SQL。"

2.2 让 Agent 真正"读到"AGENTS.md 的三个技巧

光有内容还不够,你还得确保 Agent 在动手之前真的读了、并且读懂了。三个技巧我实测下来很有用:

技巧一:放大指令权重。在 AGENTS.md 开头就写一句"你必须在执行任何代码变更前完整阅读本文件",并用一个专门的小节说明"如果本文件的要求与代码注释冲突,以本文件为准"。这样一来,即使项目里有些历史注释和新架构不一致,Agent 也优先遵循规范。

技巧二:把规范写得像路由表,而不是散文。Agent 对结构化信息的解析能力明显强于对长段落的理解。能用列表就不用段落,能用表格就不用列表。我见过一份 200 行的 AGENTS.md,里面全是密密麻麻的段落,Agent 读完跟没读一样;而一份 80 行的 AGENTS.md 用树状结构把模块依赖画清楚,效果立竿见影。

技巧三:提供"反例"作为辅助。单纯的规则是抽象的,Agent 理解"禁止越层调用"这句话字面意思很容易,但要真正把它映射到代码上还需要示例。给出常见的反面模式示例,并配上"为什么这样不对",Agent 的识别准确率会显著提升。

2.3 什么样的 AGENTS.md 是"坏规范"

我在评估一个 AGENTS.md 写得好不好时,通常看它够不够"硬"。坏规范有一个共同特征:全是形容词,没有动词。比如"项目采用清晰的分层架构""代码应该保持高内聚低耦合"——这种话人类看着有道理,Agent 看着毫无信息量。什么叫清晰?什么叫高内聚?Agent 无法从这些描述中推导出任何可操作的行为。

坏规范的另一类问题是写得太全、太杂,把代码风格指南、API 文档、部署手册都塞进来。AGENTS.md 不是知识库,它是一份"决策规则集"——应该只包含 Agent 在做代码变更时需要用到的约束。配置项解释、API 列表、运维命令这些内容放进 README 或 docs 里更合适,放进 AGENTS.md 只会稀释重点。

我来做一个对比,你就明白了:

坏规范:"本项目使用微服务架构,各服务之间解耦,可独立部署。"

好规范:"本项目有 auth-service、order-service、payment-service 三个微服务,均独立部署;服务间只允许通过 HTTP REST 接口通信,禁止共享数据库表;新增功能必须先定位所属服务,再遵循该服务的内部分层。"

3. 多语言多框架仓库的规范设计:写通用规则还是分模块规则

3.1 单一 AGENTS.md 的局限与根目录策略

在单语言单框架的项目里,一份根目录下的 AGENTS.md 通常就够了。但在多语言、多框架的仓库里,情况要复杂得多。比如一个典型的全栈仓库,前端是 React + TypeScript,后端是 Python FastAPI,可能还有一块 Go 写的异步 worker。语言不同、框架不同、约定不同、测试方式也不同。如果所有规范都塞进一个 AGENTS.md,Agent 在改前端代码时还得在一堆后端规则里搜索自己的那条,上下文效率很低。

我建议采用"根目录 AGENTS.md + 子目录 AGENTS.md"的分层策略。根目录只写跨模块的全局约束,包括:

  • 项目整体结构和服务划分
  • 语言/框架的版本清单
  • 全局的架构原则(比如:模块间通信必须走接口,禁止跨模块 import 内部类)
  • 统一的提交规范、CI 流程

然后在每个语言或框架的子目录里再放一份独立的 AGENTS.md,只约束该子目录内的改动。比如 frontend/AGENTS.md 只写 React 代码规范、状态管理选型、样式方案;backend/AGENTS.md 只写 FastAPI 的分层规则、ORM 约束、异步任务使用方式。

这样的好处是,Agent 在修改某个文件时,读完文件所在路径的 AGENTS.md 就能获得 80% 的决策信息,剩下的 20% 才是全局约束,它可以从根目录 AGENTS.md 读取。

3.2 分模块规则的目录约定与文件命名

子目录 AGENTS.md 的摆放位置,我建议放在模块根目录下,也就是你开始看代码时第一眼能看到的地方。比如:

repo-root/ ├── AGENTS.md ├── frontend/ │ ├── AGENTS.md │ └── src/ ├── backend/ │ ├── AGENTS.md │ └── app/ └── worker/ ├── AGENTS.md └── main.go

注意,不同的 Agent 工具对子目录 AGENTS.md 的读取行为不一样。我实测过,有些工具只读取根目录的 AGENTS.md,有些工具会在文件变更时顺着路径向上查找最近的 AGENTS.md。如果你的工具不支持子目录读取,那就要么把所有规范合并到一个文件里,要么在根目录 AGENTS.md 里给每个模块单独开辟一个小节。

3.3 多语言场景下的通用表达策略

多语言项目最容易踩的坑是"用某一种语言的思维写规范"。比如你写"所有类必须定义在 src/classes 目录下",这个规则在 Java 里适用,在 Go 里就是错的,因为 Go 根本没有类;你又写"所有函数必须返回 error",这在 Go 里成立,在 Python 里就很奇怪。

我的建议是,跨语言规则要和语言解耦,用"概念 + 意图"的方式表达,而不是用具体语法表达。举几个例子:

语言无关的架构意图具体到语言的落地
业务逻辑层禁止直接依赖基础设施层Java:Service 层禁止注入 JdbcTemplate;Python:service 模块禁止 import db 模块
模块间通信必须通过公共接口Go:internal 包之间只允许调公共函数;TypeScript:禁止跨包直接 import 非 index.ts 导出的符号
数据访问统一走仓储层Python:禁止在 view 函数里写 ORM 查询;Java:Repository 层唯一允许依赖 EntityManager

3.4 框架差异带来的额外约束:以 React/FastAPI/Go 为例

让我拿一个真实的多框架项目来说明。这个项目里有三块:React 前端、FastAPI 后端、Go 异步 worker。

frontend/AGENTS.md 的核心约束:

React 项目里 Agent 最容易搞乱的通常是状态管理。如果项目用了 Redux Toolkit,但 Agent 不熟悉,它可能在组件里随手写了一个 Context 来管理全局状态,结果状态散得到处都是。所以我的 frontend/AGENTS.md 里会写死:

  • 全局状态必须放在src/store下,使用 Redux Toolkit 的 createSlice;
  • 服务端数据请求必须走src/api下的统一封装,禁止在组件里直接写 fetch 调用;
  • 样式只允许使用 Tailwind CSS 或src/styles下的全局样式,禁止引入 CSS Modules 或 styled-components;
  • 组件文件命名统一使用PascalCase.tsx,测试文件与组件文件放在同一目录。

backend/AGENTS.md 的核心约束:

FastAPI 项目里,Agent 经常分不清 pydantic 模型和业务模型。有的 Agent 直接把 ORM 表对象返回给前端序列化,导致字段名和结构完全失控。我的 backend/AGENTS.md 里会写:

  • API 请求/响应模型必须定义在app/schemas.py,使用 pydantic BaseModel;
  • 数据库模型定义在app/models.py,禁止直接返回模型对象给路由层;
  • 所有业务逻辑放在app/services目录,路由函数只做参数校验和响应封装;
  • 数据库连接只在app/db.py和app/repositories中使用,禁止在 services 里直接写 SQL 语句。

worker/AGENTS.md 的核心约束:

Go worker 项目里,Agent 容易踩的坑是 goroutine 泄漏和错误处理不规范。我的 worker/AGENTS.md 里会写:

  • 所有入口逻辑写在cmd/worker/main.go,业务逻辑放在internal/processor;
  • 每个 goroutine 必须有退出机制,禁止无限制的go func()启动;
  • 错误处理遵循 Go 习惯,使用errors.Is/As判断错误类型,禁止吞错;
  • 配置加载统一使用internal/config包从环境变量读取,禁止硬编码配置。

4. 落地实操:从零给一个多模块项目编写 AGENTS.md

4.1 第一步:梳理架构地图,先把"事实"列出来

不要凭记忆写规范,先做一次完整的架构梳理。打开项目,把下面这些信息收集起来:

  1. 模块清单:项目里有哪些独立部署的模块或服务?各自用什么语言和框架?
  2. 依赖关系:模块之间怎么通信?HTTP?消息队列?gRPC?共享数据库?
  3. 目录职责:每一个顶层目录在当前语言语境下承担什么责任?
  4. 关键流程:一个典型的请求(比如"创建订单")会经过哪些模块和函数?
  5. 现有约定:代码里有没有明显的模式(比如"所有 Repository 都继承一个基类""所有 Service 的方法都返回 Result 对象")?
  6. 常见卡点:团队在过去 review 中反复提出的架构问题是什么?

这步的产出是一张架构地图,它不需要写到 AGENTS.md 里那么精炼,但一定要完整。我习惯用 Markdown 文件先记录,然后再精简成规范。比如一个订单系统的服务依赖关系,可以先记录成:

用户请求 → api-gateway → order-service (HTTP) order-service → payment-service (gRPC) order-service → inventory-service (HTTP) 所有服务 → mysql (各自独立 schema) 所有服务 → kafka (事件发布/订阅)

4.2 第二步:设定规范颗粒度,把规则分三档写

规范不是越细越好,写得太细 Agent 记不住,反而会忽略关键约束。我把规则分成三档,你按优先级写:

第一档(必须遵守):违反这些规则会导致架构腐化或线上事故。比如"禁止服务间共享数据库表""禁止在 Controller/路由中执行耗时超过 500ms 的逻辑"。这些都是架构红线,必须用强烈的措辞写清楚。

第二档(推荐模式):违反不一定会出事,但会让代码偏离团队惯例。比如"新增服务时应该先在 docs/architecture/ 下补一页设计文档""所有跨服务调用必须使用run_with_timeout包设置 5 秒超时"。

第三档(风格偏好):纯代码风格层面的约定。比如"Python 文件使用 4 空格缩进""Go 结构体字段对齐注释"。这些也可以写,但建议单独放一个小节,不要和架构规则混在一起。

写完后你要通读一遍,问自己一个问题:如果 Agent 只读到了这份文档的前 30%,它能不能避免最严重的那类错误?如果能,说明你排序对了;如果不能,把红线规则往前移。

4.3 第三步:写"示例",而不只是写"规则"

我强烈建议在每个关键规则后面加一个"正面示例 + 反面示例",这能让 Agent 的遵循率上一个大台阶。反面示例要具体,最好是从真实代码里提取的"罪行现场";正面示例要简单、直接,一看就能照做。

我拿一份真实 AGENTS.md 的片段给你看:

数据库访问约束

所有数据库查询必须通过OrderRepository,禁止在 Service 中直接使用 SqlAlchemy Session。

反面示例(禁止):

def process(order_id: int): session = SessionLocal() order = session.query(Order).filter(Order.id == order_id).one() # ... 直接在 service 里操作 session

正面示例(推荐):

def process(order_id: int): order = order_repo.get_by_id(order_id) # ... 通过 repo 操作数据

这种"规则 + 反例 + 正例"的结构,我实测能让 Agent 的错误率降低一半以上。原因很简单:LLM 的 few-shot learning 能力天然就这样,你给它一个清晰的对照,它才更容易学会你的偏好。

4.4 第四步:用"验证任务"检查 Agent 是否真的读懂了

写完 AGENTS.md 不是终点,你得验证它有没有生效。我推荐一个简单粗暴的方法:给 Agent 几个"故意设陷阱"的小任务,看它会不会踩坑。

比如你的规范里写了"禁止在 Controller 中直接调用 Repository",你就让 Agent 完成这个任务:"在UserController中新加一个接口,返回指定用户的订单数量。"如果 Agent 遵守了规范,它应该会去调用 Service 层的方法;如果它直接注入 Repository 来执行查询,说明 AGENTS.md 没有被有效读取,或者你的表述不够清晰。

我建议在刚引入 AGENTS.md 的一两周内,每次用 Agent 完成代码变更后都做一次 diff review,重点看有没有违反架构红线的地方。发现问题后不要急着骂 Agent,先回头检查规范表述是否不够具体。大多数情况下,规范改得更清晰之后,Agent 的行为就立刻改善了。

4.5 维护 AGENTS.md 的更新节奏

架构是活的,AGENTS.md 也需要跟着更新。我的经验是:每次发生架构变更时,都应该顺手更新对应的 AGENTS.md 小节。比较理想的做法是把 AGENTS.md 的维护纳入代码评审流程——凡是修改了目录结构、模块接口、依赖方舟的 PR,必须同步更新 AGENTS.md,否则 CI 挂掉。

有些团队会在 CI 里加一个轻量检查,比如"如果本次 PR 修改了模块 A,但 AGENTS.md 中关于模块 A 的内容在最近 N 次提交中没有任何更新,给出警告"。这个检查逻辑不复杂,但能有效防止规范腐烂。

5. 多语言项目中容易忽略的"隐性文化"约束

5.1 测试风格对 Agent 的影响

每个语言社区都有自己的测试文化。Go 项目里大家都写表驱动测试,Python 项目里大家更习惯 pytest fixture,TypeScript 项目里 jest 和 vitest 各有拥趸。Agent 默认会按它的训练数据里的最大公约数风格生成测试,如果不规范,它写的测试很可能和项目里现有的测试风格格格不入。

AGENTS.md 里我会专门写一节"测试风格",至少明确三点:

  • 测试文件放在哪里:和源码同目录?还是集中放test/?
  • 用什么断言风格:Go 的assert/require?Python 的pytest.raises?
  • 覆盖率门槛是多少:新增功能要不要强制补测试?

5.2 依赖管理列表应该直接写清楚

多语言项目里,Agent 在新增第三方依赖这件事上尤其"大胆"。它可能为了一个小功能随手 import 一个库,完全不考虑这个库是否已经被弃用、是否有安全隐患、是否过大。在 AGENTS.md 里写清"依赖清单"和"新增依赖的审批流程",能有效防止这种依赖膨胀。

我的做法是给每个模块单独列出"允许使用的第三方库清单"和"禁止使用的库"。比如前端模块里写"Markdown 渲染不允许引入新的富文本编辑器库,统一使用现有react-markdown封装的MarkdownView组件"。这样 Agent 在遇到"要在页面上渲染一段 Markdown"的需求时,就能马上找到正确的路径,而不是自己上网找一个库装进来。

5.3 日志、监控与可观测性约定

这是很多人写 AGENTS.md 会漏掉的一环。Agent 生成的代码,功能逻辑往往没问题,但可观测性几乎为零——不打日志、不加 trace、不设自定义 metric。在多个服务协作的项目里,这种代码一旦上线,出了问题你根本无从排查。

AGENTS.md 里我会写:所有关键业务逻辑入口必须记录结构化日志,日志必须包含request_id和user_id;对外部服务调用必须接入当前的 trace 上下文;性能敏感路径需要上报执行耗时。有个小诀窍:给 Agent 一个日志工具的示例,它就会比干巴巴地写"要打日志"听话得多。

6. 新人引入与团队推广:让 AGENTS.md 真正成为"团队资产"

6.1 从"我的规范"变成"我们的规范"

AGENTS.md 很容易成为某一个人的私藏——架构师写完之后自己用得很爽,团队其他人根本不看,Agent 也只受执行它的人影响。要让 AGENTS.md 成为团队资产,第一步是把编写过程开放出来。我建议在团队内部开一次架构评审会,专门讨论 AGENTS.md 的内容。会上让后端、前端、测试的同学各抒己见,把他们遇到过的"Agent 奇葩行为"也加进去。这样的好处是,每个人都在这份规范里有贡献,他们会主动在 PR 里引用它。

6.2 在 CI 中做"规范连通性检验"

AGENTS.md 本身是纯文本,无法被静默执行,但你可以做一些间接的连通性检验。比如:

  • 检查 AGENTS.md 中提到的目录路径在仓库里真实存在,避免规范指向不存在的路径;
  • 检查 AGENTS.md 中提到的命令(pytest、go build、npm run build)能否在 CI 环境执行通过;
  • 如果 AGENTS.md 中声明了"禁止使用某些依赖",可以在 CI 里加一个简单的 grep 或依赖扫描任务做硬校验。

这些检查逻辑都很简单,写起来不费事,但会让 AGENTS.md 不只是一份"建议文档",而是一份可以被自动验证的规范。

6.3 持续运营:用 Agent 的反馈反哺规范

最后分享一个我最近在用的新玩法:把 Agent 每次被 review 打回的原因做个统计,然后定期更新 AGENTS.md。比如你发现 Go worker 里 Agent 经常忘记处理 context 取消,那就在 worker/AGENTS.md 里加一条"所有DoSomething方法必须接受ctx context.Context作为第一个参数,并遵循取消传播语义"。这样一来,规范里的每一条都来自真实的失败经验,而不是架构师拍脑袋想出来的理论,Agent 的升级速度会明显加快。

我个人的体会是,AGENTS.md 的编写过程,其实是把一个团队积攒多年的隐性架构知识显性化的过程。它不只是给 AI 看的,更是逼着团队把"大家心里都明白但没人写下来"的规则落到纸面上。一开始写的时候你会觉得麻烦,但坚持一两个迭代之后,你会发现不光是 AI Coding Agent 变听话了,连团队新人上手的速度都快了不少。

最后再分享一个小技巧:如果你实在不知道怎么开头,先把"反面示例"写出来。从项目里找 5 个 Agent 曾经生成过的、被 code review 打回的代码片段,放进 AGENTS.md 里作为反面教材。这份材料本身,就是一份比任何专家都了解你项目的规范草稿。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询