首页 / 文章

通过FastAPI Webhook并行处理拉取请求的LangGraph代理

Webhook、Redis队列与四个LangGraph代理如何借助ngrok进行本地测试,在大约30秒内将拉取请求转化为带有评分的结构性AI评审结果。

发布者:REACTAPP.TOP阅读约 12 分钟2837 词

来源: Building a Multi-Agent PR Code Review System With LangGraph, FastAPI, Webhooks, and Docker

任何提交过拉取请求的人都知道那种等待的滋味。功能开发完成,代码被推送后,接下来可能是两小时、一整天甚至更久的时间。当同事终于开始审查时,往往会看到大量评论,其中一半都是软件本应能检测出来的问题:硬编码的API密钥、名为x的函数、容易遭受注入攻击的SQL查询。开发者修复所有问题后再次推送代码,然后继续等待。

代码审查本身非常重要,这并非我的抱怨所在。令人沮丧的是,很多初步反馈都是机械性的,每份拉取请求中出现的错误类型都差不多。该系统的设计理念是让人工智能立即捕捉到所有显而易见的问题,这样人工审查者就可以把时间花在真正需要人类判断的架构和逻辑层面。它的效果比预期要好。

为何多个智能体优于单个智能体

这种简单的设计会将 PR 的差异内容发送给模型,并要求其进行审查。对于较小的改动来说这没问题。但随着 PR 规模的增大,需要同时考虑安全性、性能和代码质量的模型会失去重点,审查结果的质量也会下降。

专业化分工可以解决这个问题。为每个智能体分配单一任务:一个专门负责安全性,一个负责性能,一个负责代码质量,还有第四个智能体则综合这三份报告,撰写出清晰专业的审查意见。这种多智能体模式带来的结果明显优于单一通才型智能体。

整体设计中,每个组件都承担一项特定职责:

You open a Pull Request on GitHub
              │
              ▼
    GitHub fires a webhook
              │
              ▼
    FastAPI receives it instantly
    └── Verifies HMAC signature
    └── Pushes job to Redis queue
    └── Returns 202 to GitHub ✅
              │
              ▼
    ARQ Worker picks up the job
              │
              ▼
    LangGraph Pipeline starts
              │
    ┌─────────┼─────────┐
    ▼         ▼         ▼
 Agent 1   Agent 2   Agent 3
 Quality   Security  Performance
    │         │         │
    └─────────┼─────────┘
              ▼
           Agent 4
        Final Reviewer
              │
              ▼
    Comment posted on your PR ✅
    (in under 30 seconds)

第一步:GitHub Webhook

一切始于Webhook。当有拉取请求被创建或更新时,GitHub会向您配置的URL发送HTTP POST请求,该请求中包含PR编号、标题、创建者、仓库信息以及触发该操作的类型。一个FastAPI服务器会监听该URL。

服务器收到请求后的首要操作是验证签名。GitHub会使用您指定的密钥,通过HMAC-SHA256算法对每个请求数据包进行签名;如果签名不匹配,请求会立即被拒绝,这样就没人能向该端点发送虚假的PR事件了。

接下来的决定至关重要:服务器不会立即开始人工智能审查。它会将任务放入 Redis 队列中,然后在不到一秒的时间内向 GitHub 返回 202 Accepted 响应。原因是 GitHub 要求在 10 秒内收到 webhook 响应,而包含三次并行 LLM 调用的完整审查过程需要 25 到 30 秒。如果直接在线审查,GitHub 会认为服务器出现故障并重新尝试,从而导致该 PR 被多次审查且出现重复评论。任务队列则彻底解决了这个问题——GitHub 可以快速得到响应,而实际工作则在后台完成。

步骤 2:使用 ngrok 进行本地开发

在本地开发时,FastAPI 服务器运行在 localhost:8000 地址,GitHub 无法从互联网访问该地址。ngrok 通过在公共 URL 和本地机器之间建立安全隧道来填补这一空白,只需一条命令即可启动它:

ngrok http 8000

它会立即返回一个类似的公共 HTTPS 地址:

https://abc123.ngrok-free.app

将该地址粘贴到 GitHub webhook 设置中,GitHub 即可直接将事件发送到笔记本电脑。每次有 PR 被创建时,请求会从 GitHub 的服务器经 ngrok 的服务器传输到本地的 FastAPI 应用。在终端中,整个交互过程会显示为单行内容:

23:03:02 POST /api/webhook    202 Accepted  ✅

只需一行代码、一秒钟的时间,GitHub 就会满足要求,而实际的工作则在幕后开始。需要注意的是:ngrok 的免费版每次重启都会生成新的 URL,因此必须每次更新 GitHub webhook 的 URL。

步骤 3:LangGraph 流程

这是该系统的核心。LangGraph 是一个用于实现有状态多智能体工作流的框架。智能体成为图中的节点,边将它们连接起来,共享状态在节点间传递。可以想象一个流程图,其中每个方块代表一个 AI 智能体,箭头则表示数据以何种顺序流向何处。

共享状态

所有代理都会读取和写入同一个共享状态对象,该对象中存储着 PR 编号与仓库详情、实际代码变更的差异内容、每个代理的审核结果以及最终整合后的评论。这些信息在系统中像接力赛中的棒子一样传递,每个代理在继续处理之前会添加自己的部分内容。

节点 0:获取 PR 信息

在任何一个代理开始工作之前,会有一个专用节点先运行,它通过 GitHub API 获取 PR 的标题、作者和分支信息,以及每个被修改文件的完整差异内容。它会将所有这些信息转换成一个结构清晰的文本字符串,以便大型语言模型读取,同时以相同格式的差异内容发送给每一位审核者。

并行处理:代理 1、2 和 3

获取信息之后,系统会同时将任务分配给三个代理。它们接收到的差异内容相同,会在同一时间开始处理,且彼此之间互不知情。

代码质量检测工具完全独立运行于自己的工作环境中。它的系统提示涵盖了可读性、命名规范、违反DRY原则的情况、错误处理机制以及代码的可维护性,最终会返回结构化的JSON数据,其中包含10分制的评分以及具体问题列表,每个问题都会标注文件名、行号和建议的修复方案。

安全检测工具仅负责查找漏洞:未经过滤的输入导致的SQL注入、硬编码的密钥、密码或令牌、缺失的身份验证检查,以及日志中存在的敏感数据。它对性能和代码风格毫无了解。

性能检测工具只关注效率问题:N+1型数据库查询模式、复杂度为O(n²)或更低的嵌套循环、异步函数中的阻塞式IO调用,以及缺失的索引或缓存机制。

并行处理对时间效率至关重要。如果依次运行,这三个智能体总共需要60到90秒;而同时运行时,它们大约在7到12秒内完成工作,这个时间是以最慢的那个智能体的处理时间为准,而非三者时间的总和。

数据汇聚点:智能体4

最终审核者会自动等待所有三个专家完成工作。LangGraph负责处理这种同步机制,因此无需编写任何等待或轮询逻辑。当三个结构化的JSON报告送达后,智能体4会执行一项操作,即用Markdown格式撰写最终审核意见。

Agent 4不会进行任何分析。它从不重新审视代码,只是整合并格式化专家们报告的内容。这是有意为之:即便其表述略有偏差,核心结论仍完全来自专家们的分析结果。它的评论以总体评价和三个智能体评分的平均值开头,接着总结每个智能体的发现,最后列出必须在合并前解决的清单,仅包含严重级和高度严重级的问题。

并行状态中的陷阱

一个细微的技术细节很容易被忽视。当并行代理处理完成并将结果写回共享状态时,每个代理只需返回其拥有的键即可。如果每个代理都返回整个状态对象,LangGraph就会同时收到每个共享字段的三个冲突副本,从而抛出InvalidUpdateError错误。解决办法很简单但并不明显:为并行代理写入的字段添加一个Annotated reducer,让每个代理仅返回自己的输出。这种问题通常是在遇到错误后才会了解到,而大多数LangGraph教程从未提及这一点。

第4步:输出结果

证据就在一次测试中。一个包含四个刻意设计的题目、硬编码的秘密信息、SQL注入漏洞、O(n²)嵌套循环以及糟糕命名方式的Python文件被推送到某个分支,并以拉取请求的形式提交。27秒后就有人对该拉取请求进行了审核。以下就是最终被采用的代码:

import sqlite3

# Hardcoded secret — security agent should catch this
API_KEY = "sk-prod-supersecretkey123"

def get_user(user_id):
    conn = sqlite3.connect("users.db")
    # SQL injection — security agent should catch this
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return conn.execute(query)

def process(users):
    # O(n²) nested loop — performance agent should catch this
    for user in users:
        for item in users:
            print(user, item)

# Bad naming — quality agent should catch this
def x(a, b, c):
    return a + b + c

以下是系统在PR中发布的内容:

## 🤖 AI Code Review

**Overall Verdict:** ❌ REJECTED
**Score:** 2.33/10

---

### 📋 Code Quality
Score: 2/10 | 8 Issues Found

Function naming is completely unclear — x, a, b, c give no
indication of purpose. No error handling on database operations.
No docstrings or type hints on any function.

### 🔒 Security
Score: 2/10 | 4 Critical Vulnerabilities

Hardcoded API key on line 3 exposes credentials in version
control — CRITICAL. SQL query on line 9 directly interpolates
user input, creating a SQL injection vulnerability — CRITICAL.

### ⚡ Performance
Score: 3/10 | 4 Issues Found

Nested loop in process() creates O(n²) complexity — will become
extremely slow as the users list grows. Database connection is
opened but never closed, creating a connection leak.

---

### 🚨 Must Fix Before Merge

- Remove API_KEY hardcoding — use environment variables instead
- Parameterize the SQL query to prevent injection attacks
- Refactor nested loop — use set operations or single pass
- Close database connections properly or use a context manager

---

Great effort on the overall structure! Addressing these critical
issues will significantly improve the security and reliability
of this code. Looking forward to the revised version. 🙌

在拉取请求创建后的27秒内,该审查工具就通过具体的行号引用指出了四类实际问题,并给出了修复建议。整个过程无需人工干预,也无需等待,直接提供结构化且可操作的反馈。

在本地运行

一切始于一个Docker命令:设置环境变量、创建GitHub令牌并配置webhook,ngrok将本地服务器与GitHub连接起来,随后一个测试拉取请求便能显示实时出现的审查结果。在Claude和OpenAI之间切换只需修改一个环境变量,相关设置步骤记录在仓库的README文件中。

构建过程中的经验总结

专用型智能体比通用型智能体表现更佳。每个智能体处理一项任务,能产生更为精准、有针对性的结果,因为安全检测智能体不会被代码风格问题干扰,而性能优化智能体也不会在变量名上消耗过多算力。

任务队列是系统架构的组成部分,而非可选功能。GitHub设定的10秒超时时间相当严格,如果没有Redis和后台工作进程,失败的交付任务将无法重试,从而导致重复审核不断累积。许多教程会跳过这一部分,但正是它保证了系统的可靠性。

并行执行会改变时间计算方式。三次依次进行的、每次耗时10秒的操作至少需要30秒,而同时进行的三个操作仅需10秒。LangGraph让实现并行处理变得极为简单,其带来的效率提升十分显著。

结构化的输出是让智能体具备实际应用能力的关键。每个智能体都会返回符合严格JSON规范的响应,如果解析失败,任务会干净利落地终止,且不会有任何内容被上传到GitHub。系统要么生成有效的审核结果,要么什么都不输出,绝不会出现乱码或无效数据。

从一开始就对大语言模型进行抽象处理。通过一个环境变量在 Claude 和 OpenAI 之间切换的工厂模式,只需大约十分钟即可构建完成,之后还能节省数小时的重构时间,因为可以自由地测试模型并比较其质量。

它对代码审查的影响

代码审查不会消失,对于架构决策、业务逻辑以及复杂的设计权衡而言,没有任何东西能替代人类。机械性的初步检查则是另一回事:硬编码的敏感信息、SQL 注入、二次循环以及不清晰的变量名都可以通过自动化手段检测,这样就能让人类审查员专注于需要人类智慧的工作。该系统能在大约30秒内发现明显的问题,让团队成员能够集中精力处理更重要的问题。