前言
在使用 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
   }- 
可能替换实现的模块 (如存储、网络库) swiftprotocol StorageService { func save(_ data: Data, key: String) } // 可以替换为 UserDefaults / Keychain / CoreData
❌ 无需使用协议的场景:
- 纯 UI 组件(UIView 子类)
- 只在一处使用的工具类
- 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 + DataSource3. 添加自动检查清单(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 项目中验证有效。