بلاگز / پروڈکشن میں RAG: آپ کا پائپ لائن ڈیمو میں کیوں کام کرتا ہے لیکن پروڈکشن میں نہیں

پروڈکشن میں RAG: آپ کا پائپ لائن ڈیمو میں کیوں کام کرتا ہے لیکن پروڈکشن میں نہیں

شائع ہوا
26 جولائی، 2026
مصنف
Faizan Nadeem
ٹیگز
RAG AI Engineering Backend Development LLM
باہم مربوط ڈیٹا نوڈز کی تجریدی بصری صورت جو ایک retrieval pipeline کی نمائندگی کرتی ہے
Unsplash پر Steve A Johnson کی تصویر

ڈیمو ہمیشہ کام کرتا ہے۔ آپ چند درجن دستاویزات کو embed کرتے ہیں، ایک سوال پوچھتے ہیں، اور ماڈل فوراً، درستگی کے ساتھ، صاف citation کے ساتھ جواب دے دیتا ہے۔ آپ اسے اپنی ٹیم کو دکھاتے ہیں۔ سب متاثر ہو جاتے ہیں۔ پھر آپ اسے حقیقی دستاویزاتی مجموعے پر چلاتے ہیں، جہاں چند درجن کی جگہ چند ہزار صفحات ہوتے ہیں، اور اسے حقیقی صارفین کے سامنے رکھتے ہیں، اور سب کچھ بکھر جاتا ہے۔ جواب مبہم ہونے لگتے ہیں۔ latency تین سیکنڈ سے اوپر نکلنے لگتی ہے۔ ماڈل پورے اعتماد سے غلط section کا حوالہ دیتا ہے۔ کوئی ایسا سوال پوچھتا ہے جس میں عین product code موجود ہو اور سسٹم پانچ ایسے chunks واپس کرتا ہے جو موضوعی طور پر متعلقہ تو ہوتے ہیں مگر مکمل طور پر بےکار۔

زیادہ تر developers RAG کو ایک ہی چیک سمجھتے ہیں: “کیا ہم نے vector database شامل کر لیا؟” یہی غلطی ہے۔ vector database ایک pipeline کا صرف ایک component ہے، خود پوری pipeline نہیں۔ ایک کام کرنے والے ڈیمو اور پروڈکشن سسٹم کے درمیان فرق کوئی زیادہ ذہین ماڈل یا بہتر prompt نہیں ہوتا، یہ تقریباً ہمیشہ اس کے نیچے موجود retrieval layer ہوتی ہے، اور خاص طور پر وہ تین چیزیں جنہیں زیادہ تر tutorials چھوڑ دیتی ہیں: آپ اپنی دستاویزات کو کیسے chunk کرتے ہیں، آپ ان میں کیسے search کرتے ہیں، اور آپ مہنگا کام دوبارہ کرنے سے کیسے بچتے ہیں۔ ان تینوں کو درست کر دیں تو نیچے آنے والی ہر چیز آسان ہو جاتی ہے۔

آپ یہ سیکھیں گے:

  • کیوں fixed-size chunking خاموشی سے retrieval quality کو خراب کر دیتی ہے، اور semantic chunking کیا درست کرتی ہے
  • کیوں pure vector search exact-match queries کو miss کرتی ہے، اور hybrid retrieval (vector + BM25) یہ خلا کیسے پُر کرتی ہے
  • query caching کس طرح stale answers دیے بغیر latency اور LLM خرچ دونوں کم کرتی ہے
  • پروڈکشن-گریڈ pipeline میں ایک واحد query کو شروع سے آخر تک کیسے trace کیا جائے
  • جب آپ کا document set اور traffic واقعی scale کرتے ہیں تو سب سے پہلے کیا ٹوٹتا ہے
  • retrieval کی وہ غلطیاں جو کرنا آسان اور نظر انداز کرنا مہنگا ہوتا ہے

فہرستِ مضامین

  1. بنیادی باتیں
  2. مکمل architecture
  3. بنیادی layers کی وضاحت
  4. ابتدا سے انتہا تک walkthrough
  5. خصوصی صورتیں
  6. scaling اور پروڈکشن چیلنجز
  7. code examples
  8. عام غلطیاں
  9. پروڈکشن best practices

بنیادی باتیں

RAG دراصل کیا کور کرتا ہے

Retrieval-Augmented Generation کو ہر ایک query کے لیے، ترتیب سے، چار سوالات کا درست جواب دینا ہوتا ہے: صارف کا اصل مطلب کیا ہے؟ آپ کی دستاویزات کے کون سے حصے اس مطلب سے متعلق ہیں؟ محدود context window میں صرف مفید حصوں کو کیسے fit کیا جائے؟ اور ایسا جواب کیسے generate کیا جائے جو retrieved مواد پر مبنی ہو نہ کہ اس پر جو ماڈل پہلے سے “جانتا” ہے؟

اپنی پہلی RAG feature بنانے والے زیادہ تر لوگ صرف تیسرے اور چوتھے سوال حل کرتے ہیں، وہ embedding call، vector similarity search، اور prompt template جوڑ دیتے ہیں، اور سمجھتے ہیں کہ پہلے دو خود بخود سنبھل جائیں گے۔ ایسا نہیں ہوتا۔ query understanding اور retrieval quality اصل مشکل مسائل ہیں۔ generation آسان حصہ ہے؛ ایک قابل LLM آپ کے دیے ہوئے تقریباً کسی بھی context سے مربوط جواب لکھ سکتا ہے، چاہے context خراب ہی کیوں نہ ہو۔ یہی اصل جال ہے: خراب retrieval کوئی error نہیں پھینکتی، بس ایک پراعتماد، رواں، مگر غلط جواب پیدا کرتی ہے۔

اسے درست کرنا اتنا اہم کیوں ہے

retrieval quality کوئی اچھی اضافی سہولت نہیں، یہ ہر اس چیز کی حدِ بالا ہے جو آپ اس کے اوپر بناتے ہیں:

  • Hallucinations اعتماد کے نقصان کو بڑھا دیتی ہیں۔ ایسا غلط جواب جو صحیح لگے، کسی جواب نہ ہونے سے بھی بدتر ہے، خاص طور پر support یا internal-knowledge tools میں جہاں چند اچھے تجربات کے بعد لوگ دوبارہ تصدیق کرنا چھوڑ دیتے ہیں۔
  • Latency کے لیے UX کی ایک سخت نچلی حد ہوتی ہے۔ تقریباً دو سیکنڈ کے بعد صارفین یہ سمجھ لیتے ہیں کہ کچھ خراب ہے، چاہے آخر میں جواب کتنا ہی اچھا کیوں نہ ہو۔
  • Cost اچھی retrieval کے ساتھ نہیں، خراب retrieval کے ساتھ scale کرتی ہے۔ وہ ٹیمیں جو درست retrieval نہیں کر پاتیں، context window میں مزید chunks بھر کر اس کی تلافی کرتی ہیں، جس کا مطلب ہے ہر ایک call پر زیادہ input tokens۔
  • ناکامیاں خاموش ہوتی ہیں۔ “غلط section retrieve ہوا” کے لیے کوئی stack trace نہیں ہوتی۔ آپ کو اس کا پتا یا تو support ticket سے چلتا ہے یا Slack پر کسی screenshot سے، اس کا مطلب ہے کہ visibility آپ کو جان بوجھ کر بنانی پڑے گی، یہ حادثاتی طور پر نہیں ہو گی۔

مکمل architecture

Documents ─▶ Chunking ─▶ Embedding ─┬─▶ Vector Index
                                     └─▶ BM25 Index

User Query ─▶ Query Processing ─▶ Hybrid Retrieval ─▶ Rerank ─▶ Cache Check
                                                                     │
                                                     ┌───────────────┴───────────────┐
                                                  cache hit                     cache miss
                                                     │                                │
                                              return cached                Context Assembly
                                                answer                            │
                                                                                  ▼
                                                                              LLM Call
                                                                                  │
                                                                                  ▼
                                                                          Cache + Return

اس پوری ساخت کے نیچے موجود رہنما اصول یہ ہے: retrieval quality، generation quality کی ایک سخت حدِ بالا ہے۔ prompt engineering کی کوئی مقدار بھی غلط context پر بنے جواب کو نہیں بچا سکتی؛ اگر کچھ ہوتا ہے تو بہتر prompt صرف غلط جواب کو زیادہ قائل کرنے والا بنا دیتی ہے۔

بنیادی layers کی وضاحت

1. Semantic Chunking

یہ کیا ہے: دستاویزات کو قدرتی معنوی حدود کے مطابق تقسیم کرنا، جیسے section headings، paragraph breaks، یا sentence-level similarity سے معلوم ہونے والی topic shifts، بجائے اس کے کہ ہر N characters یا tokens پر قطع نظر اس کے کہ اندر کیا ہے، کاٹ دیا جائے۔

یہ کیوں اہم ہے: Fixed-size chunking ایک RAG pipeline میں خاموش quality loss کی سب سے عام وجہ ہے۔ 500-token chunk boundary کو اس بات سے کوئی فرق نہیں پڑتا کہ وہ جملے کے بیچ میں آ رہی ہے، کسی numbered list کو اس کے تعارف سے الگ کر رہی ہے، یا کسی caveat کو اس rule سے جدا کر رہی ہے جس کی وہ qualification تھی۔ اس chunk کی embedding پھر آدھے خیال کی نمائندگی کرتی ہے، جس کا مطلب ہے یا تو وہ تب retrieve نہیں ہوگی جب ہونی چاہیے، یا پھر retrieve تو ہو جائے گی مگر اس context کے بغیر جس نے اسے اصل میں درست بنایا تھا۔

def semantic_chunks(sentences, embed_fn, threshold=0.75, max_tokens=400):
    chunks, current, current_tokens = [], [], 0
    prev_vec = None
    for sentence in sentences:
        vec = embed_fn(sentence)
        sim = cosine_similarity(prev_vec, vec) if prev_vec else 1.0
        too_big = current_tokens + count_tokens(sentence) > max_tokens
        if (sim < threshold or too_big) and current:
            chunks.append(" ".join(current))
            current, current_tokens = [], 0
        current.append(sentence)
        current_tokens += count_tokens(sentence)
        prev_vec = vec
    if current:
        chunks.append(" ".join(current))
    return chunks

پروڈکشن مشورہ: صرف semantic boundaries بھی کبھی کبھی بہت بڑے یا بہت چھوٹے chunks پیدا کر سکتی ہیں۔ ہمیشہ ایک سخت token ceiling کو حفاظتی حد کے طور پر رکھیں، اور ساتھ والے chunks کے درمیان تھوڑا overlap (10–15%) شامل کریں تاکہ اگر کوئی boundary dependent clause کو تقسیم بھی کر دے تو دونوں طرف کافی context باقی رہے۔

2. Hybrid Retrieval (Vector + BM25)

یہ کیا ہے: دو retrieval methods کو متوازی چلانا، semantic meaning کے لیے dense vector similarity search، اور exact-term matching کے لیے BM25 جیسا sparse keyword method، پھر دونوں ranked lists کو merge کرنا۔

یہ کیوں اہم ہے: Embeddings “ان دونوں کا مطلب ملتا جلتا ہے” جیسی چیزوں میں بہترین ہوتی ہیں اور “اس میں بالکل یہی string موجود ہے” جیسی چیزوں میں خاموشی سے کمزور۔ support bot سے error code ERR_4402 یا کسی خاص function name کے بارے میں پوچھیں، اور pure vector search خوشی سے ایسے chunks واپس کر دے گی جو موضوعی طور پر متعلقہ ہوں مگر literal identifier پر مشتمل نہ ہوں، کیونکہ embedding نے اسے ایک عمومی concept میں smooth کر دیا۔ BM25 بالکل انہی cases کو پکڑتی ہے جو vector search چھوڑ دیتی ہے، اور اس کے برعکس vector search ان paraphrased یا conceptual queries کو پکڑتی ہے جن کی source text کے ساتھ کوئی مشترک vocabulary نہیں ہوتی۔

def hybrid_retrieve(query, vector_index, bm25_index, k=10, rrf_k=60):
    vec_results = vector_index.search(query, top_k=k)
    bm25_results = bm25_index.search(query, top_k=k)
    scores = {}
    for rank, doc_id in enumerate(r.id for r in vec_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (rrf_k + rank)
    for rank, doc_id in enumerate(r.id for r in bm25_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (rrf_k + rank)
    return sorted(scores.items(), key=lambda x: -x[1])[:k]

یہ reciprocal rank fusion ہے، دو rankings کو merge کرنے کا ایک سادہ اور مؤثر طریقہ، جس میں incompatible score scales کو normalize کرنے کی ضرورت نہیں پڑتی۔

پروڈکشن مشورہ: جب query میں quoted phrases، numbers، یا identifier جیسے tokens ہوں (یعنی کوئی بھی چیز جو code یا SKU pattern سے match کرے)، تو BM25 کو زیادہ weight دیں۔ retrieval سے پہلے ایک تیز regex check ان میں سے زیادہ تر cases کو پکڑنے کے لیے کافی ہوتا ہے۔

3. Query Caching

یہ کیا ہے: query کی embedding اور آخری generated answer دونوں کو cache کرنا، query یا اس کے قریب تر duplicate کی بنیاد پر key بنا کر، تاکہ بار بار آنے والے یا بہت ملتے جلتے سوالات pipeline کے مہنگے حصوں کو مکمل طور پر skip کر سکیں۔

یہ کیوں اہم ہے: زیادہ تر پروڈکشن RAG systems میں سوالات کا ایک چھوٹا مجموعہ traffic کا غیر متناسب بڑا حصہ بناتا ہے، مثلاً “اپنا password کیسے reset کروں” یا “ہماری refund policy کیا ہے” جیسے ایک ہی طرح کے چند سوالات، جو مختلف صارفین تھوڑے مختلف انداز میں پوچھتے ہیں۔ ان میں سے ہر ایک کے لیے دوبارہ embedding اور دوبارہ generation کرنا خالص ضیاع ہے: آپ اسی جواب کے لیے وہی latency اور وہی token cost دے رہے ہوتے ہیں جو آپ پہلے ہی پیدا کر چکے ہیں۔

def get_cached_or_generate(query, redis_client, embed_fn, generate_fn, sim_threshold=0.95):
    query_vec = embed_fn(query)
    cache_key = f"rag:cache:{hash_vector(query_vec)}"
    cached = redis_client.get(cache_key)
    if cached and cosine_similarity(query_vec, cached["vec"]) > sim_threshold:
        return cached["answer"]
    answer = generate_fn(query)
    redis_client.setex(cache_key, 3600, {"vec": query_vec, "answer": answer})
    return answer

ایک pipeline جس پر میں نے کام کیا، اس میں صرف یہ layer شامل کرنے سے peak hours کے دوران اوسط query latency تقریباً 3.2 seconds سے کم ہو کر قریب 1.1 seconds رہ گئی، صرف اس لیے کہ repeat queries کا بڑا حصہ LLM تک پہنچتا ہی نہیں تھا۔ آپ کے اعداد و شمار اس بات پر منحصر ہوں گے کہ آپ کی query traffic واقعی کتنی repetitive ہے، مگر pattern تقریباً ہر جگہ یہی رہتا ہے۔

پروڈکشن مشورہ: TTL اور document updates سے جڑے invalidation path کے بغیر کبھی cache نہ کریں۔ ایسی cache جو اس document سے زیادہ دیر زندہ رہے جس سے وہ بنی تھی، وہی چیز ہے جو آپ کو پورے اعتماد سے ایسا جواب serve کراتی ہے جو پچھلے مہینے درست تھا۔

4. Reranking

یہ کیا ہے: ایک دوسرا، زیادہ مہنگا scoring pass، عموماً cross-encoder model، جو hybrid retrieval سے آئے ہوئے top 20–50 candidates پر لاگو کیا جاتا ہے، اس سے پہلے کہ آخری top-k کو context window میں بھیجا جائے۔

یہ کیوں اہم ہے: Vector اور BM25 search دونوں تیز رفتار approximation ہیں۔ یہ “شاید متعلقہ” candidates کو shortlist میں لانے میں اچھی ہیں، مگر اتنی درست نہیں کہ آپ ان کی ranking کو آخری سمجھ کر بھروسہ کر لیں۔ ایک cross-encoder جو precomputed vectors کو compare کرنے کے بجائے query اور ہر candidate کو ساتھ دیکھتا ہے، اس shortlist کو درست ترتیب دینے میں معنی خیز طور پر بہتر ہوتا ہے، بس اس کی cost اتنی زیادہ ہوتی ہے کہ اسے پورے corpus پر نہیں چلایا جا سکتا۔

پروڈکشن مشورہ: صرف shortlist کو rerank کریں، پورے corpus کو کبھی نہیں۔ حتیٰ کہ چند سو candidates پر cross-encoder چلانے سے بھی حقیقی latency بڑھتی ہے؛ پورے document set پر تو یہ بالکل ناقابلِ عمل ہے۔

5. Context Assembly & Token Budgeting

یہ کیا ہے: reranked chunks کو سوچ سمجھ کر اپنی context window میں pack کرنا، overlap کرنے والے content کو deduplicate کرنا، relevance کے حساب سے ترتیب دینا، اور token budget تک پہنچتے ہی رک جانا، بجائے اس کے کہ ہر retrieved chunk کو prompt میں انڈیل دیا جائے۔

یہ کیوں اہم ہے: “زیادہ context” ہمیشہ “بہتر context” نہیں ہوتا۔ ایک حد کے بعد اضافی chunks اس signal کو کمزور کر دیتے ہیں جس پر ماڈل کو کام کرنا ہوتا ہے، latency اور cost بڑھا دیتے ہیں، اور یہ امکان بڑھاتے ہیں کہ ماڈل کسی غیر متعلق passage سے چمٹ جائے۔ ایک سخت token budget upstream retrieval اور reranking stages کو مجبور کرتی ہے کہ وہ واقعی اپنا کام کریں، صرف مقدار پر انحصار کر کے تلافی نہ کریں۔

ابتدا سے انتہا تک walkthrough

ایک واحد query کو پوری pipeline میں trace کریں: “payment webhook retry پر timeout کیا ہے؟”

  1. Query موصول ہوئی۔ API layer خام string قبول کرتی ہے اور whitespace اور casing کو normalize کرتی ہے۔
  2. Cache check۔ Query کو embed کیا جاتا ہے اور similarity threshold سے اوپر موجود cached query vectors سے compare کیا جاتا ہے۔ cold cache کی صورت میں یہ miss ہوتا ہے۔
  3. Query processing۔ ایک lightweight check اسے ایک خاص technical term (“webhook retry”) پر مشتمل قرار دیتا ہے، جو hybrid weighting کو BM25 کی طرف تھوڑا جھکا دیتا ہے۔
  4. Hybrid retrieval۔ Vector search اور BM25 search دونوں اپنے top candidates واپس کرتی ہیں؛ reciprocal rank fusion انہیں ایک واحد ranked shortlist میں merge کر دیتی ہے۔
  5. Reranking۔ cross-encoder اصل query text کے مقابلے میں shortlist کو score دیتا ہے اور اسے دوبارہ ترتیب دیتا ہے۔ اگر reranking minimum relevance score سے اوپر کچھ بھی واپس نہ کرے، تو pipeline زبردستی کمزور context سے LLM کو generate کروانے کے بجائے fallback response پر مختصر راستہ اختیار کرتی ہے (“مجھے اس بارے میں کچھ مخصوص نہیں ملا، دوبارہ الفاظ بدل کر کوشش کریں”)۔
  6. Context assembly۔ سب سے زیادہ score والے chunks کو deduplicate، order، اور token budget تک prompt میں pack کیا جاتا ہے۔
  7. LLM call۔ ماڈل assembled context کی بنیاد پر جواب generate کرتا ہے، اس ہدایت کے ساتھ کہ source chunk کا citation دے۔
  8. Cache write۔ Query embedding اور generated answer کو cache میں TTL اور ایک version tag کے ساتھ لکھا جاتا ہے جو source document کے last-updated timestamp سے جڑا ہوتا ہے۔
  9. Response returned۔ صارف کو تقریباً ایک سے دو سیکنڈ میں جواب مل جاتا ہے، ساتھ میں مخصوص doc section کا حوالہ بھی۔

اس chain کے ہر مرحلے میں failure کا ایک واضح راستہ موجود ہے۔ یہی فرق ہے اس pipeline میں جو وقار کے ساتھ degrade کرتی ہے، اور اس میں جو upstream سے متوقع چیز واپس نہ آنے پر یا تو hang ہو جاتی ہے یا hallucinate کرنے لگتی ہے۔

خصوصی صورتیں

Tables اور structured data۔ سادہ chunking، چاہے semantic ہو یا کچھ اور، عموماً tables کو چیر پھاڑ دیتی ہے، headers کو rows سے الگ کر دیتی ہے یا table کو row کے بیچ سے تقسیم کر دیتی ہے۔ ingestion کے دوران tabular content کا پتا لگائیں اور اسے ایک unit کے طور پر chunk کریں، اسے markdown یا key-value representation میں بدل دیں جو text کے طور پر embed ہونے کے بعد بھی برقرار رہے۔

Frequently updated documents۔ وہ docs جو بار بار بدلتی ہیں (pricing pages، policy documents، changelogs) ان کے لیے cache invalidation صرف TTL کے ساتھ نہیں بلکہ content hash یا version number کے ساتھ جڑی ہونی چاہیے۔ صرف time-based cache stale answers serve کرتی رہے گی، TTL window جتنی بھی ہو، چاہے source document cache لکھے جانے کے پانچ منٹ بعد ہی بدل گیا ہو۔

Multi-turn conversations۔ ایک follow-up سوال جیسے “اور staging کا کیا؟” retrieval system کے لیے پہلے turn کے context کے بغیر کوئی معنی نہیں رکھتا۔ ایک query rewriting step شامل کریں جو حالیہ conversation history کو retrieval تک پہنچنے سے پہلے ایک self-contained query میں سمیٹ دے، ورنہ آپ کی hybrid search آدھے سوال کے خلاف retrieval کر رہی ہوگی۔

scaling اور پروڈکشن چیلنجز

ingestion scale پر embedding generation bottleneck بن جاتی ہے۔ چند دستاویزات کو synchronously embed کرنا ٹھیک ہے؛ دسیوں ہزار کو نہیں۔ ingestion کو async task queue میں منتقل کریں، اگر یہ پہلے سے آپ کے stack میں ہے تو Redis کو broker بنا کر Celery ایک فطری انتخاب ہے، اور ہر chunk کے لیے ایک request بھیجنے کے بجائے embedding calls کو batch کریں۔

corpus بڑھنے کے ساتھ vector index search سست پڑ جاتی ہے۔ چھوٹے scale پر brute-force similarity search قابلِ قبول ہے اور تقریباً 100k vectors کے بعد بکھر جاتی ہے۔ approximate nearest-neighbor index پر منتقل ہوں (HNSW عام default ہے) اور latency میں بڑے فائدے کے بدلے accuracy میں چھوٹا، قابلِ ترتیب سمجھوتہ قبول کریں۔

scale پر cache invalidation آسان نہیں، زیادہ مشکل ہو جاتی ہے۔ جب زیادہ دستاویزات زیادہ کثرت سے بدلتی ہوں، تو ایک واحد global TTL کافی نہیں رہتی۔ cache keys کو ان source documents کے version hash سے باندھیں جنہوں نے کسی مخصوص جواب میں حصہ ڈالا ہو، تاکہ ان میں سے کسی ایک کی update فطری طور پر ہر اس cached answer کو invalidates کر دے جو اس پر منحصر تھا۔

Cost خاموشی سے بڑھتی رہتی ہے۔ فی-query token tracking کے بغیر cost problem کی پہلی نشانی ماہانہ bill ہوتی ہے، کوئی خاص query نہیں۔ ہر request پر input اور output token counts log کریں اور outliers پر alert لگائیں؛ ایسی query جو معمول سے کہیں زیادہ context کھینچ رہی ہو اکثر اس بات کی علامت ہوتی ہے کہ آپ کے token budget یا reranking threshold کو مزید سخت کرنے کی ضرورت ہے۔

code examples

Semantic chunking، hybrid retrieval، اور semantic caching اوپر Core Layers میں مکمل طور پر بیان کیے جا چکے ہیں۔ ایک اور چیز جو ساتھ رکھنا مفید ہے: ایک minimal fallback guard تاکہ pipeline خالی یا تقریباً خالی context سے کبھی generate نہ کرے:

def generate_with_guard(query, context_chunks, generate_fn, min_score=0.3):
    if not context_chunks or context_chunks[0].score < min_score:
        return "I couldn't find anything specific on that in the documentation."
    context = "\n\n".join(c.text for c in context_chunks)
    return generate_fn(query=query, context=context)

یہ ایک function “خاموش غلط جواب” کو “ایماندار مجھے نہیں معلوم” میں بدلنے کی ذمہ دار ہے، ایک چھوٹی تبدیلی جو user trust کے لیے بہت کچھ کرتی ہے۔

عام غلطیاں

غلطی: vector search کو پورا حل سمجھ لینا۔ صرف vector والی pipeline منظم انداز سے exact-match queries، IDs، codes، اور quoted terms کو miss کرے گی۔ حل: بعد کی optimization کے طور پر نہیں، default کے طور پر hybrid retrieval چلائیں۔ اسے شروع میں شامل کرنا سستا اور بعد میں retrofit کرنا مہنگا ہوتا ہے، خاص طور پر جب آپ کا evaluation set پہلے ہی vector-only behavior کے گرد بن چکا ہو۔

غلطی: content boundaries کی پروا کیے بغیر fixed token count پر chunking کرنا۔ یہ “جواب تقریباً درست ہے مگر ایک اہم detail غائب ہے” کی سب سے عام وجہ ہے۔ حل: semantic انداز میں chunk کریں، حفاظتی حد کے طور پر size cap رکھیں، اور ساتھ والے chunks کے درمیان تھوڑا overlap شامل کریں۔

غلطی: invalidation strategy کے بغیر caching کرنا۔ ایسی cache جس کا source document تک واپسی کا راستہ نہ ہو، خوشی سے پچھلے مہینے کا جواب ایسے serve کرے گی جیسے وہ ابھی کا ہو۔ حل: cache keys کو صرف time-based TTL کے بجائے contributing documents کے content hash کے خلاف version کریں۔

غلطی: یہ سمجھنا کہ زیادہ retrieved chunks کا مطلب بہتر جواب ہے۔ context stuffing cost اور latency بڑھاتی ہے اور اکثر signal کو dilute کر کے جواب کو بہتر نہیں بلکہ بدتر بنا دیتی ہے۔ حل: aggressively rerank کریں اور اس چیز پر سخت token budget نافذ کریں جو واقعی prompt تک پہنچتی ہے۔

غلطی: evaluation set کے بغیر ship کرنا۔ representative queries اور متوقع sources کے ایک ثابت شدہ مجموعے کے بغیر آپ کے پاس یہ جاننے کا کوئی طریقہ نہیں کہ chunking یا retrieval میں تبدیلی نے فائدہ دیا یا نقصان۔ حل: ship کرنے سے پہلے ایک چھوٹا سا (30–50 query) evaluation set بھی بنا لیں، اور retrieval layer میں ہر تبدیلی پر اسے دوبارہ چلائیں۔

پروڈکشن best practices

  • retrieval quality کو generation quality سے الگ ناپیں۔ ایک ثابت شدہ evaluation set کے خلاف Precision@k اور recall آپ کو بتاتے ہیں کہ مسئلہ retrieval میں ہے یا prompt میں؛ جب اصل مسئلہ upstream ہو تو LLM call کو debug نہ کریں۔
  • صرف TTLs نہیں، versioned keys کے ساتھ cache کریں۔ ہر cache entry کو ان documents کے hash سے باندھیں جن سے وہ بنی تھی تاکہ document updates خودکار طور پر درست answers کو invalidate کر دیں۔
  • context assembly پر سخت token budget نافذ کریں۔ زیادہ chunks کوئی strategy نہیں؛ ایک مختصر، reranked context window تقریباً ہر بار ایک بڑے غیر ranked window سے بہتر ثابت ہوتی ہے۔
  • صرف ہر error نہیں، ہر retrieval کو log کریں۔ کون سے chunks retrieve ہوئے، کس کا score کہاں آیا، اور token budget نے کیا drop کیا، پہلی بار جب کوئی غلط جواب report کرے گا تو آپ کو یہی data چاہیے ہوگا۔
  • کمزور retrieval کے لیے fallback path بنائیں۔ کم relevance score پر ایک ایماندار “مجھے نہیں معلوم” ہمیشہ اس رواں جواب سے بہتر ہے جو ایسے context سے بنا ہو جو بمشکل سوال سے میل کھاتا ہو۔

اختتامیہ

اس میں کچھ بھی غیر معمولی نہیں ہے، semantic chunking، hybrid retrieval، اور caching layer وہ سب چیزیں ہیں جنہیں زیادہ تر backend engineers پہلے ہی بنانا جانتے ہیں۔ ڈیمو اور پروڈکشن کے درمیان خلا کسی گمشدہ breakthrough کی وجہ سے نہیں؛ مسئلہ یہ ہے کہ ڈیمو میں کبھی اتنی دستاویزات یا اتنی query variety تھی ہی نہیں کہ وہ یہ ظاہر کر سکے کہ fixed-size chunking اور vector-only search خاموشی سے کہاں کم پڑ جاتی ہیں۔ prompt کو چھیڑنے سے پہلے ان تین چیزوں کو درست کریں، اور زیادہ تر “کل تو یہ کام کر رہا تھا، آج غلط کیوں ہے” والے tickets آنا بند ہو جاتے ہیں۔

جب آپ نے اپنی RAG pipeline کو ڈیمو سے نکال کر پروڈکشن میں رکھا تو سب سے پہلے کیا ٹوٹا؟ میں واقعی سننا چاہوں گا کہ کس چیز نے آپ کو حیران کیا۔

مزید مضامین