Retrieval Augmented Generation (RAG) ایک ایسا نمونہ ہے جو کسی ایپلیکیشن کو متعلقہ ماخذ مواد کو بازیافت کرنے اور اسے ماڈل پرامپٹ میں شامل کرنے کی اجازت دیتا ہے، جس سے ماڈل اس تناظر میں جواب دے سکتا ہے۔
RAG سسٹم کی بڑی سیاق و سباق ونڈو آخری صارف کے لیے زیادہ نرم تجربہ فراہم کر سکتی ہے، لیکن اسے اچھے سیاق و سباق کے انتظام کا متبادل نہیں سمجھا جانا چاہیے۔ یہ ایک طاقتور GPU پر غیر موزوں گرافکس چلانے جیسا ہے۔ اضافی صلاحیت تھوڑی دیر کے لیے ناکارہیوں کو چھپا سکتی ہے، لیکن یہ اصلاح کے بنیادی مسئلے کو ختم نہیں کرتی۔
تاہم، یہاں تک کہ بہت بڑی سیاق و سباق والی ونڈوز کی بھی سخت حدود ہیں۔ اگر آپ ٹوکنز شامل کرتے رہتے ہیں، تو آپ بالآخر اس سے تجاوز کر سکتے ہیں۔ یہ مسئلہ صارفین کے ہارڈویئر پر اور بھی زیادہ واضح ہے، جہاں محدود میموری اور کمپیوٹ کا مطلب عام طور پر چھوٹی دستیاب سیاق و سباق والی ونڈوز ہے۔
12GB VRAM والے صارف لیپ ٹاپ پر مقامی ماڈلز کے ساتھ تجربہ کرتے ہوئے مجھے اس مسئلے کا سامنا کرنا پڑا۔ RAG نے چھوٹے ٹیسٹوں میں اچھا کام کیا، لیکن جیسے ہی دستاویز میں اضافہ ہوا، سسٹم نے مفید ٹکڑوں کی تلاش کی اور پھر بھی اچھا جواب نہیں دیا۔
مسئلہ ہمیشہ تلاش کا نہیں تھا۔ کبھی کبھی صحیح حصہ مل جاتا تھا، لیکن حتمی اشارے میں اس کی کوئی گنجائش نہیں تھی۔
یہ مضمون آپ کو اس مسئلے کے حل کے بارے میں بتاتا ہے۔
دستاویز کا خلاصہ → ٹکڑے کا خلاصہ → خام حصہ → حتمی جواب
پیٹرن تین اصولوں پر مبنی ہے:
-
تلاش کے لیے خلاصہ استعمال کریں۔
-
براہ کرم اپنے جواب کے لیے خام ٹکڑوں کا استعمال کریں۔
-
آپ کے ماڈل میں کیا آتا ہے اس کا تعین کرنے کے لیے سیاق و سباق کے بجٹ کا استعمال کریں۔
ڈیمو کو سادہ اور آسان رکھنے کے لیے، ساتھی ریپوزٹری میں Python اور TypeScript کی چھوٹی مثالوں کے ساتھ ساتھ ایک آسان ان-میموری سرچ اسٹور اور ایک آسان جواب ایکسٹریکٹر استعمال کیا گیا ہے۔ یہ آپ کو انحصار کے مکمل اسٹیک کو انسٹال کرنے، ماڈلز کو ڈاؤن لوڈ کرنے، ایک بڑے لینگویج ماڈل (LLM) سرور کو چلانے، ایمبیڈنگ سروس ترتیب دینے، یا ویکٹر ڈیٹا بیس کو ترتیب دینے کے بغیر مضمون کے بنیادی خیالات کو عملی شکل میں دیکھنے کی اجازت دیتا ہے۔
سیٹ اپ کا یہ عمل آسانی سے ایک وقف شدہ مضمون ہو سکتا ہے، اس لیے اس ٹیوٹوریل میں ہم چلانے کے قابل مثالوں کو چھوٹے سیاق و سباق کے RAG پیٹرنز پر مرکوز رکھیں گے جیسے تلاش کے خلاصے، جوابات کے لیے خام حصے، اور دکھائی دینے والے سیاق و سباق کے بجٹ۔
ریپوزٹریز پروڈکشن گریڈ ماڈل کے معیار کے بجائے ڈیٹا فلو اور ڈیبگنگ پیٹرن کا مظاہرہ کرتی ہیں۔ پروڈکشن میں، آپ سادہ سمریزر، ان میموری میں مماثلت کی تلاش، اور ٹوکن تخمینہ لگانے والے کو اپنے ماڈل، ایمبیڈنگ اسٹور، ری رینکر، اور ٹوکنائزر سے بدل سکتے ہیں۔
انڈیکس
کیا لاگو کرنا ہے
اس ٹیوٹوریل میں، ہم ایک چھوٹی ٹریننگ RAG پائپ لائن نافذ کرتے ہیں جو تین سطحوں پر دستاویزات پر کارروائی کرکے سیاق و سباق کی رکاوٹوں کا انتظام کرتی ہے۔
-
دستاویز کا ریکارڈ ممکنہ دستاویزات کو منتخب کرنے کے لیے استعمال ہونے والا ایک مختصر خلاصہ شامل ہے۔
-
حصہ ریکارڈ دستاویز کے اندر ممکنہ حصوں کو منتخب کرنے کے لیے ایک مختصر خلاصہ اور خام ماخذ کا متن شامل ہے۔
-
خام سیاق و سباق ایک مقررہ ٹوکن بجٹ میں شامل منتخب خام ٹکڑوں پر مشتمل ہے۔
اہم فرق یہ ہے کہ خلاصہ صرف یہ فیصلہ کرنے کے لیے استعمال ہوتا ہے کہ کہاں دیکھنا ہے۔ اسے حتمی ثبوت کے طور پر استعمال کرنے کا ارادہ نہیں ہے۔
خلاصہ ضروری ہے کیونکہ بہت سے نقصانات ہیں۔ آپ معلومات کو کم کر سکتے ہیں اور صارف کے سوال کا جواب دینے کے لیے درکار تفصیلات چھوڑ سکتے ہیں۔ اس کے برعکس، کچے ٹکڑے بڑے ہوتے ہیں لیکن اصل الفاظ کو برقرار رکھتے ہیں۔
ڈیمو ہر سوال کے لیے ایک ٹریس پرنٹ کرتا ہے۔
-
دستاویز کے خلاصے کے مناظر
-
حصہ کے خلاصے کے مناظر
-
خام ٹکڑوں پر مشتمل ہے۔
-
کچے ٹکڑوں کو چھوڑ دیا گیا۔
-
جواب
وہ ٹریس ڈیبگنگ انٹرفیس ہے۔ اس سے پتہ چلتا ہے کہ آیا تلاش ناکام ہوئی یا سیاق و سباق کا بجٹ بہت کم تھا تاکہ مفید شواہد کو چھوڑ دیا جا سکے۔
شرطیں
پیروی کرنے کے لیے، آپ کو درج ذیل میں سے ایک کی ضرورت ہوگی:
یا:
اگر آپ پہلے ہی سے واقف ہیں تو آپ اس مضمون سے زیادہ سے زیادہ فائدہ اٹھائیں گے:
-
بنیادی Python یا TypeScript نحو
-
ٹرمینل میں کمانڈ چلائیں۔
-
چھوٹی ڈیٹا کلاسز، فنکشنز، فہرستیں، یا نقشے پڑھیں
-
ایل ایل ایم پرامپٹس اور سیاق و سباق کی ونڈوز کے بارے میں عمومی خیالات
-
بنیادی RAG آئیڈیا: متعلقہ سورس ٹیکسٹ تلاش کریں، اسے پرامپٹ میں شامل کریں، اور اس تناظر میں جواب دیں۔
ویکٹر ڈیٹا بیس، بلٹ ان APIs، LangChain، LlamaIndex، یا مقامی LLM ترتیب دینے کے ساتھ پہلے سے کسی تجربے کی ضرورت نہیں ہے۔
اس مثال کے لیے LLM فراہم کنندہ، ایمبیڈنگ API، یا ویکٹر ڈیٹا بیس کی ضرورت نہیں ہے۔ وہ استعمال کرتے ہیں:
-
LLM خلاصہ کے متبادل ذرائع کے طور پر جملے کا اخراج۔
-
تلاش کے اندراج کے متبادل ذرائع کے طور پر Wordbag cosine مماثلت۔
-
ٹوکنائزرز کے متبادل کے طور پر کریکٹر پر مبنی ٹوکن تخمینہ مقرر کیا گیا ہے۔
میں نے اس عمل کو وقت بچانے اور مثالوں کو آزمانا آسان بنانے کے لیے منتخب کیا ہے، جبکہ اصل مقصد کو برقرار رکھا ہے۔ یہ تلاش کا راستہ بھی دکھاتا ہے۔
ڈیفالٹ RAG چھوٹی سیاق و سباق والی ونڈوز کے ساتھ کیوں ناکام ہو سکتا ہے۔
ایک بنیادی RAG لوپ عام طور پر اس طرح لگتا ہے:
دستاویز لوڈ کریں → دستاویز کو ٹکڑوں میں تقسیم کریں → ٹکڑوں کو شامل کریں → پیرنٹ ٹکڑوں کو بازیافت کریں → بازیافت شدہ ٹکڑوں کو پرامپٹ میں ڈالیں → جواب کے لیے ماڈل سے پوچھیں۔
یہ ایک اچھا نقطہ آغاز ہے۔ تاہم، دو مختلف مسائل ایک فقرے میں پوشیدہ ہیں "والدین کے حصے کو بازیافت کریں”۔
سب سے پہلے، آپ کو متعلقہ معلومات تلاش کرنے کی ضرورت ہے۔ یہ تلاش کا معیار ہے۔
دوسرا، آپ کو اس بات کا تعین کرنے کی ضرورت ہے کہ بازیافت شدہ مواد میں سے کون سا حقیقت میں آپ کے حتمی اشارے پر فٹ بیٹھتا ہے۔ یہ سیاق و سباق کا بجٹ ہے۔
بڑے ہوسٹنگ ماڈلز میں، ہو سکتا ہے کہ آپ کو اس مسئلے کا فوراً نوٹس نہ ہو۔ آپ اسے مقامی ماڈل یا چھوٹی سیاق و سباق والی ونڈو میں جلدی سے دیکھ سکتے ہیں۔
ناکامی کے طریقوں میں شامل ہیں:
-
بازیافت کرنے والوں کو مفید نگٹس ملتے ہیں۔
-
پرامپٹ بلڈر اسے شامل کرنے کی کوشش کرے گا۔
-
سیاق و سباق کا بجٹ بھرا ہوا ہے۔
-
کچھ ٹکڑوں کو چھوڑ دیا گیا تھا۔
-
فائنل ماڈل کبھی بھی چھوڑے ہوئے ٹکڑوں کو نہیں دیکھے گا۔
-
جواب نامکمل ہیں یا کہیں کہ "مجھے نہیں معلوم۔”
تلاشوں کا معائنہ کرنا اور یہ دیکھنا کہ آیا متعلقہ ٹکڑوں کو واپس کر دیا گیا ہے، الجھن کا باعث ہو سکتا ہے۔ تاہم، ایک تلاش جو حصہ لوٹاتی ہے وہ ماڈل کی طرح نہیں ہے جو اس حصے کو دیکھتا ہے۔
محدود ہارڈ ویئر پر RAG سسٹم تیار کرتے وقت یہ فرق اہم ہو جاتا ہے۔
خلاصہ روٹنگ کیسے کام کرتی ہے۔
تمام خام ٹکڑوں کو براہ راست بازیافت کرنے کے بجائے، آپ خلاصوں سے ایک روٹنگ پرت بنا سکتے ہیں۔
انڈیکس کرتے وقت:
-
دستاویز لوڈ کریں۔
-
ہر دستاویز کو ٹکڑوں میں تقسیم کریں۔
-
ہر ٹکڑے کا خلاصہ کریں۔
-
ٹکڑوں کے خلاصے کو ایک دستاویز کے خلاصے میں کم کریں۔
-
دستاویز کے خلاصے کو دستاویز کے خلاصے کے ذخیرے میں محفوظ کریں۔
-
حصہ کے خلاصے فی دستاویز کے ٹکڑے سمری اسٹور میں اسٹور کریں۔
-
خام ٹکڑوں کو تلاش کی میز میں رکھیں۔
انڈیکسنگ پائپ لائن مندرجہ ذیل ہے:
سوال کے وقت:
-
ممکنہ دستاویزات کو منتخب کرنے کے لیے دستاویز کا خلاصہ تلاش کریں۔
-
صرف دستاویز میں حصہ کے خلاصے تلاش کریں۔
-
chunk summary hits کو واپس raw chunk IDs میں تبدیل کریں۔
-
اختیاری طور پر ملحقہ ٹکڑوں کو شامل کرتا ہے۔
-
حتمی سیاق و سباق کے بجٹ میں خام ٹکڑوں کو ڈالیں۔
-
یہ صرف کچے ٹکڑوں میں جواب دیتا ہے۔
استفسار کا راستہ روٹنگ کے لیے ڈائجسٹ کا استعمال کرتا ہے اور پھر جواب دینے سے پہلے خام حصوں پر واپس چلا جاتا ہے۔

یہ دو مفید خصوصیات فراہم کرتا ہے:
یہ ڈیبگ کرنے کی جگہ بھی فراہم کرتا ہے۔ اگر سسٹم کمزور جواب دیتا ہے تو ٹریس کا معائنہ کریں۔ کیا آپ دستاویز کے درست خلاصے سے میل کھاتے ہیں؟ کیا آپ صحیح ٹکڑوں کے خلاصے سے میل کھاتے ہیں؟ کیا خام حصہ حتمی سیاق و سباق کے لیے مناسب تھا؟ کیا انہوں نے بجٹ کی وجہ سے اسے چھوڑ دیا؟
دستاویزات اور ٹکڑوں کی نمائندگی کیسے کریں۔
ڈیٹا کا ڈھانچہ جان بوجھ کر چھوٹا ہے کیونکہ اس میں اس پائپ لائن کے لیے صرف ضروری معلومات شامل ہیں۔ ایک حقیقی نظام شاید مزید میٹا ڈیٹا کا اضافہ کرے گا۔
ازگر کے ورژن یہ ہیں:
from dataclasses import dataclass
@dataclass(frozen=True)
class SearchDocument:
page_content: str
metadata: dict[str, str | int]
@dataclass(frozen=True)
class DocumentRecord:
doc_id: str
source: str
text: str
summary: str
@dataclass(frozen=True)
class ChunkRecord:
chunk_id: str
doc_id: str
source: str
index: int
text: str
summary: str
previous_chunk_id: str | None
next_chunk_id: str | None
کہ DocumentRecord پوری دستاویز اور خلاصہ محفوظ کریں۔ کہ ChunkRecord یہ خام حصہ، اس کا خلاصہ، اور پچھلے اور اگلے حصوں کے لنکس کو محفوظ کرتا ہے۔
یہ پڑوسی روابط مفید ہیں کیونکہ ٹکڑوں کی حدود مصنوعی ہیں۔ اگر تلاش میں حصہ 4 ملتا ہے، تو جواب حصہ 3 سے شروع ہو سکتا ہے یا حصہ 5 کے ساتھ جاری رہ سکتا ہے۔
ایک انڈیکس تلاش کرنے کے قابل اسٹوریج اور تلاش کا نقشہ دونوں کو برقرار رکھتا ہے۔
@dataclass(frozen=True)
class HierarchicalIndex:
documents_by_id: dict[str, DocumentRecord]
chunks_by_id: dict[str, ChunkRecord]
chunks_by_doc_id: dict[str, list[ChunkRecord]]
document_summary_store: SimpleVectorStore
chunk_summary_stores_by_doc_id: dict[str, SimpleVectorStore]
سب سے اہم سوالات یہ ہیں:
chunk = index.chunks_by_id[chunk_hit.metadata["chunk_id"]]
وہ لائن بازیافت شدہ سمری ہٹ کو حتمی جواب میں استعمال ہونے والے خام سورس ٹیکسٹ میں تبدیل کرتی ہے۔
کسی دستاویز کو کچے ٹکڑوں میں کیسے تقسیم کیا جائے۔
ڈیمو مارک ڈاون فائل کو پیراگراف میں تقسیم کرتا ہے اور پیراگراف کو ایک ساتھ گروپ کرتا ہے جب تک کہ ہدف کے کریکٹر سائز تک نہ پہنچ جائے۔
CHUNK_SIZE = 420
def split_text(text: str) -> list[str]:
chunks = []
current_paragraphs = []
current_size = 0
for paragraph in re.split(r"ns*n", text.strip()):
paragraph = paragraph.strip()
if not paragraph:
continue
if current_paragraphs and current_size + len(paragraph) > CHUNK_SIZE:
chunks.append("nn".join(current_paragraphs))
current_paragraphs = []
current_size = 0
current_paragraphs.append(paragraph)
current_size += len(paragraph)
if current_paragraphs:
chunks.append("nn".join(current_paragraphs))
return chunks
نوٹ کرنے کے لئے ایک اہم بات یہ ہے کہ یہ تمام استعمال کے معاملات کے لئے بہترین سپلٹر نہیں ہے. اسے جان بوجھ کر پڑھا جا سکتا ہے۔
پروڈکشن سسٹم میں، آپ ٹوکنائزر سے آگاہ سپلٹرز، مارک ڈاؤن سے آگاہ سیکشنز، سیمنٹک چنکنگ، یا پیرنٹ چائلڈ چنکنگ استعمال کر سکتے ہیں۔ لیکن اس سے کوئی فرق نہیں پڑتا ہے کہ آپ کون سا آپشن منتخب کرتے ہیں، خیال وہی رہتا ہے۔ خام ٹکڑوں کو حتمی ثبوت کے طور پر رکھیں۔
ٹکڑوں اور دستاویزات کا خلاصہ کیسے کریں۔
ڈیمو کو چلانے کے لیے آسان بنانے کے لیے، یہ مضمون LLM خلاصہ کے بجائے جملہ نکالنے کا استعمال کرتا ہے۔ RAG کی اہم اصطلاحات پر مشتمل جملے اسکور کریں اور سرفہرست جملوں کو برقرار رکھیں۔
def summarize_text(text: str, max_sentences: int = 2) -> str:
sentences = [
sentence.strip()
for sentence in re.split(r"(?<=[.!?])s+", " ".join(text.split()))
if sentence.strip()
]
if len(sentences) <= max_sentences:
return " ".join(sentences)
scored_sentences = []
for position, sentence in enumerate(sentences):
sentence_words = words(sentence)
term_score = sum(3 for word in sentence_words if word in IMPORTANT_TERMS)
first_sentence_bonus = 1 if position == 0 else 0
scored_sentences.append((term_score + first_sentence_bonus, position, sentence))
selected = sorted(scored_sentences, key=lambda item: (-item[0],item[1]))[:max_sentences]
selected.sort(key=lambda item: item[1])
return " ".join(sentence for _score, _position, sentence in selected)
ایک حقیقی نظام میں، یہ فنکشن ایک چھوٹے مقامی ماڈل یا ہوسٹڈ ماڈل کو کہتے ہیں۔ فوری ہدایات مندرجہ ذیل ہیں:
-
بازیافت کے لیے ان ٹکڑوں کا خلاصہ کریں۔
-
ناموں، رکاوٹوں، فیصلوں، غلطیوں، نمبروں، اور ڈومین سے متعلق مخصوص اصطلاحات کو محفوظ رکھتا ہے۔
-
صارف کے سوالات کا جواب نہ دیں۔
ٹکڑوں کے خلاصے کو خام ٹکڑوں کی جگہ نہیں لینا چاہیے۔ ہمارا واحد مقصد تلاش کو آسان بنانا ہے۔
خلاصہ کو بار بار کم کرنے کا طریقہ
ایک عام غلطی تمام ٹکڑوں کے خلاصے کو ایک پرامپٹ میں ڈال کر دستاویز کا خلاصہ بنانا ہے۔
combined = "nn".join(chunk_summaries)
document_summary = summarize(combined)
یہ چند ٹکڑوں کے لیے کام کرتا ہے، لیکن سینکڑوں ٹکڑوں کے لیے نہیں۔ ہم نے ابھی سیاق و سباق کی ونڈو کے مسئلے کو رسپانس ٹائم سے انڈیکسنگ ٹائم میں منتقل کر دیا ہے۔
بیچوں میں خلاصے کو کم کرنا ایک بہتر طریقہ ہوگا۔
حصہ کا خلاصہ → بجٹ بیچ → بیچ کا خلاصہ → اعلیٰ سطح کا خلاصہ → حتمی دستاویز کا خلاصہ۔
کمی کا عمل درج ذیل ہے:

بجٹ پیکنگ کی خصوصیات میں شامل ہیں:
def pack_summaries_by_token_budget(
summaries: list[str],
token_budget: int,
) -> list[list[str]]:
batches = []
current_batch = []
current_tokens = 0
for summary in summaries:
summary_tokens = approximate_tokens(summary)
if current_batch and current_tokens + summary_tokens > token_budget:
batches.append(current_batch)
current_batch = []
current_tokens = 0
current_batch.append(summary)
current_tokens += summary_tokens
if current_batch:
batches.append(current_batch)
return batches
اور تکراری کمی کا لوپ یہ ہے:
def recursively_reduce_summaries(summaries: list[str]) -> str:
if not summaries:
return "No summary available."
current_summaries = summaries
level = 1
while len(current_summaries) > 1:
batches = pack_summaries_by_token_budget(
current_summaries,
SUMMARY_REDUCTION_INPUT_TOKEN_BUDGET,
)
if len(batches) == len(current_summaries):
batches = force_summary_reduction_progress(current_summaries)
print(
f"Reducing {len(current_summaries)} summaries into "
f"{len(batches)} batch summaries at level {level}"
)
current_summaries = [reduce_summary_batch(batch) for batch in batches]
level += 1
return summarize_text(current_summaries[0], max_sentences=3)
متبادل اہم ہے۔
if len(batches) == len(current_summaries):
batches = force_summary_reduction_progress(current_summaries)
اگر ہر خلاصہ دوسرے خلاصوں میں فٹ ہونے کے لیے بہت بڑا ہے، تو سادہ بجٹ کمپیکشن آگے نہیں بڑھے گا، اس لیے سمریوں کو جوڑنے سے کمپیکشن جاری رہے گا۔
درجہ بندی کے اشاریہ کو کیسے نافذ کیا جائے۔
اگر آپ کے پاس دستاویز کا ریکارڈ اور حصہ کا ریکارڈ ہے، تو آپ دو قسم کے اسٹور بناتے ہیں۔
-
دستاویز کے خلاصے کے لیے ایک ذخیرہ
-
دستاویز کے لحاظ سے گروپ کردہ ٹکڑوں کے خلاصوں کے لیے ایک ذخیرہ
دستاویز کے خلاصے کے ذخیرے یہ ہیں:
document_summary_store = SimpleVectorStore(
[
SearchDocument(
page_content=record.summary,
metadata={"doc_id": record.doc_id, "source": record.source},
)
for record in document_records
]
)
پھر ہم ٹکڑوں کو دستاویز کے مطابق گروپ کرتے ہیں۔
chunks_by_doc_id: dict[str, list[ChunkRecord]] = {}
for chunk in chunk_records:
chunks_by_doc_id.setdefault(chunk.doc_id, []).append(chunk)
اس کے بعد ہم فی دستاویز ایک حصہ سمری اسٹور بناتے ہیں۔
chunk_summary_stores_by_doc_id = {}
for doc_id, doc_chunks in chunks_by_doc_id.items():
chunk_summary_stores_by_doc_id[doc_id] = SimpleVectorStore(
[
SearchDocument(
page_content=chunk.summary,
metadata={
"chunk_id": chunk.chunk_id,
"doc_id": chunk.doc_id,
"source": chunk.source,
"chunk_index": chunk.index,
},
)
for chunk in doc_chunks
]
)
یہی چیز تلاش کو درجہ بندی بناتی ہے۔ پہلی تلاش ایک دستاویز کو منتخب کرتی ہے، اور دوسری تلاش صرف منتخب دستاویز کے اندر تلاش کرتی ہے۔
خلاصہ کے ذریعے تلاش کرنے کا طریقہ
سوال پوچھتے وقت، پہلے دستاویز کا خلاصہ تلاش کریں۔
document_hits = index.document_summary_store.similarity_search(
question,
k=min(DOC_RETRIEVAL_K, len(index.documents_by_id)),
)
اس تلاش میں k کنٹرول کرتا ہے کہ آپ کے اسٹور کے کتنے سرفہرست نتائج واپس آنے چاہئیں۔
اس کے بعد یہ ہر منتخب دستاویز کے اندر حصہ کے خلاصے کو بازیافت کرتا ہے۔
chunk_hits = []
seen_chunk_ids = set()
for document_hit in document_hits:
doc_id = str(document_hit.metadata["doc_id"])
chunk_store = index.chunk_summary_stores_by_doc_id[doc_id]
doc_chunk_count = len(index.chunks_by_doc_id[doc_id])
per_doc_hits = chunk_store.similarity_search(
question,
k=min(CHUNK_RETRIEVAL_K_PER_DOC, doc_chunk_count),
)
for chunk_hit in per_doc_hits:
chunk_id = str(chunk_hit.metadata["chunk_id"])
if chunk_id in seen_chunk_ids:
continue
chunk_hits.append(chunk_hit)
seen_chunk_ids.add(chunk_id)
دیکھیں کہ یہاں کیا تلاش کیا جا رہا ہے۔ یہ ایک خلاصہ ہے۔
خلاصہ خیالات میں شامل ہیں: chunk_idتاہم، خام ٹکڑے اصل الفاظ اور تفصیلات کو برقرار رکھتے ہیں جو شاید سمری سے ہٹا دی گئی ہوں، اس لیے حتمی جواب اب بھی اس ID سے وابستہ خام حصہ کا استعمال کرے گا۔
بجٹ کے خام سیاق و سباق کو کیسے نافذ کیا جائے۔
ٹکڑوں کا خلاصہ بازیافت کرنے کے بعد، ہم ہٹ کو واپس خام ٹکڑوں میں تبدیل کرتے ہیں۔
ڈیمو میں، ہم پڑوسی ٹکڑوں کو بھی شامل کرتے ہیں۔
def candidate_raw_chunks(
chunk_hits: list[SearchDocument],
index: HierarchicalIndex,
) -> list[ChunkRecord]:
candidates = []
seen_chunk_ids = set()
for chunk_hit in chunk_hits:
chunk = index.chunks_by_id[str(chunk_hit.metadata["chunk_id"])]
related_chunk_ids = [chunk.chunk_id]
if EXPAND_NEIGHBOR_CHUNKS:
related_chunk_ids.extend([chunk.next_chunk_id, chunk.previous_chunk_id])
for chunk_id in related_chunk_ids:
if chunk_id is None or chunk_id in seen_chunk_ids:
continue
candidates.append(index.chunks_by_id[chunk_id])
seen_chunk_ids.add(chunk_id)
return candidates
پھر حتمی سیاق و سباق کے بجٹ کا اطلاق کریں۔
def build_raw_context(
chunk_hits: list[SearchDocument],
index: HierarchicalIndex,
) -> tuple[str, list[tuple[ChunkRecord, int]], list[tuple[ChunkRecord, int]]]:
included_chunks = []
skipped_chunks = []
used_tokens = 0
for chunk in candidate_raw_chunks(chunk_hits, index):
raw_context_part = format_raw_chunk(chunk)
raw_context_tokens = approximate_tokens(raw_context_part)
if used_tokens + raw_context_tokens > RAW_CONTEXT_TOKEN_BUDGET:
skipped_chunks.append((chunk, raw_context_tokens))
continue
included_chunks.append((chunk, raw_context_tokens))
used_tokens += raw_context_tokens
included_chunks.sort(key=lambda item: (item[0].source, item[0].index))
context = "nn---nn".join(
format_raw_chunk(chunk)
for chunk, _tokens in included_chunks
)
return context, included_chunks, skipped_chunks
اس مرحلے پر آپ کو بہت سے RAG کیڑے نظر آئیں گے۔
اگر سسٹم مفید ٹکڑوں کو بازیافت کرتا ہے لیکن ان کو چھوڑ دیتا ہے کیونکہ اشارے بھرے ہوئے ہیں، مسئلہ دستاویز کی بازیافت کا نہیں ہے۔ سیاق و سباق کا بجٹ۔
ڈیمو کو کیسے چلائیں۔
ساتھی ذخیرہ میں ایک ہی مثال کے دو ورژن ہیں۔
اپنے Python ورژن کو ساتھی ریپوزٹری روٹ سے چلائیں۔
cd python
python3 -m small_context_rag_solution --question "Why can RAG fail when the context budget is too small?"
TypeScript ورژن چلائیں۔
cd typescript
npm install
npm run demo
آپ سوال کے جھنڈے کو چھوڑ کر دونوں مثالوں کو انٹرایکٹو طریقے سے بھی چلا سکتے ہیں۔ زمرہ q، quitیا exit انٹرایکٹو موڈ سے باہر نکلیں۔
ازگر:
python3 -m small_context_rag_solution
ٹائپ اسکرپٹ:
npm run build
npm start
پہلے سے طے شدہ خام سیاق و سباق کا بجٹ جان بوجھ کر چھوٹا ہے۔ RAW_CONTEXT_TOKEN_BUDGET=250. یہ کسی بھی چھوڑے ہوئے ٹکڑوں کو ظاہر کرے گا۔
250 بمقابلہ 1200 ٹوکن ٹیسٹ کی تشریح کیسے کریں۔
دونوں بجٹ کے ساتھ ایک جیسے سوالات پوچھیں۔
ازگر:
RAW_CONTEXT_TOKEN_BUDGET=250 python3 -m small_context_rag_solution --question "Why can RAG fail when the context budget is too small?"
RAW_CONTEXT_TOKEN_BUDGET=1200 python3 -m small_context_rag_solution --question "Why can RAG fail when the context budget is too small?"
ٹائپ اسکرپٹ:
RAW_CONTEXT_TOKEN_BUDGET=250 npm run demo
RAW_CONTEXT_TOKEN_BUDGET=1200 npm run demo
250 ٹوکنز کے بجٹ کے لیے، خام سیاق و سباق بنانے والے میں صرف دو ٹکڑے ہوتے ہیں۔
دیگر 5 منتخب ٹکڑوں کو چھوڑ دیں۔
-
doc-003-large_rag_notes-chunk-003(تقریباً 117 ٹوکن) -
doc-003-large_rag_notes-chunk-001(تقریباً 116 ٹوکن) -
doc-003-large_rag_notes-chunk-002(تقریباً 120 ٹوکن) -
doc-001-context_window_notes-chunk-001(تقریباً 131 ٹوکن) -
doc-001-context_window_notes-chunk-002(تقریباً 73 ٹوکن)
1200 ٹوکنز کے بجٹ کے ساتھ، آپ جو بھی خام حصہ منتخب کرتے ہیں وہ موزوں ہوگا۔
-
doc-001-context_window_notes-chunk-001(تقریباً 131 ٹوکن) -
doc-001-context_window_notes-chunk-002(تقریباً 73 ٹوکن) -
doc-003-large_rag_notes-chunk-001(تقریباً 116 ٹوکن) -
doc-003-large_rag_notes-chunk-002(تقریباً 120 ٹوکن) -
doc-003-large_rag_notes-chunk-003(تقریباً 117 ٹوکن) -
doc-003-large_rag_notes-chunk-004(تقریباً 110 ٹوکن) -
doc-003-large_rag_notes-chunk-005(تقریباً 121 ٹوکن)
منتخب کچے ٹکڑوں کو نہیں چھوڑتا۔
یہ خاکہ دونوں سیاق و سباق کے بجٹ کے درمیان فرق کو ظاہر کرتا ہے۔

1,200 ٹوکن کی حد ایک حقیقی سسٹم کے لیے ابھی بھی ایک بہت چھوٹی سیاق و سباق کی ونڈو ہے، لیکن یہ 250 سے بہت بڑی ہے۔ اس مثال میں، آپ واضح طور پر دیکھ سکتے ہیں کہ جب پرامپٹ بلڈر میں زیادہ جگہ ہوتی ہے تو ایک ہی تلاش کا راستہ مختلف طریقے سے برتاؤ کرتا ہے۔
یہی وجہ ہے کہ میں شامل اور چھوڑے گئے دونوں حصوں کو پرنٹ کرنا پسند کرتا ہوں۔ ڈیبگنگ کے عملی سوالات کے جوابات دینے میں آپ کی مدد کرتا ہے۔
کیا تلاش سے ثبوت چھوٹ گئے، یا فوری اسمبلی نے اسے ضائع کر دیا؟
ڈیمو جواب کے آسان مراحل کا استعمال کرتا ہے، لہذا حتمی جواب کے صحیح الفاظ پر زیادہ توجہ نہ دیں۔ اصل LLM پرامپٹ میں درج ذیل ہدایات شامل ہیں:
-
صرف نیچے دیے گئے کچے نگٹس سے جواب دیں۔
-
اگر ایک خام حصہ متعدد متعلقہ وجوہات پر مشتمل ہے، تو ان سب کو شامل کریں۔
-
کثیر الجہتی جوابات کے لیے گولیوں والی جامع فہرستوں کو ترجیح دیں۔
-
اگر کچے نگٹ میں کافی ثبوت نہیں ہیں تو کہہ دیں۔
زیادہ سیاق و سباق خود بخود آپ کے جواب کو بہتر نہیں بناتا ہے۔ پرامپٹ کو اب بھی ماڈل کو یہ بتانے کی ضرورت ہے کہ اضافی ثبوت کو کیسے استعمال کیا جائے۔
اس کا موجودہ RAG ٹیکنالوجی سے کیا تعلق ہے؟
یہ نمونہ تحقیق کے لیے بالکل نیا نہیں ہے۔ یہ کئی نظریات کا عملی مجموعہ ہے جو RAG ماحولیاتی نظام میں پہلے سے موجود ہیں۔
LangChain چھوٹے ذیلی حصوں کو بازیافت کرنے اور پھر بڑے پیرنٹ دستاویز کو واپس کرنے کے لیے ParentDocumentRetriever سے متعلقہ تکنیکوں کا استعمال کرتا ہے۔
اس کا تعلق LlamaIndex دستاویز کے خلاصہ انڈیکس سے بھی ہے، جو متعلقہ دستاویزات کو منتخب کرنے کے لیے دستاویز کے خلاصے کا استعمال کرتا ہے اور پھر ان دستاویزات کے لیے نوڈس کو بازیافت کرتا ہے۔
اور یہ تصوراتی طور پر RAPTOR سے متصل ہے، ایک تلاش کا طریقہ جو متن کو بار بار کلسٹر کرکے اور خلاصہ کرکے درخت بناتا ہے۔
مضمون کا یہ ورژن جان بوجھ کر آسان ہے۔
-
کوئی جھرمٹ نہیں ہے۔
-
کوئی فریم ورک کی ضروریات نہیں ہیں.
-
ڈیمو کو ویکٹر ڈیٹا بیس کی ضرورت نہیں ہے۔
-
اس میں کوئی دلیل نہیں ہے کہ حتمی جواب کے لیے صرف خلاصہ ہی کافی ہے۔
مقصد شفاف پیٹرن پیش کرنا ہے جو اندرونی طور پر سمجھنا آسان ہے اور بھاری فریم ورک پر انحصار کیے بغیر آپ کی ضروریات کے مطابق ڈھالنا ہے۔ میرے مقامی ماڈل کے کام کا ایک مفید حصہ علیحدگی تھا۔
اس پیٹرن کو کب استعمال کرنا ہے۔
یہ نمونہ درج ذیل صورتوں میں مفید ہے:
-
محدود VRAM کے ساتھ مقامی ماڈلز چلائیں۔
-
سیاق و سباق کی کھڑکی چھوٹی یا مہنگی ہے۔
-
بہت ساری دستاویزات ہیں، لیکن صرف چند ہی ہر سوال سے متعلق ہیں۔
-
میں قابل معائنہ سرچ ٹریکنگ چاہتا ہوں۔
-
تلاش کے لیے مجھے خلاصہ چاہیے، لیکن جوابات کے لیے مجھے خام متن چاہیے۔
-
آپ کو اشاریہ سازی اور جواب دینے کے دوران لامحدود اشارے سے گریز کرنا چاہیے۔
مندرجہ ذیل صورتوں میں یہ کم مفید ہے:
-
اصل دستاویز پہلے ہی چھوٹی ہے۔
-
پورا کارپس پرامپٹ میں آرام سے فٹ ہوجاتا ہے۔
-
ایک درست مطلوبہ الفاظ کی تلاش کافی ہے۔
-
متعدد دستاویز کی روٹنگ کی ضرورت نہیں ہے۔
-
آپ خود بہت سے خام حصوں کو تلاش کرنے اور دوبارہ درجہ بندی کرنے کے متحمل ہوسکتے ہیں۔
تجارت بھی ہوتی ہے۔ یہ پیٹرن انڈیکسنگ آپریشنز کو جوڑتا ہے۔
یہ عام طور پر دستاویزات کے معاونین، تحقیقی ٹولز، اندرونی علمی بنیادوں، یا مقامی ماڈل پروجیکٹس کے لیے موزوں ہے جہاں اشاریہ سازی ایک بار ہوتی ہے اور سوالات متعدد بار ہوتے ہیں۔
نتیجہ
RAG کو صرف "ایک حصہ بازیافت کریں اور اسے پرامپٹ میں چسپاں کریں" کے طور پر مت سمجھیں۔
چھوٹے سیاق و سباق کے نظام کے لیے، تلاش کے لیے روٹنگ اور بجٹ کی ضرورت ہوتی ہے۔ جیسے جیسے پروجیکٹ بڑے پیمانے پر بڑھتے ہیں، یہاں تک کہ بہت بڑے سیاق و سباق والی ونڈوز والے جدید ہارڈ ویئر پر بھی، سسٹم کا اچھا ڈیزائن بنیادی بن جاتا ہے۔
پیٹرن تین عملی اصولوں پر ابلتا ہے:
-
خلاصے آپ کو متعلقہ ماخذ مواد تلاش کرنے میں مدد کرتے ہیں۔
-
کچے نگٹس جواب فراہم کرتے ہیں۔
-
سیاق و سباق کا بجٹ طے کرتا ہے کہ آپ کے ماڈل میں کیا آتا ہے۔
اس حل نے محدود ہارڈ ویئر پر زیادہ قابل اعتماد مقامی RAG سسٹم تیار کرنے میں مدد کی۔ اس نے غلطیوں کو ڈیبگ کرنا بھی آسان بنا دیا کیونکہ میں بالکل دیکھ سکتا تھا کہ کون سے خلاصے مماثل ہیں، کون سے خام ٹکڑوں کو منتخب کیا گیا ہے، اور کون سے خام ٹکڑوں کو چھوڑ دیا گیا ہے۔
چاہے آپ اپنا RAG مقامی طور پر چلا رہے ہوں یا ہوسٹڈ ماڈل استعمال کریں، اگر آپ چھوٹے ماڈلز، محدود سیاق و سباق کی ونڈوز، یا سخت پرامپٹ بجٹ کے ساتھ کام کر رہے ہیں، تو بڑی سیاق و سباق والی ونڈوز پر پیسہ خرچ کرنے سے پہلے اس پیٹرن کو آزمانے کے قابل ہے۔