प्रोग्रेस लेबल में छिपा आउट-ऑफ़-मेमोरी क्रैश
सितंबर के अंत में एक शाम, एक उपयोगकर्ता के वर्कस्पेस में AI एजेंट एक टर्न के बीच में आउट-ऑफ़-मेमोरी एरर से क्रैश हो गया। स्पष्ट संदिग्ध, यानी एक साथ बहुत सारे सब-एजेंट, या बहुत बड़ा हो गया संदर्भ, दोनों गलत निकले। असली कारण एक स्टेटस लेबल था: जब मॉडल एक बड़ी शेल कमांड को कुछ अक्षरों के टुकड़ों में स्ट्रीम कर रहा था, तो हम हर टुकड़े पर प्रोग्रेस लेबल के रूप में अब तक की पूरी कमांड दोबारा भेज रहे थे। 124 KB की एक कमांड लगभग 41,000 इवेंट्स और लगभग 690 MB कतार में लगी स्ट्रिंग्स बन गई।
एजेंट अपना काम कैसे स्ट्रीम करता है
Mythex पर बनाते समय, एजेंट आपके प्रोजेक्ट के निजी क्लाउड सैंडबॉक्स में चलता है। जैसे-जैसे मॉडल लिखता है, उसका आउटपुट आपके ब्राउज़र में वापस स्ट्रीम होता है: टेक्स्ट शब्द-दर-शब्द दिखता है, और जब वह कोई टूल चलाता है, जैसे एक शेल कमांड जो फ़ाइल लिखती है, तो आप उसका लेबल लाइव अपडेट होते देखते हैं, ताकि पता रहे वह क्या कर रहा है।
मॉडल छोटे टुकड़ों में स्ट्रीम करता है। जिस मॉडल के साथ हम चल रहे थे, उसके टुकड़े अक्सर केवल तीन अक्षर के होते थे।
पहली धारणाएँ गलत थीं
क्रैश एक स्केलिंग समस्या जैसा दिख रहा था, इसलिए हमने वहीं से शुरुआत की।
- समानांतर सब-एजेंट? टर्न में केवल दो इस्तेमाल हुए थे, और हर एक लगभग 1.5 MB स्टेट थामे था। इतना पर्याप्त नहीं।
- बचे हुए ट्रेसेज़? हमारी ट्रेसिंग एक रन ट्री मेमोरी में रखती थी, पर वह क्रैश के समय के हीप के आकार के आसपास भी नहीं थी।
दोनों कहानियाँ विश्वसनीय थीं, और दोनों पर विश्वास करना सस्ता था। दोनों में से कोई भी क्रैश को दोबारा नहीं बना सका। हम जो सबक बार-बार सीखते हैं: जब तक कोई धारणा विफलता को दोबारा न बनाए, वह कारण नहीं है।
इसे असल में दोहराना
हमने एक अस्थायी सैंडबॉक्स बनाया, जिसकी मेमोरी सीमाएँ प्रोडक्शन जैसी थीं, और एक नकली मॉडल जो यथार्थ तीन-अक्षर वाले टुकड़ों में एक बड़ी फ़ाइल स्ट्रीम करता था। हमने सॉकेट के दूसरे सिरे के रीडर को भी धीमा बनाया, असली नेटवर्क की तरह।
इसी से बात बनी। हीप बढ़ा और प्रोसेस उसी एरर के साथ मर गया।
जब हम उसे देख सके, तो तंत्र सीधा था। जब मॉडल एक बड़ी कमांड लिख रहा था, इस मामले में एक heredoc जो 124 KB फ़ाइल लिखता था, तो हमारा स्ट्रीमिंग कोड हर टुकड़े पर टूल का लेबल अपडेट करता था। लेबल नए तीन अक्षर नहीं था; वह अब तक की पूरी कमांड थी। तो इवेंट्स बढ़ते गए: 3 अक्षर, फिर 6, फिर 9, … 124 KB तक, लगभग 41,000 बार। सॉकेट उतनी तेज़ी से नहीं भेज पा रहा था जितनी तेज़ी से वे बनते थे, इसलिए वे उसके राइट बफ़र में स्ट्रिंग्स के रूप में जमा होते गए, लगभग 690 MB।
समाधान
- लेबल को सीमित करें। स्टेटस लेबल को केवल यह बताना होता है कि क्या हो रहा है। अब वह 200 अक्षरों तक सीमित है, और हम आंशिक कमांड को दोबारा स्कैन करने की दर भी थ्रॉटल करते हैं।
- द्विघात जोड़ना रोकें। स्ट्रीम किए गए टुकड़ों को बार-बार स्ट्रिंग कॉन्कैटिनेशन से जोड़ना बड़े आउटपुट के लिए O(n²) है। हम चंक-आधारित तरीके पर चले गए, जो रेखीय समय में जोड़ता है।
उसके बाद हमने देखा कि बाढ़ जैसे आउटपुट पर और क्या असीमित रूप से बढ़ सकता है, और उसे भी सीमित किया:
- चैट में दिखाया गया लाइव कमांड आउटपुट बैच किया जाता है और अंतिम 8,000 अक्षरों तक रखा जाता है।
- अलग-अलग इवेंट लाइनें 4 MB तक सीमित हैं।
- रीकनेक्ट होने के बाद ब्राउज़र के पकड़ने के लिए इस्तेमाल होने वाला रिप्ले बफ़र प्रति प्रोजेक्ट 8 MB तक सीमित है।
- पुराने चेकपॉइंट्स टर्न के साथ-साथ साफ़ होते हैं, नवीनतम स्टेट और पिछले टर्न्स के लिए ज़रूरी एंकर रखते हुए।
बाढ़ का सही परीक्षण
दो बातों ने इसका परीक्षण उतना आसान नहीं होने दिया जितना होना चाहिए था:
- असली मॉडल सहयोग नहीं करते। किसी मॉडल से एक विशाल फ़ाइल प्रिंट करने को कहें, तो वह आमतौर पर मना करेगा, सारांश देगा, या उसे ज़्यादा समझदारी से लिखेगा। बाढ़ का परीक्षण करने के लिए आपको एक स्क्रिप्टेड मॉडल चाहिए जो ठीक वही करे जो आप बताएँ, असली एजेंट कोड के ज़रिए चलते हुए।
- स्क्रिप्ट्स को मॉडल की तरह व्यवहार करना होगा। हमारे पहले स्क्रिप्टेड मॉडल ने हर जवाब पर एक ही रिस्पॉन्स ID दोबारा इस्तेमाल किया, इसलिए एजेंट फ़्रेमवर्क ने हर जवाब को पिछले का प्रतिस्थापन माना और टर्न जल्दी खत्म कर दिए। यह UI रिफ़्रेश बग जैसा लगा। वह था नहीं, वह टेस्ट हार्नेस हमसे झूठ बोल रहा था। हमें स्क्रिप्ट में मॉडल की असली संदर्भ सीमा भी लागू करनी पड़ी, वरना मेमोरी के आँकड़े अवास्तविक होते।
हमने क्या सीखा
- स्ट्रीमिंग UI छोटी गलतियों को बढ़ाते हैं। जो भी "हर चंक के लिए" भेजा जाए, वह दसियों हज़ार बार भेजा जाता है। डेल्टा भेजें, या पेलोड सीमित करें।
- हर उस बफ़र को सीमित करें जो धीमी सीमा पार करता है। सॉकेट, रिप्ले लॉग और चेकपॉइंट्स सभी को एक ऊपरी सीमा चाहिए।
- ठीक करने से पहले दोहराएँ। हमारी पहली दो व्याख्याएँ सही लगीं और गलत थीं।
अगर हाल में Mythex चैट में बहुत लंबी फ़ाइल बिना एजेंट के रुके दिखी है, तो यह उसी का हिस्सा है।