把 Cursor 的烂输出变成好代码,这 10 个 Prompt 改造让我省了 80% 改稿时间

封面图

你有没有遇到过这种情况——

让 Cursor 给你的 Spring Boot 服务加一个接口,它给你写了个方法,但没有写测试,也没有更新 OpenAPI 文档,路由规则也跟项目里其他接口风格不一样。改了一圈,还不如自己写快。

或者让它重构一段 goroutine 代码,结果它把你的 channel 改成了 sync.WaitGroup,逻辑倒是通了,但引入了一个新的竞态条件。你盯着 diff 看了五分钟,才看出哪里不对。

我用 Cursor 写代码一年多,踩过的坑让我得出一个结论:Cursor 给出的答案质量,70% 取决于你怎么问,30% 才是模型本身的能力。不是 Cursor 不够好——是你给它的信息不够好,它只能乱猜。

下面 10 个技巧,每一个都附有原理解释和 before/after 对比。全是后端场景,没有废话。

10 个 Cursor Prompt 技巧速查
图:10 个技巧按场景分类,按需取用

技巧 1:给任务加边界,不要说「帮我改一下」

原理:「改一下」对 Cursor 来说是个开放指令,它会基于上下文推测你的意图。推测错了,给你的就是半成品。边界越清楚,推测空间越小,输出越准。

Before(坏 Prompt):

帮我改一下这个接口,让它性能好一些

After(好 Prompt):

重构 OrderService.getOrderList() 方法,目标:减少 N+1 查询。
要求:
1. 用 @EntityGraph 替换懒加载,只拉 order + orderItems
2. 不改方法签名
3. 不引入新的缓存层
4. 改完后在同文件里补充对应的单元测试

第二版明确了目标(N+1)、约束(不改签名、不加缓存),还追加了验收条件(补测试)。Cursor 不需要猜你想要什么,直接按清单执行。

这个技巧在 Go 场景下同样成立。比如让它处理 goroutine 泄漏,你需要告诉它:哪个 goroutine、在什么条件下泄漏、是否允许引入 context.WithTimeout,而不是「帮我解决 goroutine 泄漏问题」。

技巧 2:用 @ 符号引入上下文,而不是粘贴代码

原理:把代码粘到对话框会快速占满 context window,而且模型看的是你粘进来的快照,不是文件的最新状态。@filename 让 Cursor 直接读当前文件,更省 token,语义更准。

Cursor 的 @ 符号有 8 种类型,后端最常用的是这几个:

@ 类型 适用场景
@文件名 引入某个具体文件,如 @OrderService.java
@Codebase 让 Agent 全库搜索相关代码,不知道在哪就用它
@Docs 引入官方文档,防止 Cursor 用过期 API 语法
@Web 实时查网上的最新信息,比如框架 changelog
@Git 引用最近的提交,做「这次改动有什么问题」分析

Before(坏 Prompt):

[粘贴了 200 行代码]
帮我给这个类加一个缓存层

After(好 Prompt):

@OrderRepository.java @CacheConfig.java
在 OrderRepository 里给 findByUserId 加 Redis 缓存,
缓存 key 格式参考 CacheConfig 里已有的命名规范,
TTL 30 分钟,缓存穿透用空值处理

不需要粘贴代码,Cursor 会读文件当前状态。有命名规范的文件也引进来,它会自动对齐风格。

Cursor @ 符号速查:后端工程师最常用的 5 种
图:@ 符号 5 种类型及使用频率速查

技巧 3:告诉 Cursor「什么不要动」

原理:Cursor 默认是「完成任务型」,如果你不说清楚边界,它可能顺手动一些它认为可以改的地方,比如重命名变量、调整格式、改掉你故意保留的逻辑。

Before(坏 Prompt):

帮我在 PaymentService 里加一个退款方法

After(好 Prompt):

在 @PaymentService.java 里新增 refund(Long orderId, BigDecimal amount) 方法。
约束:
- 不修改现有任何方法的签名和逻辑
- 不改变类的字段
- 异常处理风格跟 charge() 方法保持一致

加了「约束」段之后,Cursor 知道这是个「只增不改」的任务。这个技巧在 gRPC stub 代码里尤其重要——你的 proto 生成代码不能被它改动,必须显式告知。

对于 Go 项目,常见的约束是:「不改 interface 定义」、「不改 error 类型,沿用项目里的 errors.Wrap 风格」、「不引入新的 package」。把这些写进 Prompt,你 review diff 的时间至少减一半。

技巧 4:用 Plan Mode,复杂任务先出计划再执行

原理:直接让 Agent 执行复杂任务,它会边想边写,容易写到一半改方向,或者漏掉你以为它会做的步骤。Plan Mode 把「规划」和「执行」拆开,你可以在执行前检查并修改计划,把不对的地方提前拦下来。

激活方式:在 Agent 输入框按 Shift + Tab 切换到 Plan Mode。

Before(直接执行):

帮我把 UserService 里的同步数据库调用改成异步的

Cursor 直接开始改,你发现它把某些需要强一致的调用也改异步了,回滚起来很麻烦。

After(Plan Mode 先出计划):

[Plan Mode] 把 @UserService.java 里的数据库调用改成异步。
需要先告诉我:
1. 哪些方法应该改异步,哪些必须保持同步(理由)
2. 用什么异步方案:CompletableFuture 还是 Spring @Async
3. 事务处理会有哪些影响

Cursor 会生成一份 Markdown 计划,列出它打算做的事。你审查后,可以直接编辑计划——删掉不该改的方法,加上你遗漏的约束,然后再点执行。这比事后 review diff 要高效得多。

计划可以保存到 .cursor/plans/ 目录,下次接着做或者交给队友都方便。

技巧 5:一次只做一件事,不要用「并且」连接两个任务

原理:Cursor 的上下文窗口是有限的。一个 Prompt 里塞两件事,它会同时处理,但注意力被分散,两件事都做得不彻底。分成两次对话,每次聚焦一件事,输出质量明显更高。

Before(坏 Prompt):

帮我把这个接口加上限流,并且把响应体改成统一的 ApiResponse 格式,
还有加一下日志

After(分三次做):

# 第一次
@OrderController.java
给 getOrderList 接口加 @RateLimiter 注解,配置每秒最多 100 次请求,
超限时抛 RateLimitException,不要动其他逻辑

# 确认没问题后第二次
@OrderController.java @ApiResponse.java
把 getOrderList 的返回值改成 ApiResponse<List<OrderDTO>>,
参考 @OrderController.java 里 createOrder 方法的返回格式

# 第三次
只给 getOrderList 加 SLF4J 日志,记录入参和执行时间,日志级别 INFO

每次改完看一下 diff,确认无误再做下一步。这比一次 commit 里三件事混在一起好 review 得多。

技巧 6:提供「好例子」,而不是描述「我想要的风格」

原理:「风格一致」这种描述太抽象,Cursor 无法量化。给它一个已有的好代码作为参照,它会对齐格式、命名惯例、注释风格,不需要猜。

Before(坏 Prompt):

帮我写一个新的 Repository 类,风格要跟项目里其他 Repository 一致

After(好 Prompt):

参照 @OrderRepository.java 的结构,帮我新建 RefundRepository.java。
要参照的点:
- @Repository 注解位置
- 方法命名规范(findBy 前缀)
- @Transactional 的使用位置
- 错误处理用 Optional 返回

新类需要的方法:findByOrderId, findByUserId, save

这个技巧在 Go 里也一样好用。给它看你项目里一个写得比较好的 handler,然后说「按这个模式写一个新的」,比你用文字描述「用 error wrapping、注意 defer 关闭资源、记得加 trace log」准多了。

技巧 7:明确指定验收标准,让 Cursor 知道「做完」是什么意思

原理:Cursor 不知道你的完成标准是什么,它给你一个能编译过的代码就会停下来。把验收标准写进 Prompt,它会主动补全测试、处理边界情况,而不是给你半成品。

Before(坏 Prompt):

帮我写一个分页查询接口

After(好 Prompt):

在 @OrderController.java 里实现分页查询接口 GET /orders。

验收标准:
1. 支持 page(从 1 开始)和 pageSize(默认 20)参数
2. pageSize 超过 100 时返回 400 错误
3. 返回 PageResponse<OrderDTO>,包含 total、pages、data 字段
4. 在 OrderControllerTest.java 里补充三个测试用例:正常分页、边界值、非法 pageSize
5. Swagger 注解完整(@Operation、@Parameter)

这种写法里有「完成标准清单」,Cursor 会逐条对照。你最后可以直接 review 这五条是否都满足,不用自己去想还缺什么。

Go 场景下,验收标准可以是:「benchmark 测试在 10000 次请求下 p99 < 5ms」、「用 go vet 和 staticcheck 都能通过」、「TestXxx 函数覆盖 happy path + 两个 error path」。

技巧 8:上下文失焦时,开新对话,用 @Past Chats 带走需要的部分

原理:一个对话里积累了太多内容后,Cursor 的注意力会分散,开始把早前讨论的方向和现在的任务混在一起,产生奇怪的输出。这不是 bug,是 context window 的物理限制。

判断标准:当你发现 Cursor 开始重复之前修过的问题,或者它的输出和你几条 Prompt 之前讨论的东西相关但和当前任务无关时,就该开新对话了。

操作方式

  1. 开一个新的 Composer 窗口
  2. @Past Chats 引用你需要的历史对话片段
  3. 在新对话里补充当前任务的完整上下文

Before(继续在失焦对话里挣扎):

不对,你刚才写的不是我要的,我上面说过要用 CompletableFuture 的

After(开新对话,重新聚焦):

@Past Chats [引用包含 CompletableFuture 决策的对话]
@OrderService.java

基于我们之前确定的异步方案(CompletableFuture),
现在只做这一件事:给 findOrdersByUserId 加上超时控制,
超时 3 秒后抛 OrderServiceTimeoutException

一个对话处理一个完整的逻辑单元,完成后另起炉灶。这个习惯养成后,你会发现 Cursor 的输出稳定性大幅提升。

技巧 9:让 Cursor 先列出假设,再写代码

原理:Cursor 遇到信息不完整的 Prompt,会选择一个它认为合理的假设然后继续。但它的假设不一定和你的预期一致。让它在写代码前先列出假设,你可以在执行前发现偏差,而不是在 review diff 时才发现。

Before(Cursor 自己假设):

帮我给用户注册加上邮箱验证

Cursor 可能假设你要发真实邮件(而不是用测试 SMTP),假设验证码有效期 15 分钟(而你想要 5 分钟),假设验证失败直接返回 400(而你的业务逻辑需要 422)。

After(先列假设):

在实现邮箱验证功能之前,先列出你的实现假设:
- 使用什么邮件发送方式
- 验证码有效期
- 已验证用户重复触发的处理
- 验证失败的 HTTP 状态码

列出后等我确认,再开始写代码

Cursor 会输出一份假设清单,你逐条确认或纠正,然后让它开始写。这一步多花 1 分钟,能省去你 10 分钟的 diff review 时间。

技巧 10:用 .mdc 规则文件固化反复输入的上下文

原理:如果你每次都要在 Prompt 里写「使用 SLF4J 不用 System.out」、「所有对外接口加 @Validated」、「返回值用 ApiResponse 包装」,这是低效的重复劳动。这类「每次对话都要遵守的约束」应该写进 .cursor/rules/ 下的 .mdc 规则文件,让 Cursor 自动加载。

注意:本篇聚焦的是每次对话里的 Prompt 写法。.mdc 规则文件的完整配置方式在「030 Cursor Rules 配置指南」里有详细说明。这里只讲如何在写 Prompt 时配合规则文件使用。

.cursor/rules/java-backend.mdc 示例片段:

---
alwaysApply: false
globs: src/main/java/**/*.java
description: Java 后端代码规范:日志、返回值、异常处理约束
---

## 代码约束
- 日志统一用 SLF4J,不用 System.out/System.err
- 所有对外 REST 接口的返回值用 ApiResponse<T> 包装
- 异常处理沿用 GlobalExceptionHandler,不在 Controller 层 try-catch
- 测试用 JUnit 5,不用 JUnit 4 注解

有了这个规则文件后,你的 Prompt 就不需要每次重复这些约束了。但如果你需要临时覆盖某条规则,在当次 Prompt 里显式说明就可以:

Prompt 里临时覆盖规则:

@OrderService.java
给 processPayment 加本地 try-catch(本次例外:不通过 GlobalExceptionHandler),
原因:这里需要回滚本地事务,不能抛给全局处理

规则文件管「永久约束」,Prompt 里管「本次例外」,分工清晰。

常见问题

Q:这 10 个技巧每次都要用吗?
不是。快速改一行代码用技巧 1 就够了。复杂的多步骤任务上技巧 4(Plan Mode)。团队协作或有强规范要求的场景上技巧 10(规则文件)。根据任务复杂度选对应的技巧,不是越多越好。

Q:给 Cursor 的 Prompt 越长越好吗?
不是。Prompt 要的是信息密度,不是字数。「不修改现有方法签名」是有效约束,「你是一个非常优秀的 Java 工程师」是无效噪音。把有效信息写进去,其余的删掉。

Q:Plan Mode 每次都比直接执行好吗?
对于改动超过 3 个文件、或者涉及架构决策的任务,Plan Mode 几乎必用。对于「在这个方法里加一行日志」这类小改动,直接让 Agent 执行反而更快。

Q:Cursor 给出的代码我不认可,但它坚持这样写怎么办?
不要在同一个对话里反复纠正。开一个新对话,用 Plan Mode 先把你的要求和约束写清楚,特别是「为什么不能用它那个方案」的理由。Cursor 在有明确反例和约束的情况下,很少会再走错误方向。

Q:Java 和 Go 项目有什么特别需要注意的 Prompt 差异?
Go 项目要特别注意:告诉 Cursor 哪些 interface 不能改(否则它会重构),error wrapping 的风格,context 传递方式。Java/Spring 项目则要注意:Bean 的作用域、事务边界(@Transactional 加在哪层)、AOP 切点是否会被它误改。这些项目特有的约束建议都写进 .mdc 规则文件。

参考资料

说白了,Cursor 不是一个「你说什么它做什么」的工具,更像一个接受书面需求的契约式协作方。你写清楚任务、约束、验收标准,它给你准确的输出;你说「帮我改一下」,它给你一个猜测。把这 10 个技巧用熟,绝大多数「Cursor 输出质量差」的问题都会消失——不是 Cursor 变好了,是你问得更好了。
beeaa00ee37c5db0e2fb2c5c5efe4f29

下一篇打算聊 Background Agent 的实战用法——什么任务适合丢给后台 Agent 跑,什么任务必须你盯着,感兴趣的关注一下。如果你身边有人还在抱怨 Cursor 总给半成品,这篇可以直接发给他。

posted @ 2026-07-07 10:54  码哥字节  阅读(151)  评论(0)    收藏  举报