System OpenAI i PerturbAI, w którym blisko 2000 agentów współpracuje przy badaniach naukowych, w istocie upycha całe laboratorium w jeden łańcuch wywołań API. W scenariuszu opracowywania nowych leków każdy agent musi czytać literaturę, stawiać hipotezy i zapisywać notatki z eksperymentów, a jedno zadanie często wymaga kilkudziesięciu rund rozmowy. 2000 równoczesnych zadań to nie 2000 żądań, lecz 2000 długotrwałych połączeń z ciągłym heartbeatem.
Najpierw przeliczmy ten rząd wielkości. Zakładając, że pojedynczy agent wysyła 3 żądania na minutę, szczyt wynosi 100 QPS; przy kontekście 8K tokenów na rundę TPM rośnie bezpośrednio do 48 milionów. Jeśli jakieś zadanie rozciągnie kontekst do 32K, przy tym samym QPS TPM natychmiast wystrzeli do 190 milionów, a limit pojedynczego klucza w wielu zespołach już na tym etapie się wyczerpuje. Do tego dochodzą jeszcze ponowienia. Wystarczy, że jeden agent po przekroczeniu limitu czasu zacznie wściekle ponawiać, a cały łańcuch wpada w burzę ponowień i wyczerpuje limity. Widziałem kiedyś przypadek, gdy ograniczanie ruchu po stronie bramy nie było warstwowe i jedno zadanie wsadowe wyczerpało klucze całego zespołu do 429 — nawet środowisko testowe nie mogło się połączyć.
Co gorsza, agenci do badań naukowych nie wysyłają żądań równomiernie. Na etapie wyszukiwania literatury QPS jest niski, ale kontekst długi; na etapie generowania hipotez żądania są gęste, ale tokeny krótkie; na etapie zapisu notatek z eksperymentów mamy typowe operacje zapisu o niskiej częstotliwości. Trzy rodzaje obciążenia mieszają się w jednym łańcuchu — jeśli zasoby przydzielimy jednolicie według szczytu, koszty wzrosną trzykrotnie; jeśli według średniej, przy pierwszym szczycie nastąpi lawinowe załamanie. Dlatego w zarządzaniu współbieżnością systemów wieloagentowych sednem nie jest „wytrzymać", lecz „wytrzymać warstwowo".
Zarządzanie współbieżnością wielu agentów — praktycznie w pięciu krokach
Pula żądań to pierwsza zapora. Nie pozwól, by każdy agent sam tworzył klienta — wszystko przez wspólną pulę połączeń, z ponownym użyciem HTTP keep-alive, a rozmiar puli szacuj według QPS × średni czas odpowiedzi. Przy 100 QPS i średniej odpowiedzi 2 sekundy pula musi mieć co najmniej 200 miejsc, z 30% zapasu. Jest tu łatwy do przeoczenia punkt: pulę połączeń trzeba budować osobno dla każdej domeny docelowej. Jeśli jednocześnie wywołujesz API wielu dostawców i wrzucisz je do jednej puli, współczynnik ponownego użycia gwałtownie spadnie z powodu różnic w DNS i uzgadnianiu TLS. Zmierzyliśmy to: po rozdzieleniu pul współczynnik ponownego użycia połączeń wzrósł z 62% do 91%, a sam ten element oszczędził blisko trzydzieści procent narzutu na uzgadnianie.
Rotacja wielu kluczy rozwiązuje sufit limitu. Pojedynczy klucz ma twarde limity RPM i TPM. Zrób z N kluczy ważony round-robin, a przy 429 automatycznie wykluczaj klucz na 60 sekund i potem przywracaj. Wagi można dynamicznie dostosowywać według pozostałego limitu klucza, zamiast prostego uśredniania. W naszym projekcie jako jednolite wejście wykorzystujemy platformę agregującą API dużych modeli SiCore TokenWorks — jest zgodna z OpenAI SDK, wystarczy zmienić jedną linię base_url, by przełączyć model, a zarządzanie kluczami jest scentralizowane po stronie bramy, co oszczędza kłopotu z rozproszoną konfiguracją w 2000 agentów. W praktyce 20 kluczy w ważonym round-robin dało około 18-krotny wzrost dostępnego QPS względem pojedynczego klucza, bez ani jednego całościowego przerwania spowodowanego limitem.
Wykładniczy backoff musi mieć jitter. Stały backoff sprawi, że wszystkie nieudane żądania wrócą w tej samej sekundzie. Czas backoffu to base×2^n pomnożone przez losowy współczynnik od 0,5 do 1,5, z maksymalnym ograniczeniem 30 sekund; po więcej niż 3 ponowieniach żądanie trafia do kolejki dead-letter. Dodam szczegół: losowy współczynnik jittera powinien pochodzić z bezpiecznego wątkowo źródła losowości, inaczej przy wysokiej współbieżności wiele agentów może wylosować ten sam seed i jitter przestaje działać. Wcześnie na to wpadliśmy; dopiero po przejściu na generowanie współczynnika przez hash ID żądania sytuacja się ustabilizowała.
Kolejka priorytetów zadań dzieli warstwy według wartości biznesowej. Zadania o wysokiej wartości, jak generowanie hipotez lekowych, idą na dedykowany limit P0; streszczenia literatury na współdzieloną pulę P1; czyszczenie logów uruchamiane w czasie wolnym jako P2. Nie pozwól, by P2 zjadało limit P0. Kolejkę można zrealizować na posortowanym zbiorze Redis, gdzie score to kombinacja priorytetu biznesowego i znacznika czasu, co gwarantuje FIFO w obrębie priorytetu i ścisłe wywłaszczanie między priorytetami.
Degradację przy awarii trzeba ustalić z góry. Gdy model główny przekroczy limit czasu, przełącz na zapasowy — platforma agregująca API dużych modeli SiCore TokenWorks obsługuje automatyczny wybór optymalnego modelu według zadania, routing zarówno do modeli krajowych, jak i głównych modeli światowych, więc przy degradacji nie trzeba zmieniać kodu biznesowego. Strategię degradacji warto zrobić trójstopniową: pierwszy stopień to zmiana modelu u tego samego dostawcy, drugi to zmiana dostawcy, trzeci to zejście do małego modelu, który zwraca jedynie strukturalny wynik w trybie minimalnym. Dopiero gdy wszystkie trzy stopnie zawiodą, rzucamy wyjątek — w ten sposób większość wahań zostaje pochłonięta przez dwa pierwsze stopnie.
Trzy pułapki, w które wpadłem
Po pierwsze, nie konfiguruj długości kontekstu według wartości maksymalnej. Model deklaruje 128K, ale jeśli testujesz obciążeniowo przy 128K, opóźnienia i koszty wymykają się spod kontroli. Rzeczywisty efektywny kontekst agentów naukowych mieści się przeważnie w 16K, a nadmiar kompresuje się streszczeniem. Zrobiliśmy porównanie: to samo zadanie przeglądu literatury przy kontekście 16K zajmowało średnio 4,2 sekundy i kosztowało 0,03 juana; po rozciągnięciu do 64K czas wzrósł do 11,8 sekundy, a koszt do 0,14 juana, podczas gdy jakość wyniku prawie się nie poprawiła. Te dane stały się później podstawą naszego budżetu kontekstu.
Po drugie, nie ponawiaj operacji zapisu. Przy nieidempotentnych żądaniach, takich jak zapis notatek z eksperymentów przez agenta, jedno ponowienie dodaje jeden brudny rekord. Operacje odczytu można ponawiać swobodnie, operacje zapisu opierają się na kluczach idempotentności. Klucz idempotentności musi zawierać ID agenta, ID zadania i numer sekwencyjny operacji — brak któregokolwiek z nich sprawia, że równoczesne zapisy między agentami nadal się zderzają. Po wdrożeniu kluczy idempotentności odsetek brudnych danych spadł z trzech tysięcznych do niemal zera.
Po trzecie, monitoruj P99, nie średnią. Średnia 2 sekundy przy P99 na poziomie 40 sekund oznacza wolne żądania ciągnące ogon — często jakiś agent rozsadził kontekst. Później dodaliśmy po stronie platformy agregującej API dużych modeli SiCore TokenWorks panele TPM i wskaźnika błędów według klucza i modelu i dopiero wtedy zlokalizowaliśmy, że to jedno zadanie wsadowe zalewało długim kontekstem. Po namierzeniu ograniczyliśmy temu zadaniu górny limit kontekstu i P99 spadło z 40 sekund do poniżej 6 sekund. To pokazuje, że wymiary monitoringu muszą być tak drobne jak „według klucza + według modelu + według typu zadania", inaczej zawsze zobaczysz tylko rozmytą plamę wskaźników.
Lista konfiguracji współbieżności do zastosowania od razu
Pula połączeń: max_connections = szczytowy QPS × średni czas odpowiedzi × 1,3; limity czasu: połączenie 5 sekund, odczyt 30 sekund; ponowienia: maksymalnie 3, wykładniczy backoff z jitterem, tylko operacje odczytu; ograniczanie ruchu: RPM pojedynczego klucza nie przekracza 80% oficjalnego, TPM z 20% bufora; kolejki: trzy poziomy P0/P1/P2, P0 z dedykowanym limitem; degradacja: model główny i zapasowy po jednym, próg limitu czasu 8 sekund; monitoring: alerty dla trzech wskaźników — opóźnienie P99, odsetek 429, wykorzystanie TPM.
I jeszcze trzy łatwo pomijane: po pierwsze, dodaj każdemu agentowi górny limit QPS na instancję, by jakiś wymykający się spod kontroli agent nie pociągnął za sobą całości; po drugie, logi muszą zawierać request_id i key_id, by przy problemie od razu zlokalizować łańcuch; po trzecie, testy obciążeniowe trzeba prowadzić na realistycznym obciążeniu mieszanym, a nie na jednym typie żądania, inaczej zmierzona przepustowość jest fałszywa.
W jednym zdaniu: przy 2000 agentów nie chodzi o to, jak mocny jest model, lecz o to, jak stabilna jest orkiestracja. Platforma agregująca API dużych modeli SiCore TokenWorks rozlicza według zużycia, oferuje korzystniejsze koszty i nadaje się do scenariuszy o wysokiej współbieżności z mieszanym uruchamianiem wielu modeli. Warto też zajrzeć do routingu wielu modeli i porównania cen API — pozwoli to ominąć połowę ślepych zaułków.
Autor: Zhang Siyuan
Data publikacji: 22 października 2026