乐于分享
好东西不私藏

我搭了一个AI代码审查机器人,每天自动Review PR——从原理到完整实现,且代码不离开内网

我搭了一个AI代码审查机器人,每天自动Review PR——从原理到完整实现,且代码不离开内网

上周发了个PR,半小时没人审。自己看了一遍,有个SQL注入没发现。合了,上线了,被安全团队找上门了。

——这场景不陌生吧?

代码审查有两个老问题:没人审审不细。前者是人的问题,后者是精力问题。连续审5个PR,第6个你基本就在走形式了。

我搭了一个AI代码审查机器人,接在GitHub上,每次开PR自动审查,几分钟出结果。Bug、安全漏洞、性能问题、代码风格,四个维度逐个扫描。不取代人工审查,但能把”走形式”那部分接过去。

今天把整个搭建过程拆开讲,包括架构设计、核心代码、踩过的坑,还有跟商业方案的对比。代码开源,你clone下来就能跑。

先说清楚:它能做什么、不能做什么

能做的:

  • 检测常见Bug模式(空指针、资源泄露、并发问题)
  • 发现安全漏洞(SQL注入、硬编码密钥、XSS)
  • 识别性能问题(N+1查询、不必要的循环)
  • 给出可维护性建议(命名、冗余代码、魔法数字)

不能做的:

  • 理解跨文件的复杂业务逻辑
  • 判断功能是否符合产品需求
  • 替代资深工程师的架构审查

一句话:它是一个不知疲倦的初级审查员,不是资深架构师。

把它当成第一道过滤,人工审查集中在它搞不定的部分。

架构:一条流水线

GitHub PR Event (Webhook / Actions)        ↓  获取 PR Diff (GitHub API)        ↓  解析 Diff → 按文件分组 → 过滤非代码文件        ↓  逐文件调用 LLM 分析(Bug/安全/性能/可维护性)        ↓  汇总审查结果 → 发布 PR Review Comment

五个步骤,每一步都很明确。重点在中间三步。

第一步:拿到Diff

通过GitHub REST API获取PR的diff。关键细节——请求头要用Accept: application/vnd.github.v3.diff,这样直接返回unified diff文本,不需要处理JSON。

async def get_pr_diff(owner, repo, pr_number):    url = f”https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}”    headers = {        “Authorization”: f”token {GITHUB_TOKEN}”,        “Accept”: “application/vnd.github.v3.diff”,    }    async with httpx.AsyncClient() as client:        resp = await client.get(url, headers=headers)        resp.raise_for_status()        return resp.text

为什么用异步?因为后面要调LLM API,同步请求会阻塞。FastAPI + httpx的组合在Python生态里最成熟。

第二步:解析Diff

这是最容易踩坑的一步。unified diff的格式看起来简单,但有几个容易忽略的点——

1. a/ 和 b/ 前缀

git diff的输出,文件路径带a/b/前缀(--- a/src/main.py / +++ b/src/main.py)。但GitHub API返回的diff有时不带。解析时要注意。

2. 行数不一定对

@@ -1,6 +1,8 @@表示原文件从第1行开始共6行,新文件从第1行开始共8行。但有时候count为0(比如纯新增文件),需要处理边界情况。

3. 大diff会撑爆上下文

一个改了50个文件的PR,diff可能有几万行。LLM的上下文窗口装不下,必须分块处理。

我的方案:按文件分组,过滤非代码文件,超大文件跳过。

# 只审查代码文件REVIEW_EXTENSIONS = {“.py”, “.js”, “.ts”, “.java”, “.go”, “.rs”}# 超过500行的文件跳过MAX_DIFF_LINES = 500

过滤非代码文件不是偷懒——审查README.mdpackage-lock.json的变更没有意义,只会浪费token和产生噪音。

第三步:调用LLM分析

这是核心。prompt设计直接决定审查质量。

我试过三种prompt策略——

策略A:自由审查(”请审查这段代码”) → 结果:泛泛而谈,说了一堆正确的废话

策略B:Checklist式审查(列出检查项,逐项打分) → 结果:太机械,每个文件都差不多,有用的信息被淹没

策略C:维度引导 + 约束条件(最终方案)

审查维度:1. Bug风险:逻辑错误、空指针、资源泄露、并发问题2. 安全问题:SQL注入、XSS、硬编码密钥3. 性能问题:不必要的循环、N+1查询4. 可维护性:命名、冗余、魔法数字约束:– 只指出真正的问题,不要为了审查而审查– 每个问题给具体行号和修改建议– 代码没问题就直接说”LGTM”

“只指出真正的问题”这句话很关键。不加这句,LLM会觉得必须找出问题,然后开始编——把正常代码硬说成有Bug。加了之后,审查的精准度明显提升。

另一个关键参数:temperature=0.3。代码审查需要确定性输出,不是创意写作。低温度让LLM更稳定地输出一致的结果。

⚠️ 踩坑提醒:LLM在代码审查上的F1分数并不高。

Meta 2025年发布的CodeReviewEval benchmark显示,GPT-4o在”发现真实缺陷”任务上F1约0.58。也就是说——它会漏掉约40%的真实Bug,同时误报约40%

所以:AI审查是第一道防线,不是最后一道。

第四步:发布评论

通过GitHub API创建PR Review。这里有个坑——

Review Comment vs Issue Comment:

  • Issue Comment(POST /issues/{n}/comments):评论挂在PR上,不关联代码行
  • Review Comment(POST /pulls/{n}/reviews):正式的代码审查,可以关联具体行

用Review Comment,因为它在GitHub的PR界面上显示得更正式,有”Approved” / “Changes Requested” / “Comment”三种状态。我选了”Comment”——只提建议,不block PR。让AI有block PR的权力?那是在给自己找麻烦。

第五步:幂等性

同一PR推了新commit,会触发新的synchronize事件。如果不做处理,之前commit的审查评论还会在。两个选择:

  1. 每次审查都发新评论
    (简单,但评论会越积越多)
  2. 更新之前的评论
    (需要追踪comment ID)

我选了方案1,在评论里加上commit SHA作标识。

简单可靠,不引入状态管理。

两种部署方式

▪ 方式一:GitHub Actions(推荐零运维方案)

# .github/workflows/review.ymlon:  pull_request:    types: [opened, synchronize]jobs:  review:    runs-on: ubuntu-latest    steps:      – uses: actions/checkout@v4      – run: pip install httpx pydantic      – run: python scripts/review.py –owner $OWNER –repo $REPO –pr $PR –commit $SHA        env:          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

优点:不需要服务器,GitHub免费额度够用。

缺点:每次审查要冷启动,大概多10-20秒。

▪ 方式二:Webhook服务器(适合自托管)

pip install -e “.[dev]”python -m src.main  # FastAPI跑在8080端口

需要在GitHub仓库的Webhook设置里配你的服务器地址。

优点:响应更快,可以加缓存和限流。

缺点:要维护一台服务器。

我的建议:先用Actions跑起来,审查量大再切Webhook。

用哪个模型?

模型
效果
成本
推荐场景
GPT-4o
全面性好,有时过度建议
~$0.01/次
不差钱
GPT-4o-mini
够用,偶尔漏检
~$0.001/次
性价比首选
DeepSeek-chat
跟4o-mini接近
~$0.0003/次
国内首选
本地模型(通过Ollama)
看模型大小,7B以下别想了
免费
隐私敏感场景

支持任何兼容OpenAI API的模型——改OPENAI_BASE_URLOPENAI_MODEL两个环境变量就行。

⚠️ 踩坑提醒:别用小模型做代码审查。

7B以下的模型(包括deepseek-r1:1.5b)在代码审查任务上几乎不可用——要么漏检严重,要么把正常代码批成Bug。至少用9B以上的模型,或者直接用API。

跟商业方案比,差在哪?

维度
自建(本文方案)
CodeRabbit
GitHub Copilot Review
成本
API费用,几毛钱/次
免费层+付费
GitHub企业版
定制度
完全自定义prompt和规则
有限自定义
基本不可自定义
隐私
代码可以不走公网(本地模型)
代码经过第三方
微软生态
维护成本
自己维护
审查质量
取决于模型和prompt
有优化过的prompt
有GitHub数据加持

选哪个?

  • 3人以下团队,PR不多(<10/天)→ 自建够用
  • 10人以上团队,需要稳定可靠 → CodeRabbit免费层或Copilot Review
  • 代码不能出内网 → 自建 + 本地模型

完整代码

GitHub仓库:https://github.com/helloworldtang/ai-pr-reviewer

clone下来,配好环境变量,跑通7个测试,接上你的仓库。10分钟搞定。

git clone https://github.com/helloworldtang/ai-pr-reviewercd ai-pr-reviewercp .env.example .env  # 填入你的配置pip install -e “.[dev]”pytest tests/ -v   # 7个测试全过

💡 一句话带走:AI代码审查不是替代人工,是帮你把”走形式”的部分自动化——省下的精力用在真正需要思考的地方。

你们团队做代码审查吗?是人工审还是用工具?最大的痛点是什么?说说你的情况,帮你看看怎么接AI。

参考来源:

  • Meta: “CodeReviewEval: A Benchmark for Automated Code Review using LLMs” (2025) — GPT-4o F1≈0.58   https://arxiv.org/abs/2505.20206
  • GitHub REST API: pulls端点diff格式文档  https://docs.github.com/cn/rest/pulls/pulls#custom-media-types-for-pull-requests
  • Anthropic: “Building Effective Agents” — 工具设计原则(专用工具优于通用工具) https://www.anthropic.com/engineering/building-effective-agents
  • PR-Agent (Codium-ai): 开源AI审查方案参考 https://github.com/The-PR-Agent/pr-agent