Strona główna / Artykuły

Przetrwanie nagłych wzrostów ruchu w systemach wieloagentowych dzięki trójwarstwowemu kontraktowi

Utrzymanie sprawności systemów wieloagentowych przy dziesięciokrotnym wzroście ruchu: mechanizm zarządzania kolejką, ograniczanie przepustowości dla poszczególnych narzędzi oraz synchroniczne sprawdzanie zasad, plus metryki i demonstracja nagłego wzrostu obciążenia.

Wydawca: REACTAPP.TOP8 min czytania1869 słów

Źródła: Multi-Agent Orchestration in Production: Handling Traffic Spikes at Scale

Graph agentów może sprawnie funkcjonować przy 10 żądaniach na sekundę, ale załamać się przy 100. Żądania się gromadzą, sprawdzanie zasad jest pomijane, wywołania narzędzi konkuruje ze sobą, a system, który wydawał się niezawodny, staje się obciążeniem. Rzadko to agenci są przyczyną problemów. Brakuje wyraźnej umowy pomiędzy kolejką, warstwą orkiestracji a zasadami bezpieczeństwa. Większość zespołów odkrywa tę lukę dopiero wtedy, gdy już istnieje obciążenie w rzeczywistym ruchu.

Dlaczego wzrost ruchu następuje szybciej, niż się spodziewano

Systemy oparte na agentach opuszczają etap demonstracyjny i przechodzą do rzeczywistej produkcji, więc wzrosty obciążenia są nieuniknione: szybka promocja, funkcja, która staje się hitem, zaplanowana praca zbiorcza lub po prostu każdy użytkownik korzystający z API we wtorek rano. Gdy obciążenie rośnie, dzieją się jednocześnie dwie rzeczy.

Po pierwsze, routowanie odbywa się w czasie rzeczywistym. Wybór agenta, który ma obsłużyć żądanie, lub decyzja o umieszczeniu go na liście oczekujących, to zadania wymagające pomiaru.

Po drugie, systemy mają tendencję do awarii pod presją. Zazwyczaj nie chodzi o spanikowanych inżynierów omijających procedury kontrolne, lecz o naiwne radzenie sobie z błędami, które domyślnie pozwalają na dalszą pracę po wygaśnięciu czasu oczekiwania, lub o alternatywne ścieżki routingu, które po cichu pomijają mechanizmy walidacji. Gdy egzekwowanie zasad znika pod obciążeniem, przyczyną jest zazwyczaj właśnie ten antypatron architektoniczny, a nie interwencja człowieka.

Rozwiązaniem jest jasna, egzekwowalna umowa pomiędzy trzema elementami: kolejką (to, co trafia do systemu), grafem orkiestracji (dokąd trafia) oraz silnikiem polityk (co może zostać zrobione). Ustalcie tę umowę wcześnie, aby nagłe wzrosty obciążenia stały się problemem rozwiązanym, a nie sytuacją awaryjną.

Umowa składająca się z trzech warstw

Warstwa 1: kolejka jako strażnik wejścia

Kolejka to pierwsze miejsce, gdzie stosuje się zasady. Zanim jakikolwiek żądanie dotrze do agenta, przechodzi kontrolę zgodnie z umową, która obejmuje trzy aspekty:

  • Priorytet. Prośby nie są równe. Interaktywne zapytania użytkowników mogą mieć wyższy priorytet niż zlecenia zbiorcze, a klient płatny może wyprzedzić klienta z wersji darmowej. Kolejka powinna zarządzać takim uporządkowaniem, aby agenci nigdy nie musieli o tym wiedzieć.
  • Limity szybkości i kwoty. Odmowa jest najtańsza, zanim zostanie zużyty jakikolwiek zasób obliczeniowy. Użytkownik, który osiągnął dzienny limit API, powinien otrzymać jasną odmowę, a nie miejsce w kolejce, co kosztuje znacznie mniej niż interwencja agenta.
  • Przestrzeganie schematu. Dane, których system nie może przetworzyć, powinny zostać odrzucone tutaj, a nie odkryte dopiero w trakcie pracy agenta.

To warstwa gwarantuje, że każda prośba znajdująca się w kolejce została zweryfikowana, uporządkowana i zatwierdzona do wykorzystania zasobów.

Warstwa 2: orkiestracja decyduje o trasowaniu i tempie

Po umieszczeniu żądania w kolejce, system orkiestracji wybiera jego destynację oraz to, co jest mu dozwolone robić. Tutaj kontrolowane są kwestie współbieżności i dostępu do narzędzi.

Umowa routingu odpowiada na kilka pytań: która grupa agentów powinna obsługiwać żądanie – na przykład szybsze agenty do prostych zapytań i wolniejsze do złożonych obliczeń? Ile żądań może jednocześnie obsłużyć każdy agent? Co się dzieje, gdy wszystkie agenty są zajęte: czy żądanie trafia do kolejki, jest odrzucane, czy stosuje się alternatywne rozwiązanie?

Umowa ograniczania wydajności narzędzi bierze pod uwagę różnice pomiędzy nimi. Wywołanie zewnętrznego API stu razy na sekundę zupełnie nie przypomina uruchamiania lokalnego narzędzia. Każde narzędzie ma własny limit współbieżności – narzędzie oparte na bazie danych może pozwalać na dziesięć jednoczesnych wywołań, podczas gdy narzędzie korzystające z API third-party może pozwolić na pięć. Gdy narzędzie osiągnie swój limit, żądanie czeka w jego własnej kolejce, a nie w głównej.

Aby utrzymać te kolejki narzędzi w dobrym stanie i zapobiec kaskadowym awariom, pomocne są elementy klasy produkcyjnej:

  • Przerywacze obwodów i buforzy tokenów szybko reagują na problemy na poziomie narzędzi, zapobiegając falom ponownych prób gdy zależność jest przeciążona.
  • Kolejki wiadomości błędnych są przeznaczone do przechowywania nieprawidłowych lub uszkodzonych danych narzędzi, dzięki czemu jeden zły żądanie nigdy nie blokuje całego cyklu agenta.
  • Bufory z mechanizmem wycieku, stosowane dla każdego narzędzia, stosują własne limity szybkości, co chroni równoległe działanie agentów przed zewnętrznymi ograniczeniami.

Dzięki tym rozwiązaniom wąskie gardła na poziomie narzędzi pozostają kontrolowane i widoczne, nie przenosząc się na cały system. Obietnica tego warstwa: żądanie trafia do agenta z wolną przepustowością, każde narzędzie przestrzega swojego limitu równoczesności, a jeśli narzędzie jest przeciążone, żądanie czeka na to narzędzie, a nie na agenta.

Warstwa 3: polityki, które nigdy się nie zmieniają

Trudność polega na tym, że zasady muszą być wykonywane za każdym razem, nawet przy dużej obciążeniu. Nie ma opcji „sprawdzić później” ani „to wydaje się bezpieczne”.

Umowa dotycząca zasad składa się z czterech klauzul:

  • Sprawdzenie zasady odbywa się przed każdym wywołaniem narzędzia, synchronicznie i blokująco.
  • Sprawdzenie jest szybkie – trwa co najwyżej kilka milisekund. Jeśli tak nie jest, projekt jest błędny.
  • Gdy sprawdzenie się nie powiedzie, żądanie również się nie powiedzie. Agent nie ma szansy na ponowną próbę ani negocjację.
  • Błędy są rejestrowane i monitorowane, a wzrost liczby błędów zasad sam w sobie jest sygnałem.

Typowe zasady wyglądają w ten sposób: agent może używać tylko narzędzi oznaczonych jako tylko do odczytu dla danego użytkownika; narzędzie może działać tylko dla autoryzowanego użytkownika; agent może wykonać co najwyżej pięć wywołań narzędzi na żądanie. Warstwa gwarantuje, że przed każdym wywołaniem narzędzia następuje sprawdzenie, a zasady są szybkie, deterministyczne i bezwzględne.

Kształt pipeline’a

W minimalnym pseudo-grafie żądanie trafia przez bramkarza kolejki, przechodzi do routera, dociera do agenta, a każde wywołanie narzędzia przechodzi przez wdrożenie zasad oraz ograniczenia specyficzne dla danego narzędzia przed wykonaniem.

[ Incoming Request ]
         │
         ▼
[ Queue Gatekeeper ]
  • Validate Schema
  • Enforce Rate Limits
  • Assign Priority
         │ (Pass)
         ▼
[ Orchestration Router ]
  • Check Agent Capacity
  • Route to Target Node
         │
         ▼
[ Tool-Level Gate ]
  • Sync Policy Check
  • Per-Tool Concurrency Limits
  • Circuit Breaker / DLQ
         │ (Pass)
         ▼
[ Tool Execution ]

Ważnym szczegółem jest to, gdzie stosowane są zasady. Obowiązują one na poziomie narzędzi, a nie agentów, co zapobiega temu, by agenci przypadkowo je obejśli, używając narzędzi w nieoczekiwany sposób.

Scenariusz gwałtownego wzrostu ruchu, z umową i bez niej

Załóżmy, że agent obsługi klienta może wyszukiwać zamówienia, przetwarzać zwroty i wysyłać e-maile, a ruch nagle wzrasta dziesięciokrotnie.

Bez umowy awaria rozwija się krok po kroku:

  • Przychodzące żądania wyczerpują dostępną liczbę agentów.
  • Agenci zaczynają kolejkować żądania do narzędzi, a narzędzie send_email gromadzi 500 oczekujących żądań.
  • Usługa e-mail zaczyna wykonywać timeouty.
  • Agenci próbują ponownie, więc teraz czeka 1 000 żądań.
  • Włączają się ścieżki awaryjne. Timeouty lub przeciążone alternatywne trasy omijają egzekwowanie zasad, aby utrzymać dostępność systemu.
  • Agent wysyła e-mail z informacją o zwrocie pieniędzy do niewłaściwego klienta.

W przypadku umowy ten sam scenariusz rozwija się inaczej:

  • System zarządzania kolejką kontroluje dostęp według priorytetu i kwoty.
  • Router stwierdza, że narzędzie do wysyłania e-maili osiągnęło swoją granicę jednoczesnych zadań wynoszącą dziesięć, więc kolejne żądania są umieszczane w kolejce do tego narzędzia, a nie do agenta.
  • Weryfikacje zgodne z zasadami są wykonywane przy każdym wezwaniu narzędzia. Jeśli agent spróbuje wysłać zwrot pieniędzy do niewłaściwego klienta, weryfikacja się nie powiedzie i żądanie zostanie odrzucone.
  • System pozostaje stabilny. Opóźnienia rosną, ale nie spada niezawodność.

Podsumowanie

Pocznij od LangGraph

Zespół już pracujący nad LangGraph posiada większość elementów bazowych. Agenci i narzędzia to węzły, a krawędzie reprezentują wybory trasowania; krawędzie warunkowe kierują ruch w zależności od stanu. Potrzebna jest kolejka na początku łańcucha – czy to Redis, RabbitMQ, czy zwykła kolejka pamięciowa – oraz warstwa pośrednicząca do zarządzania politykami wokół wywołań narzędzi.

Sam wrapper polityki może być małym dekoratorem, który przeprowadza sprawdzenia i wywołuje błąd naruszenia zasad przed wykonaniem funkcji narzędzia.

from functools import wraps
from typing import Callable, Any

class PolicyViolation(Exception):
    """Raised when policy check fails."""
    pass

def enforce_tool_policy(
    policy_fn: Callable[[str, dict], bool]
):
    """Sync tool execution guardrail."""
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            tool_name = func.__name__
            context = kwargs.get(
                "request_context", {}
            )

            # Must fail closed
            if not policy_fn(tool_name, context):
                raise PolicyViolation(
                    f"Denied: {tool_name}"
                )

            return func(*args, **kwargs)
        return wrapper
    return decorator

# Example Usage
def strict_policy_check(tool_name: str, context: dict) -> bool:
    if not context.get("is_authenticated"):
        return False

    if (context.get("role") != "admin" and tool_name.startswith("execute_")):
        return False

    return True

@enforce_tool_policy(strict_policy_check)
def execute_refund(
    order_id: str,
    amount: float,
    request_context: dict = None
):
    # Downstream API call
    return f"Refund ${amount} sent: {order_id}"

Zmierz to

Wystarczy tylko wdrożyć to i mieć nadzieję. Należy to skutecznie monitorować i obserwować:

  • Głębokość kolejki w każdej warstwie.
  • Częstotliwość oraz to, które narzędzia osiągają swoje limity.
  • Czy polityki odrzucają żądania i dlaczego.
  • Czas reakcji p50, p95 i p99, śledzony oddzielnie dla różnych typów żądań.
  • Koszt za żądanie przy obciążeniu. Jeśli rośnie, oznacza to istnienie jakiejś nieefektywności.

Sceptyczny punkt widzenia

Można słusznie stwierdzić, że to wygląda na dużą ilość infrastruktury dla problemu, który jeszcze nie istnieje. System obsługujący dziesięć żądań na sekundę i pozostający przy tym poziomie nie potrzebuje tego. Inna sprawa to funkcje, które mogą stać się bardzo popularne, lub klienci korporacyjni o nieprzewidywalnym ruchu – w takich przypadkach projekt musi być gotowy przed uruchomieniem.

Budowa takiego rozwiązania z góry kosztuje niewiele w porównaniu z jego modyfikacją po tym, jak awaria zniszczy system. Nie powinno się go też nadmiernie rozbudowywać. Prosta kolejka, funkcja weryfikacji zasad oraz ograniczenia jednoczesności dla poszczególnych narzędzi rozwiązują większość problemów z nagłymi wzrostami obciążenia. Nie są potrzebne ani algorytmy konsensusu rozproszonego, ani narzędzia do przewidywania obciążenia oparte na sztucznej inteligencji. Ważna jest jasność oraz określenie ograniczeń.

Główne punkty

  1. Nagłe wzrosty obciążenia ujawniają problemy w projekcie. Gdy system oparty na agentach zawodzi pod dużym obciążeniem, to nie sami agenci są zbyt wolni. Nikt nie ustalił jasnych zasad dotyczących tego, jak kolejka, routowanie i zasady współpracują ze sobą.
  2. Kolejka pełni również rolę punktu egzekucji. Może weryfikować, priorytetyzować i odrzucać żądania, dzięki czemu unika się marnotrawstwa zasobów obliczeniowych.
  3. Zasady są bezwzględne i niepodlegają negocjacjom. Działają na poziomie poszczególnych narzędzi, synchronicznie, za każdym razem, bez żadnych wyjątków.
  4. Ograniczaj wykorzystanie narzędzi przed agentami. Wąskie gardła zazwyczaj znajdują się poniżej w łańcuchu przetwarzania, w zewnętrznych API lub bazach danych dostarczających kontekst dla agentów, a nie u dostawcy LLM.
  5. Mierz wszystko: głębokość kolejki, jednoczesne używanie narzędzi, błędy w politykach, procentyle opóźnień oraz koszt za zapytanie. Tego, czego się nie mierzy, nie da się poprawić.

Od czego zacząć

  1. Zapisz umowę kolejki: co jest akceptowane, a co odrzucane.
  2. Zapisz umowę routingu: które agenty zajmują się jakimi zadaniami oraz jakie są limity jednoczesnego działania.
  3. Zapisz umowę polityk: jakie sprawdzenia są wykonywane przed każdym wywołaniem narzędzia.
  4. Zbuduj system przy użyciu narzędzi takich jak LangGraph, dodaj warstwę kolejki i otocz narzędzia sprawdzeniami zgodnie z politykami.
  5. Przeprowadź test obciążeniowy. Zsimuluj dziesięciokrotny wzrost obciążenia i sprawdź, co się zepsuje.

Najlepiej zrobić to wszystko przed wdrożeniem, póki jeszcze jest czas, by spokojnie się tym zająć.

Podobne artykuły

  • Dlaczego agenci AI w produkcji potrzebują czegoś więcej niż tylko dobrego promptu — kontekst, narzędzia, pamięć, radzenie sobie z błędami, śledzenie, testowanie oraz autonomia oparta na ryzyku: architektura wokół LLM, która sprawia, że agent jest niezawodny.