अधिकांश RAG डेमो लैपटॉप पर शानदार दिखते हैं। एक स्क्रिप्ट को बीस पन्नों की PDF दें, एक सवाल पूछें, और देखें कि वह सही पैराग्राफ का हवाला कैसे देता है। लेकिन उसी पाइपलाइन को प्रोडक्शन में ले जाना वह जगह है जहाँ सारा रोमांच खत्म हो जाता है। कानूनी दस्तावेज़ों को वाक्य स्तर पर बीच से काट दिया जाता है। भारी API रेफरेंस महत्वपूर्ण जानकारी को बूटलेप्लेट शोर (boilerplate noise) में डुबो देते हैं। लेटेंसी (latency) बढ़ जाती है। उपयोगकर्ता इंतज़ार करते हैं, परेशान होते हैं, और चले जाते हैं। हमने इस समस्या का बहुत कड़ा सामना किया। इसलिए हमने रिट्रीवल लेयर (retrieval layer) को पूरी तरह से बदलकर नए सिरे से एक मापे हुए और ट्यून करने योग्य सिस्टम के रूप में बनाया। इसका परिणाम एक ऐसी पाइपलाइन थी जो यूजर एक्सपीरियंस को धीमा किए बिना 95% रिकॉल (recall) हासिल करती है।
प्रोडक्शन में RAG डेमो क्यों विफल हो जाता है
हॉबी प्रोजेक्ट्स और शुरुआती चरण के प्रोडक्ट्स में स्टैंडर्ड स्टैक आश्चर्यजनक रूप से एक जैसा होता है: फिक्स्ड टोकन चंक्स (fixed token chunks), रेडीमेड एम्बेडिंग्स (off-the-shelf embeddings), और एक सिंगल वेक्टर सर्च कॉल। यह सादगी लुभावनी है, और यह तब काम करती है जब आपका कॉर्पस साफ, छोटा और सिंटैक्टिक रूप से अनुमानित हो। प्रोडक्शन डेटा इनमें से कुछ भी नहीं होता। 512 टोकन का एक फिक्स्ड चंक SaaS कॉन्ट्रैक्ट में क्षतिपूर्ति क्लॉज (indemnification clause) को बीच से काट सकता है। अचानक आपकी रिट्रीवल लेयर भाषा मॉडल (language model) को एक कानूनी दायित्व का आधा हिस्सा दे देती है और उससे लायबिलिटी से जुड़ा सवाल पूछने को कहती है। मॉडल hallucinate करने लगता है क्योंकि कॉन्टेक्स्ट टूट चुका होता है।
बड़े तकनीकी दस्तावेज़ इस समस्या को और बढ़ा देते हैं। API डॉक्यूमेंटेशन फंक्शन सिग्नेचर, टेबल और कोड ब्लॉक्स से भरा होता है। एक फिक्स्ड विंडो शायद एक TypeScript इंटरफ़ेस के बीच के हिस्से को कैप्चर कर ले, लेकिन उसके ऊपर के फंक्शन नाम और नीचे के उपयोग के उदाहरण को छोड़ दे। एम्बेडिंग वेक्टर अंततः वास्तविक क्षमता के बजाय सिंटैक्स के टुकड़ों और इनलाइन शोर का प्रतिनिधित्व करने लगता है जिसके बारे में उपयोगकर्ता पूछ रहा है। कचरा अंदर, hallucination बाहर।
टोकन काउंट के बजाय स्ट्रक्चर के आधार पर चंकिंग करें
हमने जो पहला बदलाव किया वह यह था कि चंक्स को केवल टोकन के थैले के रूप में देखना बंद कर दिया। चंक्स सिमेंटिक यूनिट्स (semantic units) होते हैं। सही रणनीति पूरी तरह से इस बात पर निर्भर करती है कि आप क्या इंडेक्स कर रहे हैं।
कानूनी दस्तावेज़ों के लिए, हम रिकर्सिव चंकिंग (recursive chunking) पर चले गए जो डॉक्यूमेंट पदानुक्रम (hierarchy) का सम्मान करती है। यह सेक्शन, सब-सेक्शन और क्लॉज को सीमाओं के रूप में मानती है। एक क्लॉज बरकरार रहता है क्योंकि क्लॉज अर्थ की एक इकाई है। यदि आप इसे बीच से काटते हैं, तो कानूनी तर्क बिखर जाता है।
API डॉक्यूमेंटेशन के लिए, स्ट्रक्चर-अवेयर चंकिंग (structure-aware chunking) फंक्शन, क्लास और एंडपॉइंट्स को एटॉमिक (atomic) मानती है। एक चंक में फंक्शन सिग्नेचर, उसके आर्गुमेंट्स और उसकी डॉकस्ट्रिंग (docstring) हो सकती है। यह केवल इसलिए अगली यूटिलिटी फंक्शन में नहीं फैलता क्योंकि टोकन काउंटर बढ़ गया है। यह एम्बेडिंग को एक विशिष्ट क्षमता पर केंद्रित रखता है।
सपोर्ट टिकट अधिक जटिल होते हैं। वे संवादात्मक, थ्रेडेड और नॉन-लीनियर होते हैं। फिक्स्ड चंक्स एक ही थ्रेड से एक इंजीनियर का स्टेटस अपडेट और एक ग्राहक की शिकायत उठा लेंगे और दिखावा करेंगे कि वे एक सुसंगत इकाई बनाते हैं। हम सिमेंटिक चंकिंग (semantic chunking) पर स्विच हो गए, जो टोकन बजट खत्म होने के बजाय विषय या वक्ता के बदलने पर विभाजित होती है।
इंटरनल विकी अक्सर किसी संगठन में सबसे अव्यवस्थित डेटा होते हैं। फॉर्मेटिंग असंगत होती है, हेडर गायब होते हैं, और सेक्शन आपस में मिल जाते हैं। इनके लिए, हम LLM-आधारित चंकिंग का उपयोग करते हैं। एक छोटा मॉडल एम्बेडिंग जेनरेट करने से पहले ही आगे पढ़ता है और तार्किक सीमाओं की पहचान करता है। इसमें कैरेक्टर स्प्लिट की तुलना में शुरुआत में अधिक लागत आती है, लेकिन रिट्रीवल की गुणवत्ता तुरंत इसकी भरपाई कर देती है।
हाइब्रिड रिट्रीवल: सिग्नल्स को मिलाएं
वेक्टर सर्च शक्तिशाली है लेकिन इसके ब्लाइंड स्पॉट्स (blind spots) होते हैं। ERR_CONNECTION_REFUSED_0x800 जैसा सटीक एरर कोड पेस्ट करें और सिमिलैरिटी सर्च एक असंबंधित मॉड्यूल के लिए ट्रबलशूटिंग गाइड लौटा सकता है क्योंकि एम्बेडिंग स्पेस ने उन्हें एक साथ क्लस्टर कर दिया था। सटीक मिलान (exact matches) मायने रखते हैं, और अकेले वेक्टर सर्च उन्हें अनदेखा कर सकता है।
BM25 के साथ कीवर्ड सर्च इस सटीक-मिलान की समस्या को खूबसूरती से हल करता है। लेकिन यह वैचारिक दूरी (conceptual distance) के मामले में विफल हो जाता है। यदि कोई उपयोगकर्ता "भारी लोड के तहत प्रदर्शन में गिरावट" (performance degradation under heavy load) के बारे में पूछता है, तो BM25 उस डायग्नोस्टिक नोट को मिस कर देगा जो "ट्रैफ़िक स्पाइक्स के दौरान धीमा थ्रूपुट" का वर्णन करता है, क्योंकि वहां पर्याप्त कीवर्ड ओवरलैप नहीं है।
हमने पक्षों को चुनना बंद कर दिया और दोनों को समानांतर में चलाना शुरू कर दिया। वेक्टर और कीवर्ड सर्च प्रत्येक अपनी रैंक की गई सूचियाँ लौटाते हैं। हम उन्हें Reciprocal Rank Fusion (RRF) के साथ मर्ज करते हैं। RRF अपनी प्रभावशीलता में सरल और सटीक है। यह प्रत्येक दस्तावेज़ को इस आधार पर स्कोर करता है कि वह प्रत्येक सूची में कहाँ स्थित है। जो दस्तावेज़ दोनों सिस्टम में शीर्ष के पास होते हैं, उन्हें भारी बढ़ावा मिलता है। जिन दस्तावेज़ों को केवल एक इंजन पसंद करता है, वे भी अंतिम कैंडिडेट सेट में अपनी जगह बना लेते हैं।
फ्यूजन के बाद, हम शीर्ष उम्मीदवारों को cross-encoder reranker के माध्यम से चलाते हैं। यह मुफ्त नहीं है। यह लगभग 50 मिलीसेकंड का कंप्यूट जोड़ता है। यह रिकॉल (recall) को 15% भी बढ़ाता है। cross-encoder पूरे क्वेरी और प्रत्येक कैंडिडेट चंक का एक साथ मूल्यांकन करता है, जिससे एक ऐसा प्रासंगिकता स्कोर (relevance score) प्राप्त होता है जो bi-encoder embedding की तुलना में कहीं अधिक सूक्ष्म होता है। वह अतिरिक्त 50 मिलीसेकंड एक फायदे का सौदा है। यह आपको LLM को कचरा कॉन्टेक्स्ट विंडो भेजने और एक भ्रमित या hallucinated उत्तर के लिए दो सेकंड प्रतीक्षा करने से बचाता है।
सर्च करने से पहले क्वेरी को ठीक करें
उपयोगकर्ता सर्च इंजीनियरों की तरह क्वेरी नहीं लिखते हैं। वे "app broken" टाइप करते हैं। वे रहस्यमयी लॉग फ्रैगमेंट (log fragments) पेस्ट करते हैं। वे अस्पष्ट और संदिग्ध प्रश्न पूछते हैं। यदि आप उन रॉ स्ट्रिंग्स (raw strings) को सीधे इंडेक्स पर भेजते हैं, तो आपको कचरा ही वापस मिलेगा।
हम रिट्रीवल इंजन (retrieval engine) के पास पहुँचने से पहले हर क्वेरी को ट्रांसफॉर्म करते हैं।
पहला, क्वेरी एक्सपेंशन (query expansion)। सिस्टम एक ही छोटे प्रश्न से कई सर्च टर्म्स जेनरेट करता है। एक उपयोगकर्ता पूछता है, "How do I fix the timeout?" इंजन इसे कनेक्शन टाइमआउट, रीड टाइमआउट, गेटवे टाइमआउट और रिट्राय लॉजिक (retry logic) को कवर करने के लिए विस्तार देता है। केवल इसी दृष्टिकोण ने हमारे रिकॉल को 78% से बढ़ाकर 96% कर दिया।
दूसरा, क्वेरी डिकम्पोजिशन (query decomposition)। जटिल प्रश्नों को छोटे उप-प्रश्नों (sub-questions) में तोड़ दिया जाता है। "What's the refund policy for enterprise customers past 90 days and how does it differ from monthly plans?" जैसी क्वेरी एक भारी-भरकम एम्बेडिंग लुकअप (embedding lookup) के बजाय दो केंद्रित सर्च में बदल जाती है। प्रत्येक उप-प्रश्न स्वतंत्र रूप से इंडेक्स को हिट करता है। परिणामों को बाद में (downstream) फिर से जोड़ दिया जाता है। यह रिट्रीवल को सटीक और केंद्रित रखता है, जिससे उस 'डाइल्यूशन' (dilution) को रोका जा सकता है जो तब होता है जब एक ही एम्बेडिंग एक साथ दर्जनों अवधारणाओं (concepts) से मेल करने की कोशिश करती है।
बेयसियन सर्च (Bayesian Search) को अपने पाइपलाइन को ट्यून करने दें
यदि आप अभी भी चंक साइज (chunk size), ओवरलैप रेशियो (overlap ratios) और रिट्रीवल वेट्स (retrieval weights) को मैन्युअल रूप से ट्यून कर रहे हैं, तो आप परफॉरमेंस का नुकसान कर रहे हैं। हमने अनुमान लगाना बंद कर दिया।
हमने एक सर्च स्पेस (search space) परिभाषित किया जहाँ चंक साइज, ओवरलैप प्रतिशत, वेक्टर-बनाम-BM25 वेट्स और reranker थ्रेशोल्ड (reranker thresholds) सभी वेरिएबल्स हैं। फिर हमने बेयसियन ऑप्टिमाइज़ेशन (Bayesian optimization) लागू किया। सैकड़ों रैंडम कॉन्फ़िगरेशन के माध्यम से ग्रिड-सर्च करने के बजाय, बेयसियन सर्च इस बात का एक प्रोबेबिलिस्टिक मॉडल (probabilistic model) बनाता है कि क्या काम करता है। यह एक कॉन्फ़िगरेशन का प्रस्ताव देता है, रिकॉल और लेटेंसी (latency) को देखता है, अपने विश्वासों को अपडेट करता है, और अगला प्रस्ताव देता है। समय के साथ, यह ऐसे संतुलन पर पहुँच जाता है जिसे कोई इंसान मैन्युअल रूप से कभी नहीं खोज पाएगा।
इसने ऐसे कॉम्बिनेशन खोजे जिन्हें हमने कभी आज़माया ही नहीं होता। भारी ओवरलैप के साथ छोटे चंक्स। डेंस वेक्टर सर्च (dense vector search) पर थोड़ा कम वेट और अधिक आक्रामक reranker थ्रेशोल्ड। इन गैर-स्पष्ट ट्रेडऑफ़्स (tradeoffs) ने हमें उच्च रिकॉल और कम लेटेंसी दोनों दिए।
यह कोई एक बार का सेटअप कार्य नहीं है। हम हर महीने हाइपरपैरामीटर ऑप्टिमाइज़ेशन (hyperparameter optimization) दोबारा चलाते हैं। आपका कॉर्पस (corpus) बदलता रहता है। उपयोगकर्ता का व्यवहार बदलता है। आपके पाइपलाइन को एक ही जगह जंग लगने के बजाय अनुकूलित (adapt) होना चाहिए।
परिणाम (The Payoff)
उस रीबिल्ड (rebuild) के कच्चे आउटपुट के साथ बहस करना मुश्किल है।
पोजीशन दस पर रिकॉल 78% से बढ़कर 95% हो गया। जब सही उत्तर हमारे नॉलेज बेस में होता है, तो हम बीस में से उन्नीस बार उसे सामने लाते हैं। 95वें पर्सेंटाइल पर लेटेंसी 850 मिलीसेकंड से घटकर 320 मिलीसेकंड हो गई। चैट भारी होने के बजाय तुरंत महसूस होती है।
बेहतर रिट्रीवल ने लैंग्वेज मॉडल को बेहतर ग्राउंडिंग (grounding) दी। हैलुसिनेशन रेट (hallucination rate) 12% से घटकर 3% हो गया। जब मॉडल के सामने सही कॉन्टेक्स्ट होता है, तो वह तथ्य गढ़ना बंद कर देता है। प्रति क्वेरी लागत 38% कम हो गई। तेज़ और सटीक रिट्रीवल का मतलब है कि अप्रासंगिक कॉन्टेक्स्ट, रिट्राय लूप और विस्तृत लेकिन बेकार प्रॉम्प्ट्स पर कम टोकन बर्बाद होते हैं।
इसे इंफ्रास्ट्रक्चर की तरह बनाएं
यदि आप प्रोटोटाइप से प्रोडक्शन की ओर बढ़ रहे हैं, तो रिट्रीवल को कॉन्फ़िगरेशन के बजाय इंफ्रास्ट्रक्चर कोड की तरह मानें।
