第18讲|AI 代码审查:让每一个 PR 都经过资深工程师把关

6 阅读1分钟

金句:Code Review 是开发团队最被低估的知识传递机制。AI 让这个机制的成本趋近于零,让每个初级工程师都能得到资深工程师级别的反馈。


一、Code Review 的现实困境

在大多数团队中,Code Review 的实际效果远不如理想:

  • 审查不及时:Reviewer 忙,PR 堆积
  • 审查不深入:快速扫一眼,只改格式问题
  • 知识鸿沟:初级开发者的 PR 缺乏有价值的指导
  • 一致性差:不同的 Reviewer 关注点不同,标准不统一

AI Code Review 正在系统性地解决这些问题。


二、五维 AI 代码审查框架

高质量的代码审查应该涵盖五个维度:

┌─────────────────────────────────────────────────────────┐
│                   代码审查五维框架                        │
├──────────────┬────────────────────────────────────────── │
│  维度 1      │  正确性审查                               │
│              │  • 逻辑是否正确?• 边界情况?• 并发安全?  │
├──────────────┼────────────────────────────────────────── │
│  维度 2      │  安全性审查                               │
│              │  • 注入风险?• 权限校验?• 数据脱敏?      │
├──────────────┼────────────────────────────────────────── │
│  维度 3      │  性能审查                                 │
│              │  • N+1 查询?• 内存泄漏?• 不必要的计算?  │
├──────────────┼────────────────────────────────────────── │
│  维度 4      │  可维护性审查                             │
│              │  • 命名语义?• 函数长度?• 注释完整?      │
├──────────────┼────────────────────────────────────────── │
│  维度 5      │  架构合规性审查                           │
│              │  • 分层是否合理?• 依赖方向?• 模式一致?  │
└──────────────┴────────────────────────────────────────── │

三、构建 AI Code Review 工具

方法一:命令行 AI 审查工具

#!/usr/bin/env python3
"""
ai_review.py - AI 代码审查工具
用法:python ai_review.py --file src/user/user.service.ts
      python ai_review.py --diff  # 审查当前 git diff
"""

import argparse
import subprocess
import anthropic
import sys

REVIEW_SYSTEM_PROMPT = """你是一位有 10 年经验的资深软件工程师,专注于代码质量、安全性和最佳实践。

请从以下五个维度进行代码审查:

## 审查维度
1. **正确性**:逻辑是否正确?是否有 Bug?边界情况是否处理?
2. **安全性**:是否存在安全漏洞(SQL注入、XSS、权限绕过等)?
3. **性能**:是否有性能问题(N+1查询、不必要的循环、内存泄漏)?
4. **可维护性**:代码是否清晰易读?命名是否语义化?
5. **架构合规**:是否遵循已有的架构模式?

## 输出格式
对每个发现的问题,请按以下格式输出:

[严重程度: 🔴 Critical / 🟡 Warning / 🟢 Suggestion]
**问题描述**:...
**位置**:第 N 行
**修改建议**:
```语言
// 修改后的代码

最后给出总评(通过/需要修改/强烈建议修改)和总体改进方向。"""

def review_code(code: str, filename: str = "", context: str = "") -> str: client = anthropic.Anthropic()

user_message = f"""请审查以下代码:

文件名:{filename} 项目上下文:{context}

{code}
```"""
    
    message = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=4096,
        system=REVIEW_SYSTEM_PROMPT,
        messages=[{"role": "user", "content": user_message}]
    )
    
    return message.content[0].text

def get_git_diff() -> str:
    result = subprocess.run(
        ['git', 'diff', '--staged'],
        capture_output=True, text=True
    )
    if not result.stdout:
        result = subprocess.run(
            ['git', 'diff', 'HEAD~1'],
            capture_output=True, text=True
        )
    return result.stdout

def main():
    parser = argparse.ArgumentParser(description='AI 代码审查工具')
    parser.add_argument('--file', help='要审查的文件路径')
    parser.add_argument('--diff', action='store_true', help='审查 git diff')
    parser.add_argument('--context', default='', help='项目上下文说明')
    args = parser.parse_args()
    
    if args.diff:
        code = get_git_diff()
        filename = "git diff"
    elif args.file:
        with open(args.file, 'r', encoding='utf-8') as f:
            code = f.read()
        filename = args.file
    else:
        print("请指定 --file 或 --diff 参数")
        sys.exit(1)
    
    if not code.strip():
        print("没有发现需要审查的代码变更")
        sys.exit(0)
    
    print(f"🔍 正在审查代码:{filename}")
    print("=" * 60)
    
    result = review_code(code, filename, args.context)
    print(result)

if __name__ == '__main__':
    main()

使用方式

# 审查单个文件
python ai_review.py --file src/auth/auth.service.ts

# 审查提交前的所有改动
git add .
python ai_review.py --diff

# 带上项目上下文
python ai_review.py --file src/payment/payment.service.ts \
  --context "这是一个支付处理服务,涉及金额计算和第三方支付接口调用"

方法二:GitHub Actions 自动化 PR 审查

# .github/workflows/ai-review.yml
name: AI Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  ai-review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Get PR diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...HEAD > pr_diff.txt
          echo "diff_size=$(wc -l < pr_diff.txt)" >> $GITHUB_OUTPUT
      
      - name: AI Review
        if: steps.diff.outputs.diff_size > '0'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REPO: ${{ github.repository }}
        run: |
          python3 << 'EOF'
          import os
          import anthropic
          import urllib.request
          import json
          
          with open('pr_diff.txt', 'r') as f:
              diff = f.read()
          
          if len(diff) > 50000:
              diff = diff[:50000] + "\n... (truncated)"
          
          client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
          
          message = client.messages.create(
              model="claude-3-5-sonnet-20241022",
              max_tokens=2048,
              messages=[{
                  "role": "user",
                  "content": f"""请审查这个 PR 的代码变更,重点关注安全性、正确性和性能问题:

{diff}

请用 Markdown 格式输出,包含:
1. 总体评价(1-2句话)
2. 主要发现的问题列表(如果有)
3. 改进建议
4. 最终结论(LGTM / 需要修改)"""
              }]
          )
          
          review_comment = f"""## 🤖 AI 代码审查报告

{message.content[0].text}

---
*此审查由 AI 自动生成,仅供参考。请结合人工审查判断。*"""
          
          # 发布 PR 评论
          api_url = f"https://api.github.com/repos/{os.environ['REPO']}/issues/{os.environ['PR_NUMBER']}/comments"
          data = json.dumps({"body": review_comment}).encode('utf-8')
          req = urllib.request.Request(
              api_url,
              data=data,
              headers={
                  'Authorization': f"token {os.environ['GITHUB_TOKEN']}",
                  'Content-Type': 'application/json'
              }
          )
          urllib.request.urlopen(req)
          print("AI 审查评论已发布")
          EOF

四、AI 发现的典型安全问题案例

案例一:不安全的反序列化

# ❌ 有问题的代码
import pickle

def load_user_data(data: bytes):
    return pickle.loads(data)  # 危险!任意代码执行风险

AI 审查输出

🔴 Critical - 安全漏洞
问题描述:使用 pickle.loads() 反序列化不可信数据,
可能导致任意代码执行(RCE)攻击。
位置:第 4 行

修改建议:使用 json.loads() 处理结构化数据,
或使用 hmac 验证签名后再进行反序列化。

案例二:不安全的文件路径

// ❌ 有问题的代码
app.get('/files/:filename', (req, res) => {
  const filepath = path.join(__dirname, 'uploads', req.params.filename);
  res.sendFile(filepath);
});

AI 审查输出

🔴 Critical - 路径遍历漏洞
问题描述:直接使用用户输入构建文件路径,
攻击者可通过 ../../../etc/passwd 访问系统文件。

修改建议:
const filepath = path.join(__dirname, 'uploads', req.params.filename);
// 验证路径是否在允许的目录内
if (!filepath.startsWith(path.join(__dirname, 'uploads'))) {
  return res.status(403).json({ error: 'Access denied' });
}

五、建立团队 AI 审查规范

规范模板

# 团队 AI 代码审查规范

## AI 审查定位
AI 审查是人工审查的"前置过滤",不替代人工审查。

## 流程
1. 开发者提交 PR
2. AI 自动审查(3分钟内完成)
3. 开发者处理 AI 发现的 Critical 和 Warning 问题
4. 人工 Reviewer 重点关注业务逻辑和架构设计

## AI 审查的权重
- 🔴 Critical:必须修复,否则 PR 不得合并
- 🟡 Warning:建议修复,需要与 Reviewer 协商
- 🟢 Suggestion:可以选择性采纳

## 豁免说明
如果 AI 误报,在代码行添加注释:
// ai-review-ignore: false-positive - 原因说明

章节小结:AI Code Review 不是要替代人工审查,而是提升审查效率、覆盖率和一致性。通过五维审查框架和自动化流水线,让每个 PR 都能得到"资深工程师"级别的初步把关,人工审查者可以把精力集中在业务逻辑和架构层面。