险些酿成P0事故!我用 AI 打造了 Android 代码评审"守门员"

摘要 :一次因"调试代码未清理"险些酿成的 P0 事故,让我意识到传统 Lint 工具的局限。本文深度复盘如何利用 Node.js + LLM (OpenAI/Ollama) 构建一套支持 Regex 静态拦截 + AI 深度语义分析 的混合式代码评审工具。不仅开源了核心逻辑,还提供了从 Git Hook 到 GitLab CI 的完整落地方案。
引言:被一行测试代码"偷袭"的 P0 事故
上周五发版前夕,我险些酿成一次"P0级事故"。起因极其简单:我在调试"新人大礼包"弹窗时,为了绕过后端接口的频控限制,直接修改了判断方法的返回值。
java
// 预期逻辑:需要校验频控和用户身份
// private boolean shouldShowMarketingDialog(UserProfile user) {
// return user.isNew() && frequencyController.check();
// }
// 实际提交的代码(调试完漏删了!):
private boolean shouldShowMarketingDialog(UserProfile user) {
return true;
}
提交代码时,由于涉及几十个文件的 UI 样式调整,我完全忘记了还原这段逻辑 。 Reviewer 在快速浏览 Diff 时,被大量的 UI 代码分散了注意力。更要命的是,这种写法连 Lint 都不会报警 ------因为在 Java/Kotlin 中,一个方法直接返回 true 是完全合法的(常用于 Mock 或临时开关),不像 if (true) 那样显眼,这导致它完美避开了所有静态检查。
🔥 万幸的是,测试同学在上线前的 Regression 阶段拦住了它。 如果没拦住,后果不堪设想:新版本上线后,所有用户 ------无论新老、无论场景------只要打开 App 就会遭到这个弹窗的"骑脸输出"。这就好比你刚进家门,就有人把一张传单糊你脸上。这绝对是一次标准的 P0 级事故。
事后复盘时,我背脊发凉。这次侥幸逃过一劫,但暴露了两个致命问题:
- 改动是"隐形"的,风险极大 :这行代码混在几十个文件的 UI 调整中,Reviewer 根本没注意到逻辑被篡改了。如果等到上线后才发现,后果就是全量用户投诉,损失不可估量。
- "低级错误"让人挫败 :写了几年代码,还犯这种忘记删调试代码的错误,说实话,感觉很差,显得非常不专业。
我意识到,靠"人的自觉"和"下次注意"是绝对靠不住的 。人总会疲惫,总会犯错。必须从流程上想办法,引入一个不知疲倦的"守门员",在代码合入前自动拦截这种低级失误。

基于这个痛点,我结合 AI Agent 技术开发了一个轻量级的 Review 辅助工具 aireview。它不仅能精准拦截这种"调式残留",还能结合业务上下文进行语义分析。今天就来拆解它的实现原理。
核心架构:为什么不直接用现成的商业工具?
市面上很多 AI Review 工具虽然强大,但往往很难满足团队的定制化需求。在构建 aireview 时,我确定了四个核心目标:
- 零成本/低成本 (Free & Affordable) :
- 支持 Ollama 本地部署(如 DeepSeek-Coder, Llama 3),完全免费且数据不出域。
- 兼容 OpenAI 协议,可使用各大大模型厂商赠送的免费 Token(新账号福利),灵活切换。
- 流程完全可控 (Controllable Workflow) :
- 既可以在本地
pre-commit阶段运行小模型进行快速检查。 - 也可以在 CI/CD 阶段调用云端大模型进行深度评审。
- 既可以在本地
- 数据驱动治理 (Data Statistics) :
- 不仅仅是拦截,更要记录。工具会自动生成
.jsonl统计文件,让我们知道团队中哪些规范最容易被打破(例如:80% 的阻断是因为命名不规范),从而进行针对性的技术分享。
- 不仅仅是拦截,更要记录。工具会自动生成
- Prompt 快速验证 (Prompt Verification) :
- 基于本地 Git 提交,可以实时调整 Prompt 并验证效果,不需要等待漫长的 CI 流水线。
基于这些目标,我设计了 "混合双打" 的架构:
- 第一道防线(静态拦截) :用极低成本的 Regex 正则,秒杀显而易见的"低级错误"(如通用命名
handle,temp,硬编码中文等)。 - 第二道防线(AI 深度评审):只有通过了静态拦截,才将 Diff 投喂给 LLM,进行逻辑漏洞、规范一致性等深度检查。
1. 第一道防线:静态规则引擎
针对那次 P0 事故,我定义了一套"黑名单"。这些命名虽然合法,但在大型项目中极易造成混淆。
在 lib/core/review.js 中,我实现了一个轻量级的分析器:
javascript
// lib/core/review.js (核心片段)
export function analyzeViolationsResult(diffText) {
// 💀 死亡名单:禁止使用的通用方法名
const badMethods = [
"handle", "doIt", "process", "getData", "setInfo",
"onEvent", "check", "verify", "temp", "test1"
];
// 💀 交互黑洞:禁止使用的通用按钮文案
const badButtons = [
"确定", "提交", "下一步", "处理", "执行"
];
// 正则匹配函数定义
const methodRegexes = [
/\bfun\s+([A-Za-z0-9_]+)\s*\(/, // Kotlin
/\b(public|private|protected)\s+(static\s+)?[A-Za-z0-9_<>\[\]]+\s+([A-Za-z0-9_]+)\s*\(/ // Java
];
// ... 遍历 Diff 行进行匹配 ...
if (badMethodHits.length > 0) {
return {
blocks: true,
markdown: "### 🛑 命名违规\n" + badMethodHits.join("\n")
};
}
}
这层拦截是毫秒级 的。只要你写了 fun handle(),Pre-commit Hook 会直接拒绝你的提交,根本不需要消耗 AI Token。
2. 第二道防线:LLM 深度评审
当代码通过了静态检查,真正的 AI 评审才开始。这里有两个关键点:上下文注入 和 Prompt 工程。
上下文注入 (Context Injection)
单纯把 Diff 扔给 AI,它只能看懂语法。为了让它懂"业务规范",我们需要注入项目特有的规则文档。
我在项目中实现了一套动态规则加载机制。工具会自动识别变更文件的类型,加载对应的规范文档:
- Kotlin/Java/XML -> 加载
android_guidelines_specific.md - Dart/Yaml -> 加载
flutter_guidelines_specific.md - Swift/ObjC -> 加载
ios_guidelines_specific.md - Go/Python/JS -> 加载
backend_guidelines_specific.md - 所有文件 -> 额外加载
coding_guidelines_general.md(通用规范)
在调用 LLM 时,这些规则会被动态拼装进 System Prompt:
javascript
// 伪代码逻辑:lib/core/review.js
export function getRules(files) {
let rules = [read('general_rules.md')]; // 基础通用规范
// 根据文件后缀动态加载特定规范
if (files.some(f => f.endsWith('.kt'))) {
rules.push(read('android_rules.md'));
}
if (files.some(f => f.endsWith('.dart'))) {
rules.push(read('flutter_rules.md'));
}
// ... 其他语言 ...
return rules.join("\n\n");
}
const systemPrompt = `
你是一个资深的技术专家。
请根据以下【项目编码规范】对代码 Diff 进行审查:
${getRules(changedFiles)}
重点关注:
1. 潜在的 Bug 和逻辑漏洞
2. 是否违反了上述特定语言的规范
3. 性能隐患
`;
// 💡 实战技巧:Prompt 对比
// Bad Prompt: "帮我看看这个代码哪里有问题" -> AI: "这段代码看起来不错,但建议..." (废话文学)
// Good Prompt: "作为资深 Android 专家,找出 Diff 中的逻辑漏洞、内存泄漏风险。仅输出 P0/P1 级问题,忽略格式问题。" -> AI: "Line 14: 存在空指针风险..." (直击要害)
本地与云端模型的灵活切换
考虑到隐私和成本,我设计了双模支持:
- 公司项目:连接私有部署的 Ollama (DeepSeek-Coder / Llama 3),数据不出内网。
- 开源项目:使用 OpenAI (GPT-4o) 获取最强能力。
javascript
// lib/core/review.js
export async function callAI(prompt) {
const ollamaModel = process.env.OLLAMA_MODEL;
const openaiKey = process.env.OPENAI_API_KEY;
if (ollamaModel) {
// 🏠 本地模式:调用 Ollama 接口
return callOllama(prompt, ollamaModel);
}
if (openaiKey) {
// ☁️ 云端模式:调用 OpenAI 接口
return callLLM(prompt);
}
throw new Error("❌ 未配置模型");
}
实战落地:从 CLI 到 CI/CD
工具写好了,怎么让大家用起来?我提供了两种无痛接入方式。
1. 极客模式:Pre-commit Hook
为了让开发者在 git commit 时就能收到反馈,我写了一个 install_hook.sh 脚本,将工具挂载到 .git/hooks/pre-commit。
效果: 当你试图提交烂代码时,终端会直接报错并拦截:
bash
$ git commit -m "feat: add login"
🤖 AI Code Review Running...
Checking local diff...
### 🛑 命名违规
- fun process() {
- val temp = "test"
❌ Commit blocked. Please fix the issues above.
2. 团队模式:GitLab CI 集成
对于存量代码或 Merge Request,我们在 CI 流水线中集成了这个工具。只需在 .gitlab-ci.yml 中引用模板:
yaml
include:
- project: 'infra/ai_rules'
file: '/aireview/ci-template.yml'
ai_code_review:
variables:
OPENAI_MODEL: "gpt-4o"
CI 跑完后,机器人会直接在 Merge Request 的评论区贴出评审报告(Markdown 格式),包含修改建议和代码对比。

效果与反思
个人洞察 (Insight)
- AI 不是银弹,但是最好的"副驾驶" 。它在发现逻辑死锁、业务一致性上依然不如人类资深专家,但在规范执行 和查漏补缺上,它比人类更有耐心,更不知疲倦。
- Prompt > Model 。很多人觉得 AI Review 效果不好,往往是因为没给它"规范文档"。你如果不告诉它什么是"好代码",它怎么知道什么是"坏代码"?
- Local First 是未来 。随着 DeepSeek-Coder V2 等开源模型的强大,在本地跑 Review Agent 已经完全可行。这解决了代码隐私的最大痛点。
总结
那次"虚惊一场"的经历虽然没有造成实质损失,但它给了我们极大的警示,也催生了这个工具。现在,aireview 已经无缝融入了我的日常开发流。但这只是开始,我仍在持续打磨其背后的 Agent 智能体,通过不断优化推理逻辑和规则库,让它在实战中越来越完善,最终成为那个无可替代的"代码守门员"。
如果你也想为你的项目加上这道防线,或者对源码感兴趣,欢迎在评论区交流!
未来展望 :下一步,我计划引入 Agent 自动修复 能力。不仅仅是指出问题,而是直接生成 git apply 补丁,让开发者一键修复。