Cursor 生成重复逻辑

AI 又写了一个 formatDate,但 src/utils 里早就有——已有 helper 在检索视野外,让它发现得了才能复用。

你让 Composer 写一段格式化日期的逻辑,它给了你一个 function formatDate(d) {...}。打开 src/utils/date.ts,里面早就有一份成熟的 formatDate 实现,连 timezone / locale 都处理了。问题不是模型偷懒,而是它的嵌入检索没把已有 helper 拉进上下文——它”诚实”地认为这个仓库没有,于是从头写。

修这类问题要从两侧入手:让模型主动查 + 让已有 helper 更容易被查到。

常见原因

1. 已有 helper 不在 retrieval 上下文里

仓库 1 万文件,Composer 检索只取 top-K 块。src/utils/date.ts 没匹配上当前 prompt 的关键词,根本没进 prompt。

如何判断:Composer 答完后追问 “List every file you read”;如果 src/utils/date.ts 不在,就是没看到。

2. helper 放在不明显的位置

packages/feature-a/internal/helpers/date.ts 这种深层路径相似度排名低,而且文件名 date.ts 太常见。

如何判断:你自己用 rg "function formatDate" grep 一下,看找到几处、分别在哪。

3. 命名风格和模型默认起名不一致

仓库里叫 toLocaleDateStringrenderDatedisplayDate,模型偏向起 formatDate。检索做 semantic search 多少能命中,但 ranking 仍偏低。

如何判断:把仓库现有命名和模型给的新命名列出来对比,是不是风格不同。

4. 没有中心化 utility 索引

没有 src/utils/README.md、没有 .cursorrules 列 helper 清单。模型每次都得从 0 探索。

如何判断ls src/utils/ 看有没有 README.md / index.ts 这种 entry point。

5. monorepo 多包重复造的轮子已经存在

不仅模型造重复,仓库本身就有多份。packages/web 一个 formatDatepackages/admin 又一个,模型只看到一个、又造了第三个。

如何判断rg "function formatDate" --type ts,看一个仓库里出现几次。

6. Agent 没主动 grep 就开始写

agent 模式有 grep_search 工具,但模型不一定调用。直接写代码而不先搜。

如何判断:Composer 这条消息的 tool call 链里没有 grep_search / read_file 步骤。

动手前先确认

  • 确认问题是在 Composer / Cmd+K / chat 哪个入口;Cmd+K 不走全仓搜,必然重造。
  • 复现前先 commit,避免后续合并 helper 时丢追踪。
  • 记下 Cursor 版本和当前模型;不同模型主动 grep 的倾向不同(opus 比 sonnet 更愿意先搜)。

需要收集的信息

  • Composer 给的新 helper 函数全文。
  • 仓库里已有相似 helper 的路径 + 实现。
  • Composer 这条消息的 tool call 链截图。
  • .cursorrules 全文(看有没有”复用优先”条款)。
  • src/utils/ 或对应 utility 目录结构。

最短修复路径

按”立刻修这次 → 系统改善发现性”排序。

Step 1:写之前让模型主动搜

新 prompt 模板:

Before writing new code:
1. Use grep_search to find existing functions related to "date formatting" / "format date" / "render date".
2. List what you found with paths.
3. If a suitable one exists, use it and explain why.
4. Only if nothing suitable exists, write new — and propose a location.

Step 2:写 utility README + index.ts 当目录

src/utils/README.md

# Utilities

| Function | File | Purpose |
|---|---|---|
| formatDate | date.ts | Locale-aware date formatting |
| formatCurrency | money.ts | Currency formatting with i18n |
| parseInvoice | invoice.ts | Parse invoice payloads |
| isValidEmail | validation.ts | RFC-5322 email check |

然后所有相关 prompt 在 Composer 里 @src/utils/README.md 一下,模型会直接看到清单。

Step 3:.cursorrules 加复用规则

# .cursorrules
- Before writing a new utility function, search src/utils/ and packages/shared/ for existing implementations.
- Common helpers live in:
  - src/utils/date.ts (date / time formatting)
  - src/utils/money.ts (currency)
  - src/utils/validation.ts (input validation)
- If you must create a new helper, place it in src/utils/ and update src/utils/README.md.
- DO NOT inline helpers inside component files; extract to src/utils/.

Step 4:发现 diff 里有重复立即回复

You wrote a new `formatDate` function, but src/utils/date.ts already has one with timezone handling.
Replace your implementation with `import { formatDate } from "@/utils/date"`.
Update any other duplicated helpers similarly.

Step 5:仓库级去重扫描

# 找命名相似的函数
rg "^(export )?(async )?function (\w+)" --type ts -o -r '$3' | sort | uniq -c | sort -rn | head -30

# 找 formatDate 类同名重复
rg "function formatDate" --type ts

把发现的重复合并成单一源,update 调用点。这是给以后的 AI 提供单一锚点。

Step 6:给重要 helper 起独特的名字

formatDate 太通用。改成 formatInvoiceDate / formatRelativeDate / formatLocaleDate,retrieval 命中精度上去,模型也不会随便重写一个同名的。

怎么确认已经修好

  • 同一个 prompt 重跑,看模型先 grep 再说话,而不是直接写代码。
  • diff 里新增 helper 数量明显减少。
  • 让队友用同一 prompt 跑,确认 rules 在团队配置里生效。

如果还是没修好

  • 把 prompt 缩到只问”是否有 X helper”,看模型搜不搜得到。
  • 回滚 .cursorrules 最近改动,逐条确认哪条规则在起作用。
  • 在 forum.cursor.com 搜 “duplicate helper composer”;附 prompt + tool calls 截图。
  • 抓 View → Output → Cursor 的 agent 日志贴 Bug Reports。

预防建议

  • 每个包一个 utils/ 目录 + README index,给模型显式入口。
  • .cursorrules 写”复用优先”原则 + 主要 helper 路径清单。
  • 重要 helper 命名要有领域信号(formatInvoiceDateformatDate 好检索)。
  • PR review 时把”是否复用已有 helper”列入 checklist,发现重复要求合并。
  • 季度做一次 utility 去重扫描,把多份相似实现合并,更新 README。

相关阅读

标签: #排查 #Cursor #排查 #重复逻辑