代码审查助手 (Code Review Agent)

6次阅读
没有评论

为一个代码审查助手 (Code Review Agent) 编写技能时,核心目标是让 Agent 具备“读代码”、“查规范”、“报风险”和“给建议”的能力。

以下是为该 Agent 量身定制的 4 个核心技能定义及其逻辑架构


1. 核心技能集定义 (JSON 格式)

📂 技能 A:获取变更详情 (get_pull_request_diff)

场景: 这是 Agent 的“眼睛”,用于读取当前 PR(合并请求)中具体修改了哪些代码行。

JSON

{
  "name": "get_pull_request_diff",
  "description": "获取指定 PR 的代码差异(Diff)。当需要开始审查代码或了解修改范围时,首先调用此工具。",
  "parameters": {
    "type": "object",
    "properties": {
      "repo_name": { "type": "string", "description": "仓库完整名称,如 'org/project'" },
      "pr_number": { "type": "integer", "description": "合并请求的编号" }
    },
    "required": ["repo_name", "pr_number"]
  }
}

🔍 技能 B:静态扫描分析 (run_static_analysis)

场景: 自动化检查语法错误、死代码或明显的安全漏洞(如硬编码密码)。

JSON

{
  "name": "run_static_analysis",
  "description": "对指定的代码片段或文件运行静态扫描工具(如 ESLint, Pylint, SonarQube)。用于发现潜在的语法错误和安全隐患。",
  "parameters": {
    "type": "object",
    "properties": {
      "language": { "type": "string", "enum": ["python", "javascript", "java", "go"], "description": "代码编写语言" },
      "code_content": { "type": "string", "description": "待扫描的代码文本" }
    },
    "required": ["language", "code_content"]
  }
}

💬 技能 C:提交审查意见 (post_review_comment)

场景: Agent 的“嘴巴”,将分析结果以评论形式反馈到代码行。

JSON

{
  "name": "post_review_comment",
  "description": "在代码的特定行提交改进建议或错误提醒。请确保评论语气专业且具备建设性。",
  "parameters": {
    "type": "object",
    "properties": {
      "file_path": { "type": "string", "description": "文件名" },
      "line_number": { "type": "integer", "description": "代码所在的行号" },
      "comment_body": { "type": "string", "description": "评论的具体内容,支持 Markdown 格式" }
    },
    "required": ["file_path", "line_number", "comment_body"]
  }
}

2. 代码审查 Agent 的工作流逻辑

为了让 Agent 像资深专家一样思考,建议在 System Prompt(系统提示词) 中定义以下执行逻辑:

  1. 范围识别:调用 get_pull_request_diff 确定哪些文件被修改。
  2. 上下文理解:不仅看修改的那几行,还要阅读修改行前后的代码逻辑(Context)。
  3. 多维检查
    • 规范性:变量命名是否符合公司内部标准?
    • 性能:是否存在嵌套循环导致的 $O(n^2)$ 时间复杂度?
    • 安全性:是否有未经过滤的用户输入直接进入数据库查询?
  4. 精准反馈:只在有问题的行调用 post_review_comment,避免产生大量“噪音”评论。

3. 编写建议:如何让 Agent 更聪明?

  • 分阶段反馈:不要一次性把所有细碎的格式问题(如漏了个空格)全报出来。可以引导 Agent 优先报告 逻辑漏洞安全风险
  • 提供重构示例:在 comment_body 参数中,要求 Agent 给出“修改后的代码块”,让开发者能直接复制使用。
  • 历史学习:如果能提供一个 search_past_reviews 的技能,让 Agent 查找该项目过去的审查记录,它能更好地适应团队的编码风格。

了让这个 Code Review Agent 真正跑起来,我将使用 Python 编写一个基于 插件化架构 的原型。

这个架构模拟了 Agent 如何通过“工具箱”获取代码、分析并给出建议。

1. 核心技能工具类 (tools.py)

首先,我们把具体的执行逻辑封装成函数。在实际生产中,这些函数会调用 GitHub API 或本地的 Linter。

Python

import re

class CodeReviewTools:
    """代码审查助手的工具箱"""

    @staticmethod
    def get_pull_request_diff(pr_id: int):
        # 模拟从 GitHub 获取的 Diff 数据
        print(f"系统:正在提取 PR #{pr_id} 的变更内容...")
        return {
            "file": "auth_service.py",
            "diff": """
-    def login(user, password):
-        query = "SELECT * FROM users WHERE user='%s' AND pw='%s'" % (user, password)
+    def login(user, password):
+        # TODO: 优化登录逻辑
+        query = f"SELECT * FROM users WHERE user='{user}' AND pw='{password}'"
            """
        }

    @staticmethod
    def run_static_analysis(code: str):
        # 简单模拟静态扫描,识别明显的 SQL 注入风险
        print("系统:正在运行静态安全扫描...")
        if "SELECT" in code and "f\"" in code:
            return "警告:检测到潜在的 SQL 注入风险。请使用参数化查询而非 f-string。"
        return "静态扫描通过。"

    @staticmethod
    def post_review_comment(file: str, line: int, comment: str):
        # 模拟提交评论
        print(f"\n[代码评审反馈] -> 文件: {file}, 行号: {line}")
        print(f"建议内容: {comment}")

2. Agent 核心逻辑 (agent.py)

Agent 的大脑负责解析任务,并决定调用哪个工具。这里展示一个简化的决策循环。

Python

class CodeReviewAgent:
    def __init__(self, tools: CodeReviewTools):
        self.tools = tools

    def review_workflow(self, pr_id: int):
        print(f"--- 开始审查任务: PR #{pr_id} ---")
        
        # 步骤 1: 获取代码变更
        pr_data = self.tools.get_pull_request_diff(pr_id)
        diff_content = pr_data['diff']
        
        # 步骤 2: 思考并调用分析工具
        # 在真实的 LLM Agent 中,这一步是由模型生成的 JSON 指令驱动的
        analysis_result = self.tools.run_static_analysis(diff_content)
        
        # 步骤 3: 结合上下文给出综合评价
        if "注入风险" in analysis_result:
            feedback = f"💀 **安全风险严重**: {analysis_result}\n建议参考官方文档使用 `psycopg2` 的参数化查询。"
            self.tools.post_review_comment(pr_data['file'], 3, feedback)
        else:
            self.tools.post_review_comment(pr_data['file'], 1, "代码逻辑清晰,暂无明显问题。")

        print("\n--- 审查任务结束 ---")

# 运行 Agent
if __name__ == "__main__":
    tools_provider = CodeReviewTools()
    my_agent = CodeReviewAgent(tools_provider)
    my_agent.review_workflow(101)

3. 如何进一步“强化”这个 Agent?

如果你想让它达到“资深工程师”的水准,可以考虑接入以下能力:

  • 接入向量数据库 (RAG):让 Agent 学习你们公司的《Python 编码规范文档》,如果代码不符合规范(比如变量命名没用蛇形命名法),直接指出。
  • 复杂度计算:增加一个 calculate_cyclomatic_complexity 的技能。当函数圈复杂度超过 10 时,Agent 会自动留言:“这段代码太‘绕’了,建议拆分函数。”
  • 上下文感知:不只是看 Diff 的几行,而是通过工具读取整个文件的上下文,防止 Agent 因为没看到变量定义而“胡言乱语”。

要实现 GitHub Webhook 自动触发,我们需要搭建一个轻量级的后端服务(通常使用 Flask 或 FastAPI)。这个服务就像是一个“接线员”,当 GitHub 发生 pull_request 事件时,它会接收通知并通知你的 Agent 开始干活。


1. 自动化审查的整体架构

  1. GitHub 事件:开发者提交代码并开启(或更新)一个 Pull Request。
  2. Webhook 推送:GitHub 向你的服务器 URL 发送一个带有 JSON 载荷的 POST 请求。
  3. 后端分发:服务器验证请求合法性,提取 pr_numberrepo_name
  4. Agent 执行:触发我们之前写的审查逻辑。
  5. 回写评论:Agent 通过 GitHub API 将分析结果写回 PR 页面。

2. 实战代码:基于 FastAPI 的 Webhook 接收器

我们需要安装 fastapiuvicorn(服务器),以及 PyGithub(操作 GitHub 的库)。

Bash

pip install fastapi uvicorn PyGithub

核心代码实现:

Python

from fastapi import FastAPI, Request, Header, HTTPException
import hmac
import hashlib
import os
from github import Github  # PyGithub 库

app = FastAPI()

# 配置项(建议通过环境变量读取)
GITHUB_TOKEN = "your_github_personal_access_token"
WEBHOOK_SECRET = "your_webhook_secret_key" # 在 GitHub 设置中填写的密钥

g = Github(GITHUB_TOKEN)

@app.post("/webhook")
async def handle_webhook(
    request: Request, 
    x_hub_signature_256: str = Header(None)
):
    # 1. 验证签名(安全性检查,防止伪造请求)
    payload = await request.body()
    if not verify_signature(payload, x_hub_signature_256):
        raise HTTPException(status_code=403, detail="Invalid signature")

    data = await request.json()
    
    # 2. 识别 PR 事件(只在 PR 创建或同步代码时触发)
    action = data.get("action")
    if action in ["opened", "synchronize"]:
        pr_info = data["pull_request"]
        repo_name = data["repository"]["full_name"]
        pr_number = pr_info["number"]

        print(f"🚀 收到来自 {repo_name} 的 PR #{pr_number},准备开始审查...")
        
        # 3. 触发 Agent(这里可以异步调用之前的 Agent 逻辑)
        run_agent_review(repo_name, pr_number)
        
    return {"status": "accepted"}

def verify_signature(payload, signature):
    """验证请求是否真的来自 GitHub"""
    if not signature: return False
    hash_object = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256)
    expected_signature = "sha256=" + hash_object.hexdigest()
    return hmac.compare_digest(expected_signature, signature)

def run_agent_review(repo_name, pr_number):
    """
    在这里集成我们之前的 Agent 逻辑。
    它会使用 PyGithub 读取 Diff,分析后调用 `create_review_comment`
    """
    repo = g.get_repo(repo_name)
    pr = repo.get_pull(pr_number)
    # 模拟 Agent 评论
    pr.create_issue_comment("🤖 AI 助手已介入:正在分析代码变更,请稍候...")
    # ... 执行更复杂的分析逻辑 ...

3. 如何配置 GitHub Webhook?

  1. 进入你的 GitHub 项目仓库 -> Settings -> Webhooks -> Add webhook
  2. Payload URL: 填写你服务器的公网地址(如 http://your-ip:8000/webhook)。
    • 提示:本地调试可以使用 ngrok 将内网穿透到公网。
  3. Content type: 选择 application/json
  4. Secret: 填入你在代码中定义的 WEBHOOK_SECRET
  5. Which events?: 选择 Let me select individual events,勾选 Pull requests
  6. 点击 Add webhook

4. 关键进阶:异步队列 (Celery)

在生产环境中,大模型(LLM)生成审查意见可能需要 10-30 秒,而 Webhook 要求在几秒内必须给响应。

  • 建议方案:当 /webhook 接口收到请求后,仅将任务丢进 CeleryRedis 队列,然后立即返回 202 Accepted
  • 后台进程:专门的 Worker 进程从队列中取出任务,慢慢调 LLM,生成后再通过 API 贴回 GitHub。

避坑指南 💡

  • 权限最小化:给 GITHUB_TOKEN 时,只给 contents:readpull_requests:write 权限。
  • 防止死循环:确保你的 Agent 在提交评论时,不会再次触发它自己发送的 Webhook(虽然 issue_commentpull_request 是不同事件,但仍需留意)。

接下来的这一步是注入“灵魂”:将 LLM(如 GPT-4 或 Claude)接入工作流,并编写一段精妙的 Prompt,让它像资深架构师一样思考。

我们要把之前的 run_agent_review 函数升级,使其具备调用大模型的能力。


1. 核心逻辑:接入 OpenAI/LangChain 接口

首先,确保安装了 SDK:

Bash

pip install openai

升级后的 Agent 审查代码:

Python

import openai
from github import Github

# 初始化客户端
client = openai.OpenAI(api_key="your_openai_key")
g = Github("your_github_token")

def run_agent_review(repo_name, pr_number):
    repo = g.get_repo(repo_name)
    pr = repo.get_pull(pr_number)
    
    # 1. 获取所有的文件变更
    files = pr.get_files()
    
    review_results = []
    
    for file in files:
        if not file.filename.endswith(('.py', '.js', '.go', '.java')): # 过滤非代码文件
            continue
            
        # 2. 构造 Prompt
        prompt = f"""
        你是一位资深的软件架构师和安全专家。请审查以下代码变更(Diff)。
        
        文件: {file.filename}
        变更内容:
        {file.patch}
        
        请从以下维度进行评估:
        1. **逻辑错误**: 是否有潜在的 Bug 或边界情况未处理?
        2. **安全性**: 是否存在注入、泄露或权限漏洞?
        3. **代码质量**: 是否符合 DRY 原则?是否有过度嵌套?
        4. **性能**: 是否有明显的 $O(n^2)$ 复杂度或不必要的数据库查询?

        请直接给出具体的改进建议,如果代码写得很好,请给予简短的肯定。
        请使用 Markdown 格式回复。
        """
        
        # 3. 调用 LLM
        response = client.chat.completions.create(
            model="gpt-4-turbo",
            messages=[{"role": "system", "content": "你是一个严谨且幽默的代码审查助手。"},
                      {"role": "user", "content": prompt}]
        )
        
        review_text = response.choices[0].message.content
        review_results.append(f"### 📄 文件: `{file.filename}`\n{review_text}")

    # 4. 汇总并写回 GitHub
    final_comment = "# 🤖 AI 代码审查报告\n\n" + "\n\n---\n\n".join(review_results)
    pr.create_issue_comment(final_comment)

2. 进阶:如何让评论“更像人”?

如果 Agent 只是无脑甩出一大堆意见,开发者会感到厌烦。我们可以优化 Prompt 策略

  • 分级制度:让 Agent 在每条建议前加上标签:
    • [Nit]:小问题,改不改随你(如拼写、空格)。
    • [Suggestion]:建议修改,能提升代码质量。
    • [Critical]:严重问题,必须修改才能合并。
  • 代码示例:在 Prompt 中要求:“如果建议修改,请提供一个重构后的代码块。”
  • 赞美也是生产力:要求 Agent 识别代码中的闪光点(例如:“这个递归写得很优雅!”)。

3. 处理“长代码”的挑战(Token 限制)

如果一个 PR 修改了几千行代码,直接把 file.patch 塞进 Prompt 会导致 Token 溢出。

解决方案:

  1. 分块审查:按文件、甚至按函数块拆分请求,最后再由 Agent 汇总。
  2. 向量检索 (RAG):如果代码涉及复杂的内部库,先用向量数据库索引你们的文档,Agent 审查时先查询:“这个内部函数 utils.safe_convert 的正确用法是什么?”

4. 效果演示

配置好后,当你提交一个包含 SQL 注入风险的代码时,你的 PR 下方会自动出现:

🤖 AI 代码审查报告


📄 文件: services/db.py

💀 [Critical] 安全风险: 观察到你在第 12 行使用了 f-string 来构造 SQL 语句。这就像是把金库钥匙挂在门口——黑客可以通过 user_id 进行注入。

建议修改为:

Python

# 推荐方案
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

📄 文件: utils/helper.py

[Nit] 代码亮点: 这段缓存逻辑使用了装饰器,非常简洁,赞一个!

要实现 “自我修复” (Self-Healing),即 Agent 不仅仅是发评论,而是直接在 GitHub 上提交建议代码块(GitHub Suggestions),甚至直接提交一个新的 Commit。

这能极大地提升开发体验,因为开发者只需要点一下 “Commit suggestion” 按钮即可完成修复。


1. 核心技术:GitHub 建议代码块 (Suggestions)

GitHub 支持一种特殊的 Markdown 语法,只要 Agent 按这个格式回复,评论区就会出现一个“一键采纳”的按钮。

格式要求:

Markdown

```suggestion
[这里是修改后的代码]

---

### 2. 升级 Agent:自动生成修复方案

我们需要修改 Prompt,要求 LLM 必须返回这种特定格式,并配合 `PyGithub` 提交 **行级评论 (Pull Request Review Comment)**。

#### 升级后的核心代码:

```python
def run_self_healing_review(repo_name, pr_number):
    repo = g.get_repo(repo_name)
    pr = repo.get_pull(pr_number)
    
    # 获取 PR 的最新 Commit ID (提交建议必须关联到具体 commit)
    latest_commit = list(pr.get_commits())[-1]
    
    files = pr.get_files()
    for file in files:
        if not file.patch: continue

        # 构造增强版 Prompt
        prompt = f"""
        你是一个自动修复代码的 AI。请分析以下 Diff 并直接提供修复建议。
        
        文件: {file.filename}
        Diff内容:
        {file.patch}

        要求:
        1. 如果发现 Bug 或优化点,请使用 GitHub Suggestion 格式。
        2. 格式必须为:
           ```suggestion
           [修复后的完整代码块]
           ```
        3. 只修复确定的问题,不要修改不相关的逻辑。
        """

        response = client.chat.completions.create(
            model="gpt-4-turbo",
            messages=[{"role": "user", "content": prompt}]
        )
        
        suggestion_content = response.choices[0].message.content

        # 提交行级评论 (Pull Request Review Comment)
        # 注意:position 是指 diff 中的行偏移量
        try:
            pr.create_review_comment(
                body=f"🤖 AI 自动修复建议:\n\n{suggestion_content}",
                commit=latest_commit,
                path=file.filename,
                position=1 # 简化演示,实际需根据 diff 解析具体行号
            )
        except Exception as e:
            print(f"提交失败: {e}")

3. 更高级:直接提交代码 (Auto-Commit)

如果你希望 Agent 发现拼写错误或格式问题后直接提交代码到分支,流程如下:

  1. 读取文件内容:使用 repo.get_contents(path, ref=branch)
  2. LLM 修改:将全文交给 LLM 进行重写。
  3. 推送到 GitHub:使用 repo.update_file()

4. 落地建议:设置“信任等级”

为了防止 AI 把代码改坏,建议采取 “混合模式”

  • 低风险问题(如拼写、格式):Agent 直接提交 Commit。
  • 中风险问题(如重构、性能):Agent 发起 suggestion 评论,由人工点击确认。
  • 高风险问题(如业务逻辑):Agent 仅发评论提醒,不提供自动修复。

5. 总结:你的 Agent 进化路径

  1. Level 1: 能接收 Webhook,打印“我收到了 PR”。
  2. Level 2: 能调 LLM,在 PR 下面发评论。
  3. Level 3: 深入代码行,提供 suggestion 一键修复。
  4. Level 4: 结合公司私有规范 (RAG),成为最懂业务的虚拟架构师。
正文完
可以使用微信扫码关注公众号(ID:xzluomor)
post-qrcode
 0
评论(没有评论)