Minnekrasjen som gjemte seg i en fremdriftsetikett
En kveld i slutten av september krasjet AI-agenten i et brukers arbeidsområde med en feil for tom for minne midt i en tur. De åpenbare mistenkte — for mange underagenter som kjørte samtidig, eller en kontekst som hadde vokst for stor — var begge feil. Den egentlige årsaken var en statusetikett: mens modellen streamet en stor shell-kommando noen få tegn om gangen, sendte vi hele kommandoen så langt på nytt som fremdriftsetikett for hver bit. Én kommando på 124 KB ble til rundt 41 000 hendelser og cirka 690 MB med køede strenger.
Slik streamer agenten arbeidet sitt
Når du bygger på Mythex, kjører agenten inne i prosjektets private skysandkasse. Etter hvert som modellen skriver, strømmer utdataene tilbake til nettleseren din: tekst dukker opp ord for ord, og når den kjører et verktøy — for eksempel en shell-kommando som skriver en fil — ser du en etikett for det som oppdateres live, så du vet hva den gjør.
Modellen streamer i små biter. Med modellen vi kjørte, var bitene ofte bare tre tegn lange.
De første hypotesene var feil
Krasjen så ut som et skaleringsproblem, så vi startet der.
- Parallelle underagenter? Turen hadde bare brukt to, og hver holdt rundt 1,5 MB med tilstand. Ikke nok.
- Beholdte sporinger? Sporingen vår holdt et kjøretre i minnet, men det var langt fra størrelsen på heapen ved krasjen.
Begge var plausible historier, og begge var billige å tro på. Ingen reproduserte krasjen. Lærdommen vi stadig må lære på nytt: en hypotese er ikke en årsak før den reproduserer feilen.
Å reprodusere det på ekte
Vi bygde en engangssandkasse med de samme minnegrensene som produksjon, og en falsk modell som streamet en stor fil i realistiske biter på tre tegn. Vi gjorde også leseren i andre enden av socketen treg, som et ekte nettverk.
Det gjorde susen. Heapen steg, og prosessen døde med samme feil.
Mekanismen var enkel når vi kunne følge den. Mens modellen skrev en stor kommando — i dette tilfellet et heredoc som skrev en fil på 124 KB — oppdaterte streamingkoden vår verktøyets etikett for hver bit. Etiketten var ikke de nye tre tegnene; den var hele kommandoen så langt. Så hendelsene vokste: 3 tegn, så 6, så 9, … opp til 124 KB, omtrent 41 000 ganger. Socketen klarte ikke å sende dem så fort de ble produsert, så de hopet seg opp i skrivebufferen som strenger — rundt 690 MB av dem.
Rettelsen
- Begrens etiketten. En statusetikett trenger bare å si hva som skjer. Den er nå begrenset til 200 tegn, og vi begrenser hvor ofte vi skanner den delvise kommandoen på nytt.
- Stopp kvadratisk sammenslåing. Å sette sammen streamede biter med gjentatt strengsammenkobling er O(n²) for store utdata. Vi gikk over til en chunk-basert tilnærming som legger til i lineær tid.
Deretter lette vi etter alt annet som kunne vokse uten grenser ved en flom av utdata, og begrenset det:
- Live kommandoutdata som vises i chatten, samles i batcher og holdes til de siste 8 000 tegnene.
- Enkeltlinjer med hendelser er begrenset til 4 MB.
- Replay-bufferen en nettleser bruker for å ta igjen etter gjentilkobling, er begrenset til 8 MB per prosjekt.
- Gamle checkpoints beskjæres underveis i en tur, og beholder den nyeste tilstanden og ankerpunktene tidligere turer trenger.
Å teste floods ærlig
To ting gjorde dette vanskeligere å teste enn det burde vært:
- Ekte modeller samarbeider ikke. Be en modell skrive ut en gigantisk fil, og den vil vanligvis nekte, oppsummere eller skrive det mer fornuftig. For å teste floods trenger du en skriptet modell som gjør nøyaktig det du sier, og kjører gjennom den ekte agentkoden.
- Skript må oppføre seg som modeller. Vår første skriptede modell gjenbrukte samme respons-ID på hvert svar, så agentrammeverket behandlet hvert svar som en erstatning for det forrige, og avsluttet turer for tidlig. Det så ut som en feil i UI-oppdateringen. Det var det ikke — det var testoppsettet som løy for oss. Vi måtte også håndheve modellens faktiske kontekstgrense i skriptet, ellers var minnetallene urealistiske.
Det vi tok med oss
- Streaming-UI-er forsterker små feil. Alt du sender «per bit» blir sendt titusener av ganger. Send deltaer, eller begrens nyttelasten.
- Begrens hver buffer som krysser en treg grense. Sockets, replay-logger og checkpoints trenger alle et tak.
- Reproduser før du retter. Våre to første forklaringer hørtes riktige ut og var feil.
Hvis du nylig har sett en veldig lang fil dukke opp i Mythex-chatten uten at agenten stoppet opp, er dette en del av forklaringen.