AI 生成代码的思考与实践

AI 生成代码的思考与实践

怎么让 AI 生成的代码真实可运行而不是"看起来对"?本文记录一套已落地的实现方案------包括架构设计、知识管理、沙箱验证、自动修复------以及在真实技术栈上反复踩坑后的思考。

核心问题:80% 和 20%

业界有一个务实判断:大模型做 80% 的脚手架和通用代码,剩下 20%(业务补偿、异常流)交给开发人员 Review 并手写补全

这个判断基本成立,但那 80% 里还能再切两刀,且每刀的质量保障机制完全不同:

层次 占比 生成策略 质量保障 实测成熟度
架构层(脚手架) ~40% 最强模型一次性生成 ,人工 Review 后冻结为工程模板。后续新项目只复制这份固化模板,不再让 LLM 随意改动 冻结即保障:模板一旦验证通过就不再变 接近 100%
通用业务代码 ~40% LLM 按服务契约并行生成 + 沙箱验证 + 自动修复 三道门禁:沙箱运行验证 + CI 静态质量门禁(SonarQube 覆盖率/圈复杂度)+ 人工 MR 审批 70~90% 命中率
业务逻辑 + 异常流 ~20% 人类定义验收标准,AI 执行 人工审批最终通过的 MR 演进中

关键设计决策:

架构层不交给 Agent 循环。架构决策的变更频率极低(一年改不了几次),且每次变更都需要人工判断------这类代码的生命周期是"生成一次 → 人工确认 → 长期复用"。流程是"最强模型出初版 → 人工逐行 Review → 冻结为模板 → 后续直接复制"。只有技术栈升级或架构调整时,才重新走一次这个流程。

业务层可以放心交给 Agent 异步处理。Agent 修改的是业务实现类,即使质量不佳,CI 里的 SonarQube 质量门禁(覆盖率 >80%、圈复杂度 <10)也会阻断不达标代码合入。人工只需负责审批最终通过的 MR。

一个关键认知:那 20% 的重心不是"手写补全代码",而是"写清楚什么算对"。当人类把验收标准写成测试用例,AI 就能拿着标准自驱修复------这比人工补代码的杠杆大一个量级。

三道门禁的分工

erlang 复制代码
第一道 · 沙箱运行验证(生成时)
  构建 → 启动 → CRUD + 分页冒烟 → 前端 Vite 构建
  回答的问题:代码能不能跑?

第二道 · CI 静态质量门禁(提交时)
  SonarQube:覆盖率 >80%、圈复杂度 <10、代码异味扫描
  回答的问题:代码质量够不够?

第三道 · 人工 MR 审批(合并前)
  人类看业务逻辑正确性、架构合理性、安全风险
  回答的问题:这个改动该不该上线?

三道门各管一段,互不替代:沙箱拦"跑不了的",SonarQube 拦"跑得了但质量差的",人类拦"质量好但业务错的"。

实现方案:一条完整管线

复制代码
需求 → 技术栈锁定 → 检索+知识注入 → 模板+LLM 并行生成 → 沙箱运行验证 → 自动修复 → 交付

以下按管线各环节展开。

1. 技术栈锁定(给生成器确定性)

用户四级联动选栈(前端→语言→框架→持久层),确认后不可更改。锁定带来两个好处:

  • 依赖版本可以钉死进提示词(LLM 不再写训练数据里的旧版本号)
  • 服务契约可以预定义(方法签名、路由规则、返回结构全部固定)

实测中最大的一类失败------"LLM 写了旧版本 API"------靠版本钉子直接消除。比如 MyBatis-Plus 3.5.9+ 把分页插件挪进独立构件、移除了 ServiceImpl,这些信息钉进提示词后,相关问题不再出现。

2. 确定性模板 + LLM 并行生成(分层策略)

能确定的不交给 LLM:pom.xml、实体类、配置文件、建表 SQL------这些文件的"正确版本"完全由规则决定,用模板渲染,零幻觉零漂移。

需要智能的才交给 LLM:Controller、Service、前端页面------这些需要理解业务语义,由 LLM 按服务契约并行生成(3 路并发),互相独立不等待。

生成用快模型 (照契约填空不需要深度推理),修复用旗舰模型(从报错反推根因需要更强的推理能力)------成本和速度双优。

3. 沙箱验证(不信"看起来对")

生成的代码在一次性 Docker 容器里真实运行

  • 后端冒烟:构建 → 启动 → 增删改查 + 分页五个接口逐一验证
  • 前端构建:Vite 离线构建,检查 import 一致性
  • 隔离:无网络、限内存/CPU、超时强杀------LLM 产物当不可信代码对待
  • 快速失败:进程死亡秒级判负(附带应用日志),只有"活着但没就绪"才等

这一步是整个系统的信任锚点:后续所有环节(修复、知识沉淀、模板升级)都以"沙箱通过"为唯一准入标准。

4. 自动修复(阶梯式)

冒烟失败后进入修复循环,最多三轮:

复制代码
第 1 轮快修:关闭思考模式,旗舰模型,30-60 秒/文件
第 2 轮快修:换个角度再试
第 3 轮深思:开启思考模式,从报错深度推理根因

修复提示词注入全项目源码 (不只是出错文件),让模型能看到跨文件签名一致性;支持多文件输出,一次修复多个文件的联动问题。

两个实战教训:

  • 测试脚本不作为修复对象------它是验证工具,不是被测代码
  • 确定性模板禁止被修复重写------模板是已验证的正确实现,LLM 改了反而引入回归

5. 知识体系(越用越准)

三层知识,按加载时机和信任等级分层:

规则(每次都读):实体规范、REST 约定、分页协议------必须精简,防止上下文膨胀。判断标准:只有"每次生成都必须遵守"的才留在这里。

技能 (按栈加载):实战验证过的坑。frontmatter 声明适用栈和版本范围,按技术栈匹配注入。版本门控:每次生成前先查 Context7 / GitHub 获取当前版本,与技能记录比对------一致才启用,不一致回退官方文档重新验证。

问题库(按症状查):真实故障的症状→根因→修复,人工确认后入库。修复遇到新报错先查库,命中直接注入已知方案。

知识流动规则 :问题库反复命中 → 升级为技能(从"事后修"变"事前防");技能每次生效 → 越用越准。核心纪律:没过沙箱冒烟的代码不能进技能库

6. 知识新鲜度:Context7 而不是自建

脚手架知识不需要自己维护------Context7 按需检索官方文档的最新版本和代码示例,比自建知识库更及时且零维护成本。

但 Context7 也有局限(topic 匹配偏模糊、偶尔给旧写法),所以优先级是:自验证技能 > Context7 示例。冲突时以经实战验证的为准。

7. 工程模式进技能:分布式事务、幂等、消息可靠

当系统复杂度从"单表 CRUD"升级到"多服务协同",会出现一类跨栈通用的工程模式------它们不是某个框架的 API 用法,而是"无论用 Spring Boot 还是 Gin 还是 FastAPI,都必须正确处理的架构约束"。

这类知识的正确归属是技能层:做成带触发条件的技能文件,agent 在生成相关代码时自动加载,在沙箱验证时作为检查规则。

分布式事务

LLM 的训练数据里充满"教科书版 2PC"和"过度简化的 Saga 示例",直接生成几乎必然不可用。需要把团队的实际决策固化成技能:

yaml 复制代码
---
stack: global
stage: generating
when: "涉及跨服务写操作"
---
# 分布式事务模式

## 版本无关
- 单体应用内多表写:用本地事务(BEGIN/COMMIT),不引入 Saga
- 跨服务写:优先最终一致性(消息驱动 + 补偿),不用 2PC/XA
- Saga 编排:每个参与方必须实现「正向操作 + 补偿操作」两个接口
- 补偿必须幂等(网络重试会调多次)
- 编排状态持久化(重启不丢上下文)

沙箱验证规则(agent 生成后自查):

  • 如果代码里有跨服务调用 + 写操作,检查是否声明了事务边界
  • Saga 参与方必须有 compensate 方法
  • 编排器的状态变更必须先写库再发消息(避免消息先到、状态未落)
幂等性兜底

LLM 生成的代码默认不幂等------同样请求发两次,创建两条记录。这在重试场景下是灾难。技能应该规定:

python 复制代码
# 幂等模板(agent 生成写接口时自动套用)
def create_order(idempotency_key: str, payload: dict):
    existing = db.query(IdempotentRecord, key=idempotency_key)
    if existing:
        return existing.result          # 幂等返回
    result = do_create(payload)          # 正常执行
    db.save(IdempotentRecord(key=idempotency_key, result=result))
    return result

沙箱验证规则

  • 对每个写接口连续调两次相同 payload,断言返回一致且数据库只多一条记录
  • 消息消费者必须处理"重复投递"(at-least-once 语义下必然发生)
消息队列重试与死信

这是 LLM 最容易写出"能编译但上线就出问题"代码的领域。需要把生产级约定固化:

场景 LLM 默认写法(有问题) 技能纠正后
消费失败 直接 throw,消息无限重投 指数退避重试 N 次 → 进死信队列
死信处理 没有死信消费者 死信落地 + 告警 + 人工介入入口
消费确认 处理前就 ACK 处理成功后才 ACK(at-least-once)
顺序消息 假设全局有序 按 partition/key 有序,消费者不依赖跨分区顺序
消息幂等 默认消息不重复 按 message_id 去重(配合幂等表)

沙箱验证规则

  • 消费者代码里不能有"裸 throw"(必须 catch → retry → dead letter)
  • ACK 必须在业务逻辑执行成功之后
  • 死信队列必须有消费者(不能只生产不消费)
这类技能的共同特征

这三类模式有以下共性:

  1. 跨栈通用 :不依赖特定框架,Java/Go/Python 都适用 → 技能标记 stack: global
  2. 有明确对错判据 :沙箱里可以写成自动化断言 → 不只是"提示词建议",是可执行规则
  3. LLM 默认写法是错的:训练数据偏学术示例 → 需要人工经验覆盖
  4. 出错代价高:不是编译报错,是上线后数据不一致 → 必须在生成阶段就拦住

所以这类知识不应该散落在提示词里,而应该结构化为"生成模板 + 验证断言"的成对技能------agent 生成时套模板,沙箱验证时跑断言,两端闭环。

8. 架构验证与安全部署:从"代码能跑"到"流量可切"

代码通过沙箱冒烟只是第一步------"能跑"和"能接生产流量"之间还有一整段工程要过。这段也应该整理成技能,由 agent 在生成和部署两个阶段自动执行。

架构级验证(CI 门禁的一部分)
检查项 工具 阻断级别 说明
循环依赖 mvn dependency:analyze / jdeps Blocker 循环依赖是架构腐化的第一步,必须在 CI 阶段就阻断
合约测试 Pact / Spring Cloud Contract Blocker 消费方驱动的接口契约------接口改了但消费方不知道,比编译错误更致命
性能基准 JMH / wrk 基线对比 Warning → Blocker 新版本吞吐量下降超过阈值(如 10%)时阻断合并

循环依赖检测的 Blocker 意味着:宁可让构建失败,也不让带循环依赖的代码进主干------因为修循环依赖的成本随时间指数增长。

老模块升级:绞杀者模式

对已有系统的模块升级,不用"一次性替换"(风险太大),而用绞杀者模式(Strangler Fig)

markdown 复制代码
老模块(继续跑)────┐
                    ├── 网关按路由规则分流 ──→ 新模块(逐步接流量)
                    │
新模块(并行开发)──┘

关键风险点是数据兼容性:新老模块并行期间共享同一个数据库,新模块写的表结构/字段必须向后兼容------老代码还在读。这意味着:

  • 新增字段可以,删除/重命名字段不行(先双写过渡,确认老代码不再读后才删)
  • 数据迁移脚本必须可回滚(切流量前留快照)
  • 新老模块的 ORM 映射必须经过同一份 schema 契约测试
灰度发布:权重 + Header 双通道

新版本部署 1~2 个节点后,不直接全量切流量,而是通过网关层精细控制:

权重通道:新节点网关权重设为 1%(或 5%),生产流量按比例小规模试探。

Header 通道 :内部测试人员的请求强制携带 X-Canary: test,网关识别后强制路由到新节点------无论 IP 怎么变(手机切 Wi-Fi、VPN 重连),测试请求始终打到新版本。

双通道的核心优势:

  • 如果新节点不可用,权重自动失效,流量秒级切回老节点(不需要人工干预)
  • 测试人员的请求始终可控,不会因为 IP 变化导致路由漂移
  • 测试人员可以随时"强制体验新版本",不受权重比例限制
观察期:两个阶段的硬指标

灰度不是"部署完等一等",而是盯具体指标跑够具体时长

阶段 时长 关注指标 原因
功能期 30 分钟 业务错误码(如支付失败率) 功能性问题暴露快------路由错了、参数解析错了、权限校验漏了
性能期 1 小时 GC 频率和年轻代吞吐量 新代码的日志框架可能引入额外对象分配,GC 压力上来需要一个完整周期才看得到

两个阶段都通过 → 逐步提权重(5% → 25% → 50% → 100%);任一阶段指标异常 → 立即回切。

部署策略选型
场景 策略 说明
常规迭代(接口不变) 滚动更新 逐个节点替换,零停机,资源利用率高
大版本更新(接口/存储变更) 蓝绿更新 新旧两套完整环境并行,验证后切 DNS/网关,可秒级回切

选型逻辑:变更越大,回切速度要求越高。滚动更新的回切是"再滚一轮"(分钟级),蓝绿的回切是"切回旧环境"(秒级)------大版本变更时多花一倍资源买这个秒级回切,值得。

部署知识如何进体系

和分布式事务/幂等/消息可靠一样,这些部署知识也应该做成**"生成模板 + 验证断言"的成对技能**:

  • 生成时:agent 生成的代码自动附带 Dockerfile(滚动更新用)或 docker-compose 双环境(蓝绿用);网关配置里预埋灰度权重规则和 Header 路由规则
  • 验证时 :CI 门禁自动跑 dependency:analyze(循环依赖 Blocker)、合约测试、性能基准对比
  • 部署时:灰度脚本自动执行两阶段观察(30min 功能 + 1h 性能),指��达标才提权重

最终效果:从"AI 生成代码"到"流量切换"整条链路都有自动化保障,人类只在两个地方介入------定义阈值(什么算异常)和按下最终的 100% 切换确认

实测数据

技术栈组合 全流程通过 生成耗时(含修复)
SpringBoot + MyBatis-Plus + Vue ~3 分钟
FastAPI + SQLAlchemy + Vue / React ~2 分钟
Gin / Echo + GORM + Vue ~2 分钟
Flask + SQLAlchemy + Vue ~2 分钟
Django + Django ORM + Vue ~4 分钟
SpringBoot + JPA + Vue ~5 分钟

每个组合都经过多轮真实踩坑才通过------比如 Java 栈历经六类问题(Maven 离线缓存身份、MyBatis-Plus 拆模块、冷启动超时、类型混用、私加依赖、schema 触发器),每一类修复后都沉淀进了技能或问题库。

一个重要发现:思考模式的成本

实测发现 qwen3.8 系列默认开启"思考模式"(先生成长篇推理再输出代码),单文件生成从秒级膨胀到 5-13 分钟。关闭思考模式enable_thinking: false)后:

  • 生成本质是"照契约填空",不需要深度推理 → 关掉后速度提升 5-10 倍,正确率不变
  • 修复需要"从报错推理根因" → 保留思考能力给旗舰修复模型

这个发现改变了性能优化的方向:瓶颈不在代码量,而在模型在不必要的推理上消耗的时间

演进方向

终局判断:架构层靠"强模型一次生成+冻结"保证输出稳定一致,业务层靠"Agent 自由生成+三道门禁过滤"实现近自主,人类只审批最终通过的 MR 并按下发布键

复制代码
架构层(冻结制)              业务层(门禁制)
──────────────────           ──────────────────
最强模型一次性生成             Agent 自由生成业务类
人工 Review 后冻结为模板        沙箱运行验证(能跑?)
后续只复制,LLM 不碰           CI 质量门禁(SonarQube)
模板升级才重启生成流程          人工 MR 审批(该上线?)

五条演进路径:

  1. 架构层冻结管线:新栈或版本升级时,最强模型生成完整工程 → 人工逐行 Review → 冻结为模板 → 后续只复制。模板是"经人审批的资产",不是"AI 的输出"
  2. CI 质量门禁接入:沙箱验证通过后自动提 MR → SonarQube 跑覆盖率/圈复杂度/代码异味 → 不达标自动打回 Agent 重改 → 达标后人工审批
  3. 业务测试用例接入沙箱:人类写"金额超限要拒绝"这类验收标准,沙箱同时跑 CRUD 冒烟和业务断言
  4. Spec 层相似度判定:新项目和已有项目比实体关系+操作语义相似度,高相似直接走填空模式
  5. 工具按需递增:先跑稳 5 工具单代理,再逐步接入向量检索、代码仓库阅读、外部系统

MR 审查的三层基础设施:从"前期人工多"到"未来全自动"

AI 生成的新代码,在提交 MR 合并前,未来一定是 AI 先扫有问题再人工。前期人工参与会多一些,但必须提前把三层基础设施搭好------缺一不可,它们是从"人工兜底"演进到"基本全自动"的唯一路径。

第一层 · 评估基准(离线评估与回放)------迭代的基石

没有这一层,无法判断 AI 的迭代方向是变强了还是退步了。

黄金评测集(Golden Dataset):从历史人工 Review 通过的代码中,抽取 100~200 个典型场景(如"复杂事务""缓存穿透防护""空指针防护"),人工写好"标准答案"(期望的代码片段或行为断言)。

回放比对(Replay) :每次升级大模型版本或调整提示词后,不直接上线,先在黄金集上跑一遍:

markdown 复制代码
新模型/新提示词 → 黄金集回放 → 相似度/覆盖率/圈复杂度对比
                                    │
                    指标不降级 → 允许发布新流程
                    指标降级 → 阻断,回滚到上一版

这就是"迭代框架"的技术锚点:任何改动(模型版本、提示词、技能文件)都必须先过黄金集回放,不降级才发布。没有这个锚点,调整提示词后感觉"似乎更好了",实际上可能引入了多个新的回归。

第二层 · 执行管道(AI 扫描 + 确定性门禁)------两段式

"AI 先扫"必须分两段,不能一开始就全交给大模型:

阶段一 · 硬规则门禁 :先跑 SonarQube / Checkstyle 等确定性工具。圈复杂度 >10、缺少单元测试、代码异味的,直接红牌阻断------不经过大模型。

markdown 复制代码
MR 提交 → SonarQube 硬规则 ──不达标──→ ❌ 红牌(不进大模型)
              │达标
              ↓
        大模型语义扫描 ──发现问题──→ 💬 AI Comment(评论,不阻断)
              │无问题                │
              ↓                      ↓
          ✅ 进入人工审批         人工确认后修复合并

阶段二 · AI 语义门禁 :只有硬规则通过的代码,才交给大模型做"业务逻辑漏洞扫描"(比如检查是否漏掉了 @Transactional 回滚、是否存在缓存穿透风险)。

权限渐进策略:前期 AI 只提"评论(Comment)",不拥有"阻断(Block)"权限。运行 3 个月、误报率稳定在可接受范围后,才将权限升级为"阻断"。这避免了早期 AI 误判导致正常代码被卡住。

第三层 · 反馈闭环(人工修正记录)------从人工到全自动的唯一桥梁

前期人工参与多不是问题,问题是人工改完的代码没有沉淀

强制反馈机制:开发者在合并 MR 时,必须勾选或填写"AI 误判/漏判原因"(比如"AI 没考虑到财务四舍五入规则""AI 漏了跨时区的时间处理")。

结构化存储:这些反馈不是写在评论里就完了,而是结构化存入数据库(关联 MR diff + AI 扫描结果 + 人工修正内容),形成"AI 错在哪 → 人怎么改的"配对记录。

回流机制:定时通过 RAG 向量化或微调(SFT)输入后续模型。每一轮人工修正,都让 AI 下次少犯一个错。

核心逻辑:早期代码质量靠人工,后期全自动靠的是持续积累的"人工修正记录"。没有第三层,将永远停留在"人工 Review"阶段,无法演进到"AI 自主审查"。

三层之间的关系

markdown 复制代码
评估基准(第一层)──→ 保证迭代方向正确(不倒退)
       │
执行管道(第二层)──→ 保证日常 MR 质量可控(渐进信任)
       │
反馈闭环(第三层)──→ 保证人工经验不断回流(越用越准)

三层缺一:没有评估基准,迭代方向不可知;没有执行管道,审查完全依赖人工;没有反馈闭环,人工经验无法沉淀,无法从"人工多"过渡到"全自动"。

总结

AI 生成代码的核心不是"让 AI 写更多代码",而是建一套分层信任体系

  • 架构层靠冻结:最强模型出一次,人工 Review 后固化为模板------输出稳定一致,后续不经 LLM
  • 业务层靠门禁:Agent 自由生成,沙箱拦"跑不了的",SonarQube 拦"质量差的",AI 语义扫描拦"质量好但逻辑错的",人工拦"AI 漏判的"
  • 迭代靠三层基础设施:黄金集回放保证不倒退,两段式门禁渐进信任,人工反馈结构化回流
  • 踩过的坑沉淀成知识(下次绕过)
  • 人从"写代码"转向"审批 MR + 定义什么叫对 + 教会 AI 什么叫对"

这套体系的价值不在于替代程序员,而在于:架构层的确定性和业务层的自由度各得其所,人类从"写代码的人"变成"教 AI 写代码的人",最终只需在门禁末端做最终判断

相关推荐
VIP_CQCRE1 小时前
在 Visual Studio 里接入 Ace Data Cloud:让 IDE 拥有 OpenAI 兼容 AI 能力
openai·ai编程·开发工具·visual studio·ace data cloud
码事漫谈1 小时前
DeepSeek 明天又降价(涵历史价格对比)
后端
前端精髓1 小时前
NestJS 是什么(对着 Spring Boot 一起学习)
spring boot·后端·学习
国奉1 小时前
从零设计一个 iOS 文件浏览器:Sandbox、FileManager、Document Picker 与文件架构
后端
学者猫头鹰1 小时前
AI大模型应用开发理论指南(Java/SpringAI 体系)
ai编程
前端兰博2 小时前
04-数据库-MySQL
后端·mysql
她的男孩2 小时前
接口加密做成框架级能力有多难?我扒了 3600 行源码:从 RSA 握手到落库密文迁移
人工智能·后端·架构
掘金挖土2 小时前
前端手摸手跑路之 AI 应用开发(二)
前端·后端