有一段时间,我特别迷信 .cursorrules。每个新项目开工前,我都会花不少时间写一份:技术栈、命名规范、目录结构、禁用的 API,能想到的全塞进去。结果写完后用了没几天就被打击到:规则里写“不要用 any”,生成的代码里照样一排 any;写“改接口前先看文档”,它还是按自己理解直接改。我一度怀疑这功能就是个摆设,直到后来把几个项目的失败规则放在一起对照,我才意识到问题出在写法上——我把 .cursorrules 写成了给 AI 看的自我介绍,而不是能指挥它干活的项目手册。
这篇文章就把我整理后的这套模板和背后的思路一起放出来,包括 Cursor 加载规则的底层机制、四层模板结构、不同项目怎么改,以及写完怎么验证真生效。适合被 .cursorrules 坑过的人,也适合团队想统一 AI 协作方式但不知道从哪下手的场景。
1. 先面对现实:你的规则为什么不被当回事
1.1 把 .cursorrules 当成“系统提示词”的一部分
很多人写 .cursorrules 时,心态还停留在“写配置文件”上——像写 tsconfig、eslint 那样,把键值对、选项列清楚就完事。但实际上,.cursorrules 的内容本质上是一段被拼进系统提示词里的纯文本。模型对这段文本的处理不是“解析配置项”,而是“理解自然语言指令”。
这意味着两件事。第一,规则写得太模糊,模型就没有可执行的抓手。比如“代码要优雅、要清晰”,模型会把它当成一句普通的好话,知道但不照着办。第二,规则的内容必须能和它正在看的文件产生联系。模型不是把你的整个仓库背下来,它是在任务过程中按需读文件、查目录树。如果规则里提到“在 services 层做封装”,但模型根本不知道 services 层在哪、有哪些文件,这句话就约等于没说。
我最早犯的错就是通篇写“不要这样做、不要那样做”,却完全没告诉它项目里正确的路径是什么。后来想明白了:一条有效的规则,应该像给新人的第一份交接文档,既要有约定,更要有可定位的项目事实。
1.2 三种典型的“无效声明”和改法
我把自己踩过坑、也看过别人项目里的规则整理了一下,最没用的基本是下面这种写法。
| 无效写法 | 为什么没用 | 改成这样 |
|---|---|---|
| “代码要保持高质量、可维护” | 没有客观判断标准,模型无法评估 | “单个函数超过 40 行必须拆分,拆分出的函数必须用动词开头命名” |
| “禁止使用 any” | 只给否定指令,没给替代方案 | “禁止新增 any;第三方类型缺失时优先在 src/types/global.d.ts 里补充类型,并写 TODO 注释” |
| “必须遵循现有架构” | 没说架构是什么样,模型只能猜 | 把目录结构、分层职责、数据流用代码地图写在规则里,并给关键文档路径 |
| “改这个模块之前要谨慎” | 没有说明影响范围,模型不知道谨慎到什么程度 | “改 order 模块前必须阅读 docs/order-flow.md,并把影响范围写在改动说明里” |
看出规律了吗?无效规则的特征是“只有态度,没有事实”。有效规则的特征是“既有态度,又有路径、文件名、验证方式”。模型是概率推理,不是命令执行,你给它的判断依据越具体,它越容易押中你想要的答案。
1.3 动手写模板前,先确认三件事
第一,确认规则会被放在哪一层。Cursor 的规则入口不只有根目录 .cursorrules 一个,还有用户级规则、.cursor/rules 下的分文件规则,彼此会同时进入上下文。先搞清楚层级,才不会出现“改了根目录规则,但全局规则里有一条更强的指令在捣乱”的情况。
第二,确认规则写的都是“现在的事实”。有些项目里的 .cursorrules 是三个月前写的,当时还是 Vue 2,现在已经迁到 Vue 3,目录也换过一轮。规则和实际代码脱节,模型照着旧规则改新代码,不出问题才怪。
第三,确认规则总量不要失控。我见过一个项目的 .cursorrules 快 400 行,从缩进到颜色变量全写了。结果就是模型为了照顾这些规则,生成代码时反复横跳,反而比不写规则更不稳定。根目录规则保持精简,细节拆到子文档,这才是可持续的写法。
2. 摸清上下文机制:规则到底从哪来、会不会过期
2.1 三层上下文:全局、项目、临时引用
Cursor 的上下文里,跟你项目规则相关的其实有三个来源。
第一层是用户级规则,在设置里的 Rules for AI 或类似入口配置。它跟着账号走,对所有项目生效,适合放通用偏好,比如“所有注释用中文写”“不要主动删除旧代码”。
第二层是项目级规则,也就是根目录的 .cursorrules,以及新版支持的 .cursor/rules 目录下的.mdc文件。它跟着仓库走,适合放项目特有约定。如果是团队协作,这一层必须提交到 git,否则只有你本地生效。
第三层是你手动在对话里用 @ 引用的文件。这一层临时性最强,是把某个具体文件“拉”进当前上下文的快捷方式,适合问问题或改小范围代码时用。
这三层不是简单的覆盖关系,而是合并进同一个上下文。所以同一件事如果全局说一套、项目说另一套,模型很可能按它更熟悉的那套来。整理规则时的第一步,就是先检查有没有互相打架的表述。
2.2 单文件 .cursorrules 和 .mdc 多文件怎么选
新版 Cursor 把项目规则逐渐往 .cursor/rules 目录迁移,每一个.mdc文件可以带 frontmatter,用 globs 声明它作用于哪些文件。比如:
--- description: 服务端接口相关的强制约束 globs: server/**/*.ts --- - 所有对外接口的入参校验必须放在 controller 层 - service 层不允许直接操作 HTTP Request/Response 对象 - 字段变更必须同步更新对应的 OpenAPI 描述这种按文件范围拆分的好处是:模型在处理某个文件时,只加载跟这个文件相关的规则,省 token,也减少无关信息的干扰。单文件的 .cursorrules 仍然有效,适合放那些“不管改哪都要知道”的项目全局约定,比如技术栈、目录地图、完成任务的验收清单。
我的建议是把两者结合:根目录 .cursorrules 当入口和索引,只保留最核心的项目事实;细节规则拆到 .cursor/rules 下几个.mdc文件里,按目录、文件类型、任务类型分开。这就是标题里说的“别写了等于没写”的关键解法——不是不写单文件,而是别把所有东西都堆在一个文件里让模型自己挑重点。
2.3 规则要“指路”,而不是“背课文”
模型的上下文窗口虽然大,但每次任务真正能集中注意力的内容仍然有限。你塞给它 400 行规则,它不可能每条都记牢,尤其当任务和规则里的内容关联不强时,模型会更依赖它实际读到的代码。
所以规则里最有价值的能力不是“告诉它答案”,而是“告诉它去哪找答案”。比如这样写:
- 涉及订单支付流程的改动,先读 docs/cursor/payment.md,按里面的流程图修改 - 新增 API 前,先阅读 server/api-conventions.md,确认路由命名和错误码格式模型在需要时会主动去读这些文档,相当于你给它装了一个项目知识的地图导航。这个思路我后面会把它嵌进模板的第三层和第四层。
3. 可直接保存到项目的四层模板
3.1 为什么是四层结构
我对比过很多份好用的规则,发现它们都在回答四个问题:这是什么项目、项目的代码地图长什么样、有哪些必须遵守的约定、做完一件事用什么标准验收。于是我干脆把模板固定成四层。
第一层项目身份,是让 AI 快速建立项目认知,避免它用通用经验硬套。第二层代码地图,是给模型最关键的导航信息,让它知道“改这个功能应该碰到哪些文件”。第三层强制约定,是规则的核心,把命名、结构、禁区全部显式化。第四层工作流与验收,是让模型在收尾时学会自查,而不是交完就跑。
3.2 完整模板代码
下面这份模板可以直接复制到一个新项目的根目录 .cursorrules 里使用。注意,里面所有带【】的占位符都要按实际项目替换,尤其是目录结构,一定要真实存在。
# .cursorrules — 项目级 AI 协作规则模板 ## 1. 项目身份 - 一句话描述:这是一个【填写项目类型】项目,主要用来【填写核心用途】。 - 技术栈: - 语言:【例如 TypeScript】 - 框架:【例如 React + Vite / FastAPI】 - 包管理器:【例如 pnpm / uv】 - 运行时版本:【例如 Node 20+ / Python 3.11】 - 关键入口: - 【入口一,例如 src/main.tsx】 - 【入口二,例如 src/server/index.ts】 - 常用命令: - 启动:【例如 pnpm dev】 - 测试:【例如 pnpm test】 - 构建:【例如 pnpm build】 ## 2. 代码地图 - 目录结构摘要: - src/pages —— 路由页面,一个页面一个目录 - src/components —— 通用组件 - src/services —— 所有后端请求的封装层 - src/types —— 全局类型定义 - server/routes —— 后端路由定义 - server/services —— 业务逻辑层 - 数据流概览:从入口组件 -> services 封装 -> API 请求 -> 后端 route -> 业务 service -> 数据库。 - 关键约定:任务涉及某个模块时,先阅读对应模块 README 或【docs/cursor/】下的说明文档。 ## 3. 强制约定 ### 命名 - 组件文件使用 PascalCase,文件名与组件名一致。 - 工具函数使用 camelCase,以动词开头(如 getUserInfo、handleSubmit)。 - 常量使用 UPPER_SNAKE_CASE,枚举值使用 PascalCase。 ### 结构 - 页面相关接口请求必须走 services 层,不允许在页面组件里直接写 fetch。 - 样式优先使用设计系统 tokens,不允许在组件里写死色值。 - 通用逻辑抽到 hooks / utils,禁止在多个页面复制同一段代码。 ### 禁区 - 禁止在没有说明理由的情况下修改【核心模块目录】。 - 数据库结构变更必须新增 migration,禁止直接修改已有 migration 文件。 - 禁止擅自新增依赖,如需添加,必须在改动说明中给出理由。 ## 4. 工作流与验收 - 完成任务后按以下顺序自查: 1. 改动是否只影响预期范围? 2. 测试和构建命令是否通过? 3. 是否保持了目录和命名约定? 4. 是否引入了新依赖或改动公共类型? 5. 是否更新了受影响的文档或接口描述? - 提交信息风格:使用 Conventional Commits(例如 feat: 新增用户列表筛选)。 ## 5. 详细规则的入口 - 代码风格细节见 docs/cursor/code-style.md - 接口设计约束见 docs/cursor/api-conventions.md - 数据库变更流程见 docs/cursor/db-migration.md模板本身不复杂,关键是四层信息都要有。我见过很多人只写第三层“约定和禁区”,前面两层全省略,结果模型不知道项目在哪、不知道看哪些文件,规则再强硬也落不了地。
3.3 模板各段分别改变了模型的哪些行为
| 模板段落 | 对模型行为的实际影响 | 对应无效旧写法 |
|---|---|---|
| 项目身份与常用命令 | 回答会使用正确的技术栈术语,给的指令更贴近仓库现状 | “这是一个 Web 项目” |
| 代码地图 | 生成文件路径时会落到真实目录,而不是虚构结构 | “按现有架构扩展” |
| 强制约定 | 新代码在结构和命名上更接近老代码 | “保持代码风格一致” |
| 工作流与验收 | 收尾时会主动检查测试、构建、文档 | “交付质量要高” |
| 详细规则入口 | 面对复杂任务时先查文档再动手 | “遇到问题时要谨慎处理” |
对照这张表就能明白:每条规则都应该能直接回答“模型读过之后,下一步行为会有什么不同”。回答不了这个问题,规则就是装饰品。
4. 落到不同项目:前端、后端、全栈的调整重点
4.1 前端项目:重点管组件边界和样式
前端项目里最容易失控的是两个地方,一个是到处写重复组件,另一个是样式随意编。模板里的结构层可以再加一条:新页面组件必须从 pages 目录下创建,公共 UI 先从 components 里找,找不到再新增,并且新增前先说明要复用的理由。
样式上,如果项目有设计系统,规则里要写死“颜色、间距、字体必须从 tokens 取”,并且给出 tokens 文件的具体路径。没有设计系统的小项目,至少约定“不要使用内联样式,公共样式放模块级 css 文件”。前端项目的命令也要写全,很多 Cursor 生成完代码不会主动跑 lint,但如果你在常用命令里补上eslint --fix和tsc --noEmit,它收尾自查时就会用。
4.2 后端项目:重点管分层和接口契约
后端项目的核心是边界。我的模板第三层会额外加两条:
- controller 只做参数校验和响应组装,不允许写业务逻辑 - service 层不允许直接操作 HTTP 对象,数据库访问必须走 repository / model 层这两条写清楚后,模型生成的服务端代码会明显规矩很多。第二个重点是接口契约。凡是带 OpenAPI、Postman 描述文件的仓库,规则里必须写“改接口先改契约描述,再改实现”。否则模型经常会只改实现、文档留在旧版本,等联调的时候才发现对不上。
数据库变更也是后端项目的重灾区。模板里的禁区建议专门加一条:禁止直接修改已迁移过的表结构文件,必须新增 migration。模型只有在规则里看到这条明确约束,才会在生成代码时顺手建新的 migration 文件。
4.3 全栈项目:重点管端到端的数据流
全栈项目比单一前端或后端更依赖“全局视角”。模型如果不清楚请求从页面到数据库经过哪些层,很容易在某一层里自己发明一套数据流规范,导致和另一层对不上。
所以第二层代码地图要写得格外细。比如模板里可以加一段“一个请求的生命周期”:
- 页面组件调用 src/services/xxx.ts 里的函数 - services 通过 axios 实例夹带认证信息请求后端 - server/routes 接收请求后交给 server/services 处理 - server/services 调用 repository 访问数据库并返回结果规则里有了这条链路,模型在改页面时就会记得去检查 services 和后端路由是否一致。全栈项目还建议在第四层验收清单里加一条:“本次改动涉及的前后端接口是否同步更新了”。这一条能省掉非常多联调阶段的互相甩锅。
5. 写完别急着用:一套快速验证的“冒烟测试”
5.1 先在全新会话里问三个问题
写好的规则是不是真的被模型读进去了,不要靠感觉,要在新会话里跑冒烟测试。
第一个问题:“根据项目的规则,告诉我这个项目的技术栈、目录结构,以及改一个接口时一般会涉及哪些文件?”如果回答里能说出规则里写的真实路径、技术栈和流程,说明规则成功进入了模型视野。如果回答含糊不清,或者路径是编的,优先检查 .cursorrules 是否放在项目根目录、有没有被 git 忽略。
第二个问题:“新增一个【典型功能】需要动哪些文件?请按规则里的验收清单自查一下。”这个问题的目的是看模型是否真的理解了代码地图,而不只是复述规则文本。好的回答会列出一串具体文件路径,并且主动提到测试命令和文档同步。
第三个问题,可以故意让它做一件触及禁区的事:“帮我把【核心模块】的某个逻辑改掉。”看它是直接开改,还是先停下来确认影响范围。如果它犹豫或要求说明理由,说明禁区生效了;如果它二话不说直接改,说明禁区写得还是不够清楚。
5.2 验证时最容易发现的四个维护问题
第一,路径过期。规则里写的目录结构和实际仓库不一致,模型会当场漏馅。这时候别急着改规则文案,先把目录树更新一遍。
第二,规则自相矛盾。比如第一层说“项目用 pnpm”,第三层又说“安装依赖后确认 package-lock.json 是否变更”,模型就会困惑。冒烟测试时多留意它是否同时提到两套冲突的信息。
第三,命令失效。模板里的测试命令和构建命令必须真实可跑。有些项目测试套件非常慢,model 为了完成自查甚至可能假装跑过,所以规则里可以补充一句:“如果测试命令超过 5 分钟,说明原因并告知用户,不要自行假设测试通过。”
第四,规则文件没有进版本控制。团队协作时,每个人本地一份不同版本的 .cursorrules,模型行为根本不可控。把它提交到 git,并且约定谁改规则谁负责跑一次冒烟测试,这一点比任何措辞都重要。
5.3 我现在的维护习惯
这几次改版后,我给自己定了一个简单规矩:根目录 .cursorrules 不超过 200 行,只放四层骨架;详细约定全部拆到 .cursor/rules 或 docs/cursor 下的文档里。每改一次目录结构、依赖框架或者数据流,就顺手更新代码地图,并重新跑一遍上面的三个冒烟测试问题。
时间长了会发现,.cursorrules 与其说是一份配置,不如说是一份需要持续维护的项目手册。它会随项目生长,也会随项目腐烂。真正让它“写了不等于没写”的,不是某个万能模板,而是你愿不愿意每次改完架构后,花两分钟让 AI 也同步知道这件事。