
最近在带团队做 AI 辅助开发的落地发现了一个很有意思的现象同样是用 AI 写代码有的人写出来的代码越改越顺有的人写出来的代码越改越乱。后来复盘时我意识到问题不在 AI 工具本身而在于我们使用 AI 的方式。有人是在“用 AI 真编码”有人却只是在“氛围编码”。“氛围编码”vibe coding这个词最近讨论度很高指的是开发者把代码编写几乎完全托付给 AI自己只负责描述愿望、不断试错甚至在完全不理解代码的情况下接受 AI 的输出。这种方式偶尔能跑通小 Demo但只要项目规模稍微上来代码失控、安全隐患、重构成本都会成倍增长。与其被 AI 牵着走不如建立一套属于自己的 AI 编码工作流。这篇文章我会围绕“规则、提示、审查”三个环节完整拆解一套从需求到落地的 AI 辅助开发流程。无论是刚接触 AI 编程工具的新手还是正在寻找规范方法的团队技术负责人这份实战笔记都能给你一个可以直接复用的框架。1. 真编码与氛围编码本质区别在哪里1.1 氛围编码是什么先来看一个典型的氛围编码场景开发者打开 Cursor 或 GitHub Copilot输入一句“帮我写一个用户登录接口”AI 返回了一大段代码开发者没细看直接粘贴进项目运行有报错就把报错贴给 AI循环往复直到浏览器里看到登录页面可以跳转就觉得“功能完成了”。这个过程的特征非常明显对 AI 生成的代码缺乏理解不知道每一段代码为什么存在。注意力放在“程序能不能跑起来”而不是“代码是否正确、安全、可维护”。很少主动审查输出遇到问题靠 AI 反复试错而不是靠自己的分析和判断。代码质量完全取决于 AI 的“临场发挥”俗称开盲盒。氛围编码最危险的地方在于它让人产生一种“我在写代码”的错觉。实际上代码是 AI 写的需求是模糊的错误是 AI 修的行为人是缺席的。一旦项目代码量变大那些 AI 早期生成但没人理解的功能会变成一颗颗定时炸弹。1.2 真编码应该是什么样子所谓“用 AI 真编码”并不等于不用 AI更不意味着所有代码都要手敲。它的核心是AI 负责生成候选方案人类负责制定规则、定义输入、审查输出并对最终进入仓库的每一行代码负全责。真编码的工作流通常包含三个固定动作写规则提前告诉 AI 能做什么、不能做什么、应该遵循什么风格。写提示不是一句话需求而是带背景、约束、验收标准的完整指令。做审查对 AI 生成的代码进行逐层检查包括功能、安全、边界和可维护性。可以这样理解氛围编码把 AI 当“代笔”真编码把 AI 当“协作者”。代笔写完你就签字协作者交稿后你会审稿、批注、要求修改。1.3 为什么需要一套工作流即便抛开质量问题不谈单纯从效率角度流程化使用 AI 也明显更划算。完整的规则和提示能减少 AI 生成的废代码审查环节能提前拦截缺陷避免后期返工。更关键的是这套工作流让 AI 的使用经验可以沉淀、可以共享、可以改进。因此下文将围绕“规则、提示、审查”三个词展开。这套方法不绑定任何特定工具VS Code 插件、JetBrains 插件、独立 IDE、网页版助手都可以适用区别只是配置文件的具体位置和格式。2. 环境准备与工具选型2.1 AI 编程工具怎么选AI 辅助编程的工具主要分为三类编辑器插件型如 GitHub Copilot、通义灵码直接在现有 IDE 里提供补全和问答能力适合大多数日常开发场景。独立 IDE 型如 Cursor、Windsurf把 AI 能力深度集成到编辑器的操作、调试和文件管理中适合需要多文件重构、跨文件理解的任务。对话型助手如 Claude、ChatGPT 等通用大模型能力全面但与代码仓库的集成度较低适合思路梳理、代码解释、测试用例设计。我的建议是不要纠结于“哪个工具最强”而是看你当前的开发环境。如果团队已经统一使用 IntelliJ IDEA那 Copilot 或通义灵码插件最省事。如果愿意接受一个新 IDE且经常要跨文件改代码Cursor 这类深度集成工具体验更好。本文的规则文件示例以通用 Markdown 格式为主你可以放到任何工具的规则配置或知识库中。2.2 需要准备的基础设施除了 AI 工具本身建议至少准备以下内容设施用途Git 仓库记录 AI 生成代码的变更便于审查和回滚规则文件编码前先加载约束 AI 行为提示模板把高频需求固化成标准化提示审查清单人工检查时逐项核对CI 流水线让机械检查先行减轻人工审查负担重点提醒无论 AI 工具多强Git 仓库始终是底线。所有 AI 生成的代码都必须先经过本地分支走代码评审后再合并。不要直接在主干上让 AI 改代码。2.3 示例项目结构本文后面的实战案例会以一个简单的用户管理系统为例。项目技术栈选用 Spring Boot 3 Java 17 Maven这是当前后端开发中比较常见且稳定的组合。如果你在实际项目中使用 Python FastAPI、Go Gin 或 Node.js本文的方法论同样适用只是语言写法需要相应调整。示例项目结构如下user-system/ ├── .ai/ │ ├── rules.md # AI 编码规则 │ └── prompts/ │ └── create-user.md # 提示词模板 ├── src/ │ └── main/ │ ├── java/com/example/usersystem/ │ │ ├── controller/ │ │ ├── service/ │ │ └── repository/ │ └── resources/ │ └── application.yml ├── ddl/ │ └── user.sql # 建表语句 └── docs/ └── review-checklist.md # 审查清单目录不复杂但每个文件承担明确职责。后面我们会逐步填充这些文件。3. 完整工作流拆解规则、提示、审查3.1 规则层给 AI 立规矩先来看一个常见问题为什么 AI 生成的代码风格五花八门因为默认情况下大模型会综合它训练数据中的各种风格而不是你团队的标准。想让输出稳定就必须主动告诉 AI 规则。规则文件不需要写得像法律条文但必须明确、可执行、覆盖高频场景。以下是一份精简但足够使用的规则文件。# 文件路径.ai/rules.md # AI 编码规则适用于所有 AI 辅助任务 ## 1. 代码风格 - Java 代码使用 Spring Boot 惯例Controller 只做参数接收和响应封装业务逻辑放 Service。 - 方法必须包含 Javadoc 注释说明参数、返回值、可能抛出的异常。 - 不允许使用 System.out.println 输出日志必须使用 SLF4J Logger。 - 所有字符串拼接优先考虑使用 String.format 或 MessageFormat。 ## 2. 安全边界 - 所有新增接口必须校验入参包括 null 检查、长度检查、格式检查。 - 不允许在日志中打印密码、token、身份证号码等敏感字段。 - SQL 操作必须使用参数绑定禁止拼接 SQL 字符串。 - 用户密码必须使用 BCrypt 加密存储禁止明文保存。 ## 3. 变更管理 - 每次任务只做一件事不要顺手修改无关文件。 - 涉及数据库变更时必须提供 DDL 脚本并说明是否需要数据迁移。 - 删除代码前确认是否还有调用方禁止直接删除认为“没用”的方法。规则文件写好后在不同工具中的接入方式不同。Cursor 可以配置在项目根目录的.cursorrules或通过项目规则导入Copilot 可以通过.github/copilot-instructions.md配置其他工具也基本都有类似的指令文件或系统提示词位置。关键点只有一个AI 每次开始任务前必须读取这份规则。3.2 提示层把需求说清楚的艺术很多开发者使用 AI 时只给一句话“写个用户注册接口”。这不是提示这是碰运气。一个合格的提示应当像一份微型需求文档至少包含以下模块背景信息项目是什么样的技术栈、目标用户是谁、这次任务属于什么模块。功能需求需要实现哪些功能点每个功能点的输入输出是什么。约束条件必须遵守的规则、不得使用的技术点、兼容性要求。验收标准怎样才算完成哪些测试场景必须通过。输出要求希望 AI 返回什么内容是否需要附带 SQL、测试用例或说明文档。下面是一个标准化提示模板。# 文件路径.ai/prompts/create-user.md ## 任务背景 项目是一个基于 Spring Boot 3 的用户管理系统使用 MySQL 数据库MyBatis 访问持久层。 本次需要新增“用户注册”功能。 ## 功能需求 1. 用户提交 username、password、email完成注册。 2. 用户名唯一若已存在则返回错误码 USER_EXISTS。 3. 密码长度 8 到 32 位必须包含字母和数字。 4. 密码使用 BCrypt 加密后存储。 5. 注册成功后返回用户 ID同时返回 201 状态码。 ## 约束条件 - 严格遵守 .ai/rules.md 中的编码规则。 - 不要引入新的第三方依赖除非和本项目现有依赖一致。 - 不要修改已有的实体类表结构若需要扩展字段先说明 DDL。 ## 验收标准 - 提供完整 Controller、Service、Mapper 层代码。 - 提供对应 DDL 脚本。 - 提供三个测试用例的代码正常注册、重复用户名、密码不合规。 - 所有代码必须能通过 Maven 编译。 ## 输出格式 先输出实现思路说明再输出分文件代码每个文件标明完整路径。可能有人会问每次写这么长提示会不会太累实际上高频任务只需要写一次模板之后复制修改参数即可。更推荐的做法是把常见任务模板放在.ai/prompts/目录里和代码一起维护让团队共享。3.3 审查层最后的守门员提示写得再好规则的约束再强AI 仍然可能产出错误的代码。因此审查环节不可跳过。审查分为两层机器审查和人工审查。机器审查的核心目标是把低级问题拦截在提交之前。推荐在项目里接入以下工具编译检查Maven 或 Gradle 的 compile 任务确保代码语法正确。静态检查以 Java 为例可以使用 Checkstyle 或 SpotBugsPython 项目可以用 Ruff 或 Pylint。单元测试提示中要求 AI 生成的测试用例全部纳入 CI 执行。代码格式化使用 Spotless 或 prettier 统一格式。人工审查的核心目标是检查机器检查覆盖不到的问题。每个开发者可以根据项目类型自定义审查清单但下面这些项目是通用的检查维度关键问题功能逻辑业务逻辑是否正确是否所有分支都有处理边界条件空值、超长输入、并发请求是否处理安全问题是否校验权限是否存在注入风险敏感信息是否泄露性能隐患是否存在 N1 查询事务范围是否合理可维护性命名是否清晰方法是否过长是否留有无注释魔法值测试覆盖测试用例是否覆盖正常路径和异常路径实际操作中我习惯在 AI 生成代码后复制到独立分支逐文件对照审查清单走一遍。如果发现问题继续用 AI 修改但修改之后必须重复相同的审查流程。循环数次直到所有检查项通过为止。4. 实战案例从规则到落地这一节我们完整演示一个任务从规则配置、提示输入到代码落地的全过程。以用户注册功能为例分步带你看各个环节如何配合。4.1 准备数据库建表脚本-- 文件路径ddl/user.sql CREATE TABLE t_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键ID, username VARCHAR(32) NOT NULL COMMENT 用户名, password VARCHAR(128) NOT NULL COMMENT 加密后的密码, email VARCHAR(64) DEFAULT NULL COMMENT 邮箱, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 用户表;注意细节username建了唯一索引这与需求中的“用户名唯一”对应password字段长度设置为 128因为 BCrypt 加密后的字符串长度通常在 60 字符左右128 足够且留有余量。4.2 配置 Spring Boot 基础依赖创建项目时使用 Spring Initializr选择以下依赖Spring WebMyBatis / MyBatis-Spring-Boot-StarterMySQL DriverLombok可选用于简化实体类代码spring-security-crypto用于 BCrypt最轻量在pom.xml中补充密码加密所需的依赖!-- 文件路径pom.xml 中的依赖片段 -- dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId /dependency这里不需要额外指定版本号Spring Boot 3 的依赖管理会统一管理版本。如果你的项目有自定义 BOM请以实际依赖约束为准。在application.yml中配置数据库连接。示例使用本地 MySQL密码等敏感信息不要提交到仓库建议通过环境变量注入。# 文件路径src/main/resources/application.yml spring: datasource: url: jdbc:mysql://localhost:3306/user_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: ${DB_USERNAME:root} password: ${DB_PASSWORD:}4.3 向 AI 发起完整提示准备好草表、依赖、规则之后就可以把上面的提示模板粘贴给 AI。这里可以对比两种输入方式的差别氛围编码版“写一个用户注册接口。”真编码版把 3.2 节的完整提示加上建表脚本和项目结构一并粘贴要求 AI 输出分文件代码。第二种方式看起来更花时间但 AI 返回代码的可用率通常远超第一种。尤其是“请提供测试用例”和“先说明思路”这两个要求会显著减少 AI 糊弄式输出。4.4 AI 输出示例与人工修正为了演示这里给出一个符合规则要求的核心代码片段。先看数据实体类// 文件路径src/main/java/com/example/usersystem/entity/User.java package com.example.usersystem.entity; import java.time.LocalDateTime; /** * 用户实体类对应 t_user 表。 */ public class User { private Long id; private String username; private String password; private String email; private LocalDateTime createdAt; private LocalDateTime updatedAt; public Long getId() { return id; } public void setId(Long id) { this.id id; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public String getEmail() { return email; } public void setEmail(String email) { this.email email; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt createdAt; } public LocalDateTime getUpdatedAt() { return updatedAt; } public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt updatedAt; } }实体类不需要太多逻辑关键点是字段与表结构一一对应。如果团队使用 Lombok可以简化为Data EqualsAndHashCode等注解但注意要让 AI 遵循团队已有的写法不要同一项目换来换去。再来看 Service 层的核心逻辑// 文件路径src/main/java/com/example/usersystem/service/UserService.java package com.example.usersystem.service; import com.example.usersystem.entity.User; import com.example.usersystem.mapper.UserMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; /** * 用户业务处理服务。 */ Service public class UserService { private static final Logger log LoggerFactory.getLogger(UserService.class); private final UserMapper userMapper; private final BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder(); public UserService(UserMapper userMapper) { this.userMapper userMapper; } /** * 注册新用户。 * * param username 用户名 * param password 明文密码 * param email 邮箱 * return 新用户 ID */ public Long register(String username, String password, String email) { validateUsername(username); validatePassword(password); User existingUser userMapper.findByUsername(username); if (existingUser ! null) { throw new RuntimeException(用户名已存在); } User user new User(); user.setUsername(username); user.setPassword(passwordEncoder.encode(password)); user.setEmail(email); userMapper.insert(user); log.info(用户注册成功username{}, username); return user.getId(); } private void validateUsername(String username) { if (username null || username.trim().length() 4) { throw new IllegalArgumentException(用户名长度至少 4 位); } } private void validatePassword(String password) { if (password null || password.length() 8 || password.length() 32) { throw new IllegalArgumentException(密码长度必须为 8 到 32 位); } if (!password.matches(.*[a-zA-Z].*) || !password.matches(.*\\d.*)) { throw new IllegalArgumentException(密码必须同时包含字母和数字); } } }这里有几个值得注意的地方使用了BCryptPasswordEncoder满足规则中“密码必须 BCrypt 加密”的要求。日志中没有抛出密码内容只记录了用户名满足“敏感信息不落日志”的规则。校验逻辑单独抽成私有方法方便后续扩展比如加邮箱格式校验。实际项目中建议自定义业务异常类这里用RuntimeException是为了演示简洁项目中不要直接抛裸RuntimeException。Controller 层代码如下// 文件路径src/main/java/com/example/usersystem/controller/UserController.java package com.example.usersystem.controller; import com.example.usersystem.service.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * 用户接口。 */ RestController RequestMapping(/api/users) public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService userService; } /** * 用户注册接口。 * * param body 请求体包含 username、password、email * return 注册结果 */ PostMapping(/register) public ResponseEntityMapString, Object register(RequestBody MapString, String body) { String username body.get(username); String password body.get(password); String email body.get(email); Long userId userService.register(username, password, email); return ResponseEntity.status(HttpStatus.CREATED) .body(Map.of(userId, userId, message, 注册成功)); } }4.5 运行与验证完成代码后依次执行以下验证步骤# 1. 编译 mvn clean compile # 2. 运行单元测试 mvn test # 3. 启动应用 mvn spring-boot:run应用启动后用 curl 模拟一次注册请求curl -X POST http://localhost:8080/api/users/register \ -H Content-Type: application/json \ -d {username:testuser1,password:abcd1234,email:testexample.com}预期返回{userId:1,message:注册成功}再验证密码不符合规则时的返回应得到 500 或自定义业务错误响应。这里只是接口联调真正的可靠性保障靠的是测试用例。按提示模板要求AI 应该同时生成了正常注册、重复用户名、密码不合规这三类测试代码这些测试也需要逐行审查确认断言合理、不是为跑而跑的空壳用例。5. 常见问题与排查思路在实际使用这套工作流的过程中会遇到各种各样的异常现象。下面整理几个高频问题以及我的排查思路。问题现象常见原因解决思路AI 生成的代码风格和团队现有代码不一致没有配置规则文件或规则未被加载检查 AI 工具是否正确读取规则文件把规则内容同时粘贴进会话AI 介绍了一个不存在的依赖或 API大模型知识库滞后或幻觉不要直接添加依赖先去 Maven 中央仓库搜索确认无法确认就要求 AI 给出替代方案提示中明明写了约束AI 仍然违反提示过长导致模型注意力丢失把最关键约束放在提示开头和结尾同时把约束写入规则文件AI 修改一个 bug却破坏了其他功能没有独立分支和测试保护AI 修改前先跑全量测试修改后再跑回归不要一次性让 AI 改多个文件审查时不知道从哪看起缺少清单和代码量太大要求 AI 分文件输出并附上实现说明按 Controller → Service → Mapper 顺序审查反复修改后提示上下文丢失对话太长模型遗忘早期约定开启新会话重新粘贴规则文件和核心需求而不是在旧会话继续追问测试用例看起来都对但实际没测到点测试断言过弱或用例逻辑和实现相同人工检查测试输入是否覆盖边界尝试运行变异测试思路找缺口代码能运行但有 SQL 注入风险使用了字符串拼接 SQL审查 Mapper 中是否有${}语法统一改为#{}参数绑定排查的核心思路是出现问题先判断属于哪个环节。是规则没立好还是提示没问清还是审查没守住对应修改相应环节不要盲目把锅甩给 AI 工具。6. 最佳实践与工程建议6.1 将规则文件纳入版本控制规则文件不应只存在于某个开发者的本地 IDE 配置里而应该纳入 Git 仓库和代码一起评审和演进。当团队发现某类代码反复出问题时先改进规则文件而不是每次都靠人工口头提醒。规则文件本身要简洁避免写太多团队遵守不了的条款否则形同虚设。6.2 提示词模板也要版本化把高频任务模板放进.ai/prompts/目录后每次调整都走一次 Git 提交。新成员加入时可以直接复制模板上手。某个模板经过多次迭代后效果显著就在团队内分享某个模板经常导致 AI 输出偏差就复盘原因并调整提示结构。6.3 审查必须与 CI 结合纯粹依赖人工审查是不可靠的机械性的检查交给 CI 更高效。建议在 CI 流水线中加入编译检查与单测执行静态代码分析如 SpotBugs、ESLint 等覆盖率最低阈值依赖安全漏洞扫描比如 OWASP Dependency-Check这些检查完成后再由人工做业务逻辑审查。顺序不要颠倒否则常见的语法错误和低级疏漏会浪费评审人大量时间。6.4 关于 AI 使用的安全边界AI 编码过程中最容易踩的安全坑有三个这里单独提醒第一不要让 AI 处理包含生产数据的文件。需要时提供脱敏后的样例数据明确告知 AI 哪些字段属于敏感信息禁止写入代码或日志。第二涉及生产环境变更是红线。AI 生成的 DDL、迁移脚本、批量更新脚本必须经过 DBA 或高级工程师确认并在测试环境完整验证后才能执行。不要因为“AI 能写”就跳过审批流程。第三注意第三方依赖的供应链风险。AI 推荐的依赖包一定要核对名称、来源和维护状态防止被拼写相近的恶意包替代。6.5 培养 AI 时代的代码审查能力这套工作流最终对开发者的核心要求是比 AI 更懂代码。只有你具备判断好坏的能力规则、提示、审查三个环节才能形成闭环。具体到日常训练可以试试这些做法拿到 AI 代码后先不看实现自己写一版设计思路再和 AI 方案对比。每次审查至少追问一个极端输入null、超长字符串、并发调用、重复提交。定期复盘 AI 生成的代码整理自己最常见的“审查发现清单”。遇到 AI 报错时先自己读栈信息推测原因再用 AI 验证。6.6 保持人机分工的清醒认知最后强调一个关键原则AI 编码没有改变“开发者对代码负责”这件事实。AI 是你的结对编程伙伴可以大幅提升效率但绝不能让 AI 成为脚手架上的“提线人”。遇到 AI 输出的代码不问“能不能跑”多问“该不该这么写”工作流才能长久地健康运转下去。7. 总结与后续学习路线围绕“规则、提示、审查”三个关键词本文完整拆解了一套可落地的 AI 编码工作流。你可以在自己的项目中做三件立即可行的事创建一个规则文件把团队已有的编码规范和行为边界写进去。为最高频的开发任务编写一个标准提示模板。整理一份属于自己的代码审查清单并让 AI 第一次输出的代码必须经过清单校验。下一步可以继续深入的学习方向包括深入理解提示工程中上下文窗口的管理策略为团队搭建本地知识库作为 AI 的补充信息源以及把 AI 辅助编码接入到已有的 CI/CD 流水线中形成自动化评审机制。需要提醒的是无论你采用哪种 AI 工具版本更新都很快不同工具的规则配置语法可能有变化。遇到配置不生效时优先查阅官方文档再结合本文的流程框架做适配。AI 辅助编程这条路上没有终点的银弹只有不断迭代的工作流。如果你在实践中摸索出更好的规则或提示写法欢迎在评论区分享也建议先在你的项目里跑通这套方法论再结合实际场景做取舍。