Crash Kehabisan Memori yang Tersembunyi di Label Progres
Suatu malam di akhir September, agen AI di workspace seorang pengguna crash dengan error kehabisan memori di tengah sebuah giliran. Dua tersangka yang jelas, terlalu banyak sub-agen yang berjalan bersamaan atau konteks yang tumbuh terlalu besar, keduanya keliru. Penyebab sebenarnya adalah sebuah label status: saat model mengalirkan perintah shell yang besar beberapa karakter sekaligus, kami mengirim ulang seluruh perintah sejauh ini sebagai label progres pada setiap potongan. Satu perintah 124 KB menjadi sekitar 41.000 event dan sekitar 690 MB string yang mengantre.
Bagaimana agen mengalirkan pekerjaannya
Saat Anda membangun di Mythex, agen berjalan di sandbox cloud pribadi proyek Anda. Saat model menulis, keluarannya mengalir kembali ke browser Anda: teks muncul kata demi kata, dan saat ia menjalankan tool, misalnya perintah shell yang menulis file, Anda melihat label untuknya diperbarui secara langsung, sehingga Anda tahu apa yang sedang dikerjakannya.
Model mengalirkan dalam potongan kecil. Dengan model yang kami jalankan, potongan itu sering hanya tiga karakter.
Hipotesis pertama keliru
Crash itu terlihat seperti masalah skala, jadi kami mulai dari situ.
- Sub-agen paralel? Giliran itu hanya memakai dua, masing-masing menyimpan sekitar 1,5 MB state. Tidak cukup.
- Jejak yang tersimpan? Tracing kami menyimpan pohon run di memori, tetapi ukurannya jauh dari heap saat crash.
Kedua cerita itu masuk akal, dan keduanya mudah dipercaya. Tidak satu pun mereproduksi crash. Pelajaran yang terus kami pelajari ulang: sebuah hipotesis belum menjadi penyebab sampai ia mereproduksi kegagalannya.
Mereproduksinya sungguhan
Kami membangun sandbox sekali pakai dengan batas memori yang sama seperti produksi, dan model palsu yang mengalirkan file besar dalam potongan tiga karakter yang realistis. Kami juga membuat pembaca di ujung lain soket menjadi lambat, seperti jaringan sungguhan.
Itu berhasil. Heap naik dan proses mati dengan error yang sama.
Mekanismenya sederhana begitu kami bisa mengamatinya. Saat model menulis perintah besar, dalam kasus ini sebuah heredoc yang menulis file 124 KB, kode streaming kami memperbarui label tool pada setiap potongan. Label itu bukan tiga karakter baru; melainkan seluruh perintah sejauh ini. Maka event-nya bertambah: 3 karakter, lalu 6, lalu 9, dan seterusnya sampai 124 KB, sekitar 41.000 kali. Soket tidak bisa mengirimnya secepat yang diproduksi, jadi event menumpuk di write buffer-nya sebagai string, sekitar 690 MB.
Perbaikannya
- Batasi label. Label status hanya perlu mengatakan apa yang sedang terjadi. Sekarang dibatasi 200 karakter, dan kami membatasi seberapa sering perintah parsial dipindai ulang.
- Hentikan penggabungan kuadratik. Menggabungkan potongan yang dialirkan dengan konkatenasi string berulang adalah O(n²) untuk keluaran besar. Kami beralih ke pendekatan berbasis chunk yang menambahkan dalam waktu linear.
Lalu kami mencari apa pun yang bisa tumbuh tanpa batas saat banjir keluaran, dan membatasinya:
- Keluaran perintah langsung yang ditampilkan di chat dikelompokkan dan hanya menyimpan 8.000 karakter terakhir.
- Setiap baris event dibatasi 4 MB.
- Buffer replay yang dipakai browser untuk menyusul setelah menyambung ulang dibatasi 8 MB per proyek.
- Checkpoint lama dipangkas seiring berjalannya giliran, dengan tetap menyimpan state terbaru dan jangkar yang dibutuhkan giliran sebelumnya.
Menguji banjir secara jujur
Dua hal membuat ini lebih sulit diuji daripada seharusnya:
- Model sungguhan tidak mau bekerja sama. Jika diminta mencetak file raksasa, model biasanya menolak, meringkas, atau menuliskannya dengan cara yang lebih masuk akal. Untuk menguji banjir, Anda membutuhkan model terskrip yang melakukan persis apa yang Anda perintahkan, berjalan melalui kode agen yang sebenarnya.
- Skrip harus berperilaku seperti model. Model terskrip pertama kami memakai ulang ID respons yang sama di setiap balasan, sehingga framework agen memperlakukan setiap balasan sebagai pengganti balasan sebelumnya dan mengakhiri giliran lebih awal. Itu terlihat seperti bug penyegaran UI. Ternyata bukan; itu harness pengujian yang menipu kami. Kami juga harus menegakkan batas konteks nyata model di dalam skrip, atau angka memorinya tidak realistis.
Yang kami ambil dari ini
- Antarmuka streaming memperbesar kesalahan kecil. Apa pun yang dikirim “per potongan” terkirim puluhan ribu kali. Kirim delta, atau batasi payload-nya.
- Batasi setiap buffer yang melintasi batas yang lambat. Soket, log replay, dan checkpoint semuanya membutuhkan batas atas.
- Reproduksi sebelum memperbaiki. Dua penjelasan pertama kami terdengar benar dan ternyata salah.
Jika Anda baru-baru ini melihat file yang sangat panjang muncul di chat Mythex tanpa agen terhenti, inilah sebagian alasannya.