ஒரு progress label-இல் மறைந்திருந்த நினைவகப் பற்றாக்குறை செயலிழப்பு
செப்டம்பர் பிற்பகுதியில் ஒரு மாலை, ஒரு பயனரின் workspace-இல் இருந்த AI ஏஜென்ட் ஒரு turn-இன் நடுவில் out-of-memory பிழையுடன் செயலிழந்தது. வெளிப்படையான சந்தேகங்கள் இரண்டும் தவறானவை: ஒரே நேரத்தில் இயங்கிய அதிக துணை ஏஜென்ட்கள், அல்லது மிகவும் பெரிதாகிவிட்ட context. உண்மையான காரணம் ஒரு status label. மாடல் ஒரு பெரிய shell command-ஐ சில எழுத்துகளாக stream செய்தபோது, ஒவ்வொரு துண்டிலும் இதுவரையிலான முழு command-ஐ progress label ஆக நாங்கள் மீண்டும் அனுப்பினோம். 124 KB command ஒன்று சுமார் 41,000 நிகழ்வுகளாகவும், சுமார் 690 MB வரிசையில் நின்ற strings ஆகவும் ஆனது.
ஏஜென்ட் தன் வேலையை எப்படி stream செய்கிறது
Mythex-இல் நீங்கள் கட்டும்போது, ஏஜென்ட் உங்கள் திட்டத்தின் தனிப்பட்ட cloud sandbox-க்குள் இயங்குகிறது. மாடல் எழுதும்போது, அதன் வெளியீடு உங்கள் உலாவிக்கு stream ஆகத் திரும்பும்: உரை சொல் சொல்லாகத் தோன்றும். ஒரு tool ஓடும்போது, உதாரணமாக ஒரு கோப்பை எழுதும் shell command, அதற்கான ஒரு label நேரடியாக புதுப்பிக்கப்படுவதை நீங்கள் பார்ப்பீர்கள். அது என்ன செய்கிறது என்று தெரியும்.
மாடல் சிறிய துண்டுகளாக stream செய்கிறது. நாங்கள் பயன்படுத்திய மாடலில், அந்த துண்டுகள் பெரும்பாலும் மூன்று எழுத்துகள் மட்டுமே.
முதல் ஊகங்கள் தவறாக இருந்தன
செயலிழப்பு ஒரு அளவிடுதல் பிரச்சினை போலத் தெரிந்தது, அதனால் அங்கிருந்து தொடங்கினோம்.
- இணையான துணை ஏஜென்ட்களா? அந்த turn இரண்டை மட்டுமே பயன்படுத்தியிருந்தது. ஒவ்வொன்றும் சுமார் 1.5 MB நிலையை வைத்திருந்தது. போதாது.
- தக்கவைக்கப்பட்ட traces-ஆ? எங்கள் tracing ஒரு run tree-ஐ நினைவகத்தில் வைத்திருந்தது. ஆனால் செயலிழப்பின் போது இருந்த heap அளவோடு அது எங்கும் நெருங்கவில்லை.
இரண்டுமே நம்பத்தகுந்த கதைகள், இரண்டையும் நம்புவது மலிவாக இருந்தது. எதுவும் செயலிழப்பை மீண்டும் உருவாக்கவில்லை. நாங்கள் மீண்டும் மீண்டும் கற்றுக்கொள்ளும் பாடம்: செயலிழப்பை மீண்டும் உருவாக்கும் வரை ஒரு ஊகம் காரணம் அல்ல.
உண்மையாக மீண்டும் உருவாக்குதல்
உற்பத்தியின் அதே நினைவக வரம்புகளுடன் ஒரு தூக்கியெறியும் sandbox-ஐ உருவாக்கினோம். யதார்த்தமான மூன்று-எழுத்து துண்டுகளாக ஒரு பெரிய கோப்பை stream செய்த ஒரு போலி மாடலையும் சேர்த்தோம். மறு முனையில் உள்ள reader ஒரு உண்மையான நெட்வொர்க் போல மெதுவாக இருக்குமாறும் செய்தோம்.
அது வேலை செய்தது. Heap உயர்ந்தது, செயல்முறை அதே பிழையுடன் இறந்தது.
அதை நாங்கள் பார்க்க முடிந்ததும் அதன் வழிமுறை எளிமையானது. மாடல் ஒரு பெரிய command-ஐ எழுதிக்கொண்டிருந்தபோது — இந்த விஷயத்தில் 124 KB கோப்பை எழுதிய ஒரு heredoc — எங்கள் streaming குறியீடு ஒவ்வொரு துண்டிலும் tool-இன் label-ஐப் புதுப்பித்தது. அந்த label புதிய மூன்று எழுத்துகள் அல்ல; இதுவரையிலான முழு command. எனவே நிகழ்வுகள் வளர்ந்தன: 3 எழுத்துகள், பின்னர் 6, பின்னர் 9, … 124 KB வரை, சுமார் 41,000 முறை. அவை உருவாகும் வேகத்தில் socket அனுப்ப முடியவில்லை. எனவே அவை அதன் write buffer-இல் strings ஆகக் குவிந்தன. சுமார் 690 MB.
சரி
- label-க்கு வரம்பிடுங்கள். ஒரு status label நடப்பதை மட்டும் சொன்னால் போதும். இப்போது அது 200 எழுத்துகளுக்கு வரையறுக்கப்பட்டுள்ளது. பகுதியான command-ஐ எத்தனை முறை மீண்டும் ஸ்கேன் செய்கிறோம் என்பதையும் throttle செய்கிறோம்.
- இருபடி concatenation-ஐ நிறுத்துங்கள். Stream செய்யப்பட்ட துண்டுகளை மீண்டும் மீண்டும் string concatenation மூலம் இணைப்பது பெரிய வெளியீடுகளுக்கு O(n²). நேரியல் நேரத்தில் சேர்க்கும் chunk அடிப்படையிலான அணுகுமுறைக்கு மாறினோம்.
பின்னர் வெள்ளம் போன்ற வெளியீட்டில் வரம்பின்றி வளரக்கூடிய வேறு எதையும் தேடி, அவற்றையும் வரம்பிட்டோம்:
- சாட்டில் காட்டப்படும் நேரடி command வெளியீடு தொகுக்கப்பட்டு, கடைசி 8,000 எழுத்துகள் வரை வைக்கப்படுகிறது.
- தனி நிகழ்வு வரிகள் 4 MB-க்கு வரையறுக்கப்பட்டுள்ளன.
- மீண்டும் இணைந்த பின் உலாவி பிடிக்கப் பயன்படுத்தும் replay buffer ஒரு திட்டத்துக்கு 8 MB-க்கு வரையறுக்கப்பட்டுள்ளது.
- பழைய checkpoints ஒரு turn நகரும்போதே அகற்றப்படுகின்றன. சமீபத்திய நிலையையும் முந்தைய turns-க்குத் தேவைப்படும் நங்கூரங்களையும் வைத்திருக்கின்றன.
வெள்ளங்களை நேர்மையாகச் சோதித்தல்
இதைச் சோதிப்பது இருக்க வேண்டியதைவிட கடினமாக இருக்க இரண்டு விஷயங்கள் காரணமாக இருந்தன:
- உண்மையான மாடல்கள் ஒத்துழைக்காது. ஒரு பெரிய கோப்பை அச்சிடச் சொன்னால், மாடல் பொதுவாக மறுக்கும், சுருக்கும் அல்லது அதை இன்னும் நியாயமாக எழுதும். வெள்ளங்களைச் சோதிக்க, சொன்னதைச் சரியாகச் செய்யும் ஒரு script செய்யப்பட்ட மாடல் வேண்டும். அது உண்மையான ஏஜென்ட் குறியீட்டின் வழியாக இயங்க வேண்டும்.
- Scripts மாடல்கள் போல நடந்துகொள்ள வேண்டும். எங்கள் முதல் script செய்யப்பட்ட மாடல் ஒவ்வொரு பதிலுக்கும் அதே response ID-ஐப் பயன்படுத்தியது. எனவே ஏஜென்ட் framework ஒவ்வொரு பதிலையும் முந்தையதின் மாற்றாகக் கருதி, turns-ஐ முன்கூட்டியே முடித்தது. அது ஒரு UI refresh பிழை போலத் தெரிந்தது. அது அப்படியல்ல. சோதனை harness எங்களிடம் பொய் சொன்னது. Script-இல் மாடலின் உண்மையான context வரம்பையும் நாங்கள் அமல்படுத்த வேண்டியிருந்தது. இல்லையென்றால் நினைவக எண்கள் யதார்த்தமற்றவையாக இருந்திருக்கும்.
இதிலிருந்து நாங்கள் எடுத்துக்கொண்டவை
- Streaming UIs சிறிய தவறுகளைப் பெருக்கும். "ஒரு துண்டுக்கு" அனுப்பும் எதுவும் பதினாயிரக்கணக்கான முறை அனுப்பப்படும். Deltas அனுப்புங்கள், அல்லது payload-க்கு வரம்பிடுங்கள்.
- மெதுவான எல்லையைக் கடக்கும் ஒவ்வொரு buffer-க்கும் வரம்பிடுங்கள். Sockets, replay logs மற்றும் checkpoints அனைத்துக்கும் ஒரு உச்ச வரம்பு தேவை.
- சரிசெய்வதற்கு முன் மீண்டும் உருவாக்குங்கள். எங்கள் முதல் இரண்டு விளக்கங்களும் சரியாகத் தோன்றின, ஆனால் தவறாக இருந்தன.
சமீபத்தில் Mythex சாட்டில் ஏஜென்ட் தடுமாறாமல் ஒரு மிக நீண்ட கோப்பு தோன்றுவதைப் பார்த்திருந்தால், இதுதான் அதன் ஒரு காரணம்.