Cursor Rules 工程化:从单文件 alwaysApply 到 .cursor/rules 模块化与 glob 触发
很多团队的 Cursor Rules 成长史像这样:第一周写了十行「别提交密钥」;第三周变成一份包罗万象的RULES.md;再往后,每一次提问都在付「全家桶前缀税」——支付域规范出现在改按钮文案的会话里,发版清单出现在修单测的会话里。你不是没有规范,你是把规范做成了无法关闭的广播。
本文只做一件事:把 Rules 从「一篇散文」变成「可工程化的模块」,用alwaysApply/globs/ 手动@三档触发,让相关规则出现,无关规则闭嘴。与「省 Token」文互补:那边讲杠杆全景,这边把 Rules 目录与触发策略落到可复制模板。适合已经会用 Agent、但感觉「规则越写越烦」的个人与小团队。
摘要
- Always 只留铁律(安全、包管理器、语言、验证约定),目标一屏读完。
- 域规范用 glob:路径命中才注入,避免过大通配变相 Always。
- 厚清单改手动
@:发版、迁移、罕见流程不进常驻前缀。 - 迁移按五步:盘点 → 抽 core → 建域规则 → 降级厚文档 → 用无关/相关问题验触发。
- 验收看行为:无关目录提问不应刷出域规范;相关目录应自动带上关键约束。
结论:Rules 工程化的核心不是「写更多」,而是「触发要对」。写得漂亮但永远广播,仍然是失败的工程。
结论卡
| 触发 | 适用 | 反模式 |
|---|---|---|
alwaysApply: true | 跨任务铁律 | 架构 Wiki、长教程 |
globs | 某路径域规范 | 过大通配覆盖半仓 |
手动@ | 低频厚清单 | 把手册设为 Always |
| 症状 | 可能原因 | 先做什么 |
|---|---|---|
| 额度涨、回答爱说教 | Always 过长 | 砍到一屏 |
| 改前端却提支付规范 | glob 过大或误 Always | 收紧路径 |
| 发版总漏项 | 清单不在场 | 手动@清单 |
| 两套说法打架 | 规则重叠 | 合并或写优先级 |
背景与边界
Cursor 支持项目级规则(常见为.cursor/rules下的.mdc等,带 YAML front matter)。具体字段名、UI 入口随客户端版本可能微调,以你当前 Cursor 设置为准。本文不承诺「省 Token 百分之多少」;不展开 MCP 深度配置;不讲完整团队公约。字段若在你的版本叫法不同,抓住三原则即可:常驻极短、路径触发、厚文档按需。
边界:规则再好也替代不了人工审 diff;冲突规则需要优先级与示例消歧;开源模板仓更要避免把作者私人偏好写成铁律。
原理:三层触发
可以把规则想成三层上下文:
- 常驻层(Always):每轮都付费,必须极短、极硬。
- 条件层(Glob):打开/编辑匹配路径时才值得注入。
- 按需层(Manual):人知道「现在在做发版」,主动
@。
单文件 Always 的问题,是把 2、3 层全挤进 1 层。结果是:模型在错误场合「正确执行了错误规范」,你还以为是模型固执。
再补一条直觉:规则的召回率与精确率需要折中。Always 召回最高、精确率往往最低;手动@相反。Glob 是中间态,也是工程化的主战场。
步骤:从零搭模块化 Rules
步骤 1:建立目录
推荐结构:
.cursor/ rules/ 00-core.mdc 10-typescript.mdc 20-payments.mdc 30-testing.mdc 90-release.mdc .cursorignore编号前缀方便排序与评审:00永远最先看;90留给低频清单。不必迷信编号,但要有稳定约定。
步骤 2:写极简 Always(可复制)
<!-- .cursor/rules/00-core.mdc --> --- description: 全仓库铁律(尽量短) alwaysApply: true --- - 禁止把密钥、Token、私钥写入代码或提交 - 默认使用仓库已有包管理器,不擅自更换 - 改动后给出可复制的本地验证命令 - 回答使用中文;代码标识符保持仓库原语言 - 不确定时先只读调研,再提出最小改动方案自检:把文件贴进编辑器预览,是否仍「一屏内」?超过就继续砍。删形容词,留可执行句;删「尽量优雅」,留「如何验证」。
步骤 3:域规则用 glob
<!-- .cursor/rules/20-payments.mdc --> --- description: 支付域改动规范 globs: - src/payments/** - src/billing/** alwaysApply: false --- - 金额使用整数分,禁止浮点直接运算 - 写库变更必须带对应测试路径 - 禁止在未确认时改动汇率/舍入策略 - 对外回调处理要说明幂等策略<!-- .cursor/rules/10-typescript.mdc --> --- description: TypeScript/前端约定 globs: - "**/*.{ts,tsx}" alwaysApply: false --- - 优先复用现有组件与 hooks,避免平行实现 - 禁止 any 作为长期类型;必要时写明 TODO 与范围 - 用户可见文案走现有 i18n 方案(若仓库已有)<!-- .cursor/rules/30-testing.mdc --> --- description: 测试文件约定 globs: - "**/*.{test,spec}.{ts,tsx,js,jsx,py}" - "**/tests/**" alwaysApply: false --- - 先保证失败用例能复现,再改实现 - 不删除断言来「变绿」;若测试过时,说明理由并改断言 - 给出运行该文件的准确命令注意:globs写得像src/**往往过大。宁可多几个域文件,也不要用一张巨网。Monorepo 里按apps/web/**、packages/db/**拆,比按「全仓前端」更稳。
步骤 4:厚清单改为手动
<!-- .cursor/rules/90-release.mdc --> --- description: 发版前检查清单(手动 @) alwaysApply: false --- - 变更日志是否更新 - 迁移脚本是否可回滚 - 关键路径手工验收项是否列出 - 是否避免在发版窗口合并实验开关默认值变更使用时:
@90-release.mdc 请对照清单检查当前 PR 还缺什么,只输出缺口列表,不要改代码。发版、迁移、事故复盘手册,都适合这一档:你需要时它在,不需要时它不烧前缀。
步骤 5:配合.cursorignore
挡掉 Agent 爱误读的巨型产物(按仓库调整):
dist/ build/ coverage/ .next/ out/ *.min.js *.mapIgnore 不是 Rules,但同属「上下文治理」。Rules 决定「说什么规范」,Ignore 决定「别读哪些噪音文件」。
迁移五步(已有单文件时)
- 盘点:把现有条款标成「永远 / 路径 / 偶尔」。用三种颜色或表格即可,不必上工具。
- 抽 core:永远类进
00-core.mdc,删修辞,留可执行句。安全条款优先保留。 - 建域文件:路径类按目录拆;一条规则只服务一个域。重复句子删掉,引用 core。
- 降级厚文档:偶尔类去掉 Always,改手动
@。若文档极长,可放到docs/再@。 - 验证触发:
- 在无关目录问:「这个函数做什么?」——不应出现支付域细则;
- 在
src/payments改代码——应自动带上支付约束; - 发版前手动
@90-release.mdc——应产出缺口列表。
迁移可以分 PR:先砍 Always,再拆域,最后处理清单。一次大爆炸容易让同事卸载全部规则。
场景表
| 场景 | 建议 |
|---|---|
| 新同学第一次用 Cursor | 先只加00-core.mdc |
| 前端+后端同仓 | 按apps/*/packages/*拆 glob |
| 合规要求多 | 合规短条款可 Always;细则进域文件 |
| 开源模板仓 | core + 1~2 个示例域规则,避免私人 Wiki |
| 热修窗口 | 可临时@热修约定,但安全条款不降级 |
| 规则评审 | 把.cursor/rules当代码审:看触发,不看文采 |
代码:冲突时的最小消歧写法
当两份规则可能打架,在更高优先级(或 core)写清:
- 若本规则与域规则冲突:以更具体路径的域规则为准;安全与密钥条款永远优先 - 示例:支付域要求「改动必带测试」;临时热修可在 PR 描述声明补测计划,但仍禁止提交密钥更好的做法是减少重叠:域规则不要重复抄一遍 core。出现「两份都写了包管理器」时,删掉域里的那句。
若团队常吵「风格问题」,把风格从 Always 移出,改成格式化工具 + 短域规则;让 Agent 遵守工具,而不是遵守散文。
与提问习惯的配合
模块化 Rules 不是终点。没有@的「帮我看看整个项目」,仍会诱发无效搜索。建议固定句式:
@src/payments/checkout.ts @src/payments/money.ts 只改舍入相关逻辑;不要动汇率表;改完运行:pytest -q src/paymentsRules 提供「规范」,@提供「工作集」。两者缺一,都会回到「又贵又吵」。
陷阱清单
- Always 变 Wiki:架构决策全文进 Always——改成 ADR 文件,需要时
@。 - glob 过大:半仓通配等于伪装 Always。
- 规则互相打架:同一主题两套说法;合并或写优先级。
- 无验收标准:「要写测试」却不说路径与命令——改成「必须包含某测试路径且给出命令」。
- 迁完不验:用一次无关提问 + 一次域内改动验证触发。
- 把密钥写进规则示例:示例只用占位符。
- 为了工程化而工程化:三个文件都空话,不如一个诚实的短 core。
- 忽略同事卸载信号:若人人本地关掉 Rules,说明 Always 太烦或太不相关。
验证清单(今晚可做)
- 存在
.cursor/rules/00-core.mdc且alwaysApply: true - Always 一屏内;无长教程
- 至少一份域规则带明确
globs - 至少一份清单类
alwaysApply: false,需手动@ - 无关目录提问不出现域细则
- 相关目录改动能看到域约束生效(或确认已加载)
.cursorignore挡住常见产物目录- 示例中无真实密钥
失败案例:三种「看起来有 Rules」的假工程化
案例 A:单文件改名搬家
把RULES.md原样挪到.cursor/rules/00-all.mdc并设alwaysApply: true。目录对了,触发策略没变。账单与说教症状依旧。
修复:按「永远 / 路径 / 偶尔」重新剪裁,而不是只改路径。
案例 B:glob 写成「全仓前端」
globs: ["**/*.{ts,tsx}"]在巨型前端仓里几乎恒真,等于 Always。支付、管理后台、营销页共享同一套「组件规范」,Agent 在改营销文案时仍大谈设计系统。
修复:拆成apps/web/**、apps/admin/**、packages/ui/**多份规则;公共部分抽短,差异部分分开。
案例 C:清单 Always,核心反而很虚
发版清单进了 Always,安全铁律却写得含糊。结果每轮都在听「别忘了写 changelog」,密钥示例却混进某次「方便演示」的提交。
修复:优先级倒过来——安全短句进 Always;清单降级手动@。
团队落地:一周节奏建议
| 日 | 动作 |
|---|---|
| Day 1 | 盘点现有规则,标三类;冻结新增长文 Always |
| Day 2 | 提交00-core.mdc短版本,开 PR 让同事挑刺 |
| Day 3 | 拆两个最高频域规则(例如 web 与 api) |
| Day 4 | 发版/迁移清单改手动;更新 README 用法 |
| Day 5 | 做触发验收:无关提问 + 域内改动各两次 |
| Day 6~7 | 收集「想关掉 Rules」的反馈,继续砍 |
不要追求第一周完美。先让 Always 可忍受,再追求域规则覆盖率。
与省 Token、模式选择的关系
- 省 Token:Rules 分层是前缀税的第一刀;本文提供刀法细节。
- Ask / Agent / Manual:规则再好,模式错配仍会贵。只问用 Ask;要改再用 Agent;高风险收尾用 Manual。
- MCP:工具描述也会进税;Rules 工程化后,下一步通常是砍备而不用的 MCP,而不是继续加 Always。
把三件事写成墙上口号也行:短 Always、准 glob、对模式。
附录:可复制的提问与评审清单
规则写完后,用同一组问题做回归,避免「改了结构却没改行为」。
触发回归提问(复制即用):
1) 在 docs/ 或无关目录打开文件后提问:这个文件在项目中的职责是什么? 期望:回答不出现支付/发版等域细则。 2) 在域目录改一小处后提问:请按仓库规范提出最小改动方案,先不要改。 期望:出现该域关键约束(例如测试路径、整数分等)。 3) @90-release.mdc 对照当前改动列缺口。 期望:只列清单项,不借机大重构。PR 评审看这四行:
- Always 是否仍一屏?
- 新增规则的触发是 Always / glob / 手动哪一种?理由?
- 是否与已有规则重复或冲突?
- 示例里是否出现疑似密钥?
把这四行贴进 PR 模板,Rules 就会开始像代码一样被审,而不是像心情随笔一样被追加。
一句话带走
Rules 工程化 = 触发工程化。永远为真的才 Always;跟路径走的用 glob;偶尔才用的手动@。先让无关规则闭嘴,再谈把有用规则写得更漂亮。当你能用一次「无关提问」证明域规则不会冒出来,模块化才算落地,而不是只换了文件夹名字。
草稿未发布 · 作者 梧桐秋海 · 活动:九月创作之星、工具实践