बहुतेक RAG ट्युटोरियल्स तिथेच संपतात जिथून प्रत्यक्ष प्रोडक्शन सुरू होते. तुम्ही तुमचे दस्तऐवज ५१२-टोकनच्या चंक्समध्ये (chunks) विभागता, त्यांना एका सिंगल एम्बेडिंग मॉडेलमधून पाठवता आणि साध्या top-k रिट्रिव्हलसह वेक्टर डेटाबेस कॉल करता. डेमोमध्ये हे पटण्यासारखे वाटते. तुमच्या कंपनीच्या रजा धोरणाबद्दल (leave policy) बॉटला विचारा आणि तो एक सुसंगत परिच्छेद परत करतो. प्रत्येकजण होकार देतो. दुर्दैवाने, डेमो खोटे बोलतात.

प्रोडक्शनमधील कामात प्रत्येक शॉर्टकट उघड होतो. फिक्स्ड चंक्स कायदेशीर करारांमधील नुकसान भरपाईच्या (indemnification) कलमांच्या मधूनच कापले जातात. API डॉक्युमेंटेशन एकमेकांवर येणाऱ्या गोंधळासारखे (noise) बनते, ज्यामुळे तुम्हाला प्रत्यक्षात आवश्यक असलेली माहिती (signal) दबली जाते. लॅटन्सी (latency) इतकी वाढते की उत्तर येण्यापूर्वीच वापरकर्ते क्वेरी सोडून देतात. आम्ही या अडचणीचा सामना केला आणि आम्हाला सर्व काही पुन्हा तयार करावे लागले. आमचा रिट्रिव्हल लेयर "सिमँटिक सर्च आणि आशा" (semantic search and hope) वरून एका मोजमाप केलेल्या आणि इन्स्ट्रुमेंटेड पाइपलाइनमध्ये विकसित झाला. याचा निकाल ९५% रिकॉल (recall) आणि लॅटन्सीमध्ये ४०% कपात असा होता. प्रत्यक्षात काय काम करून गेले ते खाली दिले आहे.

चंक्सिंग स्ट्रॅटेजी (Chunking Strategy) दस्तऐवजाशी सुसंगत ठेवा

५१२-टोकनचा डिफॉल्ट पर्याय टिकून आहे कारण तो सोपा आहे, तो योग्य आहे म्हणून नाही. वेगवेगळ्या दस्तऐवजांमध्ये अर्थ वेगवेगळ्या प्रकारे साठवलेला असतो आणि तुमची चंक्सिंग स्ट्रॅटेजी तशीच असायला हवी.

कायदेशीर करारांसाठी (legal contracts), स्ट्रक्चरल बाउंड्रीजचा (structural boundaries) आदर करणारे रिकर्सिव्ह चंक्सिंग (recursive chunking) वापरा. कायदेशीर भाषा गुंतागुंतीची असते. एक कलम त्याच्या वरच्या विभागावर अवलंबून असते आणि वाक्याच्या मधूनच केलेला फिक्स्ड कट एखाद्या कर्तव्याचे तर्कशास्त्र नष्ट करतो. रिकर्सिव्ह चंक्सिंग टोकन मर्यादा लागू करण्यापूर्वी प्रथम नैसर्गिक विभाजकांवर — परिच्छेद, त्यानंतर वाक्ये — विभागण्याचा प्रयत्न करते. यामुळे नुकसान भरपाई किंवा दायित्वाचे (liability) कलमे अखंड राहतात.

API डॉक्युमेंटेशनसाठी, फंक्शन-अवेअर चंक्सिंग (function-aware chunking) वापरा. डेव्हलपर्स यादृच्छिक परिच्छेद शोधत नाहीत; ते एंडपॉइंट्स, पॅरामीटर्स आणि एरर सिग्नेचर्स शोधतात. एका चंक्समध्ये संपूर्ण फंक्शन सिग्नेचर, त्याचे वर्णन आणि रिटर्न स्कीमा (return schema) एक लॉजिकल युनिट म्हणून असला पाहिजे. जर तुम्ही तो ब्लॉक अर्धा कापला, तर रिट्रिव्हल सिस्टम अर्धा संदर्भ परत करते आणि जनरेशन मॉडेल उरलेली माहिती हॅलुसिनेट (hallucinates) करते.

सपोर्ट तिकिटांसाठी (support tickets), संवादाच्या टप्प्यांचे (conversation turns) अनुसरण करणाऱ्या सिमँटिक चंक्सिंगवर (semantic chunking) अवलंबून राहा. सपोर्ट थ्रेड्स रेखीय आणि पुनरावृत्ती करणारे असतात. ग्राहक समस्या पुन्हा सांगतो, एजंट लॉग्स मागतो, ग्राहक ते जोडतो. प्रत्येक टप्पा स्वतःचे एक सिमँटिक युनिट असतो. टप्प्यांनुसार चंक्सिंग केल्यामुळे कोणी काय आणि कधी म्हटले हे सुरक्षित राहते, जे तेव्हा महत्त्वाचे ठरते जेव्हा वापरकर्ता विचारतो, "एजंटने मंगळवारी काय सुचवले होते?"

अंतर्गत विकीसाठी (internal wikis), एजेंटिक चंक्सिंग (agentic chunking) वापरून पहा. एका LLM ला एक विभाग द्या आणि एक विषय कुठे संपतो आणि दुसरा कुठे सुरू होतो हे ठरवण्यास सांगा. यासाठी इनजेस्ट (ingest) वेळी अधिक खर्च येतो, परंतु विकीमध्ये खूप गोंधळ असतो. पेजेसमध्ये वेगवेगळ्या टीम्सचे असंबद्ध अपडेट्स असतात आणि मानवाने ठरवलेली सीमा क्वचितच उपयुक्त ठरते. विषयातील बदलांवर आधारित मॉडेलला सीमा ठरवू दिल्याने गोंधळ (noise) मोठ्या प्रमाणात कमी होतो.

एकाच पाइपलाइनमध्ये अनेक स्ट्रॅटेजी चालवण्यासाठी इनजेस्ट करताना दस्तऐवजांना प्रकारानुसार टॅग करणे आवश्यक आहे. शिस्तीचा हा छोटासा भाग लगेच फळ देतो.

शोध पद्धतींचे एकत्रीकरण करा, फक्त एक निवडू नका

वेक्टर सर्च (Vector search) हे हेतू (intent) समजून घेते, परंतु अचूक मॅचेसच्या (exact matches) बाबतीत ते वारंवार अपयशी ठरते. ERR_CONNECTION_REFUSED सारखा एरर कोड किंवा विशिष्ट SKU विचारला असता, डेन्स एम्बेडिंग्स (dense embeddings) अनेकदा संकल्पनात्मकदृष्ट्या समान परंतु तथ्यात्मकदृष्ट्या चुकीचे निकाल देतात. BM25, ही क्लासिक कीवर्ड स्पार्स रिट्रिव्हल (keyword sparse retrieval) पद्धत, अचूक स्ट्रिंग्स हाताळण्यात उत्तम आहे परंतु सिमँटिक बारकावे misses करते. तुम्हाला दोन्हीची गरज आहे.

हायब्रिड रिट्रिव्हल (hybrid retrieval) वापरा. वेक्टर सर्च आणि BM25 समांतर चालवा. त्यानंतर त्यांना रिसिप्रोकल रँक फ्यूजन (Reciprocal Rank Fusion - RRF) सह एकत्रित करा. RRF अशा दस्तऐवजांना प्राधान्य देते ज्यांच्याशी दोन्ही पद्धती सहमत आहेत, तरीही दोन्ही दृष्टिकोनातून मजबूत उमेदवार समोर आणते. गणित सोपे आहे आणि निकाल स्थिर आहे: कोणताही एक रिट्रिव्हल प्रकार अंतिम रँकिंगवर वर्चस्व गाजवत नाही.

फ्यूजननंतर, एक क्रॉस-एनकोडर रीरँकर (cross-encoder reranker) जोडा. पहिला टप्पा — वेक्टर प्लस स्पार्स रिट्रिव्हल — वेगवान आणि व्यापक आहे. त्यानंतर क्रॉस-एनकोडर प्रत्येक क्वेरी-दस्तऐवज जोडीला पूर्ण लक्ष देऊन स्कोअर देतो, याचा अर्थ तो मूळ प्रश्नाच्या तुलनेत उमेदवाराचे वाचन करतो. होय, यामुळे लॅटन्सी वाढते. आमच्या बाबतीत, साधारणपणे पन्नास ते शंभर मिलीसेकंद. परंतु अचूकतेतील (precision) वाढ इतकी मोठी आहे की हा बदल फायदेशीर ठरतो. जर तुम्हाला रिकॉलची काळजी असेल, तर तुम्ही हे वगळू शकत नाही.

इंडेक्स सुधारण्यापूर्वी क्वेरी सुधारा

वापरकर्ते तुमच्या सर्च इंजिनसाठी क्वेरी लिहीत नाहीत. ते मानवांसाठी लिहितात. "ते काम करत नाही" ही एक सामान्य सपोर्ट क्वेरी आहे. वैशिष्ट्याचे अस्पष्ट वर्णन ही एक सामान्य अंतर्गत विकी सर्च आहे. जर तुम्ही त्या कच्च्या इनपुटसह इंडेक्स शोधला, तर तुम्हाला कचरा (garbage) परत मिळेल.

रिट्रिव्हरपर्यंत पोहोचण्यापूर्वी क्वेरीमध्ये बदल करा.

वापरकर्त्याच्या प्रश्नाचे अनेक प्रकार तयार करण्यासाठी query expansion चा वापर करा. जर कोणी “server down” असे टाईप केले, तर तुमच्या सिस्टमने “service unavailable,” “502 error,” आणि “connection timeout” साठी देखील शोधले पाहिजे. या विविध हेतूंच्या (intent variants) प्रकारांचा समावेश केल्यामुळे आमचा recall 78% वरून 96% पर्यंत पोहोचला. ही एकच पायरी आहे आणि मिळणाऱ्या फायद्याच्या तुलनेत यासाठी खर्च जवळजवळ शून्य आहे.

जटिल प्रश्नांसाठी query decomposition चा वापर करा. जेव्हा एखादा वापरकर्ता “How do I migrate from the legacy billing API to the new one and what breaking changes affect enterprise accounts?” असा प्रश्न विचारतो, तेव्हा त्याचे उप-प्रश्नांमध्ये विभाजन करा. एक उप-प्रश्न मायग्रेशन स्टेप्सवर लक्ष केंद्रित करतो. दुसरा enterprise-specific breaking changes वर लक्ष केंद्रित करतो. प्रत्येक प्रश्न इंडेक्सच्या (index) वेगवेगळ्या भागावर परिणाम करतो. डाउनस्ट्रीम लँग्वेज मॉडेल गोंधळलेल्या कॉन्टेक्स्ट विंडोमध्ये (noisy context window) अंदाज लावण्याऐवजी, चांगल्या प्रकारे रिट्रिव्ह केलेल्या चंक्समधून (chunks) अंतिम उत्तर संकलित करते.

हायपरपॅरामीटर्सचा (Hyperparameters) अंदाज लावणे थांबवा

एकदा तुमच्याकडे अनेक chunking strategies, hybrid retrieval आणि query transformation उपलब्ध झाले की, तुम्हाला एका कॉम्बिनेटोरियल समस्येचा (combinatorial problem) सामना करावा लागतो. Chunk size, overlap, fusion weights, reranker depth आणि expansion count या सर्व गोष्टी एकमेकांशी संबंधित असतात. यातील एका गोष्टीत बदल केल्यास दुसरी गोष्ट बिघडू शकते. या संपूर्ण स्पेसमध्ये Grid search करणे वेळखाऊ आणि वाया जाणारे आहे.

त्याऐवजी Bayesian optimization वापरा. याला मशीन लर्निंग ट्यूनिंग जॉबप्रमाणे हाताळा. तुमचे उद्दिष्ट स्पष्टपणे परिभाषित करा: लेटन्सी (latency) एका मर्यादेच्या खाली ठेवून रिकॉल (recall) जास्तीत जास्त करणे. एक 'गोल्डन डेटासेट' तयार करा — काही शेकडो प्रतिनिधीत्व करणारे प्रश्न ज्यामध्ये तुम्हाला नक्की माहित आहे की कोणते चंक्स रिट्रिव्ह केले पाहिजेत. त्यानंतर Bayesian search ला कार्यक्षमतेने कॉन्फिगरेशन स्पेस शोधू द्या. हे काय काम करते याचे एक प्रोबॅबिलिस्टिक मॉडेल तयार करते आणि त्यानंतर सर्वात आशादायक क्षेत्रांची चाचणी घेते.

प्रत्येक कॅन्डिडेट कॉन्फिगरेशन स्टेजिंगमध्ये पोहोचण्यापूर्वी गोल्डन डेटासेटमधून उत्तीर्ण होणे आवश्यक आहे. जर नवीन chunk size मुळे recall कमी झाला किंवा जड (heavier) reranker मुळे लेटन्सी बजेटच्या बाहेर गेला, तर ऑप्टिमायझेशन ते आपोआप पकडते. यामुळे चर्चेतील वैयक्तिक मतांचा प्रभाव निघून जातो. तुम्ही 256 किंवा 512 टोकन्सपैकी कोणते "चांगले" आहे यावर वाद घालणे थांबवता आणि थेट निकाल वाचायला सुरुवात करता.

परिणाम

पाइपलाईनमध्ये झालेले बदल आम्ही ज्याप्रमाणे अपेक्षित होते, अगदी तसेच झाले.

  • Recall@10 78% वरून 95% पर्यंत वाढला.
  • P95 latency 850 ms वरून 320 ms पर्यंत कमी झाली.
  • Hallucination rate 12% वरून 3% पर्यंत खाली आला.
  • Cost per query मध्ये 38% घट झाली, याचे मुख्य कारण म्हणजे चांगल्या रिट्रिव्हलमुळे आम्हाला लहान जनरेशन मॉडेल आणि कमी प्रॉम्प्ट टोकन्स वापरणे शक्य झाले.

लेटन्सीमधील या घटामुळे टीममधील काही लोक आश्चर्यचकित झाले. rerankers आणि query expansion जोडल्यामुळे गोष्टींचा वेग मंदावल्यासारखा वाटू शकतो. परंतु रिट्रिव्हलची गुणवत्ता सुधारल्यामुळे, जनरेशन मॉडेलला कमी प्रॉम्प्टिंग, कमी तर्क (speculation) आणि कमी रिट्रायजची (retries) आवश्यकता होती. चांगले रिट्रिव्हल डाउनस्ट्रीममधील सर्व गोष्टी स्वस्त बनवते.

रिट्रिव्हलला इन्फ्रास्ट्रक्चरप्रमाणे माना

रिट्रिव्हल ही अशी नोटबुक नाही जी तुम्ही एकदा चालवता आणि विसरून जाता. ते एक इन्फ्रास्ट्रक्चर आहे आणि त्याचे व्यवस्थापन कोडप्रमाणे केले पाहिजे. तुमच्या chunking strategies ला व्हर्जन करा. जेव्हा लीगल टीम नवीन कॉन्ट्रॅक्ट टेम्पलेट रिलीज करते, तेव्हा ते प्रोडक्शनमध्ये पोहोचण्यापूर्वी तुमच्या recursive splitter ची चाचणी घ्या. तुमच्या गोल्डन डेटासेटला जिवंत दस्तऐवज (living documents) म्हणून ठेवा, गेल्या तिमाहीचा स्टॅटिक CSV म्हणून नाही. तुमच्या इव्हॅल्युएशन्सना CI मध्ये ऑटोमेट करा, जेणेकरून embedding model किंवा fusion weight मध्ये बदल करणारा कोणताही pull request मानवी पुनरावलोकनापूर्वीच रिकॉल आणि लेटन्सीच्या आकड्यांसह कमेंट मिळवेल.

तुमचे वापरकर्ते तुम्ही कोणते embedding model वापरता हे कधीच विचारणार नाहीत. त्यांना तुमच्या chunking heuristic किंवा तुमच्या reranker architecture बद्दल काळजी नसेल. त्यांना फक्त उत्तर बरोबर आहे का, ते वेगाने येते का आणि ते त्यांच्यावर विश्वास ठेवू शकतात का, याची काळजी असते. असा पाइपलाइन तयार करा जो तो विश्वास मिळवेल, त्याचे प्रामाणिकपणे मोजमाप करा आणि रिट्रिव्हलकडे केवळ एक नंतरचा विचार (afterthought) म्हणून पाहणे थांबवा.

Source: Optimizing RAG At Scale
Join the discussion: GyaanSetu AI Community