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 项目中验证有效。

相关推荐
飞哥数智坊10 小时前
今年我试了十几款 AI 编程工具,最终只留下这 3 个
ai编程·cursor·trae
天天扭码10 小时前
如何实现流式输出?一篇文章手把手教你!
前端·aigc·ai编程
猫头虎11 小时前
又又又双叒叕一款AI IDE发布,国内第五款国产AI IDE Qoder来了
ide·人工智能·langchain·prompt·aigc·intellij-idea·ai编程
资深程序员 哈克(21年开发经验)11 小时前
2025 年 AI编程软件 IDE 的深入对比与推荐排行:从好用到生成效果的转变
人工智能·ai编程
咖啡续命又一天11 小时前
Trae CN IDE 中 Python 开发的具体流程和配置总结
开发语言·ide·python·ai编程
谎言西西里12 小时前
用 Trae + MCP 让 AI 自动测试页面
ai编程
雪球工程师团队13 小时前
别再“苦力”写后台,Spec Coding “跑” 起来
前端·ai编程
袋鱼不重14 小时前
AI入门知识点:什么是 AIGC、多模态、RAG、Function Call、Agent、MCP?
前端·aigc·ai编程
诸神缄默不语14 小时前
AI编程:Trae CN用户规则和项目规则定义分享
ai编程·trae cn
我家领养了个白胖胖16 小时前
SSE在Spring ai alibaba中同时使用Qwen和DeepSeek模型
java·后端·ai编程