Blog

Crash z brakiem pamięci ukryty w etykiecie postępu

Pewnego wieczoru pod koniec września agent AI w przestrzeni roboczej jednego z użytkowników padł z błędem braku pamięci w środku tury. Oczywiste podejrzenia — zbyt wielu podagentów działających naraz albo kontekst, który urósł za bardzo — były błędne. Prawdziwą przyczyną była etykieta statusu: gdy model strumieniował duże polecenie powłoki po kilka znaków, za każdym razem wysyłaliśmy ponownie całe dotychczasowe polecenie jako etykietę postępu. Jedno polecenie o rozmiarze 124 KB zamieniło się w około 41 000 zdarzeń i mniej więcej 690 MB zakolejkowanych napisów.

Jak agent strumieniuje pracę

Gdy budujesz w Mythex, agent działa w prywatnym sandboksie chmurowym projektu. Gdy model pisze, jego wyjście strumieniuje się z powrotem do przeglądarki: tekst pojawia się słowo po słowie, a gdy agent uruchamia narzędzie — powiedzmy polecenie powłoki zapisujące plik — widzisz na żywo aktualizowaną etykietę, dzięki czemu wiesz, co się dzieje.

Model strumieniuje w małych kawałkach. Przy modelu, którego używaliśmy, te kawałki były często długie na trzy znaki.

Pierwsze hipotezy były błędne

Crash wyglądał na problem skalowania, więc tam zaczęliśmy.

  • Równoległe podagenty? Tura użyła tylko dwóch, każdy trzymał około 1,5 MB stanu. To za mało.
  • Zatrzymane ślady? Nasze śledzenie trzymało w pamięci drzewo uruchomień, ale było dalekie od rozmiaru sterty w chwili crashu.

Oba wyjaśnienia były wiarygodne i oba łatwo było przyjąć. Żadne nie odtworzyło crashu. Lekcja, którą wciąż na nowo odkrywamy: hipoteza nie jest przyczyną, dopóki nie odtworzy awarii.

Odtworzenie tego naprawdę

Zbudowaliśmy tymczasowy sandbox z takimi samymi limitami pamięci jak produkcja i sfałszowany model, który strumieniował duży plik w realistycznych kawałkach po trzy znaki. Spowolniliśmy też odbiorcę po drugiej stronie gniazda, jak prawdziwa sieć.

To zadziałało. Sterta rosła, a proces padł z tym samym błędem.

Mechanizm był prosty, gdy już mogliśmy go obserwować. Gdy model pisał duże polecenie — w tym przypadku heredoc zapisujący plik 124 KB — nasz kod strumieniowania aktualizował etykietę narzędzia przy każdym kawałku. Etykieta nie zawierała nowych trzech znaków; zawierała całe dotychczasowe polecenie. Zdarzenia rosły: 3 znaki, potem 6, potem 9, … aż do 124 KB, około 41 000 razy. Gniazdo nie mogło wysyłać tak szybko, jak powstawały, więc piętrzyły się w jego buforze zapisu jako napisy — około 690 MB.

Naprawa

  • Ogranicz etykietę. Etykieta statusu musi tylko mówić, co się dzieje. Jest teraz ograniczona do 200 znaków, a częstotliwość ponownego skanowania częściowego polecenia jest ograniczona.
  • Zlikwiduj kwadratowe sklejanie. Łączenie strumieniowanych kawałków przez powtarzane sklejanie napisów to O(n²) przy dużych wyjściach. Przeszliśmy na podejście oparte na fragmentach, które dokleja w czasie liniowym.

Potem poszukaliśmy wszystkiego innego, co mogło rosnąć bez ograniczeń przy lawinie wyjścia, i to ograniczyliśmy:

  • Wyjście polecenia na żywo pokazywane w czacie jest grupowane i zachowuje tylko ostatnie 8000 znaków.
  • Pojedyncze linie zdarzeń są ograniczone do 4 MB.
  • Bufor powtórki, z którego przeglądarka nadrabia zaległości po ponownym połączeniu, jest ograniczony do 8 MB na projekt.
  • Stare punkty kontrolne są przycinane w trakcie tury, zachowując najnowszy stan i punkty zaczepienia, których wcześniejsze tury potrzebują.

Uczciwe testowanie zalewów

Dwie rzeczy utrudniły testowanie bardziej, niż powinny:

  1. Prawdziwe modele nie współpracują. Poproś model o wypisanie ogromnego pliku, a zwykle odmówi, podsumuje albo napisze go sensowniej. Żeby testować zalewy, potrzebujesz skryptowanego modelu, który robi dokładnie to, co mu powiesz, działając przez prawdziwy kod agenta.
  2. Skrypty muszą zachowywać się jak modele. Nasz pierwszy skryptowany model używał tego samego identyfikatora odpowiedzi przy każdej odpowiedzi, więc framework agenta traktował każdą odpowiedź jako zastąpienie poprzedniej i kończył tury przedwcześnie. Wyglądało to na błąd odświeżania interfejsu. Nim nie było — to uprząż testowa kłamała. Musieliśmy też wymusić w skrypcie prawdziwy limit kontekstu modelu, bo inaczej liczby zużycia pamięci były nierealistyczne.

Co z tego wynieśliśmy

  • Strumieniowe interfejsy mnożą drobne błędy. Wszystko, co wysyłasz „na kawałek”, jest wysyłane dziesiątki tysięcy razy. Wysyłaj przyrosty albo ogranicz ładunek.
  • Ograniczaj każdy bufor, który przechodzi przez wolną granicę. Gniazda, logi powtórek i punkty kontrolne potrzebują sufitu.
  • Odtwórz przed naprawą. Nasze dwa pierwsze wyjaśnienia brzmiały słusznie i były błędne.

Jeśli ostatnio widziałeś w czacie Mythex bardzo długi plik pojawiający się bez zacinania agenta, to właśnie częściowo dlatego.

Mythex · Pricing · Zbuduj wszystko z AI · Szablony stron i aplikacji · Mythex w porównaniu · Słownik budowania aplikacji z AI · Darmowe narzędzia do budowania aplikacji z AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)