前几天有位同事来找我,问了一个非常现实、也很常见的问题。

"你们用 AI 写代码,为什么生成出来的 Ja va 后端代码能直接运行?我每次跟 AI 说‘写一个用户管理的 CRUD 接口’,它返回的 Spring Boot 代码里,Controller 全是 HashMap,没有 Service 层,没有参数校验,最后还返回裸的 List。我再告诉它这里不对、那里不对,反复改了七八轮,最后产出的代码还不如我自己手写得快。真要这么折腾,我早就自己写完了。"
我看了一眼他的 Prompt,内容只有一句:"写一个用户管理的 CRUD 接口,用 Spring Boot。"
随后我让他把 Prompt 改成下面这种更完整的写法——
"项目使用 Spring Boot 3.2 + MyBatis-Plus,包结构为 com.xxx.controller/service/mapper/entity。表结构如下(附 DDL)。统一返回体为 Result(code/message/data)。Controller 仅负责路由和参数校验,所有业务逻辑都放在 Service。禁止 Controller 直接操作 Mapper。"
他重新提交了一次。AI 这次生成的代码里:Controller、Service、ServiceImpl、Mapper、Entity 一应俱全,参数校验使用了 @Valid,异常统一抛出 BusinessException,返回结果也封装成了 Result。改个包名,基本就能直接运行。
同事愣了几秒:"就这么简单?"
没错,真就这么简单。同样的需求、同样的 AI 工具,结果差异并不在于你“会不会和 AI 对话”,而在于你到底给了 AI 多少可用的上下文信息。
好的 Prompt 不是技巧,而是上下文的完整打包
很多人把 Prompt 工程理解成一套“咒语”——仿佛关键词用对了,AI 就会乖乖输出高质量代码;关键词用错了,AI 就会胡乱生成。但实际做 Ja va 后端开发时你会发现,AI 不是不听话,而是你提供的信息不足,导致它无法判断“这个项目里的代码究竟应该长什么样”。
你让它生成 Ja va 代码,它确实能产出语法正确、能够编译的 Ja va 代码。但问题在于,它并不知道你的项目到底是 Spring Boot 2.7 还是 Spring Boot 3.2,也不知道你使用的是 MyBatis-Plus 还是 JPA,更不清楚异常体系是 RuntimeException 还是 BusinessException,前端统一返回结构到底是 Result 还是 ApiResponse。
这些项目约定、编码规范、技术栈约束,如果你不写进 Prompt,AI 就只能靠猜。猜对了是运气,猜错了就意味着反复重写代码,而且通常是来回返工。
其实,一个真正实用的 AI 编程 Prompt 模板,大致可以归纳为下面六个要素:
复制代码1. 角色 → 你是一个资深 Ja va 后端工程师
2. 技术栈 → Spring Boot 3.2 + MyBatis-Plus 3.5 + MySQL 8.0
3. 业务需求 → 做什么、规则是什么、边界条件是什么
4. 项目规范 → 包结构、分层规则、统一返回体、异常处理方式
5. 输出要求 → 要哪些文件、要不要注释、要不要请求示例
6. 禁止项 → 不要硬编码、不要编造不存在的类、不要省略判空
其中,第 4 项“项目规范”和第 6 项“禁止项”,恰恰是大多数人在使用 AI 生成代码时最容易漏掉的部分,而这也是 AI 输出不符合团队风格、不符合分层设计的主要原因。
接下来,我们结合四个最常见的 Ja va 后端开发场景,看看这个 Prompt 模板该如何真正落地。
场景一:生成 CRUD 接口
这是使用频率最高的场景。很多人第一反应就是一句“写一个 XX 管理接口”,然后又抱怨 AI 生成的代码质量差,比如 Controller 里直接写 SQL、直接拼 HashMap、完全没有分层。
差的写法:
复制代码写一个用户管理接口,包含增删改查。
这种写法下,AI 很可能直接返回一大段 Controller 代码,里面塞满了 HashMap 和零散逻辑。问题不在于 AI 不会写,而在于你没有明确告诉它:“这个 Spring Boot 项目里不允许这样写。”
好的写法:
复制代码你是一个资深 Ja va 后端工程师。技术栈:Spring Boot 3.2 + MyBatis-Plus 3.5 + MySQL 8.0。业务需求:实现系统用户的 CRUD 接口。
- 新增:用户名/手机号/邮箱必填,用户名唯一、手机号唯一
- 查询:支持按用户名模糊搜索 + 分页
- 更新:不允许改用户名,只能改手机号/邮箱
- 删除:软删除(改 is_deleted 字段),不允许物理删除项目规范:
- 包结构:com.xxx.controller / service / service.impl / mapper / entity
- Controller 只做路由和 @Valid 参数校验,业务逻辑全部放 Service
- 统一返回体 Result(code, message, data)
- 业务异常统一抛 BusinessException,由全局异常处理器兜底
- 分页参数封装在 PageQuery 基类中输出要求:
- Entity、Mapper(接口 + XML)、Service、ServiceImpl、Controller
- 关键逻辑加中文注释禁止项:
- 禁止 Controller 里写任何业务逻辑
- 禁止直接操作 Mapper(必须通过 Service)
- 禁止硬编码常量,统一放 Constants 类
- 禁止编造不存在的工具类
把这些关键上下文贴进去之后,AI 生成的 CRUD 代码往往只需要调整包名、补一点细节就能直接使用。真正的区别并不在于“有没有提需求”,而在于有没有明确告诉 AI:这个项目里哪些做法是禁止的。
场景二:生成 MyBatis 映射
AI 写简单 SQL 往往还可以,但一旦涉及多表关联查询、分页筛选、复杂 XML,问题就会集中暴露——不是写出了 N+1 查询,就是字段名瞎猜,要么 JOIN 别名写错。
差的写法:
复制代码写一个查询企业工单列表的接口,关联工单表、处理人表、客户表。
这种描述下,AI 大概率会返回一个 MyBatis-Plus 的 LambdaQueryWrapper 套娃写法,或者直接写出一段 JOIN 结构错误、表别名不规范的 SQL。
好的写法:
复制代码项目用 MyBatis-Plus,但多表关联查询必须写 XML Mapper,禁止用 Wrapper 拼接 JOIN。请生成一个工单列表查询:
- 关联表:t_ticket t LEFT JOIN t_user u ON t.handler_id = u.id LEFT JOIN t_customer c ON t.customer_id = c.id
- 支持按工单状态、创建时间范围筛选
- 分页返回,每页默认 20 条
- 结果包含:工单号、处理人姓名、客户名称、优先级、状态、创建时间已有 Mapper 写法参考(贴一个项目中已有的 UserMapper.xml 片段):
<select id="selectUserPage" resultType="...">
SELECT u.id, u.username, u.email
FROM t_user u
WHERE u.is_deleted = 0
= "query.keyword != null and query.keyword != ''">
AND u.username LIKE CONCAT('%', #{query.keyword}, '%')
</if>
ORDER BY u.create_time DESC
</select>请参照以上写法,生成 OrderMapper.xml 的对应查询。
在这个场景下,给 AI 看一个已有的、正确的 MyBatis Mapper 示例,效果往往比你用 100 个字去解释 XML 规范还要好。因为 AI 会直接模仿你项目中的 XML 风格——比如 判断方式、SQL 缩进格式、表别名习惯、参数命名方式。这些编码风格,仅靠口头描述通常很难说清楚。
场景三:Debug——贴报错定位问题
很多人低估了 AI 做 Debug 的能力。其实不是 AI 不会定位问题,而是大多数人提供的报错上下文远远不够,导致 AI 只能盲猜。
差的写法:
复制代码代码报错了,帮我看看。
或者只贴出一行 NullPointerException,然后把几百行 Ja va 业务代码全部粘过去,让 AI 自己找问题。
好的写法:
复制代码Spring Boot 3.2 + MyBatis-Plus 项目,数据库 MySQL 8.0。报错信息(完整堆栈):
ja va.lang.NullPointerException: Cannot invoke "com.xxx.entity.User.getId()" because "user" is null
at com.xxx.service.impl.TicketServiceImpl.assignHandler(TicketServiceImpl.ja va:47)
at com.xxx.controller.TicketController.assign(TicketController.ja va:32)
...相关代码(TicketServiceImpl.ja va 第 40-55 行):
User user = userMapper.selectByPhone(ticketDTO.getPhone());
ticket.setHandlerId(user.getId()); // ← 第 47 行,这里炸了
...预期:通过手机号查用户,关联到用户所在部门
实际:手机号对应的用户不存在时直接 NPE
当 AI 拿到这三类关键信息——完整堆栈、出错的具体代码行、你的预期结果与实际结果——它就不需要再猜你的业务意图了,通常可以直接定位问题:selectByPhone 没有做空值判断,需要补充 if (user == null),并按项目规范抛出业务异常。
堆栈应该贴多少:通常贴到你自己业务代码的那一层行号就够了,Spring、MyBatis 等框架底层堆栈一般不必全部贴出。
场景四:单元测试生成
AI 生成单元测试时最常见的问题,就是它默认只覆盖 happy path,也就是“正常流程”。如果你在 Prompt 里不明确说明要测哪些边界场景、异常场景,它大概率只会给你一个最基础的测试方法。
差的写法:
复制代码给这段代码写单元测试。
结果通常就是:只写一个正常返回的测试,简单 assert 一下结果。至于空参数、异常分支、边界值、并发情况,统统没有覆盖。
好的写法:
复制代码用 JUnit 5 + Mockito 给以下 Service 方法写单元测试。需要覆盖的场景(每个场景一个测试方法):
1. 正常流程:所有参数合法,返回预期结果
2. 参数为 null:username 为 null,抛 BusinessException("用户名不能为空")
3. 依赖异常:userMapper.insert() 抛出 DataAccessException,返回失败
4. 边界条件:username 长度等于 50(上限),应正常入库
5. 并发:两个线程同时插入相同 username,第二个应抛 BusinessException("用户名已存在")禁止项:
- 不要用 PowerMock(项目不支持)
- 不要 mock static 方法(除非必须)
- 每个测试方法名用 should_xxx_when_xxx 格式
把你希望覆盖的测试场景明确列出来,远比一句“帮我写单元测试”有效得多。AI 需要知道:这个 Service 方法有哪些边界条件、哪些异常分支、哪些行为预期。你不说,它就只会去测它认为最常见的那个路径。
四个可复用的模板
把前面的写法进一步抽象,其实就能沉淀成几个高频可复用的 Prompt 模板。下次做 AI 辅助编程时,只需要替换项目名、技术栈、业务需求即可。
CRUD 模板:
复制代码你是一个资深 Ja va 后端工程师。
技术栈:[框架 + 版本]
业务需求:[功能描述 + 校验规则 + 权限规则]
项目规范:[包结构 / 分层约束 / 统一返回体格式 / 异常处理方式]
输出要求:[要哪些文件 / 要不要注释]
禁止项:[不要做的事 1 / 不要做的事 2 / 不要做的事 3]
MyBatis 模板:
复制代码多表关联必须写 XML,禁止 Wrapper JOIN。
[贴一个项目中已有的正确 Mapper 片段]
请参照以上写法,生成 [表名] 的对应查询。
查询条件:[字段筛选规则]
分页:[每页条数]
Debug 模板:
复制代码[项目技术栈]
[完整堆栈信息]
[出错代码片段 + 行号标注]
预期:[应该怎样] 实际:[实际怎样]
单元测试模板:
复制代码用 [测试框架] 给以下方法写单测。
覆盖场景:
1. 正常流程:[预期行为]
2. [边界 1]
3. [边界 2]
禁止项:[不用的框架 / 不用的写法]
三个常见的反模式
1. 上下文过载。 有些人觉得“信息越多越好”,于是把一个包含 200 个文件的 Ja va 项目全都贴给 AI。问题在于,AI 的注意力和上下文窗口是有限的——上下文越冗长,真正关键的信息权重反而越低。更高效的做法是:只贴与当前任务直接相关的 2 到 3 个文件。
2. 一次性要求过多。 比如一句“帮我生成整个工单模块”,AI 往往会返回一套看似完整、但每个文件都差一点的代码。更合理的方式是拆步骤:先生成 Entity + Mapper,确认表字段和查询逻辑没问题;再生成 Service;最后再生成 Controller。分阶段生成,质量通常会高很多。
3. 不贴规则,误以为 AI 会自动读取项目约束。 你在项目里配置了 .cursor/rules、.codebuddy/rules,并不意味着每次生成代码时,AI 都会主动读取并严格遵守。最稳妥的做法,仍然是在当前 Prompt 里把最关键的 2 到 3 条约束、禁止项再强调一遍,这是性价比最高的方式。
所以,Prompt 写得好不好,本质上不是语言表达技巧的问题,而是你给 AI 的上下文信息,是否足够让它判断“这个项目里的代码应该怎么写”。
每次写完 Prompt,都可以先反问自己一句:AI 有没有拿到足够的信息,去理解这个项目里其他人平时是如何写代码的?
如果答案仍然不确定,那就把 DDL 补上、把已有的 Mapper 示例贴上、把分层规范写清楚、把禁止项明确列出来。对于 Ja va 后端开发、Spring Boot 项目开发、MyBatis-Plus 代码生成这类场景来说,这四件事远比任何所谓的“Prompt 咒语”都更有效。
