Cursor Rules 配置指南:提示词工程与多模型切换
先看一段你可能似曾相识的场景:你让 Cursor 写一个用户列表组件,它给你来了个 React 类组件。你说项目用的 Vue 3 + TypeScript,它改成了 Options API。你再纠正要用 Composition API + <script setup>,它终于写对了框架,但类型标注一个没有。
三轮对话,一行有效代码没产生。这不是 Cursor 不够聪明,是你没告诉它"你是谁"。新人入职第一天,你把他扔到工位上说"写代码吧",他只能按自己的习惯来。Cursor Rules 就是那份新人入职手册——你不写,别怪 AI 乱来。
.cursor/rules 目录的 MDC 格式深度拆解
MDC 文件到底是什么
MDC 不是 Cursor 发明的格式,它来自 Nuxt Content 的 Markdown Components 规范。
一个典型的 MDC 文件长这样:frontmatter 里写 description、globs、alwaysApply 三个字段,正文是具体的规则条目。这三个字段搞混了效果天差地别,逐个拆解一下。
globs 是文件匹配模式,决定规则的作用范围。
alwaysApply 控制是否无条件注入到每次对话。设为 true 就是不管聊什么都带上,设为 false 则只在匹配文件或 AI 主动引用时生效。前者谨慎使用,因为吃 token。
一个常见的坑
很多人写规则时把 globs 和 alwaysApply: true 同时设了。这么写的话 globs 直接废掉——"总是应用"意味着不需要匹配文件。不是 bug,但确实容易让人困惑。
正确的组合逻辑是四种:
alwaysApply: true且无globs:每次对话都注入,适合全局规范alwaysApply: false且有globs:对话涉及匹配文件时注入,最推荐的用法alwaysApply: false且无globs:AI 根据 description 自行判断,属于模糊匹配alwaysApply: true且有globs:等同于第一种,globs 被忽略,别这么写
提示词工程:怎么把规则写得让 AI 真正听话
写 Cursor Rules 的本质就是写提示词。写得好和写得烂,效果差距堪比 O(n) 和 O(n²)。
角色设定不是废话
"你是一个 Vue 3 专家"——这种角色设定到底有没有用?实测下来,有用,但得写对。对比两种写法。简单版只说"使用 Vue 3 Composition API 写组件";详细版则是"你是一个有 6 年 Vue 生态经验的高级前端工程师,精通 Vue 3.5 的最新特性包括 Reactive Props Destructure 和 useTemplateRef,对 TypeScript 类型体操很熟练,提出需求时会优先考虑 VueUse 里有没有现成的组合式函数"。
第二种为什么有效?它不是在"命令"AI,而是在"校准"AI 的知识权重分布。当你说"精通 Vue 3.5 的最新特性",模型会在生成时优先采样这个版本的知识,而不是回退到 Vue 2 或者早期 Vue 3 的写法。区别肉眼可见:简单版经常生成 defineComponent 包裹的写法,详细版则稳定输出 <script setup> 风格。
负向约束比正向指令更有效
告诉 AI"不要做什么"往往比"要做什么"更管用,踩过坑的人都有体会。
在 vue-components.mdc 里,我把禁止事项放在最前面并标注"权重最高":禁止 Options API、禁止 this 关键字、禁止在组件内直接 import axios(所有请求走 src/api 层)、禁止 any 类型(类型复杂就用 unknown + 类型守卫)、禁止在 <template> 中写复杂表达式(超过一个三元运算符就抽成 computed)。推荐做法放在后面:Props 类型用 interface 定义并 export、组件内超过 3 个 ref 时考虑抽成 composable、事件命名用 kebab-case。
为什么"禁止事项"要靠前?LLM 的注意力机制对文本位置敏感,靠前的指令权重更大。而且负向约束定义了明确的边界,正向建议更像"参考意见"——不遵守也不算错,AI 就容易打折扣。
用示例代码代替自然语言描述
这是提示词工程里投入产出比最高的技巧。与其用十条规则描述"组件应该怎么组织",不如直接给一个模板:
<script setup lang="ts">
// 1. 类型导入
import type { XxxProps } from './types'
// 2. 组件导入
import BaseButton from '@/components/base/BaseButton.vue'
// 3. Props & Emits
const props = defineProps<XxxProps>()
const emit = defineEmits<{
'update-value': [value: string]
'confirm': []
}>()
// 4. 组合式函数
const { data, loading } = useXxx(toRef(props, 'id'))
// 5. 响应式状态(尽量少,复杂逻辑抽 composable)
const isEditing = ref(false)
// 6. 计算属性
const displayName = computed(() =>
props.showFullName ? `${data.value.first} ${data.value.last}` : data.value.first
)
// 7. 方法(不超过 5 个,多了就该拆组件)
function handleConfirm() {
emit('confirm')
}
</script>
注释里的序号暗示了代码组织顺序,AI 会严格遵循。实测中,加上这个模板之后,AI 生成的组件结构一致性从"大约对"提升到了"几乎完全一致",review 时再也不用纠结代码顺序问题了。
多模型切换:不同场景用不同的"大脑"
Cursor 支持在对话中切换模型,但大多数人只会无脑选 claude-sonnet-4-6 一条路走到底。这就像拿牛刀杀鸡——不是不行,是浪费。
模型选择的本质是成本-质量权衡
不同任务对模型能力的要求差异很大:
- 简单代码补全用 cursor-small 或 GPT-4o-mini,快且便宜,够用
- 组件骨架生成用 Claude Sonnet 4.6,性价比最优,代码质量稳定
- 复杂架构设计(比如设计一个通用表单校验引擎)切到 Claude Opus 4.6,推理能力强,能处理多步约束
- Bug 排查用 Claude Sonnet 4.6,上下文理解能力好
- 正则和类型体操用 Claude Opus 4.6,逻辑推理类任务 Opus 明显更强
- 文档和注释生成用 GPT-4o,自然语言流畅度略好
- 代码审查用 Claude Opus 4.6,能发现深层逻辑问题
这不是拍脑袋的结论,下面用三个实际场景对比说明。场景一:表单校验引擎设计(复杂架构任务)
我们需要一个支持异步校验、联动规则和动态增减字段的表单校验引擎。同一份需求描述分别发给 Sonnet 和 Opus:
- Sonnet 的输出:给了一个基于
reactive的基础方案,校验逻辑写在一个大函数里,异步校验用Promise.all一把梭,没考虑联动字段之间的依赖顺序。经过三轮修正(补充依赖拓扑排序、拆分同步/异步校验管线、增加 debounce 策略)才落地。 - Opus 的输出:第一轮就给出了分层架构——
FieldValidator(单字段)→FormValidator(表单级编排)→ValidationPipeline(同步/异步管线分离),主动考虑了字段依赖的拓扑排序和 debounce,基本可以直接用。
**场景二:Vue 组件日常开发(常规任务)**写一个带搜索、分页和批量操作的用户列表组件。这类需求模式相对固定:
- Sonnet:稳定输出符合项目规范的
<script setup>代码,composable 拆分合理,一轮到位。 - Opus:质量略好但差距不大,响应速度明显慢,token 消耗约为 Sonnet 的 3 倍。日常组件开发用 Opus 属于性价比浪费。
场景三:TypeScript 复杂类型推导(逻辑推理任务)
需要写一个从路由配置对象递归提取所有路径参数的工具类型,类似 ExtractRouteParams<typeof routeConfig>:
- Sonnet:给出的类型在简单场景能用,但遇到嵌套路由的递归推导会丢失部分参数,需要手动补
infer分支。 - Opus:一次性处理了嵌套递归、可选参数、通配符三种情况,类型测试全部通过。
总结下来,日常开发 Sonnet 足够,涉及多步推理或复杂类型的任务再切 Opus。
在规则中针对不同模型做差异化配置
不同模型的"个性"不同。GPT 系列有时候过度解释,Claude 系列偏简洁但偶尔会自作主张重构你没让它动的代码。可以在规则中针对性地约束这些行为。针对 Claude 系列(Sonnet / Opus)的约束:
## Claude 行为约束
- 只修改我明确提到的文件,不要"顺手"重构其他代码
- 生成代码后,列出你做了哪些假设(比如假设某个接口返回什么格式)
- 不要在回复中解释基础概念,我知道什么是 ref 和 reactive
- 如果我的需求有歧义,先问清楚再写代码,不要猜
针对 GPT 系列(GPT-4o / GPT-4o-mini)的约束:
## GPT 行为约束
- 回复不要包含大段解释性文字,直接给代码
- 不要重复我说过的需求背景,直接进入实现
- 如果你不确定某个库的最新 API,直接说不确定,不要编造
- 代码注释用中文,但变量名和函数名用英文
两组约束的侧重点不同:Claude 的核心问题是"自作主张改多了",所以重点约束修改范围和假设透明度;GPT 的核心问题是"话太多",所以重点约束输出格式和冗余解释。实际使用中,这种差异化配置比一套通用规则效果更好——Claude 不再偷偷重构旁边的文件,GPT 的回复长度缩短了将近一半。
边界与风险:规则写多了反而变蠢
Token 预算是硬约束
每个模型的上下文窗口是固定的。你的规则文件吃掉多少 token,实际可用的对话空间就少多少。做过一次粗略测量:在一个规则文件总计约 3000 字的项目中,用 Cursor 内置的 token 计数器(对话框右下角的 token 用量显示)观察,Chat 模式下有效对话轮数从大约 25 轮降到了 18 轮左右(这里的"轮"指对话框出现上下文截断提示前的完整问答次数,手动计数)。换算成 token 更直观:规则文件约占 2000-3000 token,以 Claude Sonnet 的约 200k token 上下文为例,系统提示词(Cursor 内置)大约占 2000 token,你的规则文件占 1500 到 4000 token,当前文件内容占 500 到 5000 token,剩下的才是对话历史的空间。
所以规则不是越多越好。经验法则是:alwaysApply: true 的规则文件总计不超过 800 字,单个文件级规则不超过 500 字,规则目录超过 10 个文件就该审视有没有冗余。
规则"过拟合"问题
写过机器学习的人对"过拟合"不会陌生。Cursor Rules 也有这个问题——规则写得太细太死,AI 的灵活性就没了。
比如你规定"所有按钮必须使用 BaseButton,type prop 只能是 primary / secondary / danger,按钮文字不超过 4 个汉字,间距必须 12px,禁止原生 <button> 标签"。当你需要一个全屏引导页的"开始使用"按钮时怎么办?AI 会被这些规则束缚住,生成出来的代码不伦不类。
更好的写法是"常规交互使用 BaseButton 组件,特殊场景可直接使用原生元素,设计规范参考 src/styles/design-tokens.ts"。给 AI 留呼吸的空间,但告诉它去哪里找参考——这比写死每一个细节有效得多。
规则与 ESLint/Prettier 的关系
代码风格该在 Cursor Rules 里约束,还是交给 ESLint/Prettier?原则很简单:让工具管工具能管的事。Cursor Rules 应该管那些 lint 工具管不了的事:组件拆分粒度、目录结构约定、业务逻辑分层、命名语义(不只是格式,而是"这个变量该叫什么才能表达业务含义")、何时抽 composable、API 层怎么封装。这些是架构层面的约定,lint 工具鞭长莫及,正好是 AI 规则发挥作用的地方。