RAG

पहले, नाम की बात। RAG का पूरा रूप है retrieval-augmented generation: आप स्वयं प्रासंगिक टेक्स्ट लाते हैं और प्रश्न के समय उसे मॉडल को सौंप देते हैं, ताकि वह जमी हुई स्मृति की बजाय आपके दिए गए तथ्यों से उत्तर दे। यह अध्याय बताता है कि यह fetch-then-answer लूप कैसे काम करता है और कहाँ गलत हो जाता है।
Retrieve-then-generate पैटर्न
एक मॉडल को आपकी कंपनी के help docs, आपके उत्पाद की जानकारी, या प्रशिक्षण के बाद की कोई भी घटना पता नहीं होती। उनके बारे में पूछें तो वह या तो स्वीकार करेगा कि वह मदद नहीं कर सकता, या इससे भी बुरा, कुछ ऐसा गढ़ लेगा जो सही लगे। RAG इसे ठीक करता है — प्रश्न के समय मॉडल को प्रासंगिक टेक्स्ट सौंपकर।
यह विचार तब स्पष्ट होता है जब आपके पास embeddings हों — वह तरकीब जो टेक्स्ट को vectors में बदलती है जिन्हें निकटता से तुलना किया जा सकता है (cosine similarity मापती है कि दो vectors कितने करीब इशारा करते हैं)। Retrieval-augmented generation एक सामान्य model call पर जोड़े गए दो चरण हैं:
- Retrieve। उपयोगकर्ता का प्रश्न लें, अपने दस्तावेज़ों में सबसे प्रासंगिक टुकड़े खोजें, और शीर्ष मिलान एकत्र करें।
- Generate। उन टुकड़ों को prompt में context के रूप में डालें, फिर मॉडल से केवल उस context का उपयोग करके प्रश्न का उत्तर देने को कहें।
मॉडल की सामान्य भाषा क्षमता लेखन करती है; आपका retrieved टेक्स्ट तथ्य प्रदान करता है। आपको ऐसे प्रवाहमय उत्तर मिलते हैं जो आपकी सामग्री में निहित हों और अद्यतन रह सकें, क्योंकि आप नियंत्रित करते हैं कि क्या जाता है।
यह समझना उपयोगी है कि यह prediction loop के संदर्भ में इतना अच्छा क्यों काम करता है। RAG के बिना, आप मॉडल से उसके parameters में जमे patterns से एक तथ्य याद करने को कह रहे हैं, जो उसके पास नहीं भी हो सकता, जिससे एक आत्मविश्वासपूर्ण अनुमान मिलता है। RAG के साथ, तथ्य context में सामने ही रखा होता है, इसलिए मॉडल का काम "इसे याद करो" से बदलकर "सामने जो है उसे पढ़ो और उससे उत्तर दो" हो जाता है। Retrieval काम को स्मृति से पठन की ओर ले जाती है। साफ तस्वीर एक परीक्षा की है: बंद-पुस्तक परीक्षा अनुमान लगाने को आमंत्रित करती है, जबकि RAG इसे खुली-पुस्तक परीक्षा में बदल देता है जहाँ प्रासंगिक पृष्ठ मेज पर खुला होता है।
Chunking
आप पूरे दस्तावेज़ embed नहीं करते। आप पहले उन्हें छोटे chunks में विभाजित करते हैं — एक paragraph या कुछ वाक्य प्रत्येक — और उन्हें embed करते हैं। दो कारण। पहला, retrieval अधिक सटीक हो जाती है: आप एक 40-पृष्ठ की मैनुअल की बजाय वह एक प्रासंगिक paragraph खींचते हैं। दूसरा, chunks context window में फिट होते हैं, जहाँ एक विशाल दस्तावेज़ शायद न हो।
# एक छोटा chunker: paragraphs पर विभाजित करें
def chunk(text):
return [c.strip() for c in text.split("\n\n") if c.strip()]Chunk size एक संतुलन है, और इसका कारण embeddings से जुड़ता है। प्रत्येक chunk को एक single vector में बदला जाता है जो उसके पूरे meaning को सारांशित करता है। Chunk बहुत बड़ा बनाएं और वह कई विषयों को कवर करता है, इसलिए उसका एक vector उन सभी का एक धुंधला औसत है जो किसी विशिष्ट प्रश्न से अच्छी तरह match नहीं करता। बहुत छोटा बनाएं और वह उस surrounding meaning खो देता है जो उसे समझने के लिए चाहिए थी, इसलिए vector एक टुकड़े की ओर इशारा करता है। Paragraph-sized chunks में प्रत्येक एक सुसंगत विचार होता है। आप वहाँ से अपनी retrieval के प्रदर्शन के आधार पर समायोजित करते हैं।
Vectors कहाँ रहते हैं
कुछ chunks के लिए, आप vectors को memory में रख सकते हैं और उन्हें embeddings अध्याय की cosine-similarity function से compare कर सकते हैं, जो बाद के उदाहरण में किया गया है। हज़ारों या लाखों chunks के लिए, यह धीमा पड़ जाता है, और आप एक vector store की ओर जाते हैं: एक database जो scale पर निकटतम vectors को तेज़ी से खोजने के लिए बना है।
RAG सीखने के लिए आपको इसकी ज़रूरत नहीं है, और आपको अभी कोई चुनने की ज़रूरत नहीं है। कई hosted services और libraries के रूप में मौजूद हैं; concept ही मायने रखता है: embeddings store करने और उन्हें तेज़ी से search करने की कोई जगह। Memory में शुरू करें, जब collection बड़ी हो जाए तो store की ओर बढ़ें।
Grounding और citing
Generate चरण एक prompt है, और आप इसे कैसे लिखते हैं यह तय करता है कि RAG वास्तव में गढ़े हुए उत्तरों को कम करता है या नहीं। दो निर्देश भारी काम करते हैं: मॉडल को बताएं कि दिए गए context से केवल उत्तर दे, और उसे बताएं कि जब context में उत्तर न हो तो ऐसा कहे।
context_text = "\n\n".join(retrieved_chunks)
system_prompt = f'''नीचे दिए गए context का उपयोग करके ONLY प्रश्न का उत्तर दें।
यदि context में उत्तर नहीं है, तो कहें "I don't have that information."
अपने उत्तर में context के प्रासंगिक भाग को उद्धृत करें।
Context:
"""
{context_text}
"""'''यह दो पहले के पाठों को एक साथ लाता है। Delimiters और explicit "answer only from this" prompting से आते हैं। "I don't have that information" कहने की अनुमति hallucination defense है: एक grounded मॉडल जिसके पास escape hatch हो वह अनुमान लगाने वाले की तुलना में बहुत कम गढ़ता है। Grounding plus escape hatch ही गढ़े हुए उत्तरों को काटता है। स्रोत उद्धृत करने के लिए कहना users को verify करने के लिए कुछ देता है।
Nearest-neighbor से परे: hybrid search और re-ranking
Pure vector search meaning पर match करती है, जो अधिकांश समय आप यही चाहते हैं। लेकिन इसका एक blind spot है: यह एक ऐसे exact word को miss कर सकती है जो मायने रखता हो। E_4021 जैसे error code या किसी specific product name की खोज करें, और meaning-based search उन chunks को वापस दे सकती है जो उसी topic के बारे में हैं जबकि उसे skip कर देती है जिसमें exact string है।
Fix का एक नाम है, hybrid search: meaning-based search को पुराने ज़माने की keyword search के साथ मिलाएं, ताकि exact terms और general meaning दोनों को vote मिले। RAG शुरू करने के लिए आपको यह बनाने की ज़रूरत नहीं है। इसे तब reach करें जब exact terms मायने रखते हों और pure vectors उन्हें miss करते रहें।
व्यवहार में
embeddings अध्याय से search और embed helpers का पुनः उपयोग करते हुए, एक minimal RAG उत्तर:
def answer_from_docs(question, index):
# 1. retrieve: प्रश्न के लिए शीर्ष matching chunks
query_vector = embed(question)
scored = [{"text": item["text"], "score": cosine_similarity(query_vector, item["vector"])} for item in index]
top = sorted(scored, key=lambda x: x["score"], reverse=True)[:3]
# 2. generate: केवल उन chunks का उपयोग करके उत्तर दें
context = "\n\n".join(t["text"] for t in top)
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": f'Answer using ONLY this context. If it is not here, say you don\'t know.\n\n"""{context}"""'},
{"role": "user", "content": question},
],
)
return response.choices[0].message.contentतीन निकटतम chunks retrieve करें, उन्हें prompt में डालें, और मॉडल को उनसे उत्तर देने दें। यही पूरी RAG pipeline है: embeddings search एक model call को feed करती है, एक grounded prompt के साथ जो इसे एक साथ रखती है। लगभग सारी गुणवत्ता सही chunks retrieve करने में होती है।
जब आपको RAG की ज़रूरत न हो
RAG मुफ़्त नहीं है, और यह हमेशा उत्तर नहीं है। इसे skip करें जब:
- Data छोटा है और prompt में fit होता है। यदि आपका पूरा knowledge base कुछ paragraphs है, तो उसे सीधे paste करें। RAG तब के लिए है जब एक बार में सब भेजने के लिए बहुत अधिक हो।
- एक long-context model सब कुछ hold कर सकता है। कुछ models बहुत बड़े inputs accept करते हैं, एक पूरा दस्तावेज़ एक साथ लेने के लिए पर्याप्त, जो retrieval बनाने से कम काम हो सकता है।
और असली सीमा याद रखें: RAG quality ज़्यादातर retrieval quality है। यदि search step गलत chunks return करता है, तो एक perfect prompt भी उत्तर नहीं बचा सकता। Real RAG systems में अधिकांश प्रयास सही टेक्स्ट retrieve करने में जाता है, final model call में नहीं।
Embeddings और RAG मॉडल को प्रश्न के समय ज्ञान देते हैं, बिना मॉडल को बदले। अगला अध्याय, Fine-tuning, दूसरे option को cover करता है: अपने उदाहरणों से मॉडल बदलना, और prompting या RAG की तुलना में यह कब worth it है।

