仓库里全是 snake_case,Cursor Tab 补出 camelCase;现有组件都用 function declaration,Composer 给你 arrow function;测试都用 vitest,AI 写出 jest 语法。问题不是模型蠢,而是它的预训练 prior 见过的 “标准写法” 比你这个仓库的实际约定信号更强——retrieval 没给够反向证据时 prior 就赢。
修法是把约定从”隐含在代码里”变成”显式告诉模型”。
常见原因
1. 没 .cursorrules 或写得太泛
只有 “Follow project conventions” 这种空话,模型解析不出可执行规则。
如何判断:cat .cursorrules 看是不是 < 20 行的空泛宣言。
2. 仓库本身约定混杂
老代码 snake_case、新代码 camelCase,retrieval 抽到的样本两边都有。模型按训练 prior 偏好 camelCase 就给你 camelCase。
如何判断:
rg "function [a-z]+_[a-z]+" --type ts -c | head
rg "function [a-z]+[A-Z]" --type ts -c | head
两个数都大 = 混杂。
3. 目标模式的参考文件没进上下文
你想让新组件按 ProductCard.tsx 的风格写,但 retrieval 没把 ProductCard 拉进 prompt,模型只能凭训练记忆。
如何判断:Composer context 面板有没有你心里的标杆文件。
4. .cursorrules 过时
仓库 6 个月前从 pnpm 换 bun、从 jest 换 vitest,但 rules 没更新。
如何判断:rules 里写的工具 / 包管理器 vs package.json / lock 文件是否一致。
5. Tab 补全的上下文窗口小
Cursor Tab 是低延迟自动补全,只看光标附近上下文,不读全仓 rules。你 .cursorrules 写得再好它都看不见。
如何判断:Tab 经常出错而 Composer / chat 准 = Tab 上下文不够。
6. 模型训练 prior 比你的约定更强
约定本身就是少数派(如 interface I_FooState,匈牙利命名)。模型见过百万个 React 仓库,几乎没人这样命名,prior 占绝对优势。
如何判断:你的约定是否罕见、是否反 industry 主流。
动手前先确认
- 区分是 Tab / Composer / Cmd+K 哪种入口在违反约定——Tab 上下文有限,治理路径不同。
- 改
.cursorrules前 commit 一次。 - 记下 Cursor 版本和当前模型——某些模型遵循 rules 比另一些好。
需要收集的信息
- 一段 AI 给的代码 + 你期望它怎么写的对比。
.cursorrules/.cursor/rules/*.mdc全部内容。- 仓库一份标杆文件路径(你希望它效仿的)。
- 当前 Composer context 面板截图。
最短修复路径
按”立刻修 + 让规则更易遵循”。
Step 1:写一份具体的 .cursorrules
少用形容词,多写”应该 / 不应该”+ 例子:
# .cursorrules
- Use snake_case for function and variable names (project convention).
- Use function declarations for top-level functions, NOT arrow functions.
- Use vitest for tests, NEVER jest. Tests live next to source as *.test.ts.
- Imports order: 1) node built-ins, 2) external, 3) @/aliases, 4) relative.
- Prefer `interface` over `type` for object shapes; use `type` only for unions.
Example of correct style:
```ts
function parse_invoice(payload: InvoicePayload): Invoice \{
return \{ ... \};
\}
Example of WRONG style (do not output):
const parseInvoice = (payload) => \{ ... \};
50-150 行最合适,太长稀释自己。
### Step 2:用 @File 锚定标杆
每次 Composer 任务里 `@<canonical-file>`,让模型直接看一份"正确写法"。
Composer prompt: Implement function X following the exact style of @src/services/parseOrder.ts (naming, import order, error handling pattern).
### Step 3:发现偏移立刻 push back
Your output uses camelCase but this repo uses snake_case. Rewrite, matching the style of @src/utils/parse_date.ts exactly.
回话里持续这样纠正几次,本次 chat 里效果立竿见影。
### Step 4:迁移期混杂约定要二选一
如果仓库正在从 jest → vitest 迁移,rules 里写明:
- All NEW tests use vitest. Do NOT add new jest tests.
- When editing existing jest tests, leave them as jest (separate PR will migrate).
不要让模型自己猜在哪一边。
### Step 5:用 CI lint 兜底
`.cursorrules` 是软约束,CI lint 是硬约束。配 eslint / biome / ruff 规则把约定校验自动化,AI 漏的能被 lint 拦下。
```json
// .eslintrc.json 示例
{
"rules": {
"@typescript-eslint/naming-convention": [
"error",
{ "selector": "variableLike", "format": ["snake_case"] }
]
}
}
Step 6:罕见约定考虑改回主流
如果你的约定罕见且没强 buy-in,可以重新评估。模型遵循 industry standard 几乎免费,对抗 prior 要持续投入。能改成主流的约定,长期最划算。
怎么确认已经修好
- 用同一 prompt 重跑 Composer,确认代码风格符合 rules。
- 让队友打开同一 workspace 跑相同 prompt,结果一致。
- 跑 CI lint 全过,没有约定违规。
如果还是没修好
- 把 prompt 缩到最小:单文件 + 显式 @ 一个标杆。
- 回滚最近一次
.cursorrules改动,确认是哪一条没起效。 - 在 forum.cursor.com 搜 “ignores conventions”;附 rules 内容 + 输出 vs 期望对比。
- 抓 View → Output → Cursor 日志贴 Bug Reports。
预防建议
.cursorrulescommit 到仓库,跟约定变化同步更新,季度 audit 一次。- 每个 package 一份
.cursorrules,写本包独有规则。 - 标杆文件 pin 在 workspace tabs,让 retrieval 优先看到。
- CI 用 lint / formatter 自动化约定,不依赖 AI 自觉。
- 罕见约定考虑改成主流,省得长期对抗模型 prior。