Bir İlerleme Etiketinde Gizli Bellek Taşması Çökmesi
Eylül ayının bir akşamı, bir kullanıcının çalışma alanındaki yapay zeka agent'ı bir turun ortasında bellek yetersizliği (out-of-memory) hatasıyla çöktü. Bariz şüpheliler (aynı anda çalışan çok fazla alt agent ya da çok büyüyen bir bağlam) ikisi de yanlıştı. Asıl sebep bir durum etiketiydi: model büyük bir kabuk komutunu birkaç karakterlik parçalar halinde akıtırken, biz her parçada o ana kadarki tüm komutu bir ilerleme etiketi olarak yeniden gönderiyorduk. 124 KB'lık tek bir komut, yaklaşık 41.000 olaya ve kabaca 690 MB kuyruğa alınmış dizeye dönüştü.
Agent işini nasıl akıtır?
Mythex üzerinde geliştirdiğinizde agent, projenizin özel bulut sandbox'ı içinde çalışır. Model yazdıkça çıktısı tarayıcınıza akar: metin kelime kelime belirir ve bir araç çalıştırdığında (örneğin bir dosya yazan bir kabuk komutu) bunun etiketini canlı olarak güncellenirken görürsünüz, böylece ne yaptığını bilirsiniz.
Model, parçaları küçük olarak akıtır. Kullandığımız modelle bu parçalar çoğu zaman yalnızca üç karakter uzunluğundaydı.
İlk hipotezler yanlıştı
Çökme bir ölçekleme sorunu gibi görünüyordu, bu yüzden oradan başladık.
- Paralel alt agent'lar mı? Tur yalnızca ikisini kullanmıştı ve her biri yaklaşık 1,5 MB durum tutuyordu. Bu yeterli değil.
- Tutulan izler mi? İzleme sistemimiz bir çalıştırma ağacını bellekte tutuyordu, ama çökme anındaki yığının boyutuna yakın bile değildi.
İkisi de makul hikâyelerdi ve inanmak ikisi için de ucuzdu. Hiçbiri çökmeyi yeniden üretmedi. Sürekli yeniden öğrendiğimiz ders şu: bir hipotez, hatayı yeniden üretene kadar bir sebep değildir.
Gerçekten yeniden üretmek
Üretimle aynı bellek sınırlarına sahip, atılabilir bir sandbox kurduk; gerçekçi üç karakterlik parçalar halinde büyük bir dosyayı akıtan sahte bir model kullandık. Soketin öbür ucundaki okuyucuyu da gerçek bir ağ gibi yavaş yaptık.
Bu işe yaradı. Yığın büyüdü ve süreç aynı hatayla öldü.
Mekanizma, izleyebildiğimizde basitti. Model büyük bir komut yazarken (bu durumda 124 KB'lık bir dosyayı yazan bir heredoc), akış kodumuz araç etiketini her parçada güncelliyordu. Etiket yeni üç karakter değil, o ana kadarki tüm komuttu. Yani olaylar büyüyordu: 3 karakter, sonra 6, sonra 9, ... 124 KB'a kadar, yaklaşık 41.000 kez. Soket, üretildikleri hızda gönderemediği için bunlar yazma tamponunda dizeler olarak birikti; toplamda yaklaşık 690 MB.
Düzeltme
- Etiketi sınırlayın. Bir durum etiketinin yalnızca neyin olduğunu söylemesi yeterlidir. Artık 200 karakterle sınırlı ve kısmi komutu yeniden tarama sıklığını kısıtlıyoruz.
- Karesel birleştirmeyi durdurun. Akan parçaları tekrarlanan dize birleştirmesiyle eklemek, büyük çıktılar için O(n²) maliyetlidir. Doğrusal zamanda ekleyen parça tabanlı bir yaklaşıma geçtik.
Sonra çıktı seli altında sınırsız büyüyebilecek başka her şeyi aradık ve sınırladık:
- Sohbette gösterilen canlı komut çıktısı toplu işlenir ve yalnızca son 8.000 karakter tutulur.
- Tek tek olay satırları 4 MB ile sınırlıdır.
- Bir tarayıcının yeniden bağlandıktan sonra yetişmek için kullandığı tekrar oynatma tamponu proje başına 8 MB ile sınırlıdır.
- Eski kontrol noktaları, tur ilerledikçe budanır; en yeni durum ve önceki turların ihtiyaç duyduğu çapalar korunur.
Selleri dürüstçe test etmek
Bunu olması gerekenden daha zor test etmemize iki şey neden oldu:
- Gerçek modeller iş birliği yapmaz. Bir modelden devasa bir dosya yazdırmasını isterseniz, genellikle reddeder, özetler ya da daha mantıklı yazar. Selleri test etmek için, söylediğinizi tam olarak yapan ve gerçek agent kodunda çalışan betiklenmiş bir model gerekir.
- Betikler modeller gibi davranmalıdır. İlk betiklenmiş modelimiz her yanıtta aynı yanıt kimliğini yeniden kullanıyordu; agent çerçevesi de her yanıtı bir öncekinin yerine geçen bir yanıt olarak görüp turları erken bitiriyordu. Bu bir arayüz yenileme hatası gibi görünüyordu. Değildi; test düzeneği bize yalan söylüyordu. Ayrıca bellek sayılarının gerçekçi olması için betikte modelin gerçek bağlam sınırını uygulamak zorunda kaldık.
Bundan çıkardıklarımız
- Akan arayüzler küçük hataları çoğaltır. "Parça başına" gönderdiğiniz her şey on binlerce kez gönderilir. Farkları gönderin ya da yükü sınırlayın.
- Yavaş bir sınırı geçen her tamponu sınırlayın. Soketler, tekrar oynatma günlükleri ve kontrol noktalarının hepsinin bir tavanı olmalı.
- Düzeltmeden önce yeniden üretin. İlk iki açıklamamız doğru geliyordu ve yanlıştı.
Son zamanlarda Mythex sohbetinde agent takılmadan çok uzun bir dosyanın belirdiğini gördüyseniz, bunun nedenlerinden biri budur.