七牛云实训营里我做的第三个项目。给一个 GitHub PR,自动审查代码、列出问题。这类工具最容易翻车的地方不是”漏报”,而是”乱报”——对一段没问题的代码硬挑出一堆假问题,开发者看两次就不信了。所以我把整个设计的重心放在误报控制上。
核心:两段式模型路由
审查分两遍走,用不同的模型、各司其职:
- 第一遍(
deepseek-chat,便宜):快速扫整个 diff,把所有”可疑点”作为候选捞出来。这一步追求召回,宁可多捞。 - 第二遍(
deepseek-reasoner,会推理):对每一个候选单独深读一次,确认还是否决,并把它的reasoning_content(推理过程)捕获下来。被判为误报的直接丢掉。
这样既不漏(第一遍宽),又不乱报(第二遍严),而且每条留下来的问题都带可追溯的推理链,前端能展开看”它为什么觉得这是个问题”。
最能说明它有用的是一次实测:我拿一个已经合并的、正确的修复 PR 去跑,第一遍扫出 1 个候选,第二遍 reasoner 深读后判定它是误报并丢弃(理由是”这里的早期返回是正确处理”),最终输出 0 条问题。对干净代码不硬报,这正是误报控制在起作用。 反过来,拿一个我故意塞了 SQL 注入和空指针的 PR 去跑,第一遍 6 个候选,第二遍丢掉 2 个误报、保留 4 个真问题。
一次完整审查大约 21.7k token、23.8 秒。
高危问题再加一道交叉验证
对 reasoner 确认的高危问题,我再用 Azure 上的另一个模型(GPT-4.1-mini)独立判一次:
- 两个模型结论一致 → 给这条问题的置信度加分;
- 结论不一致 → 降级成中危,并标注”模型间有分歧”。
不同来源的模型互相印证,能进一步压低”言之凿凿的错误判断”。这条只在高危问题上触发,避免在低危问题上浪费额度。
一个测试本身救了我
写测试时发现,有个测试用例在静默地发起真实的 Azure 调用——这会让测试套件变慢、不稳定,还偷偷烧额度。把它揪出来改成 mock 之后,整套测试从”偶尔慢且联网”变回 0.9 秒纯本地。这件事让我更确信:测试该是确定性的、不依赖网络和额度,否则它迟早会骗你或拖累你。
UI 上的一个刻意选择
前端我特意没做成”AI 产品默认那一套”——没有彩虹渐变、没有 glow、没有玻璃拟态、没有 emoji。而是:暖色深底、单一主色(琥珀/赭,像编辑器的警告色和 CI 工具)、严重度只用左边框的语义色表示、圆角收到 2px、正文用 JetBrains Mono。目标是让它看起来像一个给开发者用的工具,而不是一个 AI demo。工具气质这东西,是靠克制堆出来的。
小结
这个项目我最满意的是它的克制:不堆功能、不乱报问题、不做花哨 UI。一个代码审查工具的可信度,恰恰来自它”敢对干净代码说没问题”。