زیادہ تر RAG ڈیموز لیپ ٹاپ پر شاندار نظر آتے ہیں۔ ایک اسکرپٹ کو بیس پاخ (twenty-page) PDF دیں، ایک سوال پوچھیں، اور اسے صحیح پیراگراف کا حوالہ دیتے ہوئے دیکھیں۔ لیکن اسی پائپ لائن کو پروڈکشن میں منتقل کرنا وہ مقام ہے جہاں خواب ٹوٹ جاتے ہیں۔ قانونی دستاویزات جملے کی سطح پر آدھی رہ جاتی ہیں۔ بھاری بھرکم API ریفرنسز اہم معلومات کو غیر ضروری تفصیلات (boilerplate noise) میں ڈبو دیتے ہیں۔ لیٹنسی (latency) بڑھ جاتی ہے۔ صارفین انتظار کرتے ہیں، بے چین ہوتے ہیں، اور چلے جاتے ہیں۔ ہم اس دیوار سے بری طرح ٹکرائے۔ چنانچہ ہم نے ریٹریول لیئر (retrieval layer) کو مکمل طور پر بنیاد سے تبدیل کر کے اسے ایک پیمائش کے قابل اور ٹیون ایبل (tunable) سسٹم کے طور پر دوبارہ تعمیر کیا۔ نتیجہ ایک ایسی پائپ لائن نکلا جو یوزر ایکسپیرینس کو سست نہ بنائے بغیر 95% ریکال حاصل کرتی ہے۔

ڈیمو RAG پروڈکشن میں کیوں ناکام ہو جاتا ہے

ہابی پروجیکٹس اور ابتدائی مرحلے کی مصنوعات میں اسٹینڈرڈ اسٹیک حیرت انگیز طور پر یکساں ہے: فکسڈ ٹوکن چنکس (fixed token chunks)، آف دی شیلف ایمبیڈنگز (off-the-shelf embeddings)، اور ایک سنگل ویکٹر سرچ کال۔ یہ سادگی پرکشش ہے، اور یہ اس وقت کام کرتی ہے جب آپ کا ڈیٹا صاف، چھوٹا، اور ساخت کے لحاظ سے قابلِ پیش گوئی ہو۔ پروڈکشن ڈیٹا ان میں سے کچھ بھی نہیں ہوتا۔ 512 ٹوکنز کا ایک فکسڈ چنک SaaS کنٹریکٹ میں انڈیمنیفیکیشن کلاز (indemnification clause) کے عین درمیان سے اسے کاٹ سکتا ہے۔ اچانک آپ کی ریٹریول لیئر لینگویج ماڈل کو قانونی ذمہ داری کا آدھا حصہ فراہم کرتی ہے اور اس سے ذمہ داری (liability) کے سوال کا جواب مانگتی ہے۔ ماڈل ہالوسینیشن (hallucinate) کرتا ہے کیونکہ سیاق و سباق (context) ٹوٹ چکا ہوتا ہے۔

بڑی تکنیکی دستاویزات اس مسئلے کو مزید بڑھا دیتی ہیں۔ API ڈاکومنٹیشن فنکشن سگنیچرز، ٹیبلز، اور کوڈ بلاکس سے بھری ہوتی ہے۔ ایک فکسڈ ونڈو TypeScript انٹرفیس کے درمیان حصے کو تو پکڑ سکتی ہے لیکن اس کے اوپر موجود فنکشن کا نام اور نیچے دی گئی استعمال کی مثال کو چھوڑ سکتی ہے۔ ایمبیڈنگ ویکٹر آخر کار اصل صلاحیت کے بجائے صرف سنٹیکس کے ٹکڑوں اور غیر ضروری شور کی نمائندگی کرنے لگتا ہے جس کے بارے میں صارف پوچھ رہا ہے۔ غلط ڈیٹا اندر، ہالوسینیشن باہر۔

ٹوکن کی تعداد کے بجائے ساخت کے مطابق چنکنگ کریں

پہلی تبدیلی جو ہم نے کی وہ یہ تھی کہ چنکس کو صرف ٹوکنز کے تھیلے کے طور پر دیکھنا بند کر دیا۔ چنکس سیمنٹک یونٹس (semantic units) ہوتے ہیں۔ صحیح حکمت عملی کا انحصار مکمل طور پر اس بات پر ہے کہ آپ کس چیز کو انڈیکس کر رہے ہیں۔

قانونی دستاویزات کے لیے، ہم ریکرسو چنکنگ (recursive chunking) کی طرف منتقل ہو گئے جو دستاویز کے ڈھانچے کا احترام کرتی ہے۔ یہ سیکشنز، سب سیکشنز، اور کلازز کو حدود کے طور پر لیتی ہے۔ ایک کلاز اپنی جگہ برقرار رہتی ہے کیونکہ ایک کلاز معنی کا ایک یونٹ ہے۔ اگر آپ اسے درمیان سے کاٹتے ہیں، تو قانونی منطق ختم ہو جاتی ہے۔

API ڈاکس کے لیے، اسٹرکچر-اوایر چنکنگ (structure-aware chunking) فنکشنز، کلاسز، اور اینڈ پوائنٹس کو ایٹامک (atomic) کے طور پر لیتی ہے۔ ایک چنک میں فنکشن سگنیچر، اس کے آرگومنٹ، اور اس کی ڈاک اسٹرنگ (docstring) شامل ہو سکتی ہے۔ یہ محض اس لیے اگلی یوٹیلیٹی فنکشن میں نہیں پھیلتا کیونکہ ٹوکن کاؤنٹر بڑھ گیا ہو۔ یہ ایمبیڈنگ کو ایک مخصوص صلاحیت پر مرکوز رکھتا ہے۔

سپورٹ ٹکٹس زیادہ الجھے ہوئے ہوتے ہیں۔ وہ بات چیت پر مبنی، تھریڈڈ، اور غیر خطی (nonlinear) ہوتے ہیں۔ فکسڈ چنکس ایک ہی تھریڈ سے انجینئر کی اسٹیٹس اپ ڈیٹ اور کسٹمر کی شکایت اٹھا لیں گے اور یہ ظاہر کرنے کی کوشش کریں گے کہ وہ ایک مربوط یونٹ ہیں۔ ہم سیمنٹک چنکنگ (semantic chunking) پر منتقل ہو گئے، جہاں ٹوکن بجٹ ختم ہونے کے بجائے موضوع یا بولنے والے کے بدلنے پر تقسیم کیا جاتا ہے۔

انٹرنل وکیز (Internal wikis) اکثر کسی تنظیم میں سب سے زیادہ غیر منظم ڈیٹا ہوتی ہیں۔ فارمیٹنگ غیر مستقل ہوتی ہے، ہیڈرز غائب ہوتے ہیں، اور سیکشنز آپس میں ملے ہوئے ہوتے ہیں۔ ان کے لیے، ہم LLM-بیسڈ چنکنگ استعمال کرتے ہیں۔ ایک چھوٹا ماڈل ایمبیڈنگ بنانے سے پہلے ہی آگے پڑھتا ہے اور منطقی حدود کی شناخت کرتا ہے۔ یہ کریکٹر اسپلٹ (character split) کے مقابلے میں شروع میں زیادہ مہنگا پڑتا ہے، لیکن ریٹریول کی کوالٹی اس کی قیمت فوری طور پر وصول کر لیتی ہے۔

ہائبرڈ ریٹریول: سگنلز کو یکجا کریں

ویکٹر سرچ طاقتور ہے لیکن اس کے کچھ اندھے دھبے (blind spots) ہیں۔ کوئی درست ایرر کوڈ جیسے ERR_CONNECTION_REFUSED_0x800 پیسٹ کریں اور سیمیلرٹی سرچ کسی غیر متعلقہ ماڈیول کے لیے ٹربل شوٹنگ گائیڈ واپس کر سکتی ہے کیونکہ ایمبیڈنگ اسپیس نے انہیں ایک دوسرے کے قریب گروپ کر دیا تھا۔ درست میچ (exact matches) اہم ہیں، اور صرف ویکٹر سرچ انہیں ختم کر سکتی ہے۔

BM25 کے ساتھ کی ورڈ سرچ اس درست میچ کے مسئلے کو خوبصورتی سے حل کرتی ہے۔ لیکن یہ تصوراتی فاصلے (conceptual distance) پر ناکام ہو جاتی ہے۔ اگر کوئی صارف "بھاری لوڈ کے تحت کارکردگی میں کمی" (performance degradation under heavy load) کے بارے میں پوچھتا ہے، تو BM25 اس تشخیصی نوٹ کو مس کر دے گی جو "ٹریفک کے دباؤ کے دوران سست تھرو پٹ" (slow throughput during traffic spikes) کی وضاحت کرتا ہے کیونکہ کی ورڈز کا ملاپ کافی نہیں ہوتا۔

ہم نے کسی ایک کا انتخاب کرنا چھوڑ دیا اور دونوں کو متوازی طور پر چلانا شروع کر دیا۔ ویکٹر اور کی ورڈ سرچ دونوں اپنی اپنی رینکڈ لسٹیں واپس کرتے ہیں۔ ہم انہیں Reciprocal Rank Fusion کے ذریعے یکجا کرتے ہیں۔ RRF اپنی تاثیر میں سادہ اور بے رحم ہے۔ یہ ہر دستاویز کو اس بنیاد پر اسکور کرتا ہے کہ وہ ہر لسٹ میں کہاں واقع ہے۔ وہ دستاویزات جو دونوں سسٹمز میں ٹاپ پر ہوتی ہیں، انہیں بڑا بوسٹ ملتا ہے۔ وہ دستاویزات جنہیں صرف ایک انجن پسند کرتا ہے، وہ بھی حتمی امیدواروں کے سیٹ میں جگہ بنا لیتی ہیں۔

فیوژن کے بعد، ہم بہترین امیدواروں کو cross-encoder reranker کے ذریعے گزارتے ہیں۔ یہ مفت نہیں ہے۔ یہ تقریباً 50 ملی سیکنڈ کا اضافی کمپیوٹیشن وقت لیتا ہے۔ یہ ریکال (recall) کو بھی 15% تک بڑھا دیتا ہے۔ cross-encoder مکمل کوئری اور ہر امیدوار chunk کا مل کر جائزہ لیتا ہے، جس سے ایک ایسا relevance score حاصل ہوتا ہے جو bi-encoder embedding کے مقابلے میں کہیں زیادہ باریک بین ہوتا ہے۔ وہ اضافی 50 ملی سیکنڈ ایک بہترین سودا ہے۔ یہ آپ کو LLM کو غلط context window بھیجنے اور پھر ایک الجھے ہوئے یا hallucinated جواب کے لیے دو سیکنڈ انتظار کرنے سے بچاتا ہے۔

تلاش کرنے سے پہلے کوئری کو درست کریں

صارفین سرچ انجینئرز کی طرح کوئریز نہیں لکھتے۔ وہ "app broken" ٹائپ کرتے ہیں۔ وہ پراسرار لاگ فرگمنٹس (log fragments) پیسٹ کرتے ہیں۔ وہ مبہم اور غیر واضح سوالات پوچھتے ہیں۔ اگر آپ ان خام (raw) اسٹرنگز کو براہ راست انڈیکس پر بھیج دیں گے، تو آپ کو کچرا ہی ملے گا۔

ہم ریٹریول انجن (retrieval engine) تک پہنچنے سے پہلے ہر کوئری کو تبدیل کر دیتے ہیں۔

پہلا، query expansion۔ سسٹم ایک مختصر سوال سے متعدد سرچ ٹرمز تیار کرتا ہے۔ ایک صارف پوچھتا ہے، "How do I fix the timeout؟" انجن اسے connection timeouts، read timeouts، gateway timeouts، اور retry logic کو کور کرنے کے لیے پھیلا دیتا ہے۔ صرف اس ایک طریقے نے ہمارے ریکال کو 78% سے بڑھا کر 96% کر دیا۔

دوسرا، query decomposition۔ پیچیدہ سوالات کو چھوٹے ذیلی سوالات میں تقسیم کر دیا جاتا ہے۔ ایک کوئری جیسے کہ "What's the refund policy for enterprise customers past 90 days and how does it differ from monthly plans؟" ایک ہی بھاری بھرکم embedding lookup کے بجائے دو مرکوز سرچز میں بدل جاتی ہے۔ ہر ذیلی سوال آزادانہ طور پر انڈیکس کو ہٹ کرتا ہے۔ نتائج کو بعد میں (downstream) دوبارہ جوڑ دیا جاتا ہے۔ یہ ریٹریول کو محدود اور درست رکھتا ہے، جو اس پھیلاؤ (dilution) کو روکتا ہے جو اس وقت ہوتا ہے جب ایک ہی embedding ایک وقت میں درجنوں تصورات سے میچ کرنے کی کوشش کرتی ہے۔

اگر آپ اب بھی chunk size، overlap ratios، اور retrieval weights کو خود سے ٹیون کر رہے ہیں، تو آپ کارکردگی کا نقصان کر رہے ہیں۔ ہم نے اندازہ لگانا چھوڑ دیا۔

ہم نے ایک سرچ اسپیس (search space) متعین کیا جہاں chunk size، overlap percentage، vector-versus-BM25 weights، اور reranker thresholds سب متغیرات (variables) ہیں۔ پھر ہم نے Bayesian optimization کا استعمال کیا۔ سینکڑوں رینڈم کنفیگریشنز کے ذریعے grid-searching کرنے کے بجائے، Bayesian search اس بات کا ایک probabilistic model بناتا ہے کہ کیا کام کرتا ہے۔ یہ ایک کنفیگریشن تجویز کرتا ہے، ریکال اور لیٹنسی (latency) کا مشاہدہ کرتا ہے، اپنے عقائد کو اپ ڈیٹ کرتا ہے، اور اگلی تجویز پیش کرتا ہے۔ وقت کے ساتھ یہ ایسے توازن پر پہنچ جاتا ہے جو کوئی انسان دستی طور پر کبھی نہیں پا سکتا۔

اس نے ایسے کمبینیشنز تلاش کیے جو ہم کبھی نہیں آزماتے پاتے۔ زیادہ overlap کے ساتھ چھوٹے chunks۔ ڈینس ویکٹر سرچ (dense vector search) پر تھوڑا کم وزن اور ایک زیادہ جارحانہ reranker threshold کے ساتھ جوڑی۔ ان غیر واضح سمجھوتوں (tradeoffs) نے ہمیں زیادہ ریکال اور کم لیٹنسی دونوں فراہم کیں۔

یہ کوئی ایک بار کا سیٹ اپ ٹاسک نہیں ہے۔ ہم ماہانہ بنیادوں پر hyperparameter optimization دوبارہ چلاتے ہیں۔ آپ کا کارپس (corpus) بدلتا رہتا ہے۔ صارفین کا رویہ تبدیل ہوتا ہے۔ آپ کے پائپ لائن کو ایک جگہ جم جانے کے بجائے خود کو ڈھالنا چاہیے۔

حاصل ہونے والا فائدہ

اس ری بلڈ (rebuild) کا خام نتیجہ ناقابلِ تردید ہے۔

پوزیشن دس پر ریکال 78% سے بڑھ کر 95% ہو گیا۔ جب درست جواب ہمارے نالج بیس (knowledge base) میں موجود ہوتا ہے، تو ہم اسے بیس میں سے بیس بار میں سے انیس بار سامنے لاتے ہیں۔ 95th percentile پر لیٹنسی 850 ملی سیکنڈ سے کم ہو کر 320 ملی سیکنڈ رہ گئی۔ چیٹ اب سست ہونے کے بجائے فوری محسوس ہوتی ہے۔

بہتر ریٹریول نے لینگویج ماڈل کو بہتر grounding فراہم کی۔ Hallucination rate 12% سے گر کر 3% ہو گیا۔ جب ماڈل کے سامنے صحیح سیاق و سباق (context) ہوتا ہے، تو وہ حقائق ایجاد کرنا بند کر دیتا ہے۔ فی کوئری لاگت (cost per query) میں 38% کمی آئی۔ تیز اور بہتر ریٹریول کا مطلب ہے کہ غیر متعلقہ سیاق و سباق، ری ٹرائی لوپس (retry loops)، اور طویل مگر بے کار پرامپٹس پر کم ٹوکنز ضائع ہوں گے۔

اسے انفراسٹرکچر کی طرح بنائیں

اگر آپ پروٹو ٹائپ سے پروڈکشن کی طرف بڑھ رہے ہیں، تو ریٹریول کو کنفیگریشن کے بجائے انفراسٹرکچر کوڈ کے طور پر سمجھیں۔