🚀 拒绝无效加班!我用 Node.js 撸了一个“全能” AI 代码审查助手 (支持 GitHub/GitLab/Gitea)
前言: 你是否也经历过这样的场景:
- 每天花费大量时间 Review 琐碎的代码格式问题?
- 新人提交的 PR 充满了
console.log和显而易见的 Bug?- 想引入 AI 辅助 Review,但市面上的工具要么太贵,要么不支持内网 GitLab/Gitea?
既然如此,不如自己动手!本文将带你从零实现一个 企业级 AI Code Review Agent,支持多平台、并发控制、ChatOps 交互,甚至能像老司机一样教你写代码。
💡 项目亮点
先看效果,这个 Bot 能做什么?
- 全平台兼容:无缝对接 GitHub、GitLab (私有化)、Gitea。
- 精准打击:不做“废话文学”,基于 AST 和 Diff 上下文,只评论变更行。
- ChatOps:在评论区输入
/review,随叫随到,立即重审。 - 防爆破设计:内置 并发队列 和 增量去重,保护你的 Token 额度,防止 API 被刷爆。
- 深度定制:支持 OpenAI / DeepSeek / Anthropic,Prompt 可完全自定义。
🏗️ 架构设计:拒绝面条代码
为了保证项目的扩展性(毕竟要适配那么多平台),我采用了 六边形架构 (Hexagonal Architecture) 的思想,将核心业务逻辑与外部依赖解耦。
系统架构图
graph TD
User["用户 / Git 平台"] -->|Webhook/CLI| Entry["入口 (Server/CLI)"]
Entry --> Core["核心逻辑"]
subgraph Core Logic
Orchestrator["审查编排器 (大脑)"]
Queue["并发任务队列"]
DiffParser["Diff 精准解析"]
PromptEng["Prompt 引擎"]
end
Core -->|调度| Queue
Queue -->|执行| IAI
Core -->|使用| IPlatform["接口: IPlatformProvider"]
Core -->|使用| IAI["接口: IAIProvider"]
IPlatform -->|实现| GitHub["GitHub Adapter"]
IPlatform -->|实现| GitLab["GitLab Adapter"]
IPlatform -->|实现| Gitea["Gitea Adapter"]
IAI -->|实现| OpenAI["OpenAI / DeepSeek"]
🛠️ 核心难点与解决方案
在开发过程中,我遇到了几个比较坑的点,这里分享一下解决方案。
1. 并发控制与 Rate Limit
痛点:一个 PR 可能包含 50+ 个文件,如果瞬间发起 50 个 OpenAI 请求,账号分分钟被封,或者服务器 OOM。
解法:引入 p-queue 实现全局并发队列。
// src/utils/queue.ts
import PQueue from 'p-queue';
// 全局单例队列,限制最大并发数为 5
export const analysisQueue = new PQueue({
concurrency: parseInt(process.env.CONCURRENCY_LIMIT || '5'),
});
在编排器中,我们将每个文件的分析任务丢进队列:
// src/core/orchestrator.ts
const tasks = targetFiles.map((file) => {
return analysisQueue.add(async () => {
logger.info({ file: file.filename }, '开始分析...');
// ... 调用 AI, 解析结果 ...
return comments;
});
});
await Promise.all(tasks);
2. 交互式体验 (ChatOps)
痛点:有时候 AI 提了建议,我改完了,但我不想推一个新的 Commit 只是为了触发 Webhook,或者我想手动触发一次复查。
解法:监听 Issue Comment / Note Hook 事件,解析指令。
我在 IPlatformProvider 接口中增加了异步 parseEvent 能力,支持“指令水合”——即收到 /review 评论时,自动反查 PR 的最新 Commit SHA,伪装成一个 PR Update 事件。
// src/platforms/github.ts
if (eventName === 'issue_comment' && body.startsWith('/review')) {
// 1. 识别指令
logger.info('收到 ChatOps 指令');
// 2. 调用 API 获取 PR 最新状态 (Head SHA)
const { data: pr } = await this.octokit.rest.pulls.get({ ... });
// 3. 构造审查事件
return {
eventType: 'pull_request',
commitSha: pr.head.sha, // 关键:获取最新的代码快照
// ...
};
}
3. 如何避免“幻觉”行号?
痛点:AI 经常会胡乱引用行号,导致评论标在错误的代码行上,甚至标在未变更的代码上。
解法:
- Diff 解析:使用
parse-diff库,严格获取变更块(Hunk)的起止行号。 - 上下文校验:在 Prompt 中明确要求 AI 返回 JSON 格式,且必须包含引用代码片段。
- 二次校验:在代码中,拿到 AI 结果后,检查该行号是否真的在 Diff 的
validLines集合中。
// 校验逻辑
const validLines = getReviewableLines(file.rawDiff);
if (!validLines.has(res.lineNumber)) {
logger.debug('AI 返回了非变更区域的行号,跳过');
continue;
}
💻 快速上手
项目已经支持 Docker 一键部署,无需复杂的环境配置。
1. 配置环境变量
新建 .env:
# AI 配置 (支持 DeepSeek!)
AI_PROVIDER=openai
AI_BASE_URL=https://api.deepseek.com
AI_API_KEY=sk-xxxx
AI_MODEL=deepseek-chat
# 平台配置
GITHUB_TOKEN=ghp_xxxx
GITHUB_WEBHOOK_SECRET=xxxx
# 审查偏好
REVIEW_LANGUAGE=zh-CN
SKIP_DRAFT_PR=true
2. Docker 启动
docker-compose up -d
3. 配置 Webhook
在你的 GitHub/GitLab 仓库设置中,添加 Webhook 指向 http://your-server:4000/webhook,勾选 Pull Request 和 Issue Comment 事件。
搞定!🚀
🔮 总结与展望
这个项目目前已经具备了生产可用的能力:
- ✅ 全自动化:从代码提交到审查意见生成,全程无感。
- ✅ 低成本:通过 Draft 过滤和文件过滤,大幅节省 Token。
- ✅ 交互性:ChatOps 让 Bot 变成了团队的一员。
未来计划 (Roadmap):
- 接入本地大模型 (Ollama),实现数据不出内网。
- 支持更复杂的上下文分析(引用分析、类型定义跳转)。
- 生成周报/质量趋势图。
如果你也对 AI 提效感兴趣,欢迎 Star ⭐️ 和 PR!
项目地址:GitHub 传送门 - ai-code-reviewer (请替换为你的实际地址)
最后,求个赞!👍 你的支持是我更新的动力!