跳到正文

天行健,君子以自强不息

Python 后端项目的 AI Agent 规则约束实践:让 AI 乖乖按你的规矩写代码

2.5k 9 分钟

📌 前言

用 AI Agent(Cursor、Copilot、Claude Code 之类)写代码有一段时间了,爽是真爽,但坑也没少踩。没有约束的 AI 就像一个能力超强但没有项目上下文的新员工:代码风格一会儿一变、动不动就给你写个五百行的”上帝函数”、改完代码注释还是上一版的、Git 提交信息写得随心所欲……

后来我开始给 AI 立”规矩”——把项目的开发规范写成规则文件,让 AI 每次干活前都必须遵守。这篇文章就以我自己的 Python 后端项目(FastAPI)为例,把我实际在用的全套规则方案分享出来。每个规则都附上了我项目里真实使用的文件原文,可以直接复制到自己项目里改改用,包括:

  • 哪些规则对开发规范有明显帮助(先规划后编码、长度/耦合限制、接口文档同步)
  • Git 提交信息生成的规范
  • VS Code / Cursor 里怎么配置这些规则
  • 为了跨工具兼容,通用的 AGENTS.md 应该怎么写

🎯 为什么需要给 AI 立规矩

先说说我遇到的真实痛点,相信大家或多或少都有共鸣:

  • 没有规划直接开写:说一个需求,AI 秒回一大段代码,写完才发现方向理解偏了,推倒重来
  • 越写越臃肿:AI 倾向于把逻辑堆在一个文件、一个函数里,耦合度爆表,后期改一处动全身
  • 文档和代码脱节:接口参数改了,docstring 还是旧的,Swagger 文档逐渐变成”僵尸文档”
  • 提交信息没法看updatefix bug改一下 满天飞,翻 Git 历史等于考古

这些问题,人类团队靠 Code Review 和团队规范约束,AI 团队就得靠规则文件来约束。而且规则必须写得足够具体、可执行,否则 AI 会”选择性遗忘”。


📋 核心规则一:先规划,后编码,后总结

这是我认为对项目开发规范帮助最大的规则,没有之一。核心思想很简单:在我明确批准计划之前,绝对不要写任何代码;写完之后还必须生成一份执行报告。

规则全文

以下是我项目里 add-new-feature.instructions.md 的完整内容(Cursor 里对应 .mdc,只是 frontmatter 换成 alwaysApply: true):

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
---
applyTo: "**"
description: "新功能或重大变更前必须先创建实施计划并等待批准,完成后生成执行报告"
---
# AI 工作流核心准则:先规划,后编码,后总结

## 指导原则
- 在为新功能或重大变更编写任何代码之前,你必须首先以 Markdown 格式创建一份详细的实施计划。
- 你必须将这份计划呈现给我,以供审查和批准。计划长度一般限制在300行以内,大规模更改则不超过600行。
- 在我明确表示批准(例如,我说"计划通过"、"同意这个计划"或"可以开始")之前,绝对不要编写任何代码。
- 代码实现完成后,必须生成一份执行报告(见步骤 4)。

## 步骤 0:查看仓库对应的代码

分析需求可能需要查看的代码,去查看对应代码再开始分析详细需要变化的代码。

## 步骤 1:分析需求并创建 MD 计划文档

当我提出一个新的开发任务时,你的首要行动是生成一个名为 `implementation_plan_xxxxx.md` 的 Markdown 文件到当前项目 `docs/plan` 目录下。这份计划必须包含以下几个部分:

1. **需求概述:** 用你自己的话简要重述任务目标,以确认你的理解无误。
2. **文件结构变更:** 清晰地列出所有需要新建的文件和需要修改的现有文件。无需写全文,清晰简要即可。
3. **核心组件/函数设计:** 详细说明关键的类、函数、组件的职责和它们的核心逻辑。
4. **数据流与逻辑:** 描述数据将如何在系统中流转,以及实现业务逻辑的关键步骤。
5. **问题与澄清:** 如果需求的任何部分让你感到模糊或不确定,在此处列出你的问题。

## 步骤 1 的标准回应格式:

"好的,收到。这是关于新功能的实施计划,请您审阅并批准。"

*(然后 AI 会生成 `implementation_plan_xxxxx.md` 的详细内容)*

## 步骤 2:等待批准

在提交计划后,你必须停下所有工作,等待我发出明确的批准指令。

## 步骤 3:执行已批准的计划

一旦我批准了计划,你将严格按照该 MD 文档中描述的方案开始编写代码。在编码过程中,要始终参考这份计划,确保最终实现与方案一致。

## 步骤 4:完成后生成执行报告

代码实现完成并自测通过后,在当前项目 `docs/reports` 目录下生成一份 `report_xxxxx.md`(文件名与对应的 `implementation_plan_xxxxx.md` 使用相同的 `xxxxx` 后缀,便于对照),内容必须包括:

1. **变更文件清单:** 本次新增了哪些代码文件、修改了哪些现有文件(列出完整路径,简要说明每个文件的改动内容)。
2. **新增/改动功能说明:** 本次改动具体实现或调整了哪些功能点,与最初计划相比是否有偏差及原因。
3. **前后端对接说明:** 新增或修改的接口(路径、方法、请求/响应结构),以及前端具体在哪些页面/组件中调用了这些接口、如何联调;若无前后端交互可注明"不涉及"。
4. **遗留问题/后续 TODO:** 尚未处理的边界情况、已知限制或后续建议(如有)。

生成报告后,用一句话告知我报告路径,例如:"已生成执行报告:docs/reports/report_xxxxx.md"。

💡 心得:这个流程看起来”多此一举”,实际大幅减少了返工。AI 写计划的过程中自己就能发现理解偏差——问题与澄清那一段经常问出我没想到的点。而且计划和报告沉淀在仓库里,一个月后回看也知道当时为什么这么改。


📋 核心规则二:代码长度与低耦合准则

AI 生成代码有个天然倾向:能写一个函数搞定的绝不拆两个。短期看很爽,长期看就是技术债。所以我定了硬性数字限制。

规则全文

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
---
applyTo: "**"
description: "编写或修改代码文件时始终遵循的长度与低耦合准则"
---
# 代码长度与低耦合准则

## 核心原则
追求低耦合、高内聚的代码结构。文件和函数过长会显著增加阅读和维护成本,应主动拆分。

## 硬性限制
- **单个代码文件**:正常情况下不超过 500 行,特殊情况(如确有必要)最多不超过 1000 行。
- **单个函数/方法**:正常情况下不超过 200 行,特殊情况(如确有必要)最多不超过 400 行。

## 执行方式
1. 在新建文件或给现有文件添加代码之前,先检查当前文件行数。如果即将超出限制,**主动提出拆分方案**(例如拆分成多个模块/子组件/工具函数文件),而不是直接把代码堆进去。
2. 如果一个函数逻辑复杂、接近或超过 200 行,优先考虑:
- 提取独立的子函数,每个子函数只负责单一职责
- 将不同关注点(如数据获取、校验、业务逻辑、渲染/输出)拆分到不同函数或文件中
3. 拆分时以"职责单一"为准则,而不是简单地按行数机械切割;避免为了凑数而拆出没有实际意义的函数。
4. 如果因为语言/框架限制(比如某些配置文件、生成代码)无法满足限制,需要在代码注释或提交说明中简要说明原因。

## 目的
- 便于代码审查和后续维护
- 降低修改一处影响多处的风险(降低耦合)
- 让每个文件/函数的职责一目了然

📋 核心规则三:低耦合架构原则

仅仅限制行数不够——文件很短但互相乱依赖,一样是高耦合。所以又补了一条架构层面的规则:

规则全文

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
35
---
applyTo: "**"
description: "编写或修改代码结构、模块划分、依赖关系时始终遵循"
---
# 低耦合架构原则

## 背景
仅靠限制文件/函数行数不足以保证低耦合——文件很短但相互直接依赖内部实现细节、共享全局状态,同样会导致高耦合。本规则补充架构层面的原则。

## 核心原则

1. **单一职责**
- 每个模块/类/函数只负责一件事。如果一个函数需要用"并且"才能描述它做了什么(如"校验参数并写数据库并发通知"),就应该拆分。

2. **依赖方向清晰,避免循环依赖**
- 模块之间的依赖关系应该是单向的(如:路由层 → 服务层 → 数据访问层),禁止出现 A 依赖 B、B 又依赖 A 的情况。
- 上层模块不应该反向依赖下层模块的内部实现细节,只依赖其对外暴露的接口。

3. **通过接口/参数传递依赖,而非硬编码或直接引用全局状态**
- 优先通过函数参数、构造函数参数传入依赖(依赖注入),而不是在函数内部直接 import 并使用某个具体实现,或者依赖全局变量/单例的内部状态。
- 这样便于替换实现、便于测试(可以传入 mock/fake)。

4. **对外暴露稳定的接口,隐藏内部实现**
- 模块只暴露必要的函数/类给外部使用,内部辅助函数/中间状态不应该被外部模块直接调用或读取。
- 修改模块内部实现时,只要对外接口(函数签名、返回结构)不变,其他模块不应该被影响。

5. **避免"上帝对象/上帝模块"**
- 不要把项目中大部分逻辑都塞进一个类或一个文件(如一个 `utils.py` 塞下所有逻辑,或一个 `Manager` 类管理所有业务)。发现这种趋势时应主动提出拆分建议。

6. **共享逻辑下沉,而不是复制粘贴**
- 多个模块需要同样的逻辑时,应该提取为公共函数/公共模块,而不是复制一份代码到各处(避免后续修改一处、遗漏其他处)。

## 执行方式
- 在设计新功能的模块结构、或修改现有模块的依赖关系之前,先说明你打算如何划分职责、模块间如何依赖,确认这个划分符合上述原则。
- 如果发现现有代码已经存在循环依赖、上帝对象等问题,在改动相关代码时可以顺带提出重构建议(但不要在未经同意的情况下擅自做大范围重构)。

💡 心得:最后那句”先说明职责划分和依赖方向再动手”很关键,相当于让 AI 每次动手前先过一遍架构评审,而不是写完才发现又是循环依赖。


📋 核心规则四:FastAPI 接口文档规范

我以前用 Flask + flasgger 时要手写 YAML docstring,麻烦到经常漏改。迁移到 FastAPI 之后发现它天然友好——/docs/openapi.json 完全由代码生成,只要约定好 docstring 写法就行。这条规则我用了 applyTo: "backend/**/*.py",只在后端文件上下文中生效,不污染前端上下文。

规则全文

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
---
applyTo: "backend/**/*.py"
description: "FastAPI 路由 docstring 接口文档规范"
---
# FastAPI 路由 docstring 接口文档规范

## 背景
本项目基于 FastAPI。`/docs`(Swagger UI)与 `/openapi.json` 完全由代码生成:
- 路由函数 **docstring 第一行** → 接口 `summary`
- docstring **空行后正文** → 接口 `description`
- Pydantic 模型的 `Field(description=...)` → body 参数说明
- `Query/Form/Path(description=...)` → query/form/path 参数说明

不需要任何 YAML、`swag_from``@api` 等装饰器。

## 核心要求

1. **每个新增路由必须编写完整的中文 docstring**,至少包含:
- 一句话功能摘要(第一行,动作式,如"获取工作流任务详情")
- 空行后的详细说明:关键业务规则、边界行为(如"若 xx 为空则不修改""超时时长强制 4~15 秒")
- `Args:` 段(Google 风格):列出 path/query/form 参数,每个写明含义与约束
- `Returns:` 段:按「外层结构 + 关键字段及类型」展开,写清各字段含义;返回文件流时写明流类型与文件名规则
- `Raises:` 段(可选):列出常见错误状态码与触发条件(400/404/500 等)

2. **修改路由时同步更新 docstring**,包括:
- 新增/删除/重命名请求参数(path/query/body/form)
- 参数类型、是否必填、取值范围变化
- 响应结构新增/删除/修改字段
- 新增错误状态码或触发条件变化
- 不允许"代码改了但文档还是旧版"——docstring 与代码行为不一致视为改动未完成

3. **参数描述的三种写法**
- **body 参数**:在 Pydantic 模型字段上写 `Field(..., description="中文含义与取值范围")`
- **query/form/path 参数**:简单参数用 `Query/Form/Path(..., description="...")`;复杂说明写在 docstring 的 `Args:`
- **通用补充**:docstring 的 `Args:` 段对所有参数都可再补充上下文信息(如"下标列表,来自 xx 接口的返回顺序")

4. **格式细节**
- 返回结构要写到具体字段层级,不能只写"返回任务详情"。
- 涉及业务规则的隐含行为(如"若当前集存在同名同类型资产则跳过")要在说明或字段描述里体现。
- 中文书写,字段名保持代码中的英文原名。

## 参考模板

```python
@router.get("/workflow_tasks/{task_id}")
def get_workflow_task(task_id: str, include_content: bool = Query(True, description="是否包含脚本等大字段内容")):
"""
获取单个工作流任务的详情。

返回任务的基础信息、各 Tab 状态与资源清单;当 include_content 为 true 时,
额外返回脚本内容等大字段,供编辑页加载使用。

Args:
task_id: 工作流任务 ID(创建接口返回的 task_id)
include_content: 是否包含脚本、文案等大字段内容

Returns:
任务详情对象(外层固定包一层 "task" 键)。

Raises:
404: 任务不存在
"""
...
```

## 执行方式
- 新建路由时,docstring 与路由实现在同一次改动中一起完成,不要先写代码、"回头再补文档"。
- 修改路由时,先检查现有 docstring 的 `Args:`/`Returns:` 是否仍然准确;若本次改动涉及入参/响应变化,必须同步更新,并在回复中提醒同步更新了哪些文档字段。

💡 心得:最后那条”在回复中提醒同步更新了哪些文档字段”非常好用——AI 每次改接口都会顺手列一句”已同步更新 Args 的 xx 字段”,方便我 Review 时快速核对。


✍️ 核心规则五:Git 提交信息生成规范

提交信息这块我单独写了一个 prompt 文件,不自动生效,需要手动触发(VS Code 里在 Chat 输入 /git-commit,Cursor 里则是一个不勾选 alwaysApply.mdc)。触发后 AI 会看当前已暂存的 diff,按规范生成信息。

规则全文

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
---
mode: ask
description: "看当前暂存的更改的代码,按 Header/Body/Footer 规范写中文 commit message。"
---
看当前暂存的更改的代码,写 commit message(不要运行 git commit,只需要生成信息即可)。全部使用中文书写。

## 整体结构

message 由三部分组成:**Header(必需)****Body(可选)****Footer(可选)**,三部分之间用空行分隔:

```
<type>(<scope>): <subject>
(空一行)
<body>
(空一行)
<footer>
```

Body 和 Footer 不是每次都要写,改动简单直接省略即可,不要为了凑格式硬写。

## Header(必需,只有一行)

格式:`<type>(<scope>): <subject>`

**type**(必需):从以下选择,根据改动内容判断,选最贴切的一个;涉及多种类型时选影响最大的那个:
- feat:新增功能
- fix:修复 bug
- docs:仅文档改动
- style:不影响代码逻辑的格式调整(空格、分号等)
- refactor:重构代码(不新增功能也不修复 bug)
- perf:性能优化
- test:新增或修改测试
- chore:构建流程、依赖管理、工具配置等杂项
- ci:CI/CD 配置相关

**scope**(可选):本次改动影响的范围/模块,例如 `user``api``build` 等;判断不出合适的范围就整体省略(连括号一起省略,不要写空括号)。

**subject**(必需):
- 用简明中文描述这次改动做了什么,动词开头(如"新增""修复""调整""重构")
- 不超过 50 个字
- 结尾不加句号

## Body(可选)

改动较复杂、影响面较大、或者需要说明"为什么这么改"时才写:
- 说明改动的动机,以及和之前行为的对比
- 可以分多行、用 `-` 列点说明
- 每行尽量不超过约 72 个字符,避免过长换行影响阅读

## Footer(可选)

只在以下两种情况使用:

1. **不兼容变更**:以 `BREAKING CHANGE:` 开头,说明变动内容、原因和迁移方式。
2. **关闭 issue**:写 `Closes #123`;一次关闭多个用逗号分隔,如 `Closes #123, #245`

## Revert(特殊情况)

如果本次改动是撤销之前的某次提交:
- Header 写成 `revert: <被撤销提交的原 Header>`
- Body 固定写:`This reverts commit <hash>.`(hash 为被撤销提交的 SHA)

## 其他要求

- 描述部分只说明改了什么、为什么改,不需要写实现过程细节。

有几个设计细节我觉得很关键:

  • Body/Footer 是可省的,改动简单就直接省略——不然 AI 会为了显得专业强行水字数
  • 明确说”不要运行 git commit”,只生成信息,提交动作我自己来,避免误提交
  • 总分限制没写死字数,但”只写改了什么、为什么改,不写实现过程细节”这句已经把废话堵死了

实际生成效果类似这样:

1
2
3
4
feat(user): 新增用户封禁接口

- 封禁后登录被拒绝并返回 403
- 新增封禁原因字段,便于后台展示

比我以前手写的 update 质量好太多了。


🛠️ VS Code / Cursor 怎么配置这些规则

规则文本都是一样的,关键是放进哪个目录、写什么 frontmatter,两个工具略有差异。

Cursor 的配置方式

在项目根目录创建 .cursor/rules/ 目录,每个规则一个 .mdc 文件,通过文件头部的 frontmatter 控制生效方式:

1
2
3
4
5
---
description: 接口文档规范
globs: backend/**/*.py
alwaysApply: false
---
  • alwaysApply: true:始终注入,每次对话都生效(适合先规划、长度限制、低耦合这类全局规则)
  • globs: backend/**/*.py:只在匹配路径的文件上下文中生效(适合 FastAPI 文档规范)
  • alwaysApply: false 且无 globs:靠描述手动/AI 触发(适合 git-commit 这种按需规则)

VS Code(GitHub Copilot)的对应配置

VS Code 没有 .mdc 这个概念,对应方案是 .github/ 目录,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
你的项目/
├── .github/
│ ├── instructions/
│ │ ├── add-new-feature.instructions.md # 先规划后编码
│ │ ├── code-length-limit.instructions.md # 代码长度限制
│ │ ├── low-coupling-architecture.instructions.md # 低耦合架构
│ │ └── fastapi-route-docstring.instructions.md # 仅 backend/**/*.py 生效
│ └── prompts/
│ └── git-commit.prompt.md # 手动 /git-commit 触发
└── docs/
├── plan/ # 实施计划存放目录
└── reports/ # 执行报告存放目录

两个工具的对应关系记一张表就够了:

Cursor (.mdc) 含义 VS Code 等价写法
alwaysApply: true 始终注入 applyTo: "**".instructions.md
globs: backend/**/*.py 按路径生效 applyTo: "backend/**/*.py".instructions.md
alwaysApply: false 且无 globs 手动触发 .github/prompts/*.prompt.md,聊天里用 /文件名 调用

启用步骤

  1. VS Code 装 GitHub Copilot Chat 扩展(新版默认支持 instructions 文件;不生效的话检查设置 chat.instructionsFilesLocations 是否包含 .github/instructions
  2. 在 Chat 面板点击”添加上下文” → “Instructions” 确认当前应用的规则
  3. git-commit.prompt.md 不会自动生效,在 Chat 里输入 /git-commit 手动触发

如果嫌拆多个文件麻烦,也可以把全局规则全合并进一个 .github/copilot-instructions.md,这是官方推荐的最简方式,效果等价。我个人更喜欢拆成多个文件——改某条规则时不会误动其他规则,看 git diff 也清晰。


🌍 通用兼容方案:AGENTS.md 怎么写

上面的配置都是工具专属的(Cursor 认 .mdc,Copilot 认 .instructions.md)。但现在 AI 编码工具越来越多——Claude Code、OpenAI Codex、Gemini CLI……总不能让每个工具配一份吧?

好在社区逐渐收敛到了一个通用约定:项目根目录放一个 AGENTS.md,目前已经被 Cursor、Codex、Zed 等 20 多个工具支持,地位相当于”给 AI 看的 README”。我的做法是把所有工具都应该遵守的全局规则放到这个文件里,内容尽量工具无关,不写 applyTo 这种工具专属的语法。

我的 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
35
36
37
38
39
40
41
42
43
44
45
46
47
# 项目 AI 协作规则

## 一、先规划,后编码,后总结

- 在为新功能或重大变更编写任何代码之前,必须先以 Markdown 格式创建一份详细的实施计划,呈现给我审查和批准。计划长度一般限制在 300 行以内,大规模更改则不超过 600 行。
- 在我明确表示批准(例如"计划通过""同意这个计划""可以开始")之前,绝对不要编写任何代码。
- 分析需求前,先查看仓库中对应的现有代码,再开始分析详细需要变化的部分。

### 计划文档要求

新任务开始时,生成 `implementation_plan_xxxxx.md` 到项目 `docs/plan` 目录,包含:
1. 需求概述:用自己的话简要重述任务目标
2. 文件结构变更:列出所有需要新建/修改的文件
3. 核心组件/函数设计:关键类、函数、组件的职责和核心逻辑
4. 数据流与逻辑:数据如何在系统中流转
5. 问题与澄清:需求中模糊或不确定的部分

提交计划后必须停下等待明确批准,批准后严格按计划执行,编码过程中始终参考该计划。

### 执行报告要求

代码实现并自测通过后,在 `docs/reports` 目录生成 `report_xxxxx.md`(与对应 `implementation_plan_xxxxx.md` 使用相同后缀),包含:
1. 变更文件清单:新增/修改了哪些文件,简要说明改动内容
2. 新增/改动功能说明:具体实现了哪些功能点,与计划相比有无偏差
3. 前后端对接说明:新增/修改的接口(路径、方法、请求/响应结构),前端在哪些页面/组件中调用、如何联调;无前后端交互则注明"不涉及"
4. 遗留问题/后续 TODO

生成后用一句话告知报告路径。

## 二、代码长度与低耦合准则

- 单个代码文件:正常不超过 500 行,特殊情况最多不超过 1000 行。
- 单个函数/方法:正常不超过 200 行,特殊情况最多不超过 400 行。
- 新建或添加代码前先检查文件行数,接近超限时主动提出拆分方案,而不是直接堆代码。
- 拆分以"职责单一"为准则,不要为凑数机械切割。
- 因语言/框架限制无法满足时,在注释或提交说明中简要说明原因。

## 三、低耦合架构原则

- **单一职责**:一个模块/函数只做一件事,需要用"并且"描述功能时就该拆分。
- **依赖方向清晰**:模块间依赖单向(如路由层 → 服务层 → 数据访问层),禁止循环依赖;上层只依赖下层对外接口,不依赖内部实现细节。
- **依赖注入优先**:通过参数/构造函数传入依赖,而非硬编码 import 具体实现或依赖全局状态,便于替换和测试。
- **接口稳定,隐藏实现**:模块只暴露必要接口,内部辅助函数/中间状态不被外部直接调用。
- **避免上帝对象/上帝模块**:发现某个文件或类塞下了大部分逻辑时,主动提出拆分建议。
- **共享逻辑下沉**:多处需要相同逻辑时提取为公共函数/模块,不要复制粘贴。
- 设计新功能的模块结构或修改依赖关系前,先说明职责划分和依赖方向,确认符合以上原则。
- 发现现有代码已有循环依赖、上帝对象等问题,可在改动相关代码时顺带提出重构建议,但未经同意不要擅自做大范围重构。

技术栈专属规范单独放一份:backend_AGENTS.md

FastAPI 的文档规范属于技术栈特定内容,我放在单独的 backend_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
# FastAPI 路由 docstring 接口文档规范

## 背景
本项目基于 FastAPI。`/docs`(Swagger UI)与 `/openapi.json` 完全由代码生成:
- 路由函数 docstring 第一行 → 接口 summary
- docstring 空行后正文 → 接口 description
- Pydantic 模型的 Field(description=...) → body 参数说明
- Query/Form/Path(description=...) → query/form/path 参数说明

不需要任何 YAML、swag_from、@api 等装饰器。

## 核心要求

1. 每个新增路由必须编写完整的中文 docstring,至少包含:
- 一句话功能摘要(第一行,动作式,如"获取工作流任务详情")
- 空行后详细说明:关键业务规则、边界行为
- Args 段(Google 风格):列出 path/query/form 参数,写明含义与约束
- Returns 段:外层结构 + 关键字段及类型,写清各字段含义
- Raises 段(可选):常见错误状态码与触发条件(400/404/500 等)

2. 修改路由时同步更新 docstring(参数变化、类型变化、响应结构变化、错误码变化等),不允许"代码改了文档没改"。

3. 参数描述写法:
- body 参数:Pydantic 字段上写 Field(..., description="中文含义与取值范围")
- query/form/path 参数:简单参数用 Query/Form/Path(..., description="...");复杂说明写在 docstring 的 Args 段

4. 格式:返回结构写到具体字段层级;隐含业务规则要体现在说明里;中文书写,字段名保持代码中的英文原名。

## 执行方式
- 新建路由时,docstring 与实现同一次改动完成,不要"回头再补文档"。
- 修改路由时先检查现有 Args/Returns 是否仍准确,涉及入参/响应变化必须同步更新,并在回复中提醒同步更新了哪些字段。

写 AGENTS.md 的几个注意点

  • 只写通用约定:工作流、代码规范、架构原则这些跟工具无关的放这里
  • 工具专属配置留在各自的配置里:Copilot 的 applyTo 语法、Cursor 的 alwaysApply,不适合塞进通用文件
  • 内容可以比 instructions 版本精简AGENTS.md 本质是”游戏规则总述”,触发工具再去找详细规则;细节全部塞进去反而稀释重点
  • 用命令式口吻:AI 更吃”必须””禁止”这种明确的祈使语气,模棱两可的”建议”会被忽略

🧩 一些踩过的坑

最后聊几点实践中总结的经验:

  1. 规则要可执行,别写成口号。”代码要优雅”这种话 AI 会当耳边风,”文件不超过 500 行”才会真的去数行数。

  2. 规则不是越多越好。给 AI 塞几十条规则,它会开始选择性遵守。我现在常驻生效的核心就三条:先规划、控长度、低耦合,剩下的按需触发。

  3. 规则也要迭代。发现 AI 反复犯某个错误,别只是对话里纠正,把教训沉淀进规则文件,一劳永逸。

  4. 手动触发适合低频规则。像 Git 提交这种每次都要人工确认的场景,做成手动触发的 prompt 比自动生效更可控。

  5. 别让 AI 自己跑不该跑的命令。git-commit 规则里那句”不要运行 git commit”是吃过亏之后加的——有一次 AI 顺手就给我提了,截胡得赶紧加上。


🔚 总结

给 AI Agent 立规矩这件事,本质上和给团队成员写开发规范是一回事——区别只是 AI 真的会 100% 照字面执行(包括你写得含糊的地方),所以规则要写得具体、可执行、能验证

我目前在用的这套文件结构:

  • AGENTS.md:跨工具通用的全局规则(规划流程、代码长度、架构原则)
  • backend_AGENTS.md:FastAPI 专属的接口文档规范
  • .github/instructions/*.md + .github/prompts/git-commit.prompt.md:VS Code Copilot 落地方案
  • .cursor/rules/*.mdc:Cursor 落地方案(内容与上面相同,仅 frontmatter 不同)

配上之后,AI 从”不可控的天才”变成了”靠谱的熟练工”,虽然还没法完全替代 Review,但至少它提交的代码我已经敢先看业务逻辑、而不是先数行数了。

希望这些经验对你有帮助,欢迎在评论区分享你自己的 AI 规则约束心得~