资讯动态

AI代码评审Skill:基于git diff的证据链设计与落地实践

发布时间:2026/10/8 16:40:31 来源:尧图企业网站定制
1. 为什么“AI 写完代码”只是上半场真正卡脖子的是合并那一下我最近半年在团队里推 AI 辅助编码从最早的补全插件一路用到现在的 Agent 式自动改代码感受特别深生成代码这件事门槛已经被拉得很低了。你给一个足够清晰的上下文不管是让模型补一个函数、重构一个模块还是根据 issue 直接产出 patch它都能给你一份看起来像模像样的git diff。但真正让我和身边几个做基础架构的朋友头疼的从来不是“AI 能不能写出来”而是**“这份 diff 我到底敢不敢点那个 Merge 按钮”**。这个标题里说的“有证据链的代码评审 Skill”本质上就是在解决这个心理门槛。它不是一个单纯的 lint 工具也不是那种只会说“这段代码看起来不错”的聊天机器人而是一套把 AI 生成的改动、评审意见、验证结果串成可追溯链条的评审能力。你可以把它理解成一个专门盯git diff的 Agent它不负责写代码它负责在你合并之前把“这段改动为什么能合、凭什么能合、合了之后会不会炸”讲清楚并且留下证据。适合谁来参考我觉得有三类人最需要。第一类是已经在用 AI 写代码、但每次合并都心里发虚的开发者你缺的不是生成能力是评审和验证的闭环。第二类是在团队里负责代码质量、想引入 Agent 做评审但不知道怎么落地的人你需要一套可复现的 Skill 设计思路。第三类是对 Agent 开发感兴趣、想找一个真实场景练手的人代码评审这个场景比“帮我订机票”那种 demo 有价值得多因为它有明确的输入输出、有客观的验证标准、有真实的失败代价。我下面会把这套东西拆开讲为什么评审 Skill 要围绕git diff来设计、证据链到底包含哪些东西、怎么一步步搭起来、以及我在实操里踩过的那些坑。全程按我自己的落地经验来说不搞虚的。2. 代码评审 Skill 的整体设计与思路拆解2.1 为什么评审对象必须是 git diff而不是整个仓库很多人做 AI 评审的第一反应是把整个项目丢给模型让它“看看有没有问题”。我试过效果很差原因有三个。第一上下文窗口再大也扛不住一个真实仓库你塞进去的代码越多模型注意力越分散最后它只会挑几个显眼的地方说些正确的废话。第二评审的本质是“针对改动”而不是“针对存量”一个仓库里躺着的历史代码可能有几百个问题但你这次要合的是这 20 行改动评审必须聚焦。第三diff 天然带有结构信息哪些行是新增、哪些是删除、改了哪个文件、影响了哪些函数这些信息本身就是评审的线索。所以这套 Skill 的第一个设计决策就是输入锁定为git diff而不是整个代码库。具体来说我会让它拿到的输入包含这几样东西git diff --staged或者git diff main...HEAD的输出也就是本次要合并的改动改动涉及文件的完整内容不是全部文件只取被改的那些本次改动的 commit message 和关联的 issue 描述如果有项目里已有的测试文件和 CI 配置这样做的逻辑是diff 告诉 Agent“改了什么”完整文件告诉它“改的东西在什么上下文里”issue 告诉它“为什么改”测试和 CI 告诉它“怎么验证”。四样东西凑齐评审才有依据。只给 diff它会误判只给文件它找不到重点。提示如果你的项目 diff 特别大比如一次改了 50 个文件不要硬塞。我的做法是先按目录或模块切分让 Agent 分批评审最后再做一个汇总。一次性喂太大的 diff模型会开始“偷懒”只评前面几个文件。2.2 证据链到底链的是什么从“我觉得”到“我能证明”标题里“证据链”这个词是我最看重的。普通的 AI 评审给你的是观点“这个函数可能有空指针风险”“这里建议加个边界检查”。观点的问题在于你没法判断它是真发现了问题还是模型在“礼貌性地找点话说”。而证据链要求每一条评审意见都必须挂上可验证的依据。我设计的证据链包含四个环节缺一不可环节内容作用改动定位具体到文件、行号、函数名让意见可定位不是泛泛而谈判断依据引用代码上下文、类型定义、调用方说明“为什么这么判断”验证方式对应的测试用例、可执行的检查命令说明“怎么证明这个判断”风险等级阻断合并 / 建议修改 / 仅提示让合并决策有优先级举个具体例子。假设 AI 改了一个函数把某个参数从必填改成了可选。普通评审会说“注意参数可选后调用方可能传空”。而带证据链的评审会这样说文件src/order/service.ts第 87 行createOrder的couponId参数由必填改为可选。依据该参数在函数体内第 102 行被直接用于couponRepo.findById(couponId)未做空值判断。验证方式现有测试order.service.spec.ts中没有覆盖couponId为空的用例建议补充。风险等级阻断合并。你看这条意见里有定位、有依据、有验证缺口、有等级。你拿到之后不需要再去猜直接就能决定是补测试还是改代码。这就是证据链和普通评审的区别。2.3 Skill 和普通 Agent 的区别为什么强调“Skill”这个词现在大家都在聊 Agent但很多人把 Agent 和 Skill 混着用。我自己的理解是Agent 是“谁来做”Skill 是“怎么做”。一个 Agent 可以挂载多个 Skill每个 Skill 是一套封装好的、针对特定任务的能力包含提示词、工具调用流程、输出格式约束。代码评审这个场景特别适合做成 Skill因为它有几个固定特征输入格式固定diff 上下文、评审维度相对固定正确性、安全性、性能、可维护性、输出格式需要固定结构化意见 证据链。你把它做成一个可复用的 Skill好处是一致性不管谁来用、评审哪个项目评审的维度和标准是统一的不会今天严明天松可迭代发现漏判了某类问题改 Skill 的提示词和检查项就行不用重新训模型可组合这个评审 Skill 可以和“自动补测试 Skill”“自动生成 commit message Skill”串起来形成流水线我见过太多人把评审逻辑写死在一次性的对话里用完就丢下次还得重新描述一遍需求。做成 Skill才是能沉淀下来的资产。3. 核心细节解析与实操要点3.1 评审维度的拆解别让 Agent 什么都评要分优先级一开始我让 Agent“全面评审”结果它给出的意见又长又杂既有“变量命名可以更清晰”这种鸡毛蒜皮也有“这里可能有并发问题”这种要命的。混在一起你反而不知道该看哪个。后来我把评审维度拆成了四层按优先级从高到低第一层正确性。改动是否实现了它声称要实现的功能逻辑是否有明显错误边界条件是否处理。这是唯一能“阻断合并”的层级。第二层安全性。是否有注入风险、权限绕过、敏感信息泄露、不安全的反序列化等。这一层也允许阻断合并。第三层性能与资源。是否有 N1 查询、无界循环、内存泄漏、不必要的全表扫描。这一层一般是“建议修改”。第四层可维护性。命名、注释、重复代码、复杂度。这一层是“仅提示”不阻断。拆完优先级之后Agent 的输出质量立刻上来了。因为它知道什么该重点说、什么可以一笔带过。我在 Skill 的提示词里会明确写“正确性和安全性问题必须给出证据链并标记风险等级可维护性问题只列点不展开。”这样输出就不会头重脚轻。3.2 提示词怎么写把评审标准变成可执行的指令评审 Skill 的核心是提示词。我踩过的最大坑是提示词写得太“人性化”模型就开始自由发挥。比如你写“请仔细检查代码有没有问题”它就会给你一堆模棱两可的话。后来我改成结构化指令效果稳定很多。我的提示词骨架大概是这样你是一个代码评审 Agent只评审给定的 git diff不评审其他内容。 输入 - diff本次改动 - files改动涉及的完整文件内容 - context关联的 issue 描述和 commit message - tests项目现有测试文件列表 评审规则 1. 只针对 diff 中新增或修改的行提出意见不评价未改动的历史代码 2. 每条意见必须包含文件路径、行号、问题描述、判断依据、验证方式、风险等级 3. 风险等级只能是BLOCK阻断合并、WARN建议修改、INFO仅提示 4. 如果 diff 中没有发现问题明确输出“未发现阻断性问题”不要为了凑数编造意见 5. 对于 BLOCK 和 WARN 级别的问题必须指出对应的测试缺口或验证命令 输出格式 按风险等级分组每条意见用固定字段列出最后给出一句总体合并建议。这里有几个细节值得说。“不要为了凑数编造意见”这句特别重要因为模型有讨好倾向你让它评审它总觉得不说点什么显得不专业。明确告诉它“没问题就说没问题”能大幅减少噪音。“只针对 diff 中新增或修改的行”也很关键否则它会去翻历史代码把陈年老账都翻出来评审就失焦了。3.3 工具调用让 Agent 真的去跑测试而不是“建议你跑测试”证据链里最有价值的一环是验证方式。如果 Agent 只是说“建议补充测试”那还是停留在建议层面。我做的升级是给评审 Skill 挂上执行测试的工具让它能真的去跑一遍现有测试把结果作为证据。具体流程是这样的Agent 评审完 diff 后如果发现有 BLOCK 级别的问题它会尝试执行项目里相关的测试命令比如npm test -- order.service把测试结果附在意见后面。如果测试通过但覆盖不到改动点它会明确指出“现有测试通过但未覆盖第 87 行的空值分支”。如果测试直接失败那这条意见的证据就更硬了。注意让 Agent 执行命令一定要做白名单限制。我只允许它跑测试、lint、类型检查这几类只读或安全的命令绝不允许它执行任何写操作、网络请求或删除操作。这是 Agent 安全的基本底线别嫌麻烦。3.4 输出格式的约束结构化才能被消费评审结果如果是一大段自然语言人看着累机器也没法处理。我强制要求输出结构化每条意见是一个对象包含file、line、level、message、evidence、verify这几个字段。这样带来两个好处一是可以直接渲染成 PR 评论二是可以统计“这次评审发现了几个 BLOCK、几个 WARN”形成质量趋势。我甚至把这个输出接到了一个简单的看板上每次合并前看一眼这次改动有几个阻断项、测试覆盖有没有缺口。时间长了你会发现AI 生成的代码里BLOCK 级别的问题往往集中在几个固定模式上比如空值处理、并发访问、错误吞掉。这些模式反过来又能用来优化提示词形成正循环。4. 实操过程与核心环节实现4.1 环境准备最小可运行的评审 Skill 需要什么先说清楚这套东西不需要多复杂的基建。我用的是最朴素的组合一个支持工具调用的模型接口、一个本地脚本负责收集 diff 和文件内容、一个执行器负责跑测试。整个流程可以跑在本地也可以放进 CI。下面是我实际用的目录结构review-skill/ collect.sh # 收集 diff、文件内容、上下文 review.py # 调用模型传入提示词和输入 verify.sh # 执行测试和 lint返回结果 schema.json # 输出格式约束 prompts/ review.md # 评审提示词collect.sh干的事情很直接#!/bin/bash # 收集本次改动 git diff main...HEAD /tmp/review/diff.txt # 收集改动涉及的文件 git diff --name-only main...HEAD | while read f; do echo $f /tmp/review/files.txt cat $f /tmp/review/files.txt done # 收集 commit message git log main..HEAD --prettyformat:%s%n%b /tmp/review/context.txt这一步的关键是只收集被改动的文件不要图省事把整个 src 目录都塞进去。我一开始就是这么干的结果 token 消耗翻了好几倍评审质量反而下降。4.2 评审执行一次完整的评审是怎么跑起来的收集完输入review.py负责组装提示词并调用模型。核心逻辑是读提示词模板、读输入文件、拼成最终请求、解析返回的结构化结果。这里我强烈建议用 JSON schema 约束输出而不是让模型自由输出再自己解析。自由输出的格式漂移太严重了今天用line明天用lineNumber解析脚本天天改。评审跑起来之后输出大概长这样我简化了一下{ summary: 本次改动共 3 个文件发现 1 个阻断项、2 个建议项, issues: [ { file: src/order/service.ts, line: 87, level: BLOCK, message: couponId 改为可选后未做空值判断, evidence: 第 102 行直接调用 couponRepo.findById(couponId), verify: 现有测试未覆盖空值分支建议补充用例 } ], merge_advice: 存在阻断项建议修复后再合并 }拿到这个结果我做的第一件事不是直接信而是抽查证据。我会点开对应的文件和行号确认 Agent 说的位置对不对、依据是否成立。这一步不能省因为模型偶尔会“张冠李戴”把行号算错或者把上下文搞混。抽查几次之后如果准确率稳定就可以适当放宽。4.3 验证环节让测试结果成为合并决策的硬依据评审意见是“软”的测试结果是“硬”的。我的做法是任何 BLOCK 级别的意见都必须有对应的验证动作。要么是现有测试失败要么是明确指出测试缺口。如果 Agent 说某处有并发问题但既没有测试失败也说不清怎么验证那这条意见我会降级处理不轻易阻断合并。verify.sh负责执行验证命令我给它配了白名单#!/bin/bash # 只允许这几类命令 case $1 in test) npm test -- $2 ;; lint) npm run lint ;; type) npm run typecheck ;; *) echo 命令不在白名单内拒绝执行; exit 1 ;; esac这个白名单机制是必须的。Agent 再聪明也不能让它随便执行命令。我见过有人图省事直接eval模型返回的命令字符串这是非常危险的做法一旦模型被诱导或者产生幻觉后果不可控。4.4 合并决策把评审结果变成可执行的 checklist评审跑完、验证跑完最后一步是把结果转成合并前的 checklist。我的习惯是让 Skill 输出一个简短的清单贴在 PR 描述里[ ] 阻断项已修复1 项[ ] 建议项已评估2 项其中 1 项已改1 项记录为后续优化[ ] 相关测试已补充并通过[ ] 类型检查通过这个 checklist 的价值在于它把“敢不敢合并”这个模糊的心理问题变成了几个可以打勾的具体动作。你不需要再凭感觉判断照着清单走就行。这也是我觉得这套 Skill 最实用的地方它不替你做决定但它把做决定需要的信息都摆在你面前了。5. 常见问题与排查技巧实录5.1 评审意见太多太杂怎么办这是最常见的问题。Agent 一上来给你列 20 条意见你根本看不过来。我的解决办法是在提示词里加数量约束和优先级过滤BLOCK 级别最多 5 条WARN 最多 8 条INFO 只列标题不展开。如果超过这个数量说明要么 diff 太大需要拆分要么提示词太宽松需要收紧。另外我会定期回顾“被忽略的意见”。如果某类 INFO 意见我从来不看那下次就把它从评审维度里去掉。评审不是越多越好能让你快速做决策的评审才是好评审。5.2 Agent 误报怎么处理误报一定会有关键是怎么处理。我的经验是分两步先确认是不是真误报再决定是改提示词还是加过滤规则。有些“误报”其实是 Agent 看到了你没注意的边界情况这种要保留。真正的误报比如把正确的空值判断说成缺失通常是提示词里对“空值判断”的定义不够明确我会在提示词里补一个正例和反例。我维护了一个误报记录表格式大概是这样误报描述出现次数处理方式把可选链?.误判为未判空3提示词补充说明可选链等价于判空把测试文件里的 mock 数据当成生产代码评审2输入时排除*.spec.ts文件对已存在的类型守卫视而不见1提示词要求先检查类型定义再判断这张表跑一段时间误报率会明显下降。5.3 diff 太大导致评审质量下降怎么办前面提过要拆分这里说具体怎么拆。我的拆分原则是按“逻辑单元”拆而不是按文件数量拆。比如一次改动涉及 10 个文件但其中 8 个是同一个功能的重构2 个是配置修改那就拆成两批评审一批评功能重构一批评配置。每批的 diff 控制在 500 行以内评审质量最稳定。如果实在拆不开比如一个大重构就是牵一发动全身那我会降级评审策略只评正确性和安全性跳过可维护性并且明确告诉 Agent“这是大改动优先找阻断性问题”。这样虽然覆盖不全但至少能抓住要命的点。5.4 怎么判断评审 Skill 本身有没有退化Skill 用久了会退化因为项目在变、代码风格在变提示词可能慢慢就不适配了。我用的判断方法是定期做“已知问题回放”准备一批历史上真实出现过 bug 的 diff让当前版本的 Skill 去评看它能不能把当时的问题找出来。如果找不出来说明 Skill 退化了需要更新提示词或补充检查项。这个回放集我建议至少维护 20 个案例覆盖空值、并发、边界、安全这几类高频问题。每次改完提示词跑一遍回放集通过率不下降才敢上线。这比凭感觉判断靠谱得多。5.5 团队协作时怎么统一评审标准一个人用和团队用是两回事。团队用最大的问题是每个人对“什么算阻断项”的理解不一样。我的做法是把风险等级的定义写死在 Skill 里并且做成团队共识文档BLOCK会导致运行时错误、数据错误、安全问题必须有测试证明已修复WARN可能影响性能或可维护性需要在 PR 里说明处理方式INFO风格建议不强制处理定义写清楚之后Agent 的输出就有了统一标准团队成员看同一份评审结果理解是一致的。这比每个人凭经验判断要公平得多也高效得多。6. 我对这套评审 Skill 的真实体会说实话这套东西刚搭起来的时候我一度觉得它有点“多此一举”——AI 写的代码我自己看一眼也能判断个大概何必再搞个 Agent 来评。但用了两个月之后我的看法完全变了。它最大的价值不是替我发现了多少 bug而是把“合并前的犹豫”变成了“有依据的决策”。以前我面对一份 AI 生成的 diff心里总有个声音说“要不再看看”但又不知道该看什么。现在这个声音变成了具体的 checklist阻断项修了吗、测试补了吗、类型过了吗。看完打勾心里就踏实了。还有一个意外收获是这套 Skill 反过来提升了 AI 生成代码的质量。因为我知道生成之后会被严格评审所以在写提示词让 AI 生成代码时就会主动要求它处理空值、补测试、说明边界。生成和评审形成了一个闭环整体效率反而比“生成完再人工挑毛病”高得多。如果你现在也在用 AI 写代码我建议你先别急着上复杂的 Agent 框架就从最小的一步开始把git diff收集起来写一段提示词让模型按“定位 依据 验证 等级”的格式给你评审意见然后自己抽查几条。跑通这个最小闭环你就知道这套东西对你有没有用了。至于后面要不要挂测试执行、要不要接 CI、要不要做看板都是在这个闭环跑通之后自然生长出来的。别一上来就追求大而全评审这件事能让你敢点合并按钮的才是好评审。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑