𝟯 LangGraph रीराइट्स: प्रोडक्शन चेकपॉइंटिंग ने मुझे क्या सिखाया

तीन हफ्तों तक, हमारे ऑनबोर्डिंग एजेंट ने हर उस काम को खो दिया जिसे उसे सहेजना था। लॉग्स में कुछ भी नहीं दिखा। न कोई एरर, न कोई चेतावनी। चेकपॉइंटर ने सफलता की रिपोर्ट दी, लेकिन रीज़्यूम (resume) करने पर स्टेट गायब हो गई।

सच्चाई समझने से पहले मैंने एक ही LangGraph पाइपलाइन को तीन बार रीराइट किया। फ्रेमवर्क ने बिल्कुल वही किया जो मैंने उसे करने के लिए कहा था। मैं बस उसे गलत निर्देश दे रहा था।

यदि आप प्रोडक्शन के लिए स्टेटफुल (stateful) एजेंट बनाते हैं, तो यह न मान लें कि डिफॉल्ट सेटिंग्स आपको बचा लेंगी। यहाँ बताया गया है कि क्या खराब हुआ और मैंने इसे कैसे ठीक किया।

𝗧𝗵𝗲 𝗙𝗶𝗿𝘀𝘁 𝗕𝗿𝗲𝗮𝗸: साइलेंट स्टेट लॉस

मेरा एजेंट पांच-चरणीय (five-step) ऑनबोर्डिंग फ्लो को संभालता था। मैंने प्रोग्रेस को सेव करने के लिए Postgres का उपयोग किया ताकि उपयोगकर्ता बाद में वहीं से शुरू कर सकें। लेकिन हर बार रीज़्यूम करने पर प्रक्रिया पहले स्टेप से शुरू होती थी।

इसका कारण मेरा स्टेट स्कीमा (state schema) था। LangGraph में, प्रत्येक नोड एक अपडेट लौटाता है जो स्टेट में मर्ज हो जाता है। यदि आप यह निर्दिष्ट नहीं करते हैं कि मर्ज कैसे करना है, तो डिफॉल्ट रूप से यह पुराने डेटा को ओवरराइट (overwrite) कर देता है।

मुझे लगा कि मेरी मैसेज लिस्ट में नए मैसेज जुड़ते (append) जाएंगे। इसके बजाय, प्रत्येक नए नोड ने पूरी हिस्ट्री को हटाकर केवल एक मैसेज से बदल दिया। चेकपॉइंटर ने गलत डेटा को बिल्कुल सही तरीके से सेव कर लिया था।

समाधान: स्पष्ट रिड्यूसर्स (explicit reducers) के साथ Annotated फ़ील्ड्स का उपयोग करें।

• मैसेज लिस्ट के लिए add ऑपरेटर का उपयोग करें। • डिक्शनरीज़ के लिए कस्टम मर्ज फ़ंक्शन का उपयोग करें। • "step" जैसे सिंगल वैल्यूज़ के लिए ही डिफॉल्ट ओवरराइट का उपयोग करें।

𝗧𝗵𝗲 𝗦𝗲𝗰𝗼𝗻𝗱 𝗕𝗿𝗲𝗮𝗸: डीसेरियलाइजेशन और कॉनकरेंसी

दूसरे रीराइट में दो नई समस्याओं का सामना करना पड़ा:

  1. करप्ट रोज़ (Corrupt rows): मैंने स्टेट में कस्टम ऑब्जेक्ट्स स्टोर किए थे। सीरियलाइज़र उन्हें हैंडल नहीं कर सका। इससे ऐसी रोज़ (rows) बन गईं जो मौजूद तो थीं लेकिन इस्तेमाल करने लायक नहीं थीं।
  2. डुप्लिकेट कीज़ (Duplicate keys): यदि आप तेज़ी से जवाब नहीं देते हैं, तो WhatsApp वेबहुक (webhooks) को दोबारा भेजता है। यदि एक साथ दो मैसेज आते हैं, तो ग्राफ के दो रन एक ही थ्रेड में लिखने की कोशिश करते हैं। इससे डेटाबेस कोलिजन (collisions) की समस्या हुई।

समाधान: • कस्टम ऑब्जेक्ट्स को हटा दें। केवल प्लेन डिक्शनरीज़ (dicts) और स्टैंडर्ड LangChain टाइप्स का ही उपयोग करें। • वेबहुक को ग्राफ के बाहर हैंडल करें। डुप्लिकेट्स को हटाने के लिए एक क्यू (queue) और आइडम्पोटेंसी की (idempotency key) का उपयोग करें। • डेटाबेस लॉक जोड़ें। सुनिश्चित करें कि एक समय में प्रति थ्रेड केवल एक ही रन हो।

𝗧𝗵𝗲 𝗧𝗵𝗶𝗿𝗱 𝗥𝗲𝘄𝗿𝗶𝘁𝗲: द स्टेबल पैटर्न

अंतिम वर्शन तीन सिद्धांतों पर केंद्रित था:

• छोटे ग्राफ्स: मैंने एक विशाल ग्राफ को तीन छोटे सबग्राफ्स में विभाजित कर दिया। इससे बग्स का प्रभाव क्षेत्र (blast radius) कम हो गया। • स्पष्ट चेकपॉइंट्स: मैंने हर एक नोड के बाद चेकपॉइंटिंग करना बंद कर दिया। मैं केवल सार्थक रीज़्यूम पॉइंट्स पर ही चेकपॉइंट करता हूँ। इससे डेटाबेस राइट्स में 60% की कमी आई। • आइडम्पोटेंट नोड्स: यह अत्यंत महत्वपूर्ण है। यदि कोई नोड दो बार चलता है, तो उसे समान परिणाम देना चाहिए। यदि कोई नोड देखता है कि स्टेट में कोई कार्य पहले से ही पूरा हो चुका है, तो उसे तुरंत वापस आ जाना चाहिए। यह महंगे मॉडल कॉल्स के लिए डबल-चार्जिंग को रोकता है।

आपके लिए सबक:

  • कोड लिखने से पहले रिड्यूसर सिमेंटिक्स (reducer semantics) को पढ़ें।
  • स्टेट में कस्टम ऑब्जेक्ट्स स्टोर न करें।
  • कॉनकरेंसी कंट्रोल (concurrency control) को ग्राफ के बाहर ले जाएं।
  • हर नोड को आइडम्पोटेंट (idempotent) बनाएं।

फ्रेमवर्क विफल नहीं हुआ। मेरी धारणाएं विफल रहीं।

स्रोत: https://dev.to/elenarevicheva/three-langgraph-rewrites-what-production-checkpointing-actually-taught-me-ok9

वैकल्पिक लर्निंग कम्युनिटी: https://t.me/GyaanSetuAi