Cursor Rules 编写实践:从抽象原则到可执行指令

前言

在使用 Cursor AI 编程助手的过程中,我发现规则(Rules)的编写质量直接影响 AI 的代码质量。本文将分享我从"抽象原则"到"可执行指令"的规则优化历程,希望能帮助你写出更高效的 AI 规则。

一、问题:我的第一版规则为什么失败了?

1.1 失败案例:设计原则规则

这是我最初编写的项目规范规则:

markdown 复制代码
---
alwaysApply: true
---

# 项目约定

## 设计原则

> **应用场景**:在添加、修改、重构代码时必须遵循以下设计原则

### 单一职责原则(SRP)
一个类应该只有一个引起它变化的原因

### 开闭原则(OCP)
对扩展开放,对修改关闭

### 里氏替换原则(LSP)
子类可以替换父类

### 依赖倒置原则(DIP)
依赖抽象而不是具体实现

### 接口隔离原则(ISP)
使用多个专门的接口,而不是单一的总接口

### 迪米特法则(LoD)
只与直接的朋友通信

1.2 实际遇到的问题

问题1:AI 理解不一致

  • 让 AI "添加用户登录功能",有时创建 100 行的单一类,有时创建 10 个微服务风格的类
  • 原因:"一个引起它变化的原因"过于抽象,AI 无法量化判断

问题2:过度设计

  • 简单的数据展示页面,AI 也会创建 Repository、UseCase、ViewModel 三层架构
  • 原因:没有说明"何时应用",AI 在所有场景都应用原则

问题3:无法自我纠正

  • AI 生成的代码违反了规则,但它自己检测不出来
  • 原因:规则不可验证,没有明确的检查标准

二、转折:一个成功的规则示例

在痛苦的试错后,我重写了 Serena MCP 工具的使用规则,效果立竿见影:

markdown 复制代码
---
alwaysApply: true
---

# Serena 工作流程指南

## 🎯 执行条件(快速判断)

### 立即跳过 Serena(无需思考)
以下情况**直接回答**,不启动 Serena 工作流:
- ❌ 纯概念问题("什么是..."、"解释..."、"XXX是什么意思")
- ❌ 工具使用咨询("怎么用..."、"如何优化规则")
- ❌ 通用技术讨论(不涉及本项目代码)

### 必须启动 Serena(需要操作代码)
- ✅ 使用明确触发词:`@serena`、`@项目`、`@代码`
- ✅ 包含操作动词 + 具体文件/类名:
  - "修改 XXViewController 的..."
  - "添加/删除/重构 XXX"
  - "查看/分析 XXX.swift"
- ✅ 技术问题排查:
  - "XXX 报错/崩溃/不工作"
  - "为什么 XXX 没有生效"

**快速判断口诀**:看到项目文件名或要动手改代码 → 启动 Serena,否则 → 直接回答

## ⚡ 强制步骤(项目任务必须执行)

### 步骤 1:初始化 Serena
1. 检测可用的 Serena MCP 服务器(通过工具名称前缀 mcp_serena-* 识别)
2. 调用 initial_instructions
3. 调用 check_onboarding_performed
4. 调用 list_memories
5. 调用 read_memory("project_overview")
6. 在回复开头输出:"✅ 已加载项目记忆:project_overview.md"

### 步骤 2:按需加载额外记忆
- 涉及第三方库/技术栈 → 读取 `tech_stack`
- 涉及代码实现模式 → 读取 `common_patterns`

### 步骤 3:使用 Serena MCP 工具探索代码

**🚨 严格禁止直接 read_file 读取完整代码文件!**

必须按以下顺序使用工具:

#### 探索阶段(了解代码结构)
1. **优先使用** `get_symbols_overview` 获取文件符号概览
2. **然后使用** `find_symbol` 定位具体符号(设置 `include_body=false, depth=1` 查看子符号)
3. **最后使用** `find_symbol` 读取必要的符号体(设置 `include_body=true`)

#### 编辑阶段(修改代码)
**✅ 优先使用标准编辑工具(显示 diff 预览)**

1. **首选**:`search_replace` - 大部分日常修改
   - 显示 diff 预览界面
   - 提供 undo/keep/edit 选项
   - 适用:修改方法内容、调整逻辑、更新参数等

2. **其次**:Serena MCP 符号化工具 - 大规模重构
   - `replace_symbol_body`:替换整个方法/类
   - `insert_after_symbol` / `insert_before_symbol`:插入新方法/类
   - `rename_symbol`:跨文件重命名符号
   - `find_referencing_symbols`:查找符号引用
   - 适用:重构、添加新符号、重命名操作

## 🔧 快速参考

| 任务 | 工具选择 |
| --- | --- |
| 探索代码结构 | `get_symbols_overview` → `find_symbol` |
| 日常修改 | `search_replace` ✅ |
| 大规模重构 | `replace_symbol_body` / `insert_after_symbol` |
| 跨文件重命名 | `rename_symbol` |

2.1 为什么这个规则有效?

对比两个规则的关键差异:

维度 失败的规则(设计原则) 成功的规则(Serena工作流)
触发条件 "在添加、修改、重构时" 太宽泛,所有场景都触发 "看到项目文件名或要动手改代码" 清晰的判断标准
执行路径 "遵循单一职责原则" AI 不知道具体怎么做 "步骤1→步骤2→步骤3" 明确的操作序列
禁止行为 无,AI 可能用错误方式实现 "🚨 严格禁止直接 read_file" 避免误操作
决策辅助 无,AI 每次都要思考选择 决策表,直接查表匹配场景
可验证性 无法检查是否遵守 "在回复开头输出:✅ 已加载..." 用户可见的验证点

三、核心经验:编写高质量规则的 6 个原则

原则 1:用决策树代替抽象原则

反面案例:

markdown 复制代码
### 单一职责原则
一个类应该只有一个引起它变化的原因

正面案例:

markdown 复制代码
### 新增 ViewController 时的职责划分

**判断标准**:ViewController 是否超过 200 行

#### 小于 200 行 → 允许包含:
- ✅ UI 布局代码
- ✅ 简单的数据转换(model → UI text)
- ✅ 按钮点击事件处理

#### 超过 200 行 → 必须拆分:
1. 创建 ViewModel 处理业务逻辑
2. 创建 DataSource 处理列表数据
3. ViewController 只保留 UI 交互代码

**示例:**
```swift
// ❌ 错误:300 行的 ViewController,包含网络请求和数据处理
class ProductListViewController: UIViewController {
    func loadProducts() {
        // 50 行网络请求代码
        // 100 行数据处理代码
        // 150 行 UI 更新代码
    }
}

// ✅ 正确:拆分职责
class ProductListViewController: UIViewController {  // 80 行
    let viewModel: ProductViewModel
    func loadProducts() {
        viewModel.fetchProducts()  // 业务逻辑委托给 ViewModel
    }
}

class ProductViewModel {  // 100 行
    func fetchProducts() { /* 网络 + 数据处理 */ }
}

原则 2:分层次(禁止 > 强制 > 推荐)

我的分层标准:

markdown 复制代码
## 网络请求规范

### 🚫 禁止(违反会导致编译错误或崩溃)
- 禁止在主线程调用同步网络请求
- 禁止在 ViewController 中直接使用 `URLSession`
- 禁止硬编码 API URL(必须使用 APIConfig)

### ⚠️ 强制(必须遵守,否则代码审查不通过)
- 所有网络请求必须通过 `NetworkManager` 统一处理
- 必须处理网络错误和超时(最少3种错误类型)
- 必须在请求前显示 loading,结束后隐藏

### 💡 推荐(最佳实践,但不强制)
- 优先使用 `async/await` 而非闭包回调
- 考虑实现请求缓存(对列表类接口)
- 为常用接口编写单元测试

为什么分层?

  • AI 会优先检查"禁止"规则,避免严重错误
  • "强制"规则保证代码一致性
  • "推荐"规则仅在时间充裕时应用,不会过度设计

原则 3:加触发条件,避免过度应用

反面案例:

markdown 复制代码
依赖抽象而不是具体实现

→ AI 会在所有地方创建协议,包括只用一次的简单类

正面案例:

markdown 复制代码
## 依赖注入规范

### 何时使用协议抽象?

#### ✅ 必须使用协议的场景:
1. **需要单元测试的类**(如 ViewModel、Manager)
   ```swift
   protocol NetworkService {
       func request<T>(_ endpoint: String) async throws -> T
   }
   
   class LoginViewModel {
       let network: NetworkService  // 便于测试时注入 MockNetworkService
   }
  1. 可能替换实现的模块 (如存储、网络库)

    swift 复制代码
    protocol StorageService {
        func save(_ data: Data, key: String)
    }
    // 可以替换为 UserDefaults / Keychain / CoreData

❌ 无需使用协议的场景:

  1. 纯 UI 组件(UIView 子类)
  2. 只在一处使用的工具类
  3. Model 数据类

示例对比:

swift 复制代码
// ❌ 过度设计:简单的颜色扩展也创建协议
protocol ColorProvider {
    func primaryColor() -> UIColor
}
class ColorManager: ColorProvider { ... }

// ✅ 合理:直接使用 UIColor 扩展
extension UIColor {
    static let primary = UIColor(hex: "#007AFF")
}
shell 复制代码
### 原则 4:用代码说话,正反对比

我的模板结构:

```markdown
### [规则名称]

#### 📖 规则说明
[一句话描述规则]

#### ✅ 正确示例
```swift
[符合规则的代码,带注释说明为什么好]

❌ 错误示例

swift 复制代码
[违反规则的代码,带注释说明问题在哪]

💡 为什么这样做?

解释背后的原理,用1-2句话

🔧 何时应用?

具体的触发场景

javascript 复制代码
**真实案例:**

```markdown
### 避免强制解包(Force Unwrap)

#### 📖 规则说明
禁止使用 `!` 强制解包,除非有充分理由并添加注释说明

#### ❌ 错误示例
```swift
class ProfileViewController: UIViewController {
    var user: User!  // ❌ 如果忘记赋值会崩溃
    
    func updateUI() {
        nameLabel.text = user.name  // 💥 运行时崩溃风险
    }
}

✅ 正确示例

swift 复制代码
class ProfileViewController: UIViewController {
    let user: User  // ✅ 强制在初始化时提供
    
    init(user: User) {
        self.user = user
        super.init(nibName: nil, bundle: nil)
    }
}

✅ 允许的例外情况

swift 复制代码
@IBOutlet weak var tableView: UITableView!
// ✅ 允许:Storyboard 连接的 IBOutlet 保证非空

💡 为什么这样做?

强制解包会导致运行时崩溃,而编译器无法提前发现。使用可选绑定或构造器注入可以在编译期保证安全。

🔧 何时应用?

  • 新增任何可选类型的属性时
  • Code Review 时检查所有 ! 符号
ini 复制代码
### 原则 5:提供自动检查清单

在规则末尾添加检查清单,让 AI 自我验证:

```markdown
## ✅ 代码完成后的自动检查清单

AI 在完成代码编辑后,必须自动执行以下检查:

### 🔍 代码质量检查
- [ ] 新增的类是否少于 300 行?(超过需拆分)
- [ ] 是否有超过 3 层的嵌套 if/guard?(需要提前 return)
- [ ] 是否有硬编码的字符串?(应使用 `L10n.tr()`)
- [ ] 是否有硬编码的颜色/尺寸?(应使用 `Asset.Colors` / `Metric`)

### 🚫 禁止项检查
- [ ] 是否在 ViewController 中直接使用 `URLSession`?
- [ ] 是否使用了 `!` 强制解包(除 `IBOutlet`)?
- [ ] 是否在主线程执行耗时操作?

### 📝 代码风格检查
- [ ] 方法名是否遵循驼峰命名(动词开头)?
- [ ] 常量是否使用 `let` 而非 `var`?
- [ ] 是否为复杂逻辑添加了注释?

**检查结果输出格式:**

✅ 代码质量检查通过 ⚠️ 发现 2 个建议优化项:

  • LoginViewController.swift:45 - 方法超过 50 行,建议拆分
  • UserManager.swift:23 - 发现硬编码字符串 "https://api..."
复制代码

实际效果: 添加检查清单后,AI 会在代码生成后主动报告问题,我发现 bug 的时间提前了 80%。

原则 6:渐进式编写,持续迭代

我的规则演进路径:

第 1 周:只写"禁止"规则

markdown 复制代码
## 🚫 严格禁止
- 禁止在主线程执行网络请求
- 禁止使用全局变量(除 AppDelegate)
- 禁止在 ViewController 中直接操作数据库

效果:避免了 90% 的严重错误

第 2 周:添加"强制"规则

markdown 复制代码
## ⚠️ 强制要求
- 所有网络请求必须通过 NetworkManager
- 所有字符串必须使用 L10n 多语言
- 所有颜色必须使用 Asset Catalog

效果:代码风格统一,团队协作更顺畅

第 3 周:补充"推荐"规则

markdown 复制代码
## 💡 最佳实践
- 优先使用 async/await
- 考虑添加单元测试
- 为公共方法添加文档注释

效果:代码质量提升,但不会过度设计

第 4 周:优化触发条件

markdown 复制代码
## 何时使用依赖注入?
- ViewModel/Manager 类 → 必须使用
- ViewController → 仅在需要测试时使用
- UIView 子类 → 无需使用

效果:AI 不再过度设计,简单场景保持简单

四、效果对比:数据说话

优化前(抽象原则版):

  • AI 理解偏差率:~40%(10次任务中有4次需要重新解释)
  • 平均交互轮次:5-7 轮才能得到满意代码
  • 过度设计率:~60%(简单功能也创建复杂架构)

优化后(可执行指令版):

  • AI 理解偏差率:~5%(偶尔需要澄清边界情况)
  • 平均交互轮次:1-2 轮即可
  • 过度设计率:~10%(基本符合场景复杂度)

最直观的变化:

  • 优化前:"帮我添加登录功能" → AI 创建 10 个文件,包含 Repository/UseCase/ViewModel 三层架构
  • 优化后:"帮我添加登录功能" → AI 询问:"是否需要记住密码功能?是否需要第三方登录?" 然后创建 2-3 个合理的文件

五、给读者的行动建议

立即可以做的 3 件事:

1. 为最常见的错误添加"禁止"规则(30 分钟)

markdown 复制代码
## 🚫 严格禁止
- 禁止使用强制解包 `!`(除 IBOutlet)
- 禁止在主线程执行网络请求
- 禁止创建超过 500 行的文件

2. 为核心模块添加决策树(1 小时)

markdown 复制代码
## 何时使用 ViewModel?

ViewController 行数 < 150 行
  └─ 无需 ViewModel,直接在 VC 中处理

ViewController 行数 150-300 行
  └─ 创建 ViewModel 处理业务逻辑

ViewController 行数 > 300 行
  └─ 拆分为 VC + ViewModel + DataSource

3. 添加自动检查清单(15 分钟)

markdown 复制代码
## ✅ 代码完成后必须检查
- [ ] 是否有强制解包?
- [ ] 是否有硬编码字符串?
- [ ] 类是否超过 300 行?

长期优化路径:

arduino 复制代码
第 1 周:只写"禁止"规则,避免严重错误
   ↓
第 2 周:添加"强制"规则,统一代码风格
   ↓
第 3 周:补充"推荐"规则,提升代码质量
   ↓
第 4 周:优化触发条件,避免过度设计
   ↓
持续迭代:记录 AI 误解的案例,反向优化规则

六、总结:好规则的黄金标准

一条好的 AI 规则应该满足:

1. ✅ 可判断:有明确的 if-then 条件

  • ❌ "保持代码简洁"
  • ✅ "方法超过 50 行时,拆分为多个子方法"

2. ✅ 可执行:说明具体的操作步骤

  • ❌ "使用依赖注入"
  • ✅ "在 ViewModel 构造器中传入 NetworkService 协议"

3. ✅ 可验证:提供检查标准

  • ❌ "遵循单一职责"
  • ✅ "ViewController 中不应出现 import Alamofire"

4. ✅ 可分层:区分禁止/强制/推荐

  • 让 AI 知道什么绝对不能做,什么是最佳实践

5. ✅ 有示例:正反对比 + 解释原因

  • 让 AI 理解"为什么"而不是死记"做什么"

结语

从"抽象原则"到"可执行指令",我的规则文件从 20 行增长到 200 行,但 AI 的理解偏差率从 40% 降到 5%。

记住:AI 是优秀的执行者,但需要你提供清晰的指令。与其期待 AI 理解"单一职责原则",不如直接告诉它:"超过 300 行就拆分"。

最后的建议:不要试图一次写完美规则,从解决最痛的问题开始,逐步迭代。你的每一次"AI 理解错了",都是优化规则的最佳时机。


作者注:本文所有案例均来自真实项目实践,规则文件已在 iOS Swift 项目中验证有效。

相关推荐
trsoliu8 小时前
AI辅助编程:从代码生成到实际落地的思考
ai编程
AI产品自由9 小时前
爽!AI编程2年半,终于用上了喂饭级的API!
ai编程
谁用了我的idea9 小时前
如何使用大模型提升开发能力与效率(核心知识点)
ai编程
程序员鱼皮13 小时前
刚刚 Cursor2.0炸裂发布!这3大亮点必学
程序员·ai编程·cursor
磊磊落落14 小时前
MCP 是什么?它是如何工作的?
ai编程
知了一笑15 小时前
四个月,AI为主,人为辅,一款产品两个知识库!
ai编程·知识库·ai产品·独立开发者
飞哥数智坊16 小时前
看完 Cursor 2.0,我感觉国产 AI 编程又有希望了
人工智能·ai编程·cursor
不老刘1 天前
Microsoft 365 Copilot 扩展至应用和工作流构建功能
microsoft·copilot·ai编程
Sailing1 天前
🚀🚀 从前端到AI Agent开发者,只差这一篇入门指南
前端·后端·ai编程