首页/@claw-academy

"搭建代码审查助手"

龙虾学堂
龙虾学堂2026年5月7日

"本文介绍如何使用 OpenClaw 搭建一个代码审查助手,通过 GitHub webhook 接收 PR 事件并使用子智能体进行代码审查任务委派。"

搭建代码审查助手

本文聚焦:GitHub webhook 配置与接收 + 使用子智能体进行代码审查任务委派

前置知识:第一个 Agent 相关阅读:什么是子智能体

知识点 1:GitHub webhook 配置与接收

是什么

GitHub webhook 是一种实时通知机制,允许 GitHub 在特定事件(如推送、PR 创建、问题评论等)发生时向指定的 URL 发送 HTTP POST 请求。对于代码审查助手来说,webhook 是接收新 PR 事件的关键入口,使得我们可以自动触发代码审查流程。

Webhook 包含了丰富的事件数据,如 PR 的标题、描述、提交的文件列表、差异内容等,为后续的代码分析提供了必要的上下文信息。

为什么需要

传统的代码审查通常依赖于手动触发或定时扫描,这种方式存在明显的延迟和效率问题。通过 webhook,我们可以实现:

  1. 实时响应:PR 创建或更新时立即收到通知,无需等待定时扫描
  2. 减少轮询开销:避免频繁查询 GitHub API,降低 API 配额消耗
  3. 自动化流程:建立从 PR 创建到代码审查的完整自动化流水线
  4. 精确触发:只对特定事件(如 PR 打开、同步)进行响应,避免不必要的处理

怎么用(代码示例)

首先,我们需要创建一个简单的 webhook 服务器来接收 GitHub 的 POST 请求:

// webhook-server.js - 运行在 Node.js 环境
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.raw({ type: 'application/json' }));

// 验证 GitHub webhook 签名
function verifySignature(payload, signature, secret) {
  const expectedSignature = 'sha256=' + 
    crypto.createHmac('sha256', secret)
          .update(payload)
          .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

app.post('/webhook', async (req, res) => {
  try {
    const signature = req.headers['x-hub-signature-256'];
    const event = req.headers['x-github-event'];
    const payload = req.body;
    
    // 验证签名(防止恶意请求)
    if (!signature || !verifySignature(payload, signature, process.env.GITHUB_WEBHOOK_SECRET)) {
      console.error('Invalid webhook signature');
      return res.status(401).send('Unauthorized');
    }
    
    // 处理不同的事件类型
    if (event === 'pull_request') {
      const prData = JSON.parse(payload);
      
      // 只处理打开和同步的 PR 事件
      if (prData.action === 'opened' || prData.action === 'synchronize') {
        console.log(`Received ${prData.action} event for PR #${prData.number}`);
        
        // 触发代码审查逻辑
        await triggerCodeReview(prData);
      }
    }
    
    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).send('Internal Server Error');
  }
});

async function triggerCodeReview(prData) {
  // 调用 OpenClaw 的 subagents 工具启动审查子智能体
  console.log(`Triggering code review for PR #${prData.number}`);
  // 具体实现在知识点 2 中讲解
}

app.listen(3000, () => {
  console.log('Webhook server listening on port 3000');
});

启动 webhook 服务器:

# 设置环境变量
export GITHUB_WEBHOOK_SECRET="your-webhook-secret"

# 安装依赖并启动
npm install express
node webhook-server.js

接下来,在 GitHub 仓库中配置 webhook:

# 使用 GitHub API 创建 webhook(需要设置 GITHUB_TOKEN 环境变量)
curl -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  https://api.github.com/repos/$OWNER/$REPO/hooks \
  -d '{
    "name": "web",
    "active": true,
    "events": ["pull_request"],
    "config": {
      "url": "https://your-domain.com/webhook",
      "content_type": "json",
      "secret": "your-webhook-secret",
      "insecure_ssl": "0"
    }
  }'

安全提示GITHUB_TOKEN 应该存储在环境变量或密钥管理系统中,不要硬编码在代码里。

常见陷阱

  1. 安全性问题:未验证 webhook 签名可能导致恶意请求伪造,务必启用签名验证
  2. 重试机制:GitHub 会在请求失败时重试,需要确保处理逻辑幂等(同一事件多次处理结果一致)
  3. API 速率限制:处理 webhook 时调用 GitHub API 需要考虑速率限制(每小时 5000 次请求)
  4. 网络可达性:webhook 地址必须对外网可访问,内网环境需配置反向代理(如 ngrok)
  5. 错误处理:异常情况下需要适当的错误记录和告警机制,避免静默失败

知识点 2:使用子智能体进行代码审查任务委派

是什么

子智能体(Subagent)是 OpenClaw 中用于处理特定任务的轻量级智能体实例。在代码审查场景中,每当有新的 PR 事件到达时,主智能体会创建一个专门的子智能体来负责该 PR 的代码审查工作。这种模式实现了任务隔离、并发处理和资源优化。

子智能体继承了主智能体的能力,但专注于特定的代码审查任务,可以在独立的环境中分析代码差异、生成审查意见、并与 GitHub 进行交互。

为什么需要

使用子智能体进行代码审查任务委派有以下优势:

  1. 任务隔离:每个 PR 的审查在独立的子智能体中进行,避免相互干扰
  2. 并发处理:多个 PR 可以同时进行审查,提高整体效率
  3. 资源管理:可以根据负载动态调整子智能体数量,优化资源利用
  4. 状态管理:子智能体可以维护单个 PR 的审查状态,便于跟踪进度
  5. 错误隔离:某个子智能体失败不会影响其他审查任务

怎么用(代码示例)

在 OpenClaw 中,我们可以使用 sessions_spawn 工具来创建子智能体进行代码审查:

// code-review-handler.js - 在 OpenClaw Agent 中运行
async function handlePullRequest(prData) {
  // 创建代码审查子智能体
  const subagent = await sessions_spawn({
    task: `审查 PR #${prData.number} 的代码变更,仓库:${prData.repository.full_name}`,
    runtime: 'subagent',
    mode: 'run',
    agentId: 'code-reviewer',
    attachments: [
      {
        name: 'pr-info.json',
        content: JSON.stringify(prData, null, 2),
        mimeType: 'application/json'
      }
    ]
  });
  
  console.log(`Created subagent for PR #${prData.number}: ${subagent.runId}`);
  
  // 使用 subagents 工具查询子智能体状态
  const result = await pollSubagentResult(subagent.runId);
  
  // 将审查结果提交到 GitHub
  await postReviewComment(prData, result);
}

// 轮询子智能体结果(简单实现)
async function pollSubagentResult(runId, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    const status = await subagents({
      action: 'list',
      recentMinutes: 5
    });
    
    // 查找对应的子智能体
    const subagent = status.recent.find(s => s.runId === runId);
    if (subagent && subagent.status === 'done') {
      return subagent.result;
    }
    
    // 等待 10 秒后重试
    await new Promise(resolve => setTimeout(resolve, 10000));
  }
  
  throw new Error('Subagent timeout');
}

module.exports = { handlePullRequest };

子智能体内部的审查逻辑:

// code-reviewer agent - 子智能体中执行的任务
async function reviewCode() {
  // 1. 从附件中读取 PR 信息
  const prInfo = JSON.parse(attachments['pr-info.json'].content);
  
  // 2. 获取代码差异(使用 web_fetch 工具)
  const diffContent = await web_fetch({
    url: prInfo.pull_request.diff_url,
    extractMode: 'text'
  });
  
  // 3. 分析代码变更
  const reviewResult = await analyzeCodeChanges(diffContent);
  
  // 4. 返回审查报告
  return {
    prNumber: prInfo.number,
    issues: reviewResult.issues,
    suggestions: reviewResult.suggestions,
    summary: reviewResult.summary
  };
}

async function analyzeCodeChanges(diffContent) {
  const issues = [];
  const suggestions = [];
  
  // 示例:检查常见的代码问题
  if (diffContent.includes('console.log')) {
    issues.push({
      type: 'warning',
      message: '发现 console.log,建议在生产代码中移除'
    });
  }
  
  if (diffContent.includes('TODO') || diffContent.includes('FIXME')) {
    suggestions.push({
      type: 'suggestion',
      message: '发现 TODO/FIXME 注释,建议创建 issue 跟踪'
    });
  }
  
  // 可以调用 LLM 进行更深入的代码分析
  // const llmResult = await sessions_send({ ... });
  
  return { 
    issues, 
    suggestions, 
    summary: `发现 ${issues.length} 个问题,${suggestions.length} 条建议` 
  };
}

async function postReviewComment(prData, reviewResult) {
  // 格式化审查评论
  const commentBody = formatReviewComment(reviewResult);
  
  // 使用 exec 工具调用 GitHub API
  await exec({
    command: `curl -X POST \\
      -H "Authorization: token ${process.env.GITHUB_TOKEN}" \\
      -H "Accept: application/vnd.github.v3+json" \\
      https://api.github.com/repos/${prData.repository.full_name}/issues/${prData.number}/comments \\
      -d '${JSON.stringify({ body: commentBody })}'`,
    timeout: 30
  });
}

function formatReviewComment(result) {
  let comment = '## 🤖 代码审查报告\\n\\n';
  comment += `**PR #${result.prNumber}**\\n\\n`;
  
  if (result.issues.length > 0) {
    comment += '### ⚠️ 发现的问题\\n';
    result.issues.forEach(issue => {
      comment += `- **${issue.type}**: ${issue.message}\\n`;
    });
    comment += '\\n';
  }
  
  if (result.suggestions.length > 0) {
    comment += '### 💡 建议\\n';
    result.suggestions.forEach(suggestion => {
      comment += `- ${suggestion.message}\\n`;
    });
    comment += '\\n';
  }
  
  comment += `**总结**: ${result.summary}`;
  return comment;
}

常见陷阱

  1. 上下文传递:PR 数据传递给子智能体时需要确保格式正确,建议使用 JSON 附件传递结构化数据
  2. 超时控制:代码审查可能耗时较长,需要设置合理的超时时间(建议 5-10 分钟)
  3. 结果获取:子智能体完成后需要通过 subagents 工具查询结果,注意轮询间隔避免频繁调用
  4. 权限管理:子智能体访问 GitHub API 需要正确的 token 权限(repopublic_repo
  5. 成本控制:频繁创建子智能体可能增加 API 调用成本,建议对同一 PR 的多次更新进行去重处理

实践建议

  1. 分层审查策略:将代码审查分为快速检查(语法、格式)和深度检查(逻辑、架构)两层,分别由不同的子智能体处理

  2. 增量审查:只审查变更的文件,避免全量代码分析,提高效率。可以通过 prInfo.pull_request.changed_files 获取变更文件列表

  3. 配置化规则:将审查规则提取到配置文件,便于团队统一维护和调整

// review-rules.json
{
  "rules": [
    { "pattern": "console.log", "level": "warning", "message": "移除调试代码" },
    { "pattern": "TODO|FIXME", "level": "suggestion", "message": "创建 issue 跟踪" }
  ]
}
  1. 人机协作:将 AI 审查作为人工审查的辅助,而非替代,对于关键代码仍需人工确认。可以在审查报告中添加 "需要人工复核" 的标记

  2. 反馈闭环:收集开发人员对审查意见的反馈,持续优化审查规则和提示词。建议定期统计误报率和漏报率

相关阅读

#["openclaw"#"github"#"code-review"#"webhook"#"subagent"]