把 AI 塞进研发流程半年,哪些卡点活下来了
四月份我们开始在研发流程里加 AI 卡点,到这个月正好半年。团队 14 人,一共试过九个卡点,活下来五个。先说总体结论:AI 卡点的价值不在「生成了多少内容」,而在「拦截了多少问题」——纯生成类的(写文档、写注释)普遍活不下来。
我们试过的九个卡点
| 卡点 | 位置 | 状态 | 核心数据 |
|---|---|---|---|
| 需求澄清提问 | 需求评审前 | 存活 | 平均提前发现 2.3 个遗漏点 |
| 编码实时补全 | IDE 内 | 存活 | 采纳率 31% |
| 提交前自检 | pre-commit | 存活 | 拦截 340 次带问题的提交 |
| AI 代码审查 | PR 创建时 | 存活 | 有效问题率 34% |
| 测试用例生成 | PR 创建时 | 存活 | 覆盖率 +8.2pp |
| 接口文档生成 | 合并后 | 砍掉 | 准确率 92%,但没人看 |
| 代码注释补全 | 编码时 | 砍掉 | —— |
| 发布说明生成 | 发布前 | 半存活 | 从 40 分钟降到 8 分钟 |
| 线上问题分析 | 故障时 | 存活 | MTTR 降 27% |
活下来的:提交前自检
这个是性价比最高的,也是我最推荐的起点。做法是在 pre-commit 里跑一个轻量检查,不是 lint(我们有 Checkstyle 了),而是三类 lint 抓不到的问题:
#!/bin/bash
# .husky/pre-commit + 服务端 hook 双保险
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.java$')
[ -z "$STAGED" ] && exit 0
# 只检查本次改动的 diff,不扫全量代码(快)
git diff --cached | ai-check \
--rules sensitive-data,null-risk,resource-leak,sql-injection \
--model qwen-plus \
--timeout 20s \
--fail-on high
# 超时或 AI 不可用时不阻塞提交(重要!)
[ $? -eq 124 ] && echo "AI check timeout, skipped" && exit 0
四条规则的检出情况(半年累计 2,841 次提交):
| 规则 | 命中次数 | 真问题率 |
|---|---|---|
| sensitive-data(硬编码密钥、手机号、身份证) | 47 | 89% |
| null-risk(明显未判空的链式调用) | 163 | 41% |
| resource-leak(流/连接未关闭) | 78 | 73% |
| sql-injection(字符串拼接 SQL) | 52 | 81% |
一共拦截 340 次,其中 208 次是真问题(61%)。最值钱的是 sensitive-data 那 47 次——有一半是我们自己写的测试配置里带了真实的内网账号,如果提交到仓库就是安全事故。
- 只扫 diff 不扫全量。全量扫要几分钟,没人愿意等。扫 diff 平均 3.2 秒。
- AI 不可用时不阻塞。这点我们吵过,有人觉得应该失败安全,但模型服务抖动导致的误阻塞会让大家对 hook 失去信任,最后被绕过。现在本地 hook 不阻塞,服务端 push 时的强制检查才阻塞。
活下来的:AI 代码审查
第一版上线时,AI 对每个 PR 平均提 11.4 条评论,开发的反应是「噪音太大,不看」。第二周开始有人在 PR 里回复「bot 闭嘴」。我们做了三轮收敛:
// v1:什么都提 —— 11.4 条/PR,有效问题率 9%
"Review this PR, point out any issues."
// v2:限定类别 —— 6.8 条/PR,有效问题率 17%
"Review for: NPE risks, resource leaks, concurrency issues, SQL problems."
// v3:限定类别 + 要求证据 + 分级 —— 2.9 条/PR,有效问题率 34%
"""
Review the diff. Only report issues that meet ALL criteria:
1. Can cause production failure, data error, or security problem
2. You can point to the exact line and explain the trigger condition
3. NOT a style preference or "could be better" suggestion
For each issue output:
FILE:LINE | SEVERITY(high/medium/low) | PROBLEM | TRIGGER CONDITION
If no issue meets these criteria, output exactly: NO_ISSUES
Do not praise. Do not suggest refactors. Do not mention naming.
"""
v3 的三个关键改动:
- 要求指出触发条件。这一条过滤掉了大部分臆测。AI 说「这里可能 NPE」没用,要说「当
order.getItems()返回 null 时会 NPE」才保留。 - 明确禁止夸奖和改进建议。原来 AI 会花三分之一篇幅说「代码结构清晰,命名规范」,全是废话。
- 允许输出 NO_ISSUES。不给它「必须提点什么」的压力。现在约 38% 的 PR 会收到
NO_ISSUES。
有效问题率 34% 是什么概念?我们人工 CR 的有效问题率大概在 55%(提的问题里一半是风格偏好)。AI 还差得远,但它的价值在于不疲劳——我们统计过,人工 CR 在周五下午和周一早上的检出率明显偏低,AI 不会。不过要强调一条:AI 审查不能当成「已审查」,人工仍需完整看 diff,AI 只是帮着标重点。
活下来的:测试用例生成
我们的做法是 PR 创建时,如果改动了核心业务逻辑且没带测试,自动生成测试用例草案推给作者。关键是「草案」——不是自动提交,而是作为 PR 评论贴出来,作者自己决定采纳哪些。
@Test
void should_reject_when_stock_insufficient() {
// AI 生成的草案,作者确认后手动加入
Order order = order("SKU-001", 5);
when(stockClient.available("SKU-001")).thenReturn(3);
assertThatThrownBy(() -> orderService.create(order))
.isInstanceOf(InsufficientStockException.class)
.hasMessageContaining("库存不足");
}
半年数据:生成测试用例 1,240 个,作者采纳 713 个(57%);采纳的用例里 46% 被人工修改过(主要是调边界值和 mock 数据);模块单元测试覆盖率从 61.3% 提到 69.5%。
57% 的采纳率是我认为这个卡点能活下来的原因。如果是自动提交,46% 需要修改的用例会污染代码库。而作为草案推给作者,修改的成本由作者承担,质量可控。
还有一个意外收益:AI 生成的用例里,有 23 个直接发现了真实 bug(跑不通,因为代码逻辑有问题)。这些 bug 在 Code Review 时都没被发现。
被砍掉的:接口文档自动生成
这个卡点技术上最成功——生成准确率 92%,覆盖了我们 340 个接口,文档质量比人写的还规范。但我们三个月后砍掉了。
原因是没人看。生成的文档页面月均访问 47 次,其中 31 次是我们自己人。前端同学的原话是:「我都是直接看 Swagger 或者问后端,没想起来去看这个。」
问题出在选错了卡点位置。文档没人看不是因为写得不好,是因为大家获取接口信息的路径已经固化了。我们生成了更好的文档,却没有改变任何人的工作流。教训是:AI 卡点要嵌进现有工作流,别指望它创造新工作流。后来把接口说明挪进 Swagger 注解,CI 时自动补全缺失的 @Operation 描述,效果就好了。
被砍掉的:代码注释补全
这个砍得最快,两周就下了。原因很简单:AI 生成的注释基本都是「重复代码在说什么」。
// AI 生成(毫无价值)
/** 根据用户 ID 查询用户名 */
public String getUserName(Long userId) { ... }
// AI 生成(甚至有害,把实现细节写死在注释里)
/**
* 先查缓存,缓存没有再查数据库,查到后写入缓存并设置 30 分钟过期
* @param userId 用户 ID
* @return 用户名
*/
第二种注释比没有注释更糟——代码改了注释不改,变成误导。我们统计了两周生成的 890 条注释,有实际信息量的(解释「为什么」而非「是什么」)不到 5%。后来改成只在方法体超过 50 行时才生成,且只解释「为什么这么做」,生成量降到 1/12,有用率提到 40%。
半存活的:发布说明生成
这个卡点省时间很明显——从 40 分钟手工整理降到 8 分钟(AI 生成 + 人工校对)。但「半存活」是因为我们对生成内容做了强约束。
第一版让它「根据这些 commit 生成发布说明」,结果是流水账,还经常把内部重构(「调整 XxxUtil 的包路径」)也写进去给业务方看。现在改成分类 + 模板:
"""
根据以下 commit 生成发布说明,只输出三类内容:
【新功能】面向用户可感知的变化,用业务语言描述,不要出现类名方法名
【问题修复】格式:修复了「{用户可感知的现象}」的问题
【重要变更】可能影响下游的变化(接口签名、配置项、行为变更),必须列出
忽略:纯重构、测试改动、依赖升级、格式化、注释修改
如果某一类为空,写"(无)"
"""
「忽略纯重构」这条是我们被业务方投诉后加的——他们不关心我们重构了什么,只关心会不会影响使用。现在还剩人工校对,平均 6 分钟,总归省了 26 分钟。
成本核算
半年下来的总投入:
| 项目 | 金额/时间 |
|---|---|
| 模型调用费用 | ¥3,840(月均 640) |
| 卡点开发与维护 | 1 人 × 约 60 小时 |
| 团队适应成本 | 约 2 周低效期 |
收益方面两个实在的数字:线上缺陷密度从每千行 0.42 降到 0.31,Code Review 平均时长从 4.2 小时降到 3.1 小时(都是 -26%)。缺陷密度下降不完全是 AI 的功劳,同期还做了其他改进;但 CR 时长下降基本可以归因于 AI 预处理了那些低级问题。
几条经验
- 从「拦截型」卡点做起,别从「生成型」做起。生成的东西没人看,拦截的东西有即时反馈。我们活下来的五个里有四个是拦截型。
- AI 不可用时要降级而不是阻塞。这一点决定了卡点能不能长期存活。
- 给 AI 的输出设上限。提 11 条评论等于提 0 条,因为没人看。宁可只要 2 条高价值的。
- 要求输出证据。「这里可能有问题」没用,「当 X 为 null 时会 NPE」才有用。这一条适用于所有 AI 卡点。
- 别指望 AI 改变工作流。把它嵌进大家已经在用的工具里(Swagger、PR 评论、IDE),而不是新建一个页面。
小结
半年下来我的判断是:AI 在研发流程里的价值,目前主要集中在降低低级错误的逃逸率。NPE、资源泄漏、硬编码密钥、SQL 注入、边界条件遗漏——这些人类容易疲劳而 AI 不会疲劳的领域,效果最扎实。
而在「创造性工作」上(设计文档、架构决策、复杂业务逻辑实现),AI 目前只能当副驾。我们试过让它生成技术方案,输出看起来很完整,但缺少对现有系统的理解和权衡,没法用。
接下来打算试两个方向:一是把线上故障的日志分析做成卡点(现在每次故障都要翻半小时日志);二是用 AI 做接口兼容性检查(微服务间接口变更经常漏通知下游)。两个都是「拦截型」。