📌 前言 用 AI Agent(Cursor、Copilot、Claude Code 之类)写代码有一段时间了,爽是真爽,但坑也没少踩。没有约束的 AI 就像一个能力超强但没有项目上下文的新员工:代码风格一会儿一变、动不动就给你写个五百行的”上帝函数”、改完代码注释还是上一版的、Git 提交信息写得随心所欲……
后来我开始给 AI 立”规矩”——把项目的开发规范写成规则文件,让 AI 每次干活前都必须遵守。这篇文章就以我自己的 Python 后端项目(FastAPI)为例,把我实际在用的全套规则方案分享出来。每个规则都附上了我项目里真实使用的文件原文,可以直接复制到自己项目里改改用 ,包括:
哪些规则对开发规范有明显帮助(先规划后编码、长度/耦合限制、接口文档同步)
Git 提交信息生成的规范
VS Code / Cursor 里怎么配置这些规则
为了跨工具兼容,通用的 AGENTS.md 应该怎么写
🎯 为什么需要给 AI 立规矩 先说说我遇到的真实痛点,相信大家或多或少都有共鸣:
没有规划直接开写 :说一个需求,AI 秒回一大段代码,写完才发现方向理解偏了,推倒重来
越写越臃肿 :AI 倾向于把逻辑堆在一个文件、一个函数里,耦合度爆表,后期改一处动全身
文档和代码脱节 :接口参数改了,docstring 还是旧的,Swagger 文档逐渐变成”僵尸文档”
提交信息没法看 :update、fix 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,聊天里用 /文件名 调用
启用步骤
VS Code 装 GitHub Copilot Chat 扩展(新版默认支持 instructions 文件;不生效的话检查设置 chat.instructionsFilesLocations 是否包含 .github/instructions)
在 Chat 面板点击”添加上下文” → “Instructions” 确认当前应用的规则
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 更吃”必须””禁止”这种明确的祈使语气,模棱两可的”建议”会被忽略
🧩 一些踩过的坑 最后聊几点实践中总结的经验:
规则要可执行,别写成口号 。”代码要优雅”这种话 AI 会当耳边风,”文件不超过 500 行”才会真的去数行数。
规则不是越多越好 。给 AI 塞几十条规则,它会开始选择性遵守。我现在常驻生效的核心就三条:先规划、控长度、低耦合,剩下的按需触发。
规则也要迭代 。发现 AI 反复犯某个错误,别只是对话里纠正,把教训沉淀进规则文件,一劳永逸。
手动触发适合低频规则 。像 Git 提交这种每次都要人工确认的场景,做成手动触发的 prompt 比自动生效更可控。
别让 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 规则约束心得~