Go 项目的 AGENTS.md 编写指南:工程约定、测试验证与完整模板
让 AI 帮忙修改 Go 项目时,代码能编译只是第一步。业务逻辑可能被放进 handler,请求的 context 可能在数据库调用前丢失,一个看似简单的 goroutine 也可能缺少退出路径。
这些问题往往与项目约定有关:分层如何协作,错误怎样转换,事务由谁管理,测试能否访问外部服务。如果每次任务都重新解释,既容易遗漏,也难以保持一致。
这些约定如果只留在聊天里,换一个任务就可能需要重新说明。把它们整理成项目里的 AGENTS.md,能让支持这一机制的编码助手获得持续可用的工作指引。
本文以 Go + PostgreSQL 的订单服务为贯穿示例,介绍如何把工程约定写进 AGENTS.md,并给出一份可按仓库调整的完整模板。文中的分层、路径和测试组织均为示例,需要结合项目实际取舍。
一、先明确 AGENTS.md 要解决什么问题
可以把 AGENTS.md 理解为项目交给 AI 协作者的一份工作约定。它应该帮助助手回答几个实际问题:
- 这个项目做什么,本次任务应该从哪里开始?
- 应该使用哪些命令,在哪个目录执行?
- 哪些架构和业务约束必须保留?
- 哪些操作可以继续,哪些操作需要确认?
- 如何证明这次修改已经完成?
它可以与现有文档形成这样的分工:
| 内容 | 建议放置的位置 |
|---|---|
| 项目介绍、面向使用者的安装和使用说明 | README.md |
| 人类贡献者的提交流程与协作规范 | CONTRIBUTING.md |
| AI 工作时需要持续遵守的项目约定 | AGENTS.md |
| 架构设计、数据模型和历史决策 | 专门的文档目录 |
| 本次任务的需求、范围和验收条件 | 当前任务说明或任务文档 |
这是组织内容的一种建议。已有贡献指南可以继续作为规则来源,在 AGENTS.md 中引用对应章节即可;重复维护两套相同规则,后续很容易出现分歧。
判断一条内容是否值得写入,可以问自己:如果省略它,AI 是否容易在这个项目里作出错误决定?
例如,“编写高质量代码”几乎没有提供项目知识;“金额字段统一使用整数分,禁止使用浮点数参与订单结算”会直接改变实现方式。
二、优先写清楚六类信息
1. 项目定位与必要的目录导航
用几句话交代项目用途、主要技术栈和关键边界,再列出最有助于定位任务的目录。
下面延续订单服务示例,使用 handler、service、repository 分层。这是项目选择的组织方式,Go 项目可以根据规模采用其他结构,无需为了套用模板重构目录。
## 项目概况
这是一个内部订单管理服务,使用 Go 和 PostgreSQL。
HTTP 层负责参数解析和响应转换,订单规则集中在 service 层。
## 关键目录
- cmd/server:服务入口。
- internal/handler:HTTP 接口适配。
- internal/service:订单业务规则。
- internal/repository:数据库访问。
- migrations:数据库迁移文件。
目录导航应当体现职责。完整复制一棵几十层的目录树,通常不如说明“订单规则集中在哪里”有用。
2. 可以实际执行的命令
“运行相关测试”仍然需要助手重新摸索。应写出已经核实的命令、执行位置和必要前提。
## 本地验证
以下命令均在仓库根目录执行:
- internal 下的包测试:go test ./internal/...
- 指定业务模块:go test ./internal/service/...
- 静态检查:go vet ./...
- 编译所有包:go build ./...
集成测试需要本地 PostgreSQL,连接参数见 .env.example。
无法连接数据库时,说明哪些检查未完成。
命令应从 go.mod、Makefile、测试文件和 CI 配置等真实入口中确认。项目没有配置 golangci-lint,就不要直接把它写成必须执行的检查;采用了该工具时,还应指向仓库固定的版本与配置。
环境说明也要准确:go.mod 中的 go 指令声明最低 Go 版本要求,toolchain 可以建议使用的工具链,实际选择还受到 GOTOOLCHAIN 等配置影响。团队应把本地说明与 CI 使用的工具链对齐,参见 Go 工具链文档。
对于依赖安装、数据库初始化等可能产生副作用的命令,也要交代作用对象。例如,“重建本地测试库”需要说明如何确认连接指向测试环境。
3. 会影响实现的架构与业务约束
最有价值的规则,往往来自代码里不容易看出的设计原因:
## 实现约束
- handler 通过 service 调用业务逻辑,不直接访问 repository。
- 金额以整数分存储和计算;API 输出时再按协议转换。
- 已发布的迁移文件保持不变,结构调整通过新增迁移实现。
- internal/generated 下的文件由生成器维护,修改输入源后重新生成。
必要时补一句原因。例如,旧版本客户端仍依赖某个响应字段,就应明确兼容对象,避免助手把它当作冗余字段删掉。
只写“遵循现有架构”会留下很大的解释空间;指出层间调用方向,才足以指导实际修改。
4. 操作边界与确认条件
边界应当具体到操作,让助手知道什么时候继续推进,什么时候需要补充信息。
## 操作边界
- 可以修改本次任务涉及的源码、测试和文档,并运行本地检查。
- 新增生产依赖前,说明用途、现有方案为何不足,并取得确认。
- 执行生产数据库迁移、发布或删除线上资源前,取得本次操作的明确授权。
- 日志和提交内容中不得包含令牌、密码或真实客户数据。
避免把普通编辑、阅读文件、运行本地测试也写成逐步审批流程。确认条件越模糊,协作就越容易停在无关紧要的步骤上。
还要理解这份文档的能力边界:AGENTS.md 提供行为指引,实际权限应由工具权限、环境隔离、分支保护和 CI 等机制落实。涉及生产资源的约束,应当同时在工程设施中生效。
5. 按改动类型定义验证要求
验证方式需要与风险和影响范围对应。
| 改动类型 | 可以写入的验证要求 |
|---|---|
| 文案或 Markdown | 检查链接、示例命令和格式;有文档站点时检查构建 |
| 业务逻辑 | 运行受影响包的测试,覆盖关键边界 |
| HTTP 接口 | 检查参数校验、错误响应和兼容性 |
| 数据库访问 | 运行相关集成测试,检查事务与查询行为 |
| 并发逻辑 | 检查取消和退出路径,运行受影响包的竞态检测 |
| 公共包或依赖 | 扩大测试范围,检查编译、静态分析和依赖差异 |
具体规则可以写成:
修改业务行为时,更新能验证该行为的测试。
修复缺陷时,在适合自动化的情况下补充回归用例。
优先运行直接相关的检查;修改公共模块时扩大验证范围。
无法执行检查时,报告命令、阻碍和未验证的行为。
本地检查与 CI 可以承担不同职责:本地优先提供与任务相关的反馈,CI 按项目策略运行完整检查。不要用一条笼统规则要求所有修改都重复执行最昂贵的流程。
6. 明确完成后的交付内容
“已完成”应当附带足够的信息,让维护者能够判断结果:
## 交付说明
完成后简要说明:
- 修改了什么,以及对使用者的影响。
- 执行了哪些验证,结果如何。
- 尚未验证的部分、已知限制和需要人工处理的事项。
区分已执行的检查与建议后续执行的检查。
这一部分不需要规定固定话术,重点是让结果可核实。
三、把模糊口号改成可执行的规则
写完初稿,可以逐条检查:看到这句话后,助手是否知道下一步应该怎么做?
| 模糊写法 | 更具体的写法 |
|---|---|
| 保持良好的代码风格 | 对修改的 Go 文件执行 gofmt,并运行项目已有的静态检查 |
| 注意兼容性 | 保留已公开的响应字段;变更字段含义时同步更新 API 文档与契约测试 |
| 不要随便改文件 | 聚焦任务涉及的文件;不顺手格式化无关目录 |
| 所有错误都要处理 | repository 错误按约定包装或转换;需要上层识别的错误保留错误链 |
| 遵循最佳实践 | 新增接口参考指定的现有实现,并复用已有鉴权中间件 |
一条规则可以按“触发条件 + 要求的行为 + 验证方式或原因”组织。例如:
修改订单状态转换时,同步更新状态转换测试;
系统通过这些测试约束允许和禁止的状态跳转。
格式化、静态检查和类型系统能够自动判断的规则,应优先交给工具。AGENTS.md 负责指出入口,并补足工具无法表达的语义和业务背景。
四、Go 项目值得单独写清楚的约定
Go 的语法和工具较统一,但下面这些工程决策仍需要项目给出明确答案。只选择实际影响当前仓库的条款,避免把语言教程整体搬进指引。
1. 错误如何传播、识别和记录
可以约定 service 返回业务错误,handler 再映射成 HTTP 状态与对外错误码。需要让调用者识别底层错误时,明确采用保留错误链的包装方式,并使用 errors.Is、errors.As 判断。相关机制见 Go errors 包文档。
- service 返回领域错误,由 handler 统一转换成 HTTP 响应。
- 需要上层继续识别的底层错误使用 %w 包装,避免通过错误文本判断类型。
- 按项目的日志责任边界记录错误,避免每一层重复打印同一次失败。
- 对外响应使用约定的错误码,不暴露原始 SQL、连接信息或内部堆栈。
不必机械地要求每层都包装错误。关键在于明确哪些信息需要保留、谁负责最终转换,以及哪些信息可以对外展示。
2. context、超时和 goroutine 的生命周期
对于请求内工作,应当把请求的 context.Context 传到需要取消和超时的下游调用。派生可取消 context 时,调用方需要释放相关资源;标准约定可参考 Go context 包文档。
- 请求内的 service、repository 和外部调用沿用请求 context。
- 不使用 context.Background() 替换请求 context 来绕过取消或超时。
- 创建可取消或带超时的 context 时,在合适的作用域调用 cancel。
- 新增 goroutine 时说明由谁启动、如何退出、由谁等待完成。
- 需要脱离请求生命周期的工作,使用项目已有的后台任务机制。
如果项目已有 worker、任务队列或统一停机管理,就写出对应入口。仅写“注意 goroutine 泄漏”,很难让助手知道应接入哪套机制。
3. 事务边界与数据库变更
事务的归属会直接影响接口设计和数据一致性。可以写成:
- 跨多个 repository 的原子业务操作,由 service 通过现有事务组件组织。
- 事务内的查询和写入使用同一个事务句柄,避免混用事务外连接。
- 新增查询按调用链传递 context,并检查结果遍历后的错误。
- 数据结构变更通过新增迁移实现,保留已发布迁移的内容。
这里应补充项目实际采用的数据库访问方式,例如 database/sql、sqlc 或 GORM,以及一处可以参考的事务实现。不要同时罗列多种方案,让助手猜测仓库采用哪一种。
4. 依赖和代码生成的入口
- 依赖变更同时检查 go.mod 和 go.sum,说明新增或升级原因。
- 不为修复单个问题批量升级无关模块。
- 调整依赖或导入后,按项目约定运行 go mod tidy 并检查差异。
- 修改协议或查询定义后,通过仓库固定的生成入口更新产物。
若采用 go generate,应给出目标包和所需工具版本。该命令执行源码中的生成指令,构建和测试不会自动替代这一步;使用前也应确认指令及其副作用。参见 Go 代码生成命令说明。
五、项目变大后,按职责拆分规则
单体项目通常可以先从根目录的一份文件开始。多个服务共用一个仓库时,可以按模块放置局部规则:
project/
├── AGENTS.md
├── services/
│ ├── orders/
│ │ └── AGENTS.md
│ └── billing/
│ └── AGENTS.md
└── libs/
└── AGENTS.md
根目录保留共同约定,订单与结算服务分别补充业务约束,公共库目录说明导出 API 和兼容性要求。
同时要区分单 module 仓库和多 module 仓库。后者应列出各个 go.mod 的位置、命令的执行目录及 go.work 的使用约定,分别核实测试范围。不要默认在任意仓库根目录运行 go test ./...,就能覆盖所有服务。
但文件的组织方式与工具的加载机制需要分别核实。
以 Codex 为例,官方说明的启动发现顺序是:先读取全局指引,再从项目根目录沿路径查找到当前工作目录;同一目录优先选择 AGENTS.override.md,再选择 AGENTS.md,每个目录最多采用一份。较深目录的指引在合并时可以覆盖先前冲突的内容。参见 OpenAI 官方 AGENTS.md 文档。
因此,从仓库根目录启动时,应核实目标子目录的规则是否已经加载;可以从目标模块目录启动,或在任务中明确要求先读取适用的局部指引。其他工具的查找、作用范围和冲突处理方式,应以各自文档为准。
文件名建议使用大写的 AGENTS.md。文件系统对大小写的处理可能不同,团队也不应依赖某台开发机上的宽松匹配。
六、一份适用于 Go 服务的完整模板
下面以单 module 的 Go 订单服务为例,假设仓库根目录有 go.mod,数据库为 PostgreSQL,集成测试单独放在 tests/integration,并通过 integration 构建标签启用。默认测试不访问外部服务。这些都是示例前提,复制前需要核实并替换。
# AGENTS.md
## 项目概况
这是一个 Go + PostgreSQL 的订单 API 服务。
HTTP 适配位于 internal/handler,业务规则位于 internal/service,
数据库访问位于 internal/repository。
- cmd/server:服务入口与依赖组装。
- internal/domain:订单模型、业务错误和状态定义。
- internal/generated:代码生成产物。
- migrations:数据库迁移。
- tests/integration:需要本地 PostgreSQL 的集成测试。
## 环境与命令
Go 版本要求见 go.mod,开发工具链与 CI 配置保持一致。
以下命令均在仓库根目录执行,按改动范围选择:
```bash
# 首次准备环境时下载依赖
go mod download
# 测试订单业务包
go test ./internal/service/...
# 按实际名称筛选测试;确认输出中确实执行了目标用例
go test ./internal/service/... -run '^TestCreateOrder$' -count=1
# 默认测试、静态检查与编译
go test ./...
go vet ./...
go build ./...
```
修改 Go 文件后,对修改的文件运行 gofmt。
改动并发逻辑时,额外运行受影响包的竞态检测:
```bash
go test -race ./internal/service/...
```
集成测试仅连接本地测试库,环境配置见 .env.example。
确认连接目标、准备测试数据后执行:
```bash
go test -tags=integration ./tests/integration/...
```
默认测试通过不代表集成测试已完成;无法准备环境时说明未验证的部分。
## 实现约束
- handler 负责参数解析、校验与响应转换,通过 service 调用业务逻辑。
- 数据库访问集中在 repository,跨 repository 的事务由 service 组织。
- 金额使用整数分;订单状态变化遵循现有状态转换规则。
- 需要向上识别的错误保留错误链,handler 统一转换对外错误码。
- 请求内下游调用传递请求 context;派生 context 后按作用域调用 cancel。
- 新增 goroutine 时明确退出条件与等待方式,复用现有后台任务机制。
- 优先复用现有依赖;新增生产依赖前取得确认。
- 不手工编辑 internal/generated;按 Makefile 中的 generate 目标重新生成。
- 保持已发布迁移不变,通过新增迁移调整数据库结构。
- 保持已公开 API 的兼容性;任务要求破坏性变更时说明影响。
## 验证要求
- 修改业务逻辑时,运行受影响包测试,覆盖成功、失败和关键边界。
- 修改数据库查询或事务时,运行相关集成测试。
- 修改并发逻辑时,补充相关行为测试并运行竞态检测。
- 修改公共包时,扩大测试范围,并运行 go vet 与编译检查。
- 仅修改文档时,检查链接、命令与格式;有文档构建时执行对应检查。
- 检查失败时说明原因;没有执行的检查明确标注。
## 操作边界
- 可以执行本次任务需要的本地编辑与检查。
- 保留工作区已有的无关改动。
- 不将密钥、访问令牌或真实用户数据写入代码、日志或示例。
- 发布、推送远程分支和生产数据操作需要明确授权。
## 按需阅读
- 调整服务边界时,阅读 docs/architecture.md。
- 修改数据库结构时,阅读 docs/database.md。
- 准备发布时,阅读 docs/release.md。
## 交付说明
说明改动内容、验证结果、未完成检查及剩余限制。
模板中的 make generate 约定也需要项目提供真实实现。没有生成代码、集成测试或后台任务机制时,应删除相应条款,避免留下虚假的入口。
竞态检测需要运行环境支持,而且只能发现实际执行路径中触发的数据竞争,测试通过不能证明所有并发行为都正确。执行方法、平台要求和局限见 Go 竞态检测文档。
七、几种容易降低效果的写法
把所有文档都变成每次任务的前置阅读
修正文案通常不需要先阅读部署、数据库和架构文档。可以写出触发条件,让任务自行定位所需背景。
OpenAI 在关于更新指引与提示的文章中也建议定期审视 AGENTS.md,按任务需要引用文档,减少无关的上下文和流程负担。
从其他项目复制一整套规范
复制来的 Go 工具链、HTTP 框架、目录路径和审批要求,可能与当前项目完全不符。错误命令会造成直接阻碍,错误的边界还会改变助手的工作方式。
借用结构即可,内容要从当前仓库核实。
只增加规则,从不删除旧规则
每次遇到问题都追加一段,文件很快会积累冲突。例如,一处要求所有任务运行全量测试,另一处又允许文档任务只检查构建。
新增规则时应检查已有相关条款,把重复要求合并成一个明确版本。
把临时进展当作长期约定
“本周准备重构订单模块”“当前任务已完成接口层”属于任务状态,适合放在 issue、计划或交接文档。长期规则应保存相对稳定、能被后续任务复用的信息。
八、如何判断这份文件有没有用
可以选几个真实任务进行试用:修改一处文案、修复一个业务缺陷、增加一个接口,观察助手是否能选对文件和验证方式。
重点记录这些偏差:
- 是否选择了错误的 module、Go 工具链或不存在的命令?
- 是否绕过了业务层、修改了生成文件或破坏了兼容性?
- 是否丢失了请求 context、错误链,或者遗漏事务和 goroutine 的生命周期?
- 是否漏掉关键验证,或者执行了大量无关检查?
- 是否在普通操作上反复停顿,或者越过了明确的操作边界?
然后定位问题来源:规则没有加载、文字存在歧义、命令已经过期,还是项目缺少自动化约束。问题来源不同,修正方式也不同。
如果助手根本没有加载规则,继续往文件里添加措辞通常无济于事。可以先请它列出本次采用的指引来源、准备执行的命令和适用约束,再结合实际行为检查。
每次调整目录、脚本、测试框架或发布流程,都顺手检查相关规则。对于一次偶发失误,先判断它是否会重复发生,避免为每个小问题添加永久流程。
一份有效的 AGENTS.md,会随着协作逐渐变得更准确:留下必要的项目知识,把重复判断交给工具,让后续任务更容易走到可以核验的结果。
