बहुतेक RAG प्रोटोटाइप्स अंतर्गतून (under the hood) सारखेच दिसतात. कोणीतरी एका पाइपलाइनमध्ये PDF टाकते, मजकुराचे ५१२-टोकनच्या सुटसुटीत तुकड्यांमध्ये (chunks) विभाजन करते, ते वेक्टर डेटाबेसमध्ये टाकते आणि काम पूर्ण झाले असे समजते. साध्या डेमोसाठी हे प्रभावी वाटू शकते, पण प्रत्यक्ष वापरामध्ये (production) हे कोलमडते.
एक फिक्स्ड चंक (fixed chunk) तो काय कापतोय याची पर्वा करत नाही. तो कायदेशीर कराराचे नुकसान भरपाई कलमाच्या (indemnity clause) मधोमध विभाजन करेल. तो पाच असंबद्ध API endpoints एकाच कॉन्टेक्स्ट विंडोमध्ये कोंबून मॉडेलला गोंधळात (noise) टाकून देईल. यामुळे तुम्हाला गरजेपेक्षा जास्त तुकडे शोधून घ्यावे लागतील, ज्यामुळे लॅटन्सी (latency) वाढेल आणि टोकन्सचा अपव्यय होईल. परिणाम म्हणजे अर्धवट उत्तरे, हॅल्युसिनेशन्स (hallucinations) आणि त्रस्त वापरकर्ते.
आम्ही आमचा रिट्रिव्हल लेयर (retrieval layer) पूर्णपणे मोडीत काढून पुन्हा उभारला. याचा परिणाम असा झाला की, आमची सिस्टिम ४० टक्के लॅटन्सी कमी करून ९५ टक्के रिकॉल (recall) गाठू लागली. आम्ही हे नेमके कसे केले, ते खाली दिले आहे.
प्रत्यक्ष वापरामध्ये (Production) फिक्स्ड चंक्स का अपयशी ठरतात
५१२-टोकनचा डिफॉल्ट पर्याय ही कोणतीही डिझाइन निवड नाही. तो सुरुवातीच्या एम्बेडिंग मॉडेल कॉन्टेक्स्ट विंडोज आणि लायब्ररीच्या डिफॉल्ट सेटिंग्सचा एक परिणाम आहे. हे लागू करणे सोपे आहे, पण त्यावर अवलंबून राहणे विनाशकारी ठरू शकते.
दस्तऐवज एकसमान नसतात. एक कायदेशीर कलम कोणत्याही स्पष्ट विश्रांतीशिवाय सातशे टोकन्सपर्यंत चालू शकते. जर तुम्ही ते ५१२ वर कापले, तर तुम्ही दोन विस्कळीत तुकडे (orphaned fragments) तयार करता. जेव्हा एखादा वकील किंवा कंप्लायन्स ऑफिसर दायित्व मर्यादेबद्दल (liability caps) विचारतो, तेव्हा सिस्टिम केवळ अर्धी जबाबदारी परत करते. लँग्वेज मॉडेल गहाळ झालेला अर्धा भाग स्वतःच्या मनाने तयार करते (hallucinates), किंवा त्याहून वाईट म्हणजे, ती मर्यादा अस्तित्वातच नाही असे सांगते.
API डॉक्युमेंटेशनमध्ये याच्या अगदी उलट समस्या येते. ५००-टोकनचा एक चंक संपूर्ण मॉड्यूल गिळंकृत करू शकतो: authentication headers, error codes, rate limits आणि webhook schemas. जेव्हा एखादा डेव्हलपर AUTH_4027 कसा हाताळायचा हे विचारतो, तेव्हा रिट्रिव्हर असंबद्ध फंक्शन्सचा गोंधळ समोर आणतो. मॉडेलकडे त्या सर्वांचे मिश्रण करून एक सामान्य आणि निरर्थक माहिती देण्याशिवाय पर्याय उरत नाही.
चुकीच्या चंकिंगमुळे लॅटन्सी देखील वाढते. कमकुवत तुकड्यांचा अर्थ असा की, एखादा विषय कव्हर करण्यासाठी तुम्हाला मोठा top-k वापरावा लागेल. जास्त चंक्स म्हणजे लांब प्रॉम्प्ट्स. लांब प्रॉम्प्ट्स म्हणजे संथ जनरेशन आणि जास्त खर्च. वापरकर्त्याचा अनुभव हळूहळू खालावतो.
चंकला (Chunk) दस्तऐवजाशी सुसंगत करा
आम्ही टोकन्स मोजणे थांबवले आणि मजकूर वाचायला सुरुवात केली. योग्य चंकिंग स्ट्रॅटेजी ही मूळ स्त्रोताच्या (source) रचनेवर अवलंबून असते.
कायदेशीर दस्तऐवजांसाठी (Legal documents) क्लॉज-अवेअर बाउंड्रीजसह (clause-aware boundaries) रिकर्सिव्ह कॅरेक्टर चंकिंगची गरज असते. स्प्लिटर (splitter) श्रेणीबद्ध रचनेचा (hierarchy) आदर करतो: तो प्रथम सेक्शन हेडर्स शोधतो, त्यानंतर क्रमांकित परिच्छेद आणि मग नैसर्गिक वाक्य रचना शोधतो. तो कधीही उप-कलम (sub-clause) तोडत नाही किंवा एखाद्या जबाबदारीच्या वाक्याचे तुकडे करत नाही. जेव्हा तुम्ही नुकसान भरपाईबद्दल (indemnification) उतारा शोधता, तेव्हा तुम्हाला संपूर्ण कलम, मर्यादा आणि अपवाद मिळतात.
API डॉक्युमेंटेशनसाठी स्ट्रक्चर-अवेअर चंकिंगची (structure-aware chunking) आवश्यकता असते. आम्ही टोकन बजेटनुसार नाही, तर फंक्शन डेफिनेशननुसार (function definition) डेटाचे विश्लेषण करतो. प्रत्येक चंकमध्ये संपूर्ण फंक्शन सिग्नेचर (function signature), त्याचे पॅरामीटर वर्णन आणि त्याच्या अगदी जवळच्या एरर हँडलिंग नोट्स असतात. जर एखाद्या डेव्हलपरने विशिष्ट मेथड शोधली, तर त्यांना संपूर्ण करार मिळतो, कोणत्याही अनिश्चित विभाजनामुळे तुटलेला तुकडा नाही.
सपोर्ट तिकिट्स (Support tickets) गोंधळात टाकणारी आणि नॉन-लिनिअर असतात. एखादी थ्रेड बग रिपोर्टने सुरू होऊ शकते, त्यात एखादा उपाय (workaround) दिला जाऊ शकतो आणि ती अंतर्गत एस्केलेशन नोटने संपू शकते. सिमेंटिक चंकिंग (Semantic chunking) वाक्यांमधील एम्बेडिंग साम्य मोजून विषयातील बदल ओळखते. आम्ही केवळ नैसर्गिक विषयांच्या सीमांवरच (thematic boundaries) ब्रेक घेतो, जेणेकरून लॉगिन फेल्युअरबद्दलची चर्चा बिलिंग सायकलच्या फॉलो-अपपासून वेगळी राहते.
विकिस (Wikis) सर्वात कठीण होते. ते विस्तीर्ण, एकमेकांशी जोडलेले आणि सैल पद्धतीने संघटित असतात. आम्ही एजेंटिक चंकिंगचा (agentic chunking) वापर केला, जिथे एक हलके LLM पेज वाचते आणि विषयाची सुसंगतता (thematic coherence) पाहून ब्रेक ठरवते. डेटा इनजेशनच्या वेळी (ingestion time) यासाठी थोडा जास्त खर्च येतो, परंतु तयार झालेले चंक्स स्वयंपूर्ण आणि रिट्रिव्हलसाठी तयार असतात. डिप्लॉयमेंटच्या सर्वोत्तम पद्धतींबद्दलचे पेज कोणत्याही अनिश्चित मजकूर ब्लॉक्सऐवजी तार्किक युनिट्समध्ये विभागले जाते: प्री-फ्लाइट चेक, रोलबॅक प्रक्रिया आणि मॉनिटरिंग सेटअप.
हायब्रीड रिट्रिव्हल: कीवर्ड्स आणि वेक्टर्स एकत्र
डेन्स वेक्टर सर्च (Dense vector search) अर्थ समजून घेतो, पण अचूक स्ट्रिंग्ससाठी (exact strings) तो खूप खराब आहे. जर वापरकर्त्याने AUTH_4027 सारखा अचूक एरर कोड किंवा "Stark Industries" सारखे ग्राहकाचे नाव शोधले, तर वेक्टर एम्बेडिंग्स त्यांचे लक्ष्य चुकवू शकतात, कारण ते कॅरेक्टर-लेव्हल अचूकतेऐवजी वैचारिक साम्यासाठी (conceptual proximity) ऑप्टिमाइझ केलेले असतात.
BM25 द्वारे केल्या जाणाऱ्या शुद्ध कीवर्ड सर्चमध्ये याच्या उलट दोष असतो. तो AUTH_4027 अगदी अचूकपणे शोधेल, पण "authorization failure" आणि "login denied" मधील वैचारिक दुवा (conceptual bridge) तो शोधण्यात अपयशी ठरेल.
आम्ही दोन्ही समांतरपणे चालवतो. BM25 आणि vector search एकाच कॉर्पसवर स्वतंत्रपणे काम करतात. त्यांच्या रिझल्ट लिस्ट्सना Reciprocal Rank Fusion वापरून एकत्रित केले जाते, जे उमेदवारांच्या पोझिशनल रँक्समध्ये संतुलन राखून त्यांना पुन्हा क्रमाने लावते. तुम्हाला कॅलिब्रेटेड वेट्सची (calibrated weights) गरज नसते. तुम्हाला एकाच रँक्ड लिस्टमध्ये 'exact match' ची अचूकता आणि 'semantic search' ची समज मिळते.
त्यानंतर आम्ही एक cross-encoder reranker जोडतो. हे एक वेगळे मॉडेल आहे जे मूळ क्वेरीच्या विरुद्ध प्रत्येक परिच्छेद (passage) स्कोर करते, ज्यामुळे कोणत्याही एका रिट्रिव्हरपेक्षा कितीतरी पटीने सूक्ष्म 'relevance signal' प्राप्त होतो. यामुळे सुमारे ५० मिलीसेकंदचा लॅटन्सी (latency) वाढतो. यामुळे रिकॉल (recall) १५ टक्क्यांनी वाढतो. जर तुम्हाला उत्तराच्या गुणवत्तेची काळजी असेल, तर हा बदल अनिवार्य आहे.
क्वेरी एक्सपान्शन (Query Expansion): शोध सुरू होण्यापूर्वीच त्यात सुधारणा करा
खराब क्वेरीज हे प्रत्येक रिट्रिव्हल सिस्टमचे (retrieval system) एक गुपित आहे. वापरकर्ते तुमच्या 'embedding space' प्रमाणे लिहीत नाहीत. ते "it broke" असे टाईप करतात. ते अर्धवट (truncated) स्टॅक ट्रेसेस (stack traces) पेस्ट करतात. ते असे अंतर्गत शब्द (internal jargon) वापरतात जे तुमच्या इंडेक्सने कधीही पाहिलेले नसतात.
इंडेक्सला स्पर्श करण्यापूर्वीच आम्ही क्वेरीमध्ये बदल करतो. प्रथम, आम्ही एका सिंगल क्वेरीचा विस्तार करून तीन ते पाच विविध सर्च टर्म्समध्ये रूपांतर करतो. जर मूळ क्वेरी "payment failed" असेल, तर आम्ही "transaction error," "billing declined," आणि "charge unsuccessful" साठी देखील शोध घेतो.
