Blog

Menținerea turelor agentului cu multe imagini în limitele memoriei

O tură a agentului în producție a rămas fără memorie la 952 MB, în timp ce decupa produse din șase scanări de cataloage și își analiza decupajele. Nu a fost o singură scurgere — au fost trei. Fiecare apel către model retrimitea imaginile turei la dimensiune completă; biblioteca pe care o folosim pentru a apela modelul păstra fiecare cerere activă până la finalul turei; iar trasarea din framework-ul agentului păstra intrările fiecărui apel, inclusiv imaginile, pe toată durata turei. Le-am reparat pe toate trei: imaginile sunt micșorate înainte de trimitere, iar imaginile fiecărui apel sunt eliberate imediat ce apelul se termină.

Cum vede agentul imaginile

Când construiești pe Mythex, agentul lucrează în sandbox-ul cloud al proiectului tău. Poate privi imagini — o captură de ecran atașată, un mockup, un fișier din proiect sau o imagine pe care tocmai a produs-o chiar el — trimițându-le modelului împreună cu conversația.

O tură este tot ce face agentul ca răspuns la un mesaj de la tine. În cadrul unei ture, agentul apelează modelul de multe ori: gândește, rulează un instrument, se uită la rezultat, gândește din nou. Fiecare dintre aceste apeluri trimite conversația de până atunci, iar asta include imaginile turei, codificate ca text (base64, care le face cu aproximativ o treime mai mari).

Tura care a căzut făcea ceva rezonabil. A luat șase scanări de catalog, de aproximativ 2,4 MB fiecare, a decupat produse individuale din ele și a tot recitit decupajele ca să le verifice. Asta înseamnă multe apeluri către model, fiecare cu mai multe imagini mari.

Cauza 1: fiecare apel retrimitea imaginile la dimensiune completă

Fiecare apel către model își reconstruia corpul cererii cu imaginile turei la dimensiune completă — aproximativ 20 MB de base64 la fiecare apel. Corpul este construit ca un singur șir în memoria agentului, deci fiecare apel aloca încă ~20 MB.

Trimiterea la dimensiune completă nu aducea nimic în plus. Modelul micșorează singur imaginile la aproximativ 1.300 de pixeli pe latura lungă, deci pixelii în plus costau doar memorie și timp de încărcare.

Soluția: micșorează o dată, trimite mic

  • Imaginile peste 400 KB sunt acum convertite în JPEG de cel mult 1.568 de pixeli pe latura lungă înainte de trimitere. Imaginile mai mici pleacă neschimbate.
  • Conversia folosește ImageMagick, care există în fiecare sandbox Mythex, și rulează o dată pentru fiecare fișier. O tură care arată aceleași scanări la fiecare apel refolosește copia micșorată. Cache-ul este indexat după calea, dimensiunea și data modificării fișierului, deci un fișier editat este convertit din nou, iar cache-ul are propria limită de dimensiune, ca să nu devină el cel care umple memoria.
  • Bugetul pentru imaginile dintr-un singur apel către model scade de la 16 MB la 6 MB.

Aceleași șase scanări costă acum 4,4 MB per apel, în loc de 19,7 MB.

Cauza 2: clientul modelului nu elibera niciodată o cerere

Dimensiunea fiecărei cereri era doar o parte din problemă. Memoria creștea și ea, apel după apel. Pe mediul nostru de staging, același tip de tură ținea 369 MB de memorie activă chiar și după o colectare completă a gunoiului — momentul în care tot ce nu mai este accesibil este eliberat. Eșantionarea heap-ului a arătat că toată creșterea se afla în partea bibliotecii clientului de model care codifică corpurile cererilor.

Cauza era un listener de abort. Când apeși Stop, semnalul de abort al turei se activează, iar fiecare cerere care îl ascultă se anulează. Noi transmiteam acel semnal unic, al întregii ture, fiecărei cereri. Biblioteca clientului adaugă un listener „abort” la orice semnal primește — și nu îl mai elimină niciodată.

Astfel, fiecare cerere a unei ture rămânea accesibilă din semnalul turei, cât timp dura tura: listenerul, care ținea controllerul cererii, care ținea cererea, care ținea întregul corp al cererii cu imaginile lui.

Soluția: câte un semnal pentru fiecare cerere

Fiecare cerere primește acum propriul semnal. Acesta este legat de semnalul turei doar cât timp rulează cererea respectivă — pentru un răspuns în stream, până când a fost citit ultimul fragment — și apoi este desfăcut imediat. Stop ajunge în continuare la cererea aflată în curs, dar semnalul turei ține acum un singur listener pe rând.

Testele acoperă toate cele trei aspecte:

  • După 12 apeluri, nu mai rămân listeneri pe semnalul turei. Înainte de corecție, erau 12.
  • Pentru 15 cereri de câte 8 MB, memoria rămâne constantă. Înainte, creștea cu 128 MB.
  • Apăsarea pe Stop oprește în continuare o cerere în mijlocul stream-ului ei.

Cauza 3: trasarea păstra intrările fiecărui apel

Un snapshot al heap-ului a arătat un al treilea deținător: aproximativ 6 MB păstrați per apel către model. Acesta venea din streaming-ul de evenimente al framework-ului agentului. Acesta înregistra fiecare apel către model dintr-o tură ca descendent al turei, inclusiv intrările, până la sfârșitul turei. Fiecare imagine atașată fiecărui apel rămânea în memorie împreună cu el.

Soluția: golește imaginile odată ce apelul revine

Imaginile sunt atașate doar mesajelor construite pentru un anumit apel către model. Ele nu sunt niciodată păstrate în starea salvată a conversației. Deci, după ce un apel revine, este sigur să golești datele imaginilor din acele mesaje. Trasarea păstrează o înregistrare text a apelului; pixelii dispar.

Un detaliu a contat: imaginile trebuie golite pe loc. Trasorul poate ajunge la aceleași date de imagine prin mai multe referințe de pe un mesaj, deci înlocuirea cu o listă nouă, goală, ar fi lăsat originalul — cu pixeli cu tot — accesibil. Starea salvată a conversației nu este modificată niciodată.

Un test rulează graful agentului sub același streaming de evenimente, cu o imagine de 4 MB la fiecare apel. Fără corecție, memoria creștea cu 77 MB în 16 apeluri. Cu corecția, memoria rămâne constantă.

Ce am învățat

  • Problemele de memorie pot veni în straturi. Trei cauze separate țineau imaginile prea mult, iar toate trei trebuiau eliminate ca tura să rămână în limitele memoriei.
  • Orice dai unei biblioteci poate fi păstrat. Un semnal și o trasare au supraviețuit apelurilor cărora le fuseseră date. Când memoria crește pe apel, uită-te la ce obiecte cu viață lungă atinge apelul.
  • Nu trimite ce cealaltă parte aruncă oricum. Pixelii pe care modelul i-ar fi ignorat oricum ne costau memorie la fiecare apel.
  • Testează memoria direct. Fiecare corecție a venit cu un test care măsoară creșterea heap-ului sau numărul de listeneri, astfel încât o regresie apare ca o cifră, nu ca un crash.

Aceasta este a doua poveste despre memorie a agentului în scurt timp; prima a fost o etichetă de progres retrimisă de 41.000 de ori. Dacă vrei să afli mai multe despre ce este agentul și cum funcționează, uită-te la ghidul nostru despre agenții AI și la documentația chatului.

Mythex · Pricing · Construiește orice cu AI · Șabloane pentru site-uri și aplicații · Mythex comparat · Glosar pentru crearea de aplicații cu AI · Instrumente gratuite pentru crearea de aplicații cu 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 ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)