बहुतेक टीम्स त्यांची पहिली रिट्रिव्हल सिस्टीम (retrieval system) एकाच पद्धतीने तयार करतात: प्रत्येक डॉक्युमेंटला ५१२-टोकनच्या ठराविक तुकड्यांमध्ये (chunks) विभागणे, त्यांना वेक्टर डेटाबेसमध्ये टाकणे आणि एम्बेडिंग मॉडेलने (embedding model) कष्टाचे काम करावे अशी आशा करणे. ही आशा तुम्हाला केवळ डेमोमध्येच उपयोगी पडते. प्रत्यक्ष वापरकर्त्यांसोबत काम करताना ती टिकत नाही.

प्रोडक्शनमध्ये (In production), जेव्हा तुम्ही दायित्व कलमाचा (liability clause) त्याच्या अपवादांपासून (exceptions) वेगळा करता, तेव्हा कायदेशीर करार कोलमडतो. जेव्हा कोड सॅम्पल त्याच्या फंक्शन सिग्नेचरपासून (function signature) वेगळे होते, तेव्हा API डॉक्युमेंटेशन निरुपयोगी ठरते. जेव्हा तुम्ही एखाद्या तक्रारीला तिच्या संवादाच्या इतिहासातून (conversational history) वेगळे करता, तेव्हा कस्टमर सपोर्ट थ्रेडमधील माहिती केवळ गोंधळ निर्माण करते. समस्या सहसा पाइपलाइनच्या शेवटी असलेल्या लँग्वेज मॉडेलची नसते. समस्या तुम्ही त्याला काय फीड करता (feed) यात असते.

आम्ही हा धडा कष्टाने शिकलो. आमचा सुरुवातीचा रिट्रिव्हल लेयर (retrieval layer) प्रमाणबद्ध वाटत होता पण त्याचे वर्तन विसंगत होते. म्हणून आम्ही एका साध्या कल्पनेभोवती तो पुन्हा तयार केला: रिट्रिव्हलकडे एक जादू म्हणून न पाहता, एक मोजता येण्याजोग्या इन्फ्रास्ट्रक्चर (measured infrastructure) म्हणून पहा. नेमके काय बदलले आणि आम्ही ९५ व्या पर्सेंटाईल लेटन्सी (95th-percentile latency) ८५० ms वरून ३२० ms पर्यंत कमी करत असताना रिकॉल (recall) ९५ टक्क्यांपर्यंत कसा नेला, याची माहिती खालीलप्रमाणे आहे.

फिक्स्ड-चंकचा सापळा (The Fixed-Chunk Trap)

एकसमान टोकन संख्या कोड करणे आणि स्पष्ट करणे सोपे असते. पण ही सोय एक मूलभूत सत्य लपवून ठेवते: डॉक्युमेंट्सना एक विशिष्ट रचना (structure) असते. जेव्हा तुम्ही त्या रचनेकडे दुर्लक्ष करता, तेव्हा तुम्ही त्यातील महत्त्वाचा संकेत (signal) नष्ट करता.

दहा पानांच्या मास्टर सर्व्हिस एग्रीमेंटचा विचार करा. ५१२-टोकनचा एक ठराविक तुकडा एखाद्या दायित्वाच्या मध्यभागी येईल, ज्यामुळे एखादे कलम त्याच्या मर्यादा ठरवणाऱ्या टेबलपासून वेगळे होईल. अशा वेळी रिट्रिव्हल स्टेप केवळ अर्धा विचार परत करते आणि जनरेटर (generator) उरलेली माहिती स्वतःच्या मनाने तयार करतो (hallucinates). API डॉक्युमेंटेशनमध्ये, खूप मोठा चंक (chunk) बोयलरप्लेट हेडर्समुळे एम्बेडिंगची तीव्रता कमी करतो, ज्यामुळे डेव्हलपरला आवश्यक असलेला विशिष्ट मेथड (method) दबला जातो. सपोर्ट तिकिट्समध्ये, एक फिक्स्ड विंडो संवादाकडे केवळ वाक्यांचा संच म्हणून पाहते, ज्यामुळे प्रत्यक्ष काय चुकले हे समजून घेण्यासाठी आवश्यक असलेला संवादाचा ओघ (back-and-forth) निघून जातो.

आम्ही चंक साईजकडे (chunk size) केवळ एक अंदाज लावायचा हायपरपॅरामीटर (hyperparameter) म्हणून पाहणे थांबवले. त्याऐवजी, आम्ही ते डॉक्युमेंटचा प्रकार आणि त्यातील माहितीची रचना (information architecture) यांच्यातील मॅपिंगचे एक साधन मानण्यास सुरुवात केली.

तुमच्या चंकिंगला (Chunking) डेटाशी जुळवा

उपाय म्हणजे एकच परफेक्ट चंक साईज शोधणे नाही. उपाय म्हणजे तीन वेगवेगळ्या डेटा प्रकारांसाठी तयार केलेल्या तीन वेगवेगळ्या धोरणांचा (strategies) वापर करणे.

कायदेशीर कागदपत्रे (Legal documents) आता रिकर्सिव्ह स्प्लिटिंगमधून (recursive splitting) जातात. अल्गोरिदम प्रथम सर्वात मोठ्या नैसर्गिक सीमा शोधतो—विभाग (sections), त्यानंतर उपविभाग (subsections), आणि मग क्रमांकित कलमे (numbered clauses)—आणि गरज पडल्यासच लहान तुकड्यांचा वापर करतो. यामुळे 'टर्मिनेशन क्लॉज' (termination clause) त्याच्या अटींशी जोडलेला राहतो. रिट्रिव्हल स्टेप पूर्ण लॉजिकल युनिट्स पाहू शकते, ज्यामुळे मॉडेलला गहाळ अपवाद स्वतःहून तयार करण्याची (invent missing exceptions) प्रवृत्ती कमी होते.

API आणि कोड डॉक्युमेंटेशनला स्ट्रक्चर-अवेअर चंकिंग (structure-aware chunking) दिले जाते. Markdown हेडर्स, कोड फेन्सेस (code fences) आणि पॅरामीटर टेबल्सना अ‍ॅटॉमिक युनिट्स (atomic units) म्हणून पार्स केले जाते. आम्ही कोड ब्लॉकच्या आत विभागणी करत नाही. आम्ही docstrings त्यांच्या सिग्नेचरच्या जवळ ठेवतो. परिणामी, एखाद्या विशिष्ट क्लास मेथडसाठी (class method) केलेली क्वेरी डेव्हलपरला आवश्यक असलेला संपूर्ण संदर्भ मिळवून देते: वर्णन, टाईप केलेले पॅरामीटर्स आणि कार्यरत उदाहरण.

सपोर्ट आणि संवादात्मक डेटा (Support and conversational data) साठी सिमेंटिक चंकिंगचा (semantic chunking) वापर केला जातो. टोकन्स मोजण्याऐवजी, आम्ही विषयातील किंवा हेतूतील (intent) बदल शोधतो. जर एखाद्या ग्राहकाने तिसऱ्या मेसेजमध्ये बगचे वर्णन केले आणि सातव्या मेसेजमध्ये स्टॅक ट्रेस (stack trace) पेस्ट केला, तर आम्ही मेसेज इंडेक्सऐवजी अर्थावरून चंकिंग करतो. यामुळे रिट्रिव्हल लेयर एखादे विस्कळीत वाक्य देण्याऐवजी समस्येचा संपूर्ण प्रवाह (full arc) परत करते.

केवळ वेक्टर सर्च का अपयशी ठरते

अगदी परफेक्ट चंक्स देखील केवळ वेक्टर सर्चमध्ये अपयशी ठरू शकतात. डेंस एम्बेडिंग्स (Dense embeddings) अर्थ आणि समानार्थी शब्द पकडण्यात उत्कृष्ट असतात, परंतु अचूक स्ट्रिंग्सच्या (exact strings) बाबतीत ते अनेकदा अस्पष्ट ठरतात. जर एखाद्या इंजिनिअरने ERR_CONNECTION_REFUSED हा अचूक एरर कोड शोधला, तर वेक्टर सिमिलॅरिटी (vector similarity) कदाचित डझनभर संकल्पनात्मक जवळचे शब्द दाखवेल आणि १४ व्या क्रमांकावर असलेला अचूक मॅच शोधण्यात अपयशी ठरेल.

BM25 सह कीवर्ड सर्चची (Keyword search) समस्या अगदी उलट आहे. ते अचूक टोकन्स शोधते पण सिमेंटिक हेतू (semantic intent) misses करते. "माझे डेटाबेस का बंद आहे" (why is my database down) असे विचारणाऱ्या वापरकर्त्याला "कनेक्शन टाइमआउटची समस्या निवारण" (troubleshooting connection timeouts) असे म्हणणारे डॉक्युमेंट कधीच सापडणार नाही.

आम्ही आता दोन्ही पद्धती वापरतो. वेक्टर आणि कीवर्ड रिझल्ट्स 'रेसिप्रोकल रँक फ्यूजन' (Reciprocal Rank Fusion) मध्ये टाकले जातात, जे कॅलिब्रेटेड स्कोअरची गरज न ठेवता दोन्ही रँक केलेल्या लिस्ट एकत्र करते. त्यानंतर ही एकत्रित लिस्ट 'क्रॉस-एन्कोडर रँकर' (cross-encoder reranker) मधून पाठवली जाते. रँकर हा सुरुवातीच्या रिट्रिव्हलपेक्षा संथ असतो, परंतु तो अधिक अचूक असतो कारण तो कॉम्प्रेस्ड एम्बेडिंग्सऐवजी क्वेरी-डॉक्युमेंटची सुसंगतता (relevance) थेट तपासतो. केवळ या हायब्रिड पाइपलाइनमुळे आमचा रिकॉल १५ टक्क्यांनी वाढला.

इंडेक्सवर पोहोचण्यापूर्वी खराब क्वेरीज सुधारणे

वापरकर्ते आदर्श शोध क्वेरीज (search queries) लिहीत नाहीत. ते अर्धवट लॉग लाइन्स (log lines) पेस्ट करतात. ते “it’s broken” असे टाईप करतात. ते असे तांत्रिक शब्द (jargon) वापरतात जे तुमच्या डॉक्युमेंटेशनमध्ये कधीच वापरले गेले नाहीत. जर तुम्ही कच्च्या क्वेरीवर (raw query) विश्वास ठेवला, तर तुम्ही केवळ गोंधळावर (noise) विश्वास ठेवत आहात.

आम्ही आता प्रत्येक येणाऱ्या क्वेरीला रिट्रिव्हल लेयरकडे (retrieval layer) पाठवण्यापूर्वी तीन ते पाच विविध रूपांमध्ये (variations) विस्तारतो. एक रूपांतर थेट समानार्थी शब्द (paraphrase) असू शकते. दुसरे एखाद्या काल्पनिक आदर्श दस्तऐवजाचे शीर्षक असू शकते. तिसरे रूपांतर संवादातील अनावश्यक शब्द काढून टाकून तांत्रिक कीवर्ड्सवर लक्ष केंद्रित करते. प्रत्येक रूपाचे एम्बेडिंग (embedding) केले जाते आणि शोध घेतला जातो. त्यानंतर आम्ही कॅन्डिडेट पूल्समधील डुप्लिकेट काढून टाकतो आणि ते एकत्र करतो.

हे मोफत नाही. त्या अतिरिक्त एम्बेडिंग कॉल्सना खर्च येतो आणि काही मिलीसेकंद वेळही लागतो. परंतु रिकॉलवर (recall) याचा परिणाम नाट्यमय होता: क्वेरी रिट्रिव्हलपूर्वी विस्तारल्यामुळे आम्ही ७८ टक्क्यांवरून ९६ टक्क्यांपर्यंत पोहोचलो. कारण उत्तम रिट्रिव्हलमुळे जनरेशन विंडो (generation window) लहान होते आणि मॉडेलला योग्य संदर्भासह (context) जोडले जाते, ज्यामुळे शेवटी आमचा खर्च वाचला. रिट्रिव्हलची थोडी महागडी पायरी, लांब आणि चुकीची माहिती देणाऱ्या (hallucinated) जनरेशन पायरीपेक्षा स्वस्त पडते.

अंदाज लावणे थांबवा. शोधणे सुरू करा.

एकदा का आमच्याकडे योग्य चंकिंग (chunking), हायब्रिड रिट्रिव्हल आणि क्वेरी एक्सपान्शन उपलब्ध झाले, तरीही आम्हाला एका गुंतागुंतीच्या समस्येचा सामना करावा लागला. चंक साईज (chunk size), चंक ओव्हरलॅप (chunk overlap), टॉप-के रिट्रिव्हल डेप्थ (top-k retrieval depth), रँकर कटऑफ्स (reranker cutoffs) आणि फ्यूजन वेट्स (fusion weights) या सर्व गोष्टी एकमेकांवर अवलंबून असतात. मॅन्युअल ग्रिड सर्चसाठी आठवडे लागले असते आणि तरीही आम्हाला केवळ मर्यादित निकाल (local maximum) मिळाला असता.

या स्पेसचा शोध घेण्यासाठी आम्ही बेयेशियन ऑप्टिमायझेशन (Bayesian optimization) वापरले. प्रत्येक कॉम्बिनेशनची सखोल चाचणी करण्याऐवजी, शोध अल्गोरिदम कोणत्या कॉन्फिगरेशनमुळे चांगले निकाल मिळू शकतात यावर आधारित एक विश्वास (belief) राखतो आणि हळूहळू आशादायक क्षेत्रांकडे (promising regions) वळतो.

याचे आउटपुट म्हणजे एखादी एकच परफेक्ट सेटिंग नाही. ते निवडींचे एक 'पॅरेटो फ्रंटियर' (Pareto frontier) आहे. एका बाजूला, आमच्या हाय-थ्रूपुट API सपोर्ट एंडपॉइंटसाठी ऑप्टिमाइझ केलेली एक लीन (lean) कॉन्फिगरेशन आहे: जलद इन्फरन्स (fast inference), मध्यम रिकॉल आणि शक्य तितकी कमी लॅटन्सी (latency). दुसऱ्या बाजूला, कायदेशीर पुनरावलोकनासाठी (legal review) एक आक्रमक कॉन्फिगरेशन आहे: सखोल रिट्रिव्हल, अधिक रँकिंग आणि घट्ट ओव्हरलॅप, जिथे सखोलतेसाठी मिलीसेकंदचा त्याग केला जातो. हे फ्रंटियर स्पष्ट असल्यामुळे, आम्ही 'एकच आकार सर्वांसाठी योग्य' (one size fits all) असे मानण्याऐवजी उत्पादनासाठी योग्य बिंदू निवडू शकतो.

आकडेवारी प्रत्यक्षात कशी दिसते

या बदलांमुळे सिस्टम एका नाजूक प्रोटोटाइपमधून एका मोजमाप केलेल्या प्रोडक्शन पाईपलाईनमध्ये रूपांतरित झाली.

'रिकॉल ॲट टेन' (Recall at ten) ७८ टक्क्यांवरून ९५ टक्क्यांपर्यंत सुधारला. याचा अर्थ असा की जेव्हा आमच्या कॉर्पसमध्ये योग्य उत्तर उपलब्ध असते, तेव्हा आम्ही विसाव्यापैकी एकोणीस वेळा ते शोधून काढतो.

९५ व्या पर्सेंटाईलवरील लॅटन्सी (Latency at the 95th percentile) ८५० ms वरून ३२० ms पर्यंत खाली आली. हायब्रिड स्टॅक कागदावर जड वाटत असला तरी, स्मार्ट इंडेक्सिंग, लहान रँकर्स आणि गरज असेल तेव्हाच आक्रमक चंक्स (aggressive chunks) देण्याच्या क्षमतेमुळे संपूर्ण सिस्टम अधिक वेगवान झाली.

हॅलुसिनेशन रेट (Hallucination rate)—ज्याची नोंद मानवी अ‍ॅनोटेटर्सनी एका 'गोल्डन डेटासेट'वर केली आहे—तो १२ टक्क्यांवरून ३ टक्क्यांपर्यंत खाली आला. जेव्हा मॉडेलला पूर्ण आणि संबंधित संदर्भ मिळतो, तेव्हा ते तथ्ये स्वतःहून बनवणे थांबवते.

प्रति क्वेरी खर्च $०.००८ वरून $०.००५ पर्यंत कमी झाला. उत्तम रिट्रिव्हलचा अर्थ असा आहे की LLM प्रॉम्प्ट्स अधिक लहान आणि केंद्रित असतील आणि दुरुस्तीचे प्रयत्न (recovery attempts) कमी लागतील. क्वेरी एक्सपान्शनवरील अतिरिक्त एम्बेडिंग खर्च जनरेशनमधील बचतीमुळे कमी झाला आहे.

एक गोल्डन डेटासेट तयार करा आणि रिट्रिव्हलला कोडप्रमाणे हाताळा

जर तुम्ही यातून एक गोष्ट शिकली असेल, तर ती म्हणजे मोजमापाची शिस्त (discipline of measurement). आम्ही वास्तविक प्रश्न आणि सत्यापित उत्तरांच्या स्थानांचा एक छोटा 'गोल्डन डेटासेट' तयार केला. कोणताही बदल प्रोडक्शनमध्ये येण्यापूर्वी, तो त्या डेटासेटवर चालवला जातो. रिकॉल आणि लॅटन्सीचे रिअल-टाइममध्ये मॉनिटरिंग केले जाते, केवळ नोटबुकमध्ये पाहून अंदाज लावला जात नाही.

रिट्रिव्हल ही केवळ संशोधनाची डेमो (research demo) नाही. ती एक इन्फ्रास्ट्रक्चर (infrastructure) आहे. तुमच्या इतर स्टॅकप्रमाणेच तिला युनिट टेस्ट्स, रिग्रेशन बेंचमार्क आणि ऑटोमेटेड ऑप्टिमायझेशनची गरज आहे. चंकिंग हे डॉक्युमेंट स्ट्रक्चरनुसार करा, केवळ टोकनच्या अंधश्रद्धेनुसार नाही. वेक्टर आणि कीवर्ड सर्चला रँकरसह जोडा. वापरकर्ते प्रत्यक्षात ज्या क्वेरीज लिहितात त्यांचा विस्तार करा. त्यानंतर तुमच्या अंतर्ज्ञानाऐवजी (intuition) सर्च अल्गोरिदमला सेटिंग्ज (knobs) ट्यून करू द्या.

आम्ही वर्णन केलेली ही पाईपलाईन केवळ सैद्धांतिक नाही. तुम्ही मूळ लेख येथे वाचू शकता, आणि जर तुम्हाला या विषयाची काळजी घेणाऱ्या समुदायासोबत रिट्रिव्हल इंजिनिअरिंगवर चर्चा करायची असेल, तर GyaanSetu AI group उपलब्ध आहे.