为什么需要 AI 辅助代码审查?
在传统开发流程中,代码审查(Code Review)是保证代码质量的关键环节。但人工审查面临三大痛点:
- 时间成本高:资深开发者每天花费 2-3 小时审查他人代码
- 审查质量不稳定:疲劳、时间压力导致漏掉关键问题
- 反馈延迟:PR 提交后等待审查平均耗时 4-8 小时
本教程将教你如何用 Claude API 构建一个 7x24 小时工作的智能审查机器人,在 PR 提交后 5 分钟内给出详细审查意见,覆盖代码逻辑、安全漏洞、性能优化和风格规范四大维度。
准备工作:工具与环境
实战步骤
创建 GitHub App 并配置权限
首先创建一个专用的 GitHub App 用于代码审查:
# 1. 访问 https://github.com/settings/apps 创建新 App
# 2. 配置以下权限:
# - Repository permissions:
# - Contents: Read & Write
# - Pull requests: Read & Write
# - Checks: Read & Write
# - Subscribe to events:
# - Pull request
# - Pull request review comment
# 3. 生成 Private Key(下载 .pem 文件)
# 4. 记录 App ID 和 Client Secret
权限配置要点:确保「Pull requests」事件勾选了「Opened」和「Synchronize」,这样每次 PR 更新都会触发审查。
初始化 Cloudflare Workers 项目
# 创建项目目录
mkdir code-review-bot && cd code-review-bot
# 初始化 Worker 项目
npm create cloudflare@latest code-review-worker -- --template=hello-world
# 安装依赖
npm install @anthropic-ai/sdk jose
# 生成 JWT 所需的密钥对(用于 GitHub App 认证)
ssh-keygen -t rsa256 -b 2048 -f github_app_key.pem -N ""
项目结构如下:
code-review-bot/
├── worker/
│ ├── src/
│ │ ├── index.ts # Worker 入口
│ │ ├── handlers/
│ │ │ ├── webhook.ts # GitHub Webhook 处理
│ │ │ └── review.ts # 调用 Claude API 生成审查
│ │ └── lib/
│ │ ├── github.ts # GitHub API 封装
│ │ └── claude.ts # Claude API 封装
│ ├── wrangler.toml
│ └── package.json
└── scripts/
└── install-app.sh # 安装 GitHub App 到仓库
实现 GitHub Webhook 处理器
在 worker/src/handlers/webhook.ts 中编写 Webhook 验证和事件分发逻辑:
import { verifyWebhook } from '@octokit/webhooks-methods';
export async function handleWebhook(request: Request, env: Env) {
const signature = request.headers.get('X-Hub-Signature-256');
const event = request.headers.get('X-GitHub-Event');
const payload = await request.text();
// 验证 Webhook 签名
const isValid = await verifyWebhook(env.GITHUB_WEBHOOK_SECRET, payload, signature!);
if (!isValid) {
return new Response('Invalid signature', { status: 401 });
}
// 只处理 PR 事件
if (event !== 'pull_request') {
return new Response('OK', { status: 200 });
}
const data = JSON.parse(payload);
const action = data.action;
// 只审查新 PR 和代码更新
if (action === 'opened' || action === 'synchronize') {
return handlePullRequest(data, env);
}
return new Response('OK', { status: 200 });
}
获取 PR 代码差异并构建审查上下文
在 worker/src/lib/github.ts 中实现 GitHub API 调用:
import { createAppAuth } from '@octokit/auth-app';
import { Octokit } from '@octokit/rest';
// 使用 GitHub App 认证创建 Octokit 实例
function createAppOctokit(env: Env) {
const appOctokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: env.GITHUB_APP_ID,
privateKey: env.GITHUB_PRIVATE_KEY,
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
},
});
return appOctokit;
}
// 获取 PR 的代码差异
export async function fetchPRDiff(
octokit: Octokit,
owner: string,
repo: string,
pullNumber: number
) {
const { data: pr } = await octokit.pulls.get({
owner,
repo,
pull_number: pullNumber,
});
const { data: files } = await octokit.pulls.listFiles({
owner,
repo,
pull_number: pullNumber,
});
// 构建差异上下文(限制在 50KB 以内,避免超出 token 限制)
let diffContext = '';
let totalChars = 0;
const maxChars = 50000;
for (const file of files) {
if (totalChars + file.patch!.length > maxChars) break;
if (file.filename.endsWith('.lock')) continue; // 跳过锁文件
if (file.filename.startsWith('node_modules/')) continue;
diffContext += `File: ${file.filename}\n`;
diffContext += `Status: ${file.status}\n`;
diffContext += `Changes:\n${file.patch}\n\n`;
totalChars += file.patch!.length;
}
return {
diff: diffContext,
title: pr.title,
description: pr.body || '',
branch: pr.head.ref,
baseBranch: pr.base.ref,
};
}
page/per_page 参数分批获取。
设计 Claude API 审查提示词
这是核心环节。在 worker/src/lib/claude.ts 中编写专业的审查提示词:
const CODE_REVIEW_PROMPT = `你是一位资深软件工程师,专注于代码质量、安全性和可维护性。
请审查以下 Pull Request 的代码差异,按照以下维度给出详细审查意见:
## 审查维度
1. **Bug 与逻辑错误**:空指针、边界条件、竞态条件等
2. **安全漏洞**:SQL 注入、XSS、敏感信息泄露、认证授权问题
3. **性能问题**:不必要的循环、内存泄漏、数据库查询优化
4. **代码规范**:命名一致性、函数职责单一、注释充分性
5. **可测试性**:是否易于单元测试、是否遵循依赖注入
## 输出格式
对每个问题,按以下格式输出:
### [严重程度:高/中/低] 问题简述
- **文件**: \`path/to/file.ts:行号\`
- **问题**: 详细描述问题
- **建议**: 具体修改方案,附代码示例
如果没有发现问题,请回复"✅ 代码质量优秀,未发现明显问题"。
## PR 信息
标题:{title}
描述:{description}
## 代码差异
{diff}`;
export async function generateReview(
diffContext: { diff: string; title: string; description: string },
env: Env
) {
const anthropic = new Anthropic({
apiKey: env.ANTHROPIC_API_KEY,
});
const prompt = CODE_REVIEW_PROMPT.replace('{title}', diffContext.title)
.replace('{description}', diffContext.description)
.replace('{diff}', diffContext.diff);
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-6-20260101',
max_tokens: 4096,
messages: [{ role: 'user', content: prompt }],
});
return response.content[0].text;
}
发布审查评论到 GitHub PR
将 Claude 生成的审查意见发布为 PR 评论:
export async function postReviewComment(
octokit: Octokit,
owner: string,
repo: string,
pullNumber: number,
reviewText: string
) {
// 检查是否已有本机器人的评论(避免重复)
const { data: comments } = await octokit.issues.listComments({
owner,
repo,
issue_number: pullNumber,
});
const botLogin = 'code-review-bot[bot]';
const existingComment = comments.find(c => c.user?.login === botLogin);
if (existingComment) {
// 更新已有评论
await octokit.issues.updateComment({
owner,
repo,
comment_id: existingComment.id,
body: formatReviewComment(reviewText),
});
} else {
// 发布新评论
await octokit.issues.createComment({
owner,
repo,
issue_number: pullNumber,
body: formatReviewComment(reviewText),
});
}
}
// 格式化审查意见为 Markdown
function formatReviewComment(review: string) {
return `## 🤖 AI 代码审查报告
${review}
---
*本审查由 Claude API 自动生成,仅供参考。重要问题请人工复核。*`;
}
部署 Worker 并配置 GitHub Webhook
# 1. 配置环境变量
cp wrangler.toml.example wrangler.toml
# 编辑 wrangler.toml,填入:
# - account_id (Cloudflare 账户 ID)
# - [vars] 中的 GITHUB_APP_ID, ANTHROPIC_API_KEY 等
# 2. 部署 Worker
cd worker
npx wrangler deploy
# 3. 在 GitHub App 设置中配置 Webhook
# URL: https://code-review-bot.<你的 subdomain>.workers.dev
# Secret: 与 GITHUB_WEBHOOK_SECRET 一致
# 4. 安装 App 到目标仓库
gh app install 12345 --repo owner/repo
测试完整流程
创建一个测试 PR 验证流程:
# 1. 创建测试分支
git checkout -b test/pr-review
# 2. 故意写一段有问题的代码
echo 'function calculateTotal(items) {
let total = 0;
for (let i = 0; i <= items.length; i++) { // Bug: 应该是 < 不是 <=
total += items[i].price;
}
return total;
}' > src/test.js
# 3. 提交并推送
git add . && git commit -m "test: trigger code review"
git push origin test/pr-review
# 4. 在 GitHub 创建 PR,等待 30 秒后查看评论
预期看到类似以下的审查评论:
### [高] 数组越界风险
- 文件:src/test.js:3
- 问题:循环条件使用 i <= items.length 会导致访问 items[items.length] 返回 undefined
- 建议:改为 i < items.length
```javascript
// 修复后
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
```
常见问题 FAQ
策略 1:只审查变更最大的前 10 个文件;策略 2:使用 Claude 3.5 Haiku(128K context);策略 3:按文件分批审查,合并结果。
在提示词中定义清晰的标准:高 = 会导致运行时错误或安全漏洞;中 = 影响性能或可维护性;低 = 风格问题。可以要求 Claude 按严重程度排序输出。
可以。使用 octokit.pulls.createReviewComment 并传入 path(文件路径)、line(行号)和 body(评论内容)。需要从 diff 中解析 @@ -x,y +a,b @@ 来定位行号。
策略 1:只对包含业务逻辑的文件进行审查(跳过配置文件、文档等);策略 2:使用较小的模型(Haiku)做初步筛选,Sonnet 处理复杂问题;策略 3:缓存相同文件的审查结果。
总结
- ✓ 使用 GitHub App + Cloudflare Workers 实现 7x24 小时自动审查
- ✓ 精心设计的提示词是高质量审查的关键,覆盖 Bug/安全/性能/规范
- ✓ 结构化输出便于解析和定位问题,支持行内评论
- ✓ 通过 token 管理和文件过滤控制 API 成本
- ✓ 生产环境需处理并发、重试和审查结果缓存