Agenci LangGraph działające równolegle, które przeglądają prośby o pull request za pośrednictwem webhooków FastAPI.
Jak webhook, kolejka Redis oraz cztery agenty LangGraph przekształcają prośbę o pull request w ocenioną, ustrukturyzowaną recenzję AI w ciągu około 30 sekund, przy użyciu ngrok do testów lokalnych.
Źródła: Building a Multi-Agent PR Code Review System With LangGraph, FastAPI, Webhooks, and Docker
Każdy, kto otwierał prośbę o połączenie kodu, zna to oczekiwanie. Funkcja zostaje ukończona, kod wysyłany, a potem mijają dwie godziny, cały dzień lub nawet więcej. Gdy koledzy z zespołu w końcu ją sprawdzą, pojawia się tuzin komentarzy, z których połowa dotyczy błędów, które oprogramowanie mogło samo wykryć: stała klucz API, funkcja nazwana x, zapytanie SQL podatne na iniekcję. Autor naprawia wszystko, ponownie wysyła kod i znów czeka.
Sama ocena kodu jest niezbędna, więc to nie jest temat skargi. Frustrację budzi fakt, że tak wiele informacji po pierwszej weryfikacji ma charakter mechaniczny – zawsze te same kategorie problemów w każdej prośbie o połączenie kodu. Ideą tego systemu jest umożliwienie sztucznej inteligencji natychmiastowego wykrycia wszystkich oczywistych błędów, aby ludzcy recenzenci mogli skupić się na architekturze i logice, tam gdzie rzeczywiście potrzebna jest ludzka ocena. Działa lepiej, niż można by się spodziewać.
Dlaczego kilka agentów jest lepsze niż jeden
Naiwny projekt wysyła różnicę w PR do modelu z poleceniem jej przejrzenia. To wystarcza przy małych zmianach. Gdy PR stają się większe, model, który musi jednocześnie brać pod uwagę kwestie bezpieczeństwa, wydajności i jakości kodu, traci koncentrację, a wyniki oceny się pogarszają.
Specjalizacja rozwiązuje ten problem. Każdemu agentowi przydzielamy jedno zadanie: jeden ma myśleć wyłącznie o bezpieczeństwie, drugi o wydajności, trzeci o jakości kodu, a czwarty bierze te trzy raporty i pisze klarowną, profesjonalną ocenę. Ten model wielu agentów daje znacznie lepsze rezultaty niż jeden agent ogólnikowy.
Ogólny projekt, w którym każdy komponent ma jedną odpowiedzialność:
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)
Krok 1: webhook GitHub
Wszystko zaczyna się od webhooka. Gdy otwierana lub aktualizowana jest prośba o połączenie (pull request), GitHub wysyła żądanie HTTP POST do skonfigurowanej przez Ciebie adresu URL, a w tym żądaniu znajduje się numer PR, tytuł, autor, repozytorium oraz akcja, która je wywołała. Serwer FastAPI oczekuje na to żądanie w tym adresie URL.
Pierwszym krokiem serwera w odpowiedzi na każde żądanie jest sprawdzenie podpisu. GitHub podpisywał każdy przekazane dane za pomocą hasza HMAC-SHA256, wykorzystując sekret określony przez Ciebie; niezgodność podpisu oznacza natychmiastową odrzucenie żądania, dzięki czemu nikt nie może wysyłać fałszywych zdarzeń PR do tego endpointu.
Następna decyzja jest kluczowa: serwer nie rozpoczyna natychmiast przeglądu przez AI. Umieszcza zadanie w kolejce Redis i odpowiada GitHubowi kodem 202 Accepted w mniej niż sekundę. Powodem jest to, że GitHub oczekuje odpowiedzi webhook w ciągu 10 sekund, natomiast pełny przegląd z trzema równoległymi wywołaniami LLM zajmuje od 25 do 30 sekund. Przeglądanie w trybie inline sprawiłoby, że GitHub uznałby, iż serwer zawiodł, i spróbowałby ponownie, więc PR byłby przeglądany kilka razy z duplikowanymi komentarzami. Kolejka zadań rozwiązuje ten problem całkowicie – GitHub otrzymuje szybką odpowiedź, a rzeczywła praca odbywa się w tle.
Krok 2: ngrok do lokalnego rozwoju
Podczas lokalnego rozwoju serwer FastAPI działa pod adresem localhost:8000, do którego GitHub nie może uzyskać dostępu z Internetu. ngrok wypełnia tę lukę, tworząc bezpieczny tunel pomiędzy publicznym adresem URL a lokalnym komputerem. Wystarczy jeden polecenie, aby to uruchomić:
ngrok http 8000
Od razu zwraca publiczny adres https w postaci:
https://abc123.ngrok-free.app
Wklej ten adres do ustawień webhooka GitHuba, a GitHub będzie mógł przesyłać zdarzenia bezpośrednio na laptopa. Za każdym razem, gdy otwiera się PR, żądanie przemieszcza się od serwerów GitHuba przez serwery ngrok do lokalnej aplikacji FastAPI. W terminalu cała wymiana pojawia się jako jedna linia:
23:03:02 POST /api/webhook 202 Accepted ✅
Jedna linia pod drugą – GitHub jest zadowolony, podczas gdy prawdziwa praca rozpoczyna się w tle. Jedna uwaga: darmowa wersja ngrok wydaje nowy adres URL przy każdym restartowaniu, więc adres webhooka GitHuba musi być aktualizowany za każdym razem.
Krok 3: pipeline LangGraph
To jest serce systemu. LangGraph to framework do prac złożonych z wielu agentów o stanach pamięci. Agenci stają się węzłami w grafie, łączą je krawędzie, a pomiędzy nimi przepływa wspólny stan. Wyobraź sobie schemat przepływowy, w którym każda kwadratowa komórka to agent AI, a strzałki wskazują, jakie dane przemieszczają się dokąd i w jakiej kolejności.
Wspólny stan
Wszystkie agenty czytają i zapisują dane do jednego wspólnego obiektu stanu, który przechowuje numer PR oraz szczegóły repozytorium, pobrany rozbieżność z rzeczywistymi zmianami w kodzie, wyniki przeglądania każdego z agentów oraz ostateczny skompilowany komentarz. Informacje te przekazywane są w grafie jak sztafeta – każdy agent dodaje swój element, zanim go przekaże dalej.
Węzeł 0: pobieranie PR
Specjalny węzeł działa przed każdym agentem i wysyła zapytanie do API GitHuba w celu uzyskania tytułu PR, nazwiska autora i gałęzi oraz pełnej rozbieżności każdego zmienionego pliku. Wszystko to przekształca się w uporządkowany ciąg tekstu, który mogą odczytać modele językowe, a ta sama sformatowana rozbieżność jest przekazywana każdemu z recenzentów.
Równoległe rozprzestrzenianie: agenci 1, 2 i 3
Po pobraniu danych graf rozprzestrzenia się jednocześnie do trzech agentów. Otrzymują oni tę samą rozbieżność, działają w tym samym momencie i nie wiedzą nic o siebie nawzajem.
Agent ds. jakości kodu funkcjonuje wyłącznie we własnym świecie. Jego instrukcje systemowe obejmują czytelność, zasady nazywania, naruszenia zasady DRY, obsługę błędów oraz możliwość utrzymania kodu, a on zwraca ustrukturyzowany JSON z oceną od 0 do 10 oraz listą konkretnych problemów, z których każdy zawiera nazwę pliku, numer wiersza i zalecaną poprawkę.
Agent ds. bezpieczeństwa szuka wyłącznie słabości: iniekcji SQL spowodowanych nieprzefiltrowanymi danymi wejściowymi, haseł lub tokenów zapisanych w kodzie źródłowym, braku sprawdzeń autoryzacji oraz wrażliwych danych w dziennikach. Nie wie nic o wydajności ani stylu kodu.
Agent ds. wydajności myśli wyłącznie o efektywności: wzorcach zapytań do bazy danych typu N+1, pętlach nawzajem zawartych o złożoności O(n²) lub gorszej, blokujących wywołaniach IO wewnątrz funkcji asynchronicznych oraz braku indeksów lub możliwości cache’owania.
Paralelizm ma znaczenie dla czasu. Jeśli działają sekwencyjnie, wszystkie trzy agenty potrzebują łącznie od 60 do 90 sekund. Jeśli działają jednocześnie, kończą pracę w około 7 do 12 sekund – to czas najwolniejszego z agentów, a nie suma czasu wszystkich trzech.
Fan-in: agent 4
Ostateczny recenzent automatycznie czeka, aż wszyscy trzej specjaliści skończą pracę. LangGraph zajmuje się tą synchronizacją, więc nie trzeba pisać żadnej logiki czekania ani pytań. Gdy trzy raporty przychodzą w formie zstrukturyzowanego JSON, agent 4 wykonuje jedno zadanie – pisze ostateczny komentarz recenzji w formacie Markdown.
Agent 4 nie przeprowadza żadnej analizy. Nigdy ponownie nie sprawdza kodu, a jedynie łączy i formatuje to, co zgłosili specjaliści. Jest to celowe: nawet jeśli sformułowania są nieco niestabilne, wyniki pozostają dokładnie takie same jak te uzyskane przez specjalistów. Jego komentarz zaczyna się od ogólnej oceny i wyniku będącego średnią wyników trzech agentów, następnie podsumowuje to, co znalazł każdy agent, i kończy się listą „Must Fix Before Merge” zawierającą wyłącznie krytyczne i poważne problemy.
Pułapka w stanie równoległym
Nieznaczny szczegół techniczny łatwo przeoczyć. Gdy agenci równoległe zakończą pracę i zapiszą zmiany do wspólnego stanu, każdy z nich musi zwrócić tylko klucz, który posiada. Gdyby każdy agent miał zwracać cały obiekt stanu, LangGraph otrzymywałby jednocześnie trzy sprzeczne kopie każdego wspólnego pola i wywołał błąd InvalidUpdateError. Rozwiązanie jest proste, ale nieoczywiste: należy do pól, w które zapisują dane agenci równoległe, dodać reduktora typu Annotated i sprawić, by każdy agent zwracał tylko swój własny wynik. Dowiaduje się o tym dopiero w momencie napotkania błędu, a większość tutoriali dotyczących LangGraph w ogóle o tym nie wspomina.
Krok 4: wynik
Dowód znajduje się w teście. Plik w języku Python zawierający cztery celowo umieszczone problemy, sztywno zdefiniowaną tajemnicę, lukę podatną na SQL injection, pętlę nawiasową typu O(n²) oraz nieodpowiednie nazewnictwo został umieszczony na gałęzi i otwarty jako pull request. Recenzja pojawiła się 27 sekund później. Oto kod, który trafił do projektu:
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
I oto to, co system opublikował na 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. 🙌
W ciągu zaledwie 27 sekund od utworzenia prośby o przegląd znaleziono cztery konkretne kategorie problemów wraz z konkretnymi odniesieniami do linii kodu i sugestiami naprawczymi. Nie brał w tym udziału żaden człowiek, nie było konieczności oczekiwania – od razu otrzymano uporządkowane, praktyczne informacje zwrotne.
Ruch lokalny
Wszystko zaczyna się od jednego polecenia Docker: ustawiane są zmienne środowiskowe, tworzony jest token GitHuba i konfigurowany webhook, ngrok łączy lokalny serwer z GitHubem, a testowa prośba o przegląd pokazuje, jak natychmiast pojawiają się informacje zwrotne. Przełączanie między Claude a OpenAI wymaga zmiany tylko jednej zmiennej środowiskowej, a kroki konfiguracji są opisane w pliku README repozytorium.
Lekcje z procesu budowania
Agenci specjalistyczni radzą sobie lepiej niż agenci ogólni. Praca jednego agenta na zadanie daje bardziej precyzyjne i skoncentrowane wyniki, ponieważ agent bezpieczeństwa nie jest rozpraszany przez kwestie stylu kodu, a agent optymalizacji nie marnuje tokenów na nazwy zmiennych.
Kolejka zadań jest częścią architektury, a nie opcją. 10-sekundowy limit czasu oczekiwania w GitHub jest ścisły, a bez Redis oraz procesora w tle nieudane próby dostarczenia są ponawiane, co powoduje gromadzenie się duplikatów recenzji. Wiele tutoriali pomija ten aspekt, a jednak to on sprawia, że system jest niezawodny.
Wykonywanie zadań równolegle zmienia sytuację. Trzy sekwencyjne wywołania po 10 sekund każde wymagają co najmniej 30 sekund, podczas gdy trzy równoległe – tylko 10. LangGraph ułatwia implementację równoległości, a różnica jest znaczna.
Ustrukturyzowany wynik sprawia, że agenci nadają się do użycia w produkcji. Każdy agent zwraca dane zgodne ze ścisłym schematem JSON, a jeśli parsowanie się nie powiedzie, zadanie kończy się bezbłędnie i nic nie jest wysyłane do GitHub. System albo dostarcza dobrą recenzję, albo nic, ale nigdy śmieciowych danych.
Od samego początku abstrahuj LLM. Wzorzec fabryczny, który umożliwia przełączanie się między Claude a OpenAI za pomocą jednej zmiennej środowiskowej, wymaga około dziesięciu minut na stworzenie i oszczędza godziny pracy nad refaktoryzacją później, ponieważ modele można swobodnie testować i porównywać jakość ich oceny.
Jak to wpływa na przegląd kodu
Przegląd kodu nie zniknie, a przy podejmowaniu decyzji architektonicznych, logice biznesowej oraz delikatnych kompromisach projektowych nic nie zastąpi człowieka. Inna sprawa to pierwsza, mechaniczna analiza: hardkodowane sekrety, iniekcje SQL, pętle kwadratowe oraz niejasne nazwy można zautomatyzować, co uwalnia ludzkich recenzentów do pracy wymagającej ludzkiej inteligencji. Ten system wykrywa oczywiste problemy w ciągu około 30 sekund, dzięki czemu koledzy z zespołu mogą skupić się na tych najważniejszych.
Literatura pokrewna
- Triagowanie zamówień podtrzymywania interakcji z wieloma agentami za pomocą LangGraph i Ollamy — Stworzenie grafu do triagowania zamówień z trzema agentami przy użyciu LangGraph: klasyfikatora, osoby odpowiadającej oraz ludzkiego moderatora, działającego na lokalnym Ollamie lub przez API przy jednej zmienionej linii kodu.
- Praktyczne wskazówki: Budowanie pętli przeglądu kodu z dwoma agentami przy użyciu A2A, LangGraph i — Szczegółowy przewodnik po praktycznych wskazówkach dotyczących budowy pętli przeglądu kodu z dwoma agentami przy użyciu A2A, LangGraph oraz elementów takich jak umowy, sprawdzania i gotowe fragmenty kodu przeznaczone dla zespołów wdrażających ten model.