让AI生成代码守规矩:用AGENTS.md约束封装与架构

发布时间:2026/10/16 10:39:00
让AI生成代码守规矩:用AGENTS.md约束封装与架构 1. 为什么 AI 生成的代码总爱绕过封装如果你也在用 AI 辅助写代码并且发现它经常“不守规矩”——明明项目里已经封装好了 Service 层它非要直接去调 DAO明明规定只能通过暴露的公共接口访问某个模块它偏要 import 内部实现类。你先别急着骂模型智商不够更别觉得是提示词写得不好。大概率是它在生成代码的那一刻根本不知道你的项目里还有这些“规则”。我一开始也没想明白这个事。某次用一个知名 AI 编程工具写一个订单模块我给它描述了需求它很快就生成了一大段 Controller 代码看起来结构正常。结果我一细看它居然在 Controller 里直接 new 了一个 DAO 对象绕过了整个 Service 层的业务校验和事务控制。我当时的第一反应是“这工具真不靠谱”后来才意识到它压根不知道我这个项目有 Service 层更不知道 Controller 不能直接碰 DAO 这个约定。这就是 AI 代码上下文工程要解决的问题。所谓上下文工程指的是把项目中那些“人看一遍就懂、AI 却完全不知道”的隐性约定显式地写进一个叫 AGENTS.md 的文件里。这个文件就像是给 AI 助手看的“项目实习手册”告诉它这个项目的结构是什么、哪些东西不能碰、哪些规则必须遵守。把它放在项目根目录之后AI 工具尤其是支持读取该文件的编辑器插件、命令行工具在生成代码时就能参考这些约束从而少绕过封装、少破坏架构。所以今天我就把我个人在实际项目里总结出来的四种必写项目事实分享出来顺便给你一套可以直接抄的 AGENTS.md 写法。适合在团队里推广 AI 辅助编程、但又希望代码质量不被拉垮的开发者参考。2. 四种关键项目事实写进 AGENTS.md 才有纪律性2.1 事实一项目的分层架构与依赖规则绝大多数后端项目都逃不开“表现层、业务层、数据访问层”这种三层模式或者类似的分层思想。问题在于这种东西对资深程序员来说是肌肉记忆但对 AI 来说就是一团迷雾。你不写清楚它就会根据“能用就行”的原则找到一条最短路径。我在自己的项目里会在 AGENTS.md 里专门开一节叫“分层架构”用最直白的语言写清楚Controller 层只负责参数校验和响应封装不允许写任何业务逻辑。Service 层是业务逻辑的唯一入口所有事务操作必须发生在 Service 方法内。DAO/Repository 层只做数据访问不允许被 Controller 直接引用。任何跨层调用都必须通过下一层的关键接口禁止跨两层以上直接调用。光这样写还不够我还会加上一句“为什么”因为事务控制在 Service 层Controller 直接操作 DAO 会导致数据一致性无法保证。AI 模型非常擅长“读前言后”它看到你解释了因果关系就会更倾向于遵守。事实证明这个解释比单纯列出规则更有效因为模型本身就是在预测下一个 token上下文越连贯、理由越具体它的输出就越逼近你想要的模式。2.2 事实二封装边界与接口契约封装这个东西本质上是“哪些内部实现细节不能暴露”。很多项目里某个类虽然是用 public 修饰的但它内部的某些方法、某些字段只允许在类的内部或者特定包下访问。这些规则通常靠代码规范、设计模式约定来保证而不是靠编译器。我在某次开发一个支付对接模块时核心库对外只暴露了一个PaymentClient类底层的SignatureGenerator、RequestBuilder全部标了 package-private。结果 AI 生成的测试代码为了拿到某个内部状态硬是通过反射去读私有字段差点把项目坑了。所以现在我会在 AGENTS.md 里明确列出哪些类是“公共 API”哪些类是“内部实现”并且直接写出类似“不允许通过反射修改私有属性”的禁令。这里最重要的是要有“可执行性”的描述。不要只写“请遵守封装”而要具体到类名、方法名和边界。比如公共 API 包com.example.payment.api只有这个包下的类可以被外部模块引用。内部实现包com.example.payment.internal禁止被任何外部代码直接 import。如果确实需要访问内部功能请新增公共方法到 API 层而不是绕过封装。有了这种具体到包级别的描述AI 在生成 import 语句的时候就会自动避开内部包。我在实测中发现写明“禁止 import”这些规则比单纯讲道理有用得多因为模型在生成代码时对 import 行和“禁止”这两个词特别敏感。2.3 事实三代码风格与命名规范代码风格不仅仅是为了好看更是为了让 AI 生成的代码能融入现有项目。如果你项目里统一使用UserVO、UserDTO、UserDO这种命名区分不同阶段的对象AI 却给你生成一个UserData类那它生成的所有关联代码都会变得很别扭。我在 AGENTS.md 里会列一个简短的风格速查表不求全面只求高频场景要求禁止行为请求参数对象命名以Req结尾不允许直接传 JSONObject响应对象命名以Resp结尾禁止返回底层实体对象内部数据对象命名以DO结尾不允许出现在 Controller 层业务结果判断使用统一 Result 类禁止裸返回布尔值表格这种东西AI 的解析效果很好因为结构化的信息能大幅减少歧义。你还可以在表格后面加一句话“如果无法确定某个对象属于哪种类型请先查看src/main/java/com/example/model/dto/目录下的已有例。”这相当于给了 AI 一个查询方向而不是让它凭空想象。这里有个我之前踩过的坑第一次我只写了“命名要规范”结果 AI 生成了一堆以Info结尾的类每个类都像是大杂烩。后来我改成“列出具体的后缀规则 给出一个正反示例”之后效果立竿见影。建议在写样式规范时至少给出一个完整的“正确文件 vs 错误文件”代码块放在 AGENTS.md 里作为锚点。2.4 事实四测试与构建要求很多时候 AI 生成的代码本身能跑通但一旦提交就会发现没有配套测试或者构建命令不对。原因很简单——它不知道你的项目用什么测试框架也不知道你的 CI 流程要求覆盖率达到多少。所以我需要把测试相关的“项目事实”也写进去。具体我会写这几点单元测试框架是 JUnit 5断言使用 AssertJ。所有新增业务逻辑必须至少包含一个正向用例和一个异常分支用例。运行单测跑全部 Python 测试而不是单个文件。如果改动涉及数据库 schema必须在迁移脚本里同步更新禁止在测试里手写建表语句。另外还有一个容易被忽略的细节构建命令。很多项目里mvn test和npm run test会触发代码检查Lint、覆盖率检查等额外操作。如果你不在 AGENTS.md 里告诉 AI“运行测试必须执行这个命令”它可能会直接调用一个绕过检查的内部脚本导致测试结果不可信。我在项目里还会写一句“禁止在构建过程中跳过任何检查步骤”并且在后面补上“如需临时跳过请说明原因并保留 TODO 注释”。这样做的好处是AI 生成的代码和命令会更接近一个真实团队成员的产出而不是求生欲驱动下的“能过就过”。3. 手把手写出有效的 AGENTS.md3.1 一个可以直接抄的模板下面这份内容来自我自己正在维护的某个模拟项目一个基于 Java Spring Boot 的订单系统结构通用你可以按照自己的项目替换细节。注意我特意把语气写得很“独断”因为给 AI 的指令越不模糊效果越好# 项目概述 这是一个电商订单系统后端基于 Java 17 Spring Boot 3采用标准的 Controller-Service-Mapper 三层架构。 # 分层架构声明 - Controller 不允许出现业务逻辑只做入参校验和响应封装。 - Service 是业务逻辑的唯一入口任何数据修改操作必须发生在 Service 方法内。 - Mapper 只做数据库访问禁止在 Controller 中直接调用。 - 禁止跨层调用Controller - Mapper 是绝对不允许的。 # 封装边界 - 公共依赖包com.xxx.order.api外部模块只允许引用此包。 - 内部实现包com.xxx.order.internal禁止被任何外部模块 import。 - 所有 Entity 对象即数据库映射类不得直接出现在 Controller 的返回结果中。 - 禁止使用反射读取/修改私有字段即使是在测试代码中。 # 命名与风格速查 | 对象 | 后缀 | 示例 | | --- | --- | --- | | 请求参数 | Req | CreateOrderReq | | 响应结果 | Resp | CreateOrderResp | | 数据库实体 | Entity | OrderEntity | | 业务模型 | Model | OrderModel | 统一结果返回所有接口返回 ApiResultT禁止自定义 Map 或裸返回值。 # 测试与构建 - 测试框架JUnit 5断言用 AssertJ。 - 每个 Service 方法至少两个测试正常流程 异常流程。 - 构建命令./mvnw clean verify - 运行测试时必须保持此命令不允许跳过 checkstyle 或覆盖率检查。 - 数据库变更必须通过 Flyway 迁移脚本禁止在测试代码中执行 DDL。3.2 每个部分为什么要这样写你可能注意到模板里没有写太多“请”“务必”这种客气的词全是“不允许”“禁止”。这是有原因的。AI 模型对祈使句的执行程度远高于委婉建议。把规则提炼成类似编译器和 lint 工具的检查清单模型在生成代码时就会主动避让。另外模板里大量使用了“绝对不允许”“禁止”这种强否定词是为了给模型一个明确的负面信号。我在测试中发现如果写“建议 Controller 不要调用 Mapper”AI 还是会调个一两次但写成“绝对不允许”它通常就会另寻出路要么自动帮你生成一个 Service 方法要么问一句“是否需要新增 Service 接口”。还有一个小细节每个文件夹的目录结构尽量在 AGENTS.md 里给一个简短的例子。比如在“项目概述”下面加一行“若不清楚代码放哪参考src/main/java/com/xxx/order/service/impl/中已有 ServiceImpl 的模式。”模型有了具体路径参照生成文件的模块归属准确率能提高一大截。3.3 版本管理和更新时机AGENTS.md 不是写一次就完事的。项目重构、新增模块、升级框架时都必须同步更新这个文件。否则 AI 会拿着过时的架构信息在旧结构上生成新代码。我建议把它当成正式交付物来管理纳入 Code Review 范围。我自己是在项目根目录开一个名为docs/的子目录来存这种说明文件但 AGENTS.md 必须待在根目录因为很多工具默认只读取根目录下这个固定文件名。如果放在子目录里几乎没有编辑器插件会主动去找它。另外要注意AGENTS.md 和 README.md 职责不同。README 是给人类看的可以讲背景、讲使用方式AGENTS.md 是给 AI 看的不需要任何铺垫和客套直接上规则。如果你的桌面端工具同时读取这两个文件尽量让它们不要在规则上有任何冲突。4. 验证与持续优化让 AI 真正听话4.1 写完之后怎么测试它有没有用很多人的第一反应是“写完了试试呗”但真正好的做法是设计一个“对抗性测试”。我会故意给 AI 发一个明显会破坏封装的需求比如“在 OrderController 中直接查询订单表并统计金额。”正常来说如果 AGENTS.md 生效AI 生成的结果应该提示你“这不符合分层架构要求已为你新增一个统计金额的 Service 方法”而不是直接给你写 SQL。我测试过几次凡是明确写了“Controller 禁止调用 Mapper”的项目AI 都会自己跳到 Service 层去实现逻辑。凡是没有写这个规则的项目它大概率会直接生成一段jdbcTemplate.queryForList之类的代码。这个对比非常直观一次测试就能看出 AGENTS.md 有没有起到作用。除此之外我还会做一个“风格一致性测试”让 AI 补齐一个接口方法要求参数和返回值类型都要符合项目里已有的模式。如果它生成OrderResp resp new OrderResp()而不是ApiResultOrderResp就说明命名规范里的统一返回结果那条没有吃透。这时你去改 AGENTS.md 里的描述方式换成更具体的“所有方法返回ApiResultT不允许出现其他返回类型模板”往往就解决了。4.2 常见问题与排查技巧问题一写了 AGENTS.md 之后 AI 还是无视规则。这种情况十有八九是规则和目标代码距离太远。比如你在文件的第 40 行写上“禁止反射”而 AI 的上下文窗口全部被当前文件占据它压根没读到那么远。解决办法是把最核心的规则尽可能往前放并且控制在 200 行以内保持精简。如果文件太长AI 反而会抓不住重点。问题二加入太多规则后AI 开始“过度僵化”一个简单查询它也非要加一层 Service。这种情况下我是建议在规则里加一条“如果确实只是只读查询且逻辑简单允许在 Controller 直接调用一个轻量查询服务但前提是使用专门的只读接口”。这样既守住了封装的红线又给了 AI 一个合理的弹性空间。规则不是越严越好而是要匹配项目当前的真实复杂度。问题三多人协作时 AGENTS.md 的变更被频繁覆盖。这个其实很简单把 AGENTS.md 加进 Codereview 机器人必须检查的文件列表并且在 Git 提交信息里强制要求对它的变更进行描述。我在团队里的做法是将它和数据库迁移脚本看作同等重要的“基础设施变更”任何人不允许静默修改。4.3 我的一些实战调整记录我最早写 AGENTS.md 的时候只写了“Controller - Service - Mapper”一句话。结果 AI 理解成了所有业务都必须走这套流程包括一个只读缓存查询也非要包一个 Service。后来我加了一条“只读查询如果涉及缓存可直接调用 CacheService但禁止绕过 Service 去读数据库”效果立刻恢复正常。后来又遇到过。AI 确实不跨层了但它开始频繁在 Controller 里造 Service 实例很好直接用 Spring 依赖注入会被完全忽略。于是我又在 AGENTS.md 里加了一句“所有 Service 依赖必须通过构造函数注入禁止 new Service 实例”并且在模板中把这个规则写在“依赖注入”小节。从那之后AI 生成的代码基本能直接跑偶尔还会帮我重构出更清晰的接口边界。还有一次一个测试文件里 AI 直接复制了生产代码的权限去访问内部包。当时我很恼火但后来想想它确实不知道团队测试规则里“测试可以读取内部类以验证状态”这个例外。于是我补了一条“测试代码允许访问 internal 包但前提是访问范围仅限同模块的测试目录”并把正反例子都贴出来。这样既保留了测试的便利又堵住了生产代码乱引用的漏洞。我个人现在写 AGENTS.md 已经成了一种习惯每次新建项目第一件事不是写 README而是写一份给 AI 看的规则文档。因为 README 是给后来人看的而 AGENTS.md 是给每天几十上百个 AI 调用看的。把时间花在这上面比事后盯着生成的代码一行行改要划算得多。如果你还没有自己的 AGENTS.md今天就可以动手。不用一开始就追求面面俱到先把最有把握的四种事实写进去跑几个测试用例观察一下 AI 的输出行为变化。之后每次遇到“AI 又绕过了封装”这种现场你都能把它提炼成一条新规则让这台“实习生”越用越懂你的项目。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询