Chyba out-of-memory skrytá v popisku priebehu
Jedného septembrového večera AI agent v pracovnom priestore používateľa spadol s chybou out-of-memory uprostred behu. Zjavní podozriví — príliš veľa subagentov naraz alebo príliš veľký kontext — boli obaja mylní. Skutočnou príčinou bol štítok stavu: keď model streamoval veľký shellový príkaz po niekoľkých znakoch, pri každom kúsku sme znova odoslali celý doterajší príkaz ako štítok priebehu. Jeden príkaz s veľkosťou 124 KB sa tak zmenil na približne 41 000 udalostí a zhruba 690 MB zaradených reťazcov.
Ako agent streamuje svoju prácu
Keď budujete na Mythex, agent beží v súkromnom cloudovom sandboxe vášho projektu. Ako model píše, jeho výstup sa streamuje späť do vášho prehliadača: text sa objavuje slovo po slove a keď spustí nástroj — napríklad shellový príkaz, ktorý zapíše súbor —, vidíte štítok pre neho, ktorý sa živo aktualizuje, aby ste vedeli, čo robí.
Model streamuje v malých kúskoch. S modelom, ktorý sme používali, boli tieto kúsky často dlhé iba tri znaky.
Prvé hypotézy boli mylné
Pád vyzeral ako problém škálovania, tak sme sa tam najprv pozreli.
- Paralelní subagenti? Beh použil len dvoch, každý držal okolo 1,5 MB stavu. Nie dosť.
- Zachované stopy? Naše trasovanie držalo strom behov v pamäti, ale nebolo ani zďaleka tak veľké ako halda v momente pádu.
Obe boli vierohodné príbehy a obe sa dali lacno uveriť. Ani jedna nereprodukovala pád. Lekcia, ktorú sa stále znova učíme: hypotéza nie je príčina, kým nereprodukuje zlyhanie.
Reprodukovanie naozaj
Zostavili sme dočasný sandbox s rovnakými pamäťovými limitmi ako produkcia a falošný model, ktorý streamoval veľký súbor v realistických trojznakových kúskoch. Tiež sme spomalili čítačku na druhom konci socketu, ako skutočnú sieť.
To zafungovalo. Halda rástla a proces zomrel s rovnakou chybou.
Mechanizmus bol jednoduchý, keď sme ho mohli sledovať. Keď model písal veľký príkaz — v tomto prípade heredoc, ktorý zapisoval súbor s veľkosťou 124 KB —, náš streamovací kód aktualizoval štítok nástroja pri každom kúsku. Štítok nebol nových tri znaky; bol to celý doterajší príkaz. Udalosti teda rástli: 3 znaky, potom 6, potom 9 … až do 124 KB, približne 41 000-krát. Socket ich nedokázal odoslať tak rýchlo, ako vznikali, takže sa hromadili v jeho zápisovom buffri ako reťazce — asi 690 MB.
Oprava
- Obmedziť štítok. Štítok stavu musí len povedať, čo sa deje. Teraz je obmedzený na 200 znakov a obmedzujeme, ako často znova skenujeme čiastočný príkaz.
- Zastaviť kvadratické spájanie. Spájanie streamovaných kúskov opakovaným zreťazovaním reťazcov je O(n²) pri veľkých výstupoch. Prešli sme na prístup založený na častiach, ktorý pridáva v lineárnom čase.
Potom sme hľadali čokoľvek ďalšie, čo môže rásť bez hranice pri záplave výstupu, a obmedzili sme to:
- Živý výstup príkazov zobrazený v chate sa dávkuje a drží sa posledných 8 000 znakov.
- Jednotlivé riadky udalostí sú obmedzené na 4 MB.
- Replay buffer, ktorý prehliadač používa na dobehnutie po opätovnom pripojení, je obmedzený na 8 MB na projekt.
- Staré checkpointy sa orezávajú, ako beh postupuje, pričom sa zachováva najnovší stav a kotvy, ktoré predchádzajúce kolá potrebujú.
Testovanie záplav úprimne
Dve veci to testovanie sťažili viac, než malo byť:
- Skutočné modely nespolupracujú. Požiadajte model, aby vypísal obrovský súbor, a zvyčajne odmietne, zhrnie ho alebo ho napíše rozumnejšie. Na testovanie záplav potrebujete skriptovaný model, ktorý robí presne to, čo mu poviete, a beží cez skutočný kód agenta.
- Skripty sa musia správať ako modely. Náš prvý skriptovaný model používal pri každej odpovedi rovnaké ID odpovede, takže framework agenta považoval každú odpoveď za náhradu predchádzajúcej a kolá ukončoval skôr. Vyzeralo to ako chyba obnovy UI. Nebola to ona — klamal nás testovací harness. Museli sme tiež v skripte vynucovať skutočný limit kontextu modelu, inak boli čísla pamäte nerealistické.
Čo sme si z toho odniesli
- Streamovacie UI násobia malé chyby. Čokoľvek posielate „na kúsok“, sa pošle desaťtisíckrát. Posielajte delty alebo obmedzte veľkosť dát.
- Ohraničte každý buffer, ktorý prechádza pomalou hranicou. Sockety, replay logy a checkpointy potrebujú strop.
- Reprodukujte pred opravou. Naše prvé dve vysvetlenia zneli správne a boli mylné.
Ak ste si v poslednom čase v chate Mythex všimli veľmi dlhý súbor, ktorý sa objavil bez toho, aby agent zasekol, je to čiastočne dôvod.