ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI写代码后不敢合并?用Skill构建可追溯证据链的代码评审方案

AI写代码后不敢合并?用Skill构建可追溯证据链的代码评审方案 1. 从“AI 能写代码”到“敢不敢合并”的真实困境最近半年我身边几乎每个开发团队都在用 AI 辅助写代码。Cursor、Copilot、Claude Code、Codex 这些工具轮番上阵生成一个函数、补全一个模块、甚至从零搭出一个 CRUD 服务都快得离谱。但有意思的是代码产出速度上去了合并请求Merge Request的通过率反而没怎么涨有些团队甚至下降了。原因不复杂。以前一个 MR 里三百行改动reviewer 一行行看心里有底。现在 AI 一口气生成八百行逻辑看着都对命名也规范注释也齐全但你就是不敢点那个 Merge 按钮。因为你不知道它有没有偷偷改掉一个边界条件有没有在异常分支里吞掉一个错误有没有把某个配置项的默认值从false改成了true。这就是标题里说的那个问题AI 写代码之后真正难的是“敢不敢合并”。写不是瓶颈审才是。而审的核心不是再看一遍代码长什么样而是要知道这段代码从哪来、改了什么、为什么这么改、有没有证据支撑它是对的。我最近在项目里落地了一套基于 Skill 机制的代码评审方案核心思路就是给每一次 AI 参与的改动附上一条可追溯的证据链。这篇文章就把这套东西拆开讲清楚它解决什么问题、Skill 怎么设计、git diff 怎么用、Agent 在其中扮演什么角色、实操中踩了哪些坑。适合正在用 AI 写代码、但被 review 环节卡住的团队和个人参考。2. 为什么传统 Code Review 在 AI 时代失效了2.1 传统 review 的隐含前提正在崩塌过去我们做代码评审默认几个前提改动量可控、作者能解释每一行、diff 是唯一的真相来源。reviewer 看 diff结合对业务的理解判断这段改动是否合理。这套流程运转了十几年靠的是“人写代码有惯性”——一个人写代码风格、思路、习惯是连续的reviewer 能顺着这个惯性去理解。AI 把这个惯性打断了。同一个 MR 里可能前半段是 Claude 写的后半段是 Copilot 补的中间还有一段是开发者自己手改的。三种“思路”混在一起diff 看起来是连续的但背后的决策逻辑是断裂的。你看到一个函数被重写不知道是 AI 觉得原来的写法不好还是开发者故意调整了业务逻辑。更麻烦的是AI 生成的代码往往表面质量很高。命名规范、注释完整、异常处理看起来也很周全。这种“表面正确”会让人放松警惕reviewer 扫一眼觉得没问题就过了。但真正的风险藏在细节里一个写成了一个try-catch把异常吞了一个并发场景下的竞态条件被忽略了。这些不是靠“看代码”能看出来的需要证据。2.2 “敢不敢合并”本质是信任问题我观察下来团队里对 AI 代码的信任度分三档。第一档是“完全不信”AI 写的全部重写等于没用。第二档是“盲目相信”AI 写的直接合出了问题再说。第三档是“有条件信任”也是我认为唯一可持续的方式你给我证据我就敢合。证据链要回答几个问题这段改动对应哪个需求或 issueAI 是基于什么上下文生成的改动前后的行为差异有没有测试覆盖有没有静态检查或类型检查通过这些信息如果能在 MR 里直接看到reviewer 的决策成本会大幅下降。这就是 Skill 要解决的问题。Skill 不是又一个 AI 写代码工具而是一个评审辅助层它把 AI 生成代码过程中的上下文、diff、检查结果串成一条链让“敢不敢合并”从主观判断变成有据可查的流程。2.3 Skill 机制为什么适合这个场景Skill 这个概念最近很热从 Claude 的 Skill 到各种 Agent 框架里的 Skill 插件本质都是把一段可复用的能力封装成标准接口。用在代码评审上Skill 的优势在于它可以被 Agent 调用也可以被 CI 调用还可以被开发者手动触发三种入口共享同一套逻辑。我选择 Skill 而不是写一个独立脚本核心原因是评审逻辑需要和 AI 生成过程解耦。生成代码的 Agent 可能是 Cursor、可能是 Claude Code、可能是自研的但评审 Skill 是统一的。不管代码从哪来都走同一套证据链检查。这样团队不需要绑定某个 AI 工具换工具的时候评审标准不变。3. 证据链代码评审 Skill 的整体设计3.1 核心思路让每一次改动都“自带说明”这套 Skill 的设计原则很简单任何进入 review 环节的改动必须附带一条可验证的证据链。证据链包含四个部分改动来源、改动意图、改动内容、验证结果。四者缺一不可缺了就在 MR 里标红reviewer 一眼能看到哪里没交代清楚。改动来源记录这段代码是 AI 生成的还是人写的用的哪个模型、哪个版本、什么 prompt 上下文。改动意图关联到具体的 issue、需求文档或对话记录。改动内容就是 git diff但要经过结构化处理不是原始 diff 直接扔出来。验证结果包括单元测试、类型检查、lint、以及针对 AI 代码特别加的“行为一致性检查”。3.2 为什么用 git diff 作为证据链的锚点git diff 是整个证据链的锚点因为它是唯一不可篡改的事实。AI 说的、开发者说的、issue 里写的都可能有偏差但 diff 是实际发生的改动。Skill 的所有分析都围绕 diff 展开从 diff 反推改动意图从 diff 定位风险点从 diff 关联测试覆盖。我试过几种 diff 处理方式。直接用git diff原始输出信息太杂reviewer 看起来累。用 GitHub/GitLab 的 diff 视图又绑定了平台。最后选择的是结构化 diff把 diff 按文件、按函数、按改动类型新增/删除/修改拆开每个改动块附带上下文行和风险标记。这样 reviewer 可以按块审而不是按行审。3.3 Skill 的输入输出定义Skill 的输入很明确一个 git ref 范围比如main..feature-branch加上可选的元数据issue 链接、AI 生成记录。输出是一份结构化的评审报告包含改动摘要哪些文件、多少行、改动类型分布风险清单每个风险点的位置、类型、严重程度证据链完整性评分四个部分各占多少分总分低于阈值就阻断合并建议操作哪些块需要人工重点看哪些可以快速过这个输出可以直接作为 MR 的评论发出去也可以作为 CI 的门禁条件。我目前的配置是证据链评分低于 70 分CI 直接 fail不允许合并。4. 核心细节解析与实操要点4.1 改动来源的采集别让 AI 生成记录丢失改动来源这块最容易出问题。AI 生成代码的时候上下文往往在对话窗口里一旦关掉就没了。我的做法是在生成阶段就埋点不管用哪个 AI 工具生成完代码后把 prompt、模型版本、生成时间写到一个.ai-trace.json文件里跟代码一起提交。这个文件不需要很复杂几个关键字段就够{ tool: claude-code, model: claude-sonnet-4, prompt_hash: a3f8..., generated_at: 2025-01-15T10:30:00Z, files_touched: [src/service/user.ts, src/utils/validate.ts], human_edited: true }human_edited这个字段很重要。如果 AI 生成后开发者手动改过reviewer 需要知道哪些部分是人工干预的。我见过太多情况AI 生成的代码有问题开发者改了一半结果 MR 里看不出来哪些是 AI 的锅哪些是人的锅。注意.ai-trace.json不要提交敏感信息prompt 里如果有业务数据或密钥只存 hash 不存原文。4.2 改动意图的关联从 diff 反推需求改动意图这块理想情况是每个 MR 都关联 issue。但实际项目里很多改动是“顺手改的”没有 issue。Skill 的处理方式是从 diff 反推意图分析改动的函数名、变量名、注释变化匹配项目里的需求文档或历史 issue。比如 diff 里出现了calculateDiscount这个函数被修改Skill 会去搜索项目文档里包含“折扣”“discount”的 issue列出最相关的几个让开发者确认。这个匹配不需要很精确目的是给 reviewer 一个上下文线索而不是自动判定。我实测下来这种反推的准确率大概在六成左右剩下的四成需要开发者手动补。但即使只补六成reviewer 的理解成本也降了很多。关键是不要让意图字段空着空着就在报告里标黄提示“此改动缺少意图说明”。4.3 结构化 diff 的生成把八百行拆成可审的块原始 diff 对 reviewer 不友好尤其是 AI 生成的大块改动。Skill 会把 diff 按以下维度拆解拆分维度说明用途按文件每个文件一个块快速定位影响范围按函数函数级改动单独成块关联测试覆盖按改动类型新增/删除/修改分开识别高风险操作按风险等级高/中/低标记优先审高风险块风险等级的判定规则我调了好几版。目前用的规则是涉及条件判断、异常处理、并发、配置默认值的改动标为高风险纯新增函数、纯注释、纯格式化标为低风险其余为中风险。这套规则不完美但比“全部一视同仁”强太多。4.4 验证结果的收集测试、类型、lint 一个不能少验证结果这部分Skill 会调用项目现有的检查工具把结果汇总。单元测试看覆盖率和通过率类型检查看有没有新增错误lint 看有没有新增警告。针对 AI 代码我额外加了一项行为一致性检查对改动前后的函数用同一组输入跑一遍对比输出是否一致。不一致就标红提示“行为可能发生变化”。这个检查用简单的脚本就能实现不需要复杂的测试框架。核心是给每个被修改的函数准备一组基准输入这些输入可以从现有测试用例里提取也可以手动构造。我一般让开发者对高风险函数手动补基准输入低风险函数用自动提取的。5. 实操过程与核心环节实现5.1 环境准备与 Skill 注册这套 Skill 我是在一个 Node.js 项目里落地的但逻辑跟语言无关。环境准备分三步装依赖、配检查工具、注册 Skill。依赖主要是simple-git读 diff、octokit/rest发 MR 评论可选、zod校验 trace 文件格式。检查工具复用项目已有的Jest 跑测试TypeScript 做类型检查ESLint 做 lint。Skill 注册这块我用的是一个轻量的 Agent 框架把评审逻辑封装成一个reviewSkill对象暴露analyze(refRange)方法。Agent 调用这个方法拿到报告CI 也调用同一个方法。这样保证手动触发和自动触发走同一套逻辑不会出现“本地看着没问题CI 挂了”的情况。5.2 核心流程从 git diff 到评审报告完整流程我拆成六步每一步都有对应的代码模块读取 diffgit diff main...feature --unified5拿带上下文的 diff。解析 diff用parse-diff库把原始 diff 解析成结构化对象按文件、hunk、行拆分。加载 trace读取.ai-trace.json校验格式缺失就标黄。风险分析对每个 hunk 跑风险规则打标签。验证收集并行跑测试、类型检查、lint、行为一致性检查。生成报告汇总成 Markdown 报告发到 MR 评论同时输出评分。这六步里风险分析是最需要调优的。我一开始规则写得太粗把所有if改动都标高风险结果报告里全是红块reviewer 直接忽略。后来改成按上下文判断如果if改动涉及边界值、、、或空值判断才标高风险普通逻辑调整标中风险。5.3 参数计算证据链评分怎么算证据链评分是这套 Skill 的门禁依据算法我调了几版目前用的是加权求和维度权重评分规则改动来源25%有 trace 且字段完整得满分缺失按比例扣改动意图25%关联 issue 得满分反推匹配得一半空着得零改动内容20%结构化 diff 完整得满分解析失败按比例扣验证结果30%测试通过类型通过lint通过行为一致得满分每缺一项扣对应分总分 100低于 70 阻断合并70 到 85 提示“建议补充证据”85 以上放行。这个阈值可以根据团队情况调我目前用的 70 是试了几次之后定的太松没效果太严开发者会绕过。提示评分不是目的目的是让开发者养成“提交前补证据”的习惯。跑了一两个月之后大部分 MR 的评分都能到 85 以上因为开发者知道缺什么会被卡。5.4 实操现场一次真实的评审记录拿最近一个真实 MR 举例。改动是给用户服务加了一个缓存层AI 生成的代码大概 400 行涉及 5 个文件。Skill 跑完之后的报告摘要改动来源有 trace模型 claude-sonnet-4human_edited: true改动意图关联到 issue #1234“用户查询性能优化”改动内容结构化 diff 拆成 12 个块其中 3 个高风险缓存失效逻辑、并发写入、默认 TTL 配置验证结果测试通过类型通过lint 通过行为一致性检查发现 1 处不一致缓存命中时的返回值类型从User变成了User | null评分 78提示“建议补充证据”。reviewer 重点看了那 3 个高风险块和 1 处不一致发现缓存命中时返回null的情况没有处理让开发者补了一个判断。整个过程大概 15 分钟如果没有这份报告reviewer 可能要花一小时逐行看还不一定发现那个null问题。6. 常见问题与排查技巧实录6.1 trace 文件丢失或格式错误这是最常见的问题。开发者用 AI 生成代码后忘了写 trace 文件或者写的时候字段不全。Skill 的处理是不阻断但标黄在报告里提示“改动来源缺失建议补充”。如果团队要求严格可以在 CI 里配置成阻断。排查技巧在项目的 pre-commit hook 里加一个检查如果 diff 里有大段新增代码比如超过 50 行但没有 trace 文件就提示开发者补。这个 hook 不需要很智能粗粒度判断就够。6.2 diff 解析失败重命名和二进制文件parse-diff对重命名和二进制文件的处理有时候会出问题。重命名文件会被拆成“删除新增”导致风险分析误判。我的处理是在解析前先跑git diff --name-status识别出重命名手动合并。二进制文件直接跳过在报告里标注“二进制文件改动需人工确认”。6.3 行为一致性检查的误报行为一致性检查有时候会误报比如函数内部重构了但外部行为没变检查却因为中间变量名变了而报不一致。我的处理是对比最终返回值不对比中间过程。如果返回值一致即使内部实现变了也判为一致。这个调整把误报率从三成降到了一成左右。6.4 开发者绕过 Skill 直接合并这是管理问题不是技术问题。我的做法是在 CI 里设门禁评分低于阈值直接 fail开发者想绕过就得改 CI 配置这个动作在团队里是可见的。另外我会定期看被绕过的 MR分析原因如果是 Skill 误判就调规则如果是开发者图省事就沟通。6.5 常见问题速查表问题现象排查思路解决方式trace 缺失报告标黄“改动来源缺失”检查.ai-trace.json是否存在补文件或在 pre-commit 加提示diff 解析错乱重命名文件被拆成删除新增跑git diff --name-status确认解析前合并重命名行为检查误报重构代码被标不一致看返回值是否真的变了改为只对比返回值评分过低CI fail无法合并看哪个维度扣分多补对应证据或调权重报告太长reviewer 不看看高风险块数量调风险规则减少误标6.6 独家避坑技巧第一个技巧trace 文件用 hash 存 prompt不存原文。我一开始存了原文结果 MR 里出现了业务数据差点出问题。后来改成只存 hash需要复现的时候用 hash 去日志系统查。第二个技巧风险规则宁少勿多。规则太多报告里全是红块reviewer 会麻木。我目前只保留最核心的几条边界值改动、异常处理改动、并发相关改动、配置默认值改动。其他都归为中低风险。第三个技巧评分阈值先松后紧。刚上线的时候设 60让开发者适应流程跑一个月后调到 70再跑一个月调到 75。一步到位设太高开发者会抵触。第四个技巧报告里给“建议操作”。不要只列问题要告诉 reviewer 哪些块可以快速过哪些块需要重点看。我一般把高风险块排在最前面附上“建议人工确认”的标记。7. 后续可以怎么扩展这套 Skill 目前只覆盖了评审环节但证据链的思路可以往前和往后延伸。往前可以在 AI 生成阶段就强制写 trace而不是生成完再补。往后可以把评审报告存档作为项目质量的历史记录后续出问题的时候可以回溯。我还试过把评审报告喂给另一个 AI 做二次分析让它总结“这个 MR 最可能出问题的地方”。效果一般因为 AI 分析 AI 代码容易陷入同样的盲区。后来改成用规则引擎做初筛人工做终审反而更稳。另一个扩展方向是多 AI 协作场景。现在一个 MR 里可能混了多个 AI 工具的产出trace 文件需要支持多条记录。我把.ai-trace.json改成了数组格式每个元素记录一次生成。这样 reviewer 能看到“这段是 Claude 写的那段是 Copilot 补的”理解成本更低。最后分享一个我在实际使用中的体会证据链的价值不在于自动化而在于让“不敢合并”变成“有据可合”。AI 写代码的速度已经够快了评审环节如果还靠人肉硬扛整个流程就会被卡住。Skill 做的是把评审需要的信息提前准备好让 reviewer 的注意力集中在真正需要判断的地方而不是花时间去找上下文。这套东西跑顺之后我们团队的 MR 平均合并时间从两天降到了半天而且合并后出问题的概率没有上升。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进