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

险些酿成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 级事故

事后复盘时,我背脊发凉。这次侥幸逃过一劫,但暴露了两个致命问题:

  1. 改动是"隐形"的,风险极大 :这行代码混在几十个文件的 UI 调整中,Reviewer 根本没注意到逻辑被篡改了。如果等到上线后才发现,后果就是全量用户投诉,损失不可估量。
  2. "低级错误"让人挫败 :写了几年代码,还犯这种忘记删调试代码的错误,说实话,感觉很差,显得非常不专业

我意识到,靠"人的自觉"和"下次注意"是绝对靠不住的 。人总会疲惫,总会犯错。必须从流程上想办法,引入一个不知疲倦的"守门员",在代码合入前自动拦截这种低级失误。

基于这个痛点,我结合 AI Agent 技术开发了一个轻量级的 Review 辅助工具 aireview。它不仅能精准拦截这种"调式残留",还能结合业务上下文进行语义分析。今天就来拆解它的实现原理。


核心架构:为什么不直接用现成的商业工具?

市面上很多 AI Review 工具虽然强大,但往往很难满足团队的定制化需求。在构建 aireview 时,我确定了四个核心目标:

  1. 零成本/低成本 (Free & Affordable)
    • 支持 Ollama 本地部署(如 DeepSeek-Coder, Llama 3),完全免费且数据不出域。
    • 兼容 OpenAI 协议,可使用各大大模型厂商赠送的免费 Token(新账号福利),灵活切换。
  2. 流程完全可控 (Controllable Workflow)
    • 既可以在本地 pre-commit 阶段运行小模型进行快速检查。
    • 也可以在 CI/CD 阶段调用云端大模型进行深度评审。
  3. 数据驱动治理 (Data Statistics)
    • 不仅仅是拦截,更要记录。工具会自动生成 .jsonl 统计文件,让我们知道团队中哪些规范最容易被打破(例如:80% 的阻断是因为命名不规范),从而进行针对性的技术分享。
  4. Prompt 快速验证 (Prompt Verification)
    • 基于本地 Git 提交,可以实时调整 Prompt 并验证效果,不需要等待漫长的 CI 流水线。

基于这些目标,我设计了 "混合双打" 的架构:

flowchart TD classDef user fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1 classDef hook fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#e65100 classDef ai fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c classDef block fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#b71c1c classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20 User["👨‍💻 Developer"]:::user -->|git commit| Hook("🪝 Pre-commit Hook"):::hook subgraph Layer1 ["🛡️ Layer 1: Static Defense (ms)"] direction TB Hook -->|Regex Check| Static{"🚫 Naming/Rules"}:::hook Static -->|Violation| Block["🛑 Block Commit"]:::block end subgraph Layer2 ["🧠 Layer 2: AI Intelligence (s)"] direction TB Static -->|Pass| Context("📂 Context Loader"):::ai Context -->|Inject Guidelines| Router{"⚙️ Model Router"}:::ai Router -->|Local/Private| Ollama["🦙 Ollama (DeepSeek)"]:::ai Router -->|Cloud/Open| OpenAI["☁️ OpenAI (GPT-4o)"]:::ai Ollama --> Analysis("📝 AI Review Analysis"):::ai OpenAI --> Analysis end Analysis -->|Issues Found| Report["⚠️ Generate Report"]:::block Analysis -->|Clean| Success["✅ Commit Success"]:::success Block -->|Feedback| User Report -->|Feedback| User
  1. 第一道防线(静态拦截) :用极低成本的 Regex 正则,秒杀显而易见的"低级错误"(如通用命名 handle, temp,硬编码中文等)。
  2. 第二道防线(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 (通用规范)
sequenceDiagram participant Git as 😺 Git Diff participant Loader as 📥 Rule Loader participant Prompt as 📝 System Prompt participant LLM as 🤖 Model Git->>Loader: Detect File Types (.kt, .dart) Loader->>Loader: Match Rules Note right of Loader: Android Rules + General Rules Loader->>Prompt: Inject Markdown Content Prompt->>LLM: Send Request (Diff + Context) LLM-->>Prompt: Return Review Result

在调用 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)

  1. AI 不是银弹,但是最好的"副驾驶" 。它在发现逻辑死锁、业务一致性上依然不如人类资深专家,但在规范执行查漏补缺上,它比人类更有耐心,更不知疲倦。
  2. Prompt > Model 。很多人觉得 AI Review 效果不好,往往是因为没给它"规范文档"。你如果不告诉它什么是"好代码",它怎么知道什么是"坏代码"?
  3. Local First 是未来 。随着 DeepSeek-Coder V2 等开源模型的强大,在本地跑 Review Agent 已经完全可行。这解决了代码隐私的最大痛点。

总结

那次"虚惊一场"的经历虽然没有造成实质损失,但它给了我们极大的警示,也催生了这个工具。现在,aireview 已经无缝融入了我的日常开发流。但这只是开始,我仍在持续打磨其背后的 Agent 智能体,通过不断优化推理逻辑和规则库,让它在实战中越来越完善,最终成为那个无可替代的"代码守门员"。

如果你也想为你的项目加上这道防线,或者对源码感兴趣,欢迎在评论区交流!

未来展望 :下一步,我计划引入 Agent 自动修复 能力。不仅仅是指出问题,而是直接生成 git apply 补丁,让开发者一键修复。

相关推荐
野生的码农10 小时前
做好自己的份内工作,等着被裁
程序员·ai编程·vibecoding
草梅友仁11 小时前
墨梅博客 1.0.0 发布与更新 | 2026 年第 2 周草梅周报
github·ai编程·nuxt.js
draking13 小时前
1小时用Skill搭一个文章数据追踪系统,踩了 3 个坑
ai编程
peterfei13 小时前
IfAI v0.2.8 技术深度解析:从"工具"到"平台"的架构演进
rust·ai编程
草帽lufei13 小时前
OpenAI API调用实践文本分类和内容生成
openai·agent
msober13 小时前
从零打造你的专属 AI Agent
agent
fox_mt14 小时前
AI Coding - ClaudeCode使用指南
java·ai编程
小碗细面16 小时前
OpenCode:你的开源 AI 编程助手完全指南
ai编程
Miku1616 小时前
使用 Claude Code 的 pptx-skills 技能生成精美 EVA 主题 PPT 完整指南
aigc·agent·claude