1. 为什么你的 Cursor 总在“自由发挥”
用 Cursor 写代码的人大概率都遇到过这种场景:你明明在项目里定好了目录结构、命名风格、错误处理方式,结果 Agent 一出手,接口命名一会儿驼峰一会儿下划线,日志库今天用log明天用logger,甚至连你反复强调的“别用 any”都当耳旁风。每次开新对话,你都得把同一段提示词再贴一遍,贴到怀疑人生。
问题的根子不在模型笨,而在于上下文没有被固化。你发给模型的提示词其实是三部分拼起来的:基础系统提示 + 你的临时输入 + 项目上下文。前两部分每次都在变,第三部分如果没人喂,模型就只能靠猜。猜出来的东西,自然和你的项目约定对不上。
Cursor Rule 就是来解决这件事的。它把“需要反复交代的约定”从聊天框里抽出来,写进.cursor/rules目录下的 MDC 文件,让模型在每次请求时自动带上。你可以把它理解成给项目配了一份“员工手册”:新来的 Agent 一进门先读手册,再干活。
这篇面向日常用 Cursor 写代码的开发者,重点讲三件事:MDC 文件的骨架怎么写、frontmatter 怎么配才能精准触发、以及怎么用对比动作验证规则真的生效了。适合已经用过 Cursor、但还在靠“复制粘贴提示词”续命的人。
2. 把提示词固化成规则:TaoToken 前置准备
规则写好了,最终还是要落到模型调用上。如果你希望规则文件里的约定能被稳定执行,模型侧的接入最好也固定下来,别今天换一个明天换一个。我自己的做法是统一走 TaoToken 的接口,模型对话、编码计划、密钥管理都在一个控制台里,省得来回切。
具体来说,日常调试规则效果时用模型对话页面直接试;写长期项目、跑 Agent 任务时用 Coding Plan;密钥在 API Keys 页面生成,接入文档在 doc 里查。这样规则文件改完,模型侧不用重新配环境,直接验证就行。
需要提前准备的东西不多:一个可用的 API Key、Cursor 里已经打开的项目、以及.cursor/rules目录(没有就手动建一个)。Key 的生成入口在控制台的 API Keys 页面,接入方式参考官方文档,模型对话入口用来做单轮验证。地址统一用https://taotoken.net/api,不要带多余参数。
注意:规则文件本身不依赖任何特定模型,但模型侧接入稳定,规则的可复现性才高。别一边改规则一边换模型,那样你分不清是规则生效了还是模型碰巧听话。
3. MDC 规则文件骨架与 frontmatter 配置
MDC 可以理解成“带元数据的 Markdown”。文件头用 frontmatter 声明这条规则怎么触发,下面正文写具体约定。先看一个最小可用的骨架:
--- description: 项目通用编码规范,约束命名、日志与错误处理 globs: alwaysApply: false --- # 项目编码规范 ## 命名 - 变量与函数使用小驼峰,类名使用大驼峰 - 常量全大写下划线分隔 - 禁止使用单字母命名,循环下标除外 ## 日志 - 统一使用项目封装的 logger,禁止直接 console.log - 错误日志必须带上下文对象,禁止只打字符串 ## 错误处理 - 异步调用必须 try/catch 或 .catch - 禁止吞掉异常,catch 块里至少要记录日志frontmatter 里几个字段决定了规则的触发方式,这是最容易配错的地方。对照表如下:
| 字段 | 作用 | 典型取值 |
|---|---|---|
| description | 规则用途说明,Agent Request 模式下模型靠它判断是否调用 | 一句话描述 |
| globs | 文件匹配模式,Auto Attached 模式下命中才加载 | src/**/*.ts |
| alwaysApply | 是否始终注入上下文 | true / false |
四种触发类型对应关系是这样的:Always 就是alwaysApply: true,规则永远在上下文里;Auto Attached 靠globs匹配,比如你打开src/api/user.ts,匹配src/**/*.ts的规则才会加载;Agent Request 靠description,模型自己判断“现在该不该用这条规则”;Manual 则要你在对话里用@规则名手动引用。
我试过把命名规范设成 Always,把“数据库迁移脚本规范”设成 Auto Attached 匹配migrations/**,效果比全塞进一条规则好很多。规则文件建议控制在 500 行以内,太长就拆成多条可组合的小规则,比如naming.mdc、logging.mdc、error-handling.mdc分开写。
项目级规则支持嵌套。你可以在根目录放全局约定,在子目录放局部约定:
project/ .cursor/rules/ base.mdc backend/ .cursor/rules/ api-style.mdc frontend/ .cursor/rules/ component-style.mdc这样后端和前端各自的约定互不干扰,Agent 走到哪个目录就读哪本手册。
4. 验证规则是否生效:一次对比请求
规则写完不验证,等于没写。最直接的办法是做一次“触发前 vs 触发后”的对比。先准备一个故意违反约定的文件,比如src/utils/format.ts:
export function Format_Date(d: any) { console.log("formatting"); return d.toISOString(); }这段代码踩了三个坑:函数名大写下划线、参数用了any、直接console.log。先不加载规则,让 Agent 检查这个文件:
请检查 src/utils/format.ts 是否符合项目编码规范,并给出修改建议。没有规则时,模型通常只会泛泛地说“建议加类型”“命名可以更规范”,不会精确指出你项目里“禁止 any”“禁止 console.log”这两条硬约定。
接着把naming.mdc和logging.mdc放进.cursor/rules,其中命名规则设alwaysApply: true,日志规则用globs: src/**/*.ts。再发一次同样的请求,这次模型的输出会明显不同:它会直接点出Format_Date违反小驼峰约定、any违反类型约束、console.log违反日志规范,并给出改写后的版本:
import { logger } from "@/lib/logger"; export function formatDate(d: Date): string { logger.info("formatting date", { input: d }); return d.toISOString(); }对比两次输出,如果第二次能稳定命中你写在规则里的具体条款,说明规则生效了。如果还是泛泛而谈,多半是 frontmatter 配错了——比如该用 Auto Attached 的写成了 Manual,模型根本没加载到。
想更省事的话,可以在模型对话页面里单轮测试规则文本,确认措辞清晰后再落盘到 MDC 文件。规则本质是提示词,指令越具体、边界越清楚,模型执行越稳。
5. 规则不生效?这几个坑我踩过
规则写了但模型不理。先查 frontmatter。alwaysApply: false且globs写错路径,规则就不会被加载。比如你写globs: src/*.ts,它只匹配src下一层,src/api/user.ts是匹配不到的,得用src/**/*.ts。
Agent Request 模式不触发。这个模式完全靠description让模型自己判断。描述写得太虚,比如“一些规范”,模型不知道什么时候该用。改成“当修改 TypeScript 文件中的函数命名或日志调用时使用”,命中率会高很多。
规则之间互相打架。根目录一条规则说“用双引号”,子目录一条说“用单引号”,模型就懵了。嵌套规则要有明确的覆盖关系,子目录规则负责细化,不要和父级直接冲突。
规则太长被截断。单文件超过 500 行,模型可能只读到前半段。把大规则拆成多条,用globs或description分别触发,比堆在一个文件里靠谱。
改了规则没重启会话。Cursor 的规则在会话开始时加载,改完 MDC 文件后最好开个新对话再验证,不然你测的还是旧上下文。
Manual 规则忘了引用。设成 Manual 的规则不会自动加载,必须在对话里用@规则名显式引用。如果你发现某条规则死活不生效,先确认它是不是 Manual 类型。
6. 把规则接进你的日常编码流
规则文件调通之后,下一步是让它和模型调用形成固定链路。我的习惯是:项目里.cursor/rules跟着代码一起进版本控制,团队里谁拉代码谁就继承这套约定;模型侧统一走 TaoToken 的接入方式,密钥在 API Keys 页面管理,接入细节查 doc,长期跑编码任务用 Coding Plan,单轮验证规则用模型对话。
这样一套下来,你不再需要每次开对话都重新交代一遍项目约定,Agent 进门先读手册,答非所问的情况会明显减少。规则写得好不好,直接决定模型是“帮你干活”还是“给你添乱”。先从一条命名规范开始,跑通触发和验证,再逐步把日志、错误处理、目录结构这些约定补进去,比一次性写一大坨更容易维护。