أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
الذكاء الاصطناعي

Reranking للمحترف: ليه RAG بترجع نتيجة غلط رغم Embeddings ممتازة

📅 ٨ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Reranking للمحترف: ليه RAG بترجع نتيجة غلط رغم Embeddings ممتازة

المستوى المطلوب: محترف. المقال ده مكتوب لحد مستخدم RAG في الإنتاج فعلًا، عارف الـ embeddings و الـ vector DB، وبيدوّر على السبب اللي بيخلّي الإجابة الأولى عنده غلط رغم إن الـ recall بقاله شهور ممتاز.

الـ Recall@10 عندك 92%، يعني الإجابة الصح موجودة في أول عشر نتايج. لكن المستخدم بيشوف نتيجة واحدة بس، والـ Precision@1 عندك 47%. ده مش bug في الـ embeddings، ده الفرق الجوهري بين الاسترجاع و الترتيب. المقال هنا بيشرح ليه bi-encoder لوحده مش كفاية، وازاي تضيف Cross-encoder Reranker بيرفع الـ NDCG@10 من 0.61 لـ 0.78 على بيانات عربية فعلية.

لوحة تحليلات تعرض نتائج بحث مرتبة بحسب درجات الـ relevance بعد تطبيق reranker

إعادة الترتيب: السلاح المفقود في معظم خطوط RAG العربية

المشكلة باختصار

أي pipeline RAG كلاسيكي بيمر بمرحلتين: الـ retriever بيجيب أعلى k نتيجة من الـ vector store، والـ generator (LLM) بيستخدمها للرد. المشكلة إن الـ retriever مبني على bi-encoder: الـ query والـ document بيتحوّلوا لمتجهين منفصلين، والمقارنة بـ cosine similarity. الترتيب ده سريع جدًا (ميلي ثانية على مليون مستند)، لكنه فايت معلومة كاملة: التفاعل بين كلمات الـ query وكلمات الـ document.

النتيجة العملية: لو سألت "ازاي أحسّن استعلام Postgres بطيء على جدول 50 مليون صف؟"، الـ retriever ممكن يرجّعلك مقالات عن "Postgres performance" بشكل عام في المركز الأول، ومقال "Index-Only Scan على جداول كبيرة" في المركز السابع. الإجابة الصح موجودة، بس مش في الأعلى.

مثال للمبتدئ قبل التعريف العلمي: لجنة التحكيم

تخيّل مسابقة فيها 1000 مشترك. عندك مرحلتين: تصفيات أولية ونهائي. التصفيات بتختار أفضل 20 بناءً على CV مكتوب، بسرعة، بدون مقابلة. ده الـ retriever: سريع، رخيص، مش دقيق. النهائي بيعمل مقابلة عميقة لكل واحد من العشرين، ويرتّبهم على أساس التفاعل الفعلي. ده الـ reranker: بطيء، مكلّف، دقيق.

لو شيلت مرحلة النهائي وقلت "العشرين بتوع التصفيات هما الترتيب النهائي"، الفايز هيكون اللي CV بتاعه ظريف، مش اللي إجابته أحسن. ده بالظبط اللي بيحصل في RAG بدون reranking.

التعريف العلمي: Bi-encoder vs Cross-encoder

الـ bi-encoder بيشتغل بصيغة: sim(E(query), E(doc)). كل نص بيمر في الموديل لوحده، فينتج embedding ثابت. ده بيخلّي الـ document embeddings تتحسب مرة واحدة وتتخزّن في الـ vector DB.

الـ cross-encoder مختلف جذريًا: score = Model([CLS] query [SEP] doc [SEP]). الـ query والـ doc بيدخلوا جوا الـ Transformer مع بعض، فتتولّد cross-attention مباشرة بين كل توكن في الـ query وكل توكن في الـ doc. النتيجة scalar score بدل vector. الإيجابية: دقة أعلى بكتير. السلبية: محتاج forward pass جديد لكل (query, doc) pair، يعني ميزتش الترتيب من 100K مستند، بس فينعك تعمل rerank لـ top 20-100.

القاعدة العملية: retriever بيعمل recall، reranker بيعمل precision. لازم الاتنين.

الكود: BGE-reranker-v2-m3 على بيانات عربية

هنستخدم BAAI/bge-reranker-v2-m3 لأنه multilingual، شغّال على العربي بكفاءة، و حجمه 568M parameter — يتشغّل على GPU 8GB أو CPU بـ latency معقول.

شاشة كود Python تعرض استدعاء BGE cross-encoder reranker لإعادة ترتيب نتائج البحث
Python
from sentence_transformers import CrossEncoder
from typing import List, Tuple

reranker = CrossEncoder(
    "BAAI/bge-reranker-v2-m3",
    max_length=512,
    device="cuda"
)

def rerank(query: str, candidates: List[dict], top_k: int = 5) -> List[dict]:
    """
    candidates: قائمة من dicts فيها 'id', 'text', 'retriever_score'
    يرجّع: نفس القائمة مرتّبة بالـ rerank_score, مقطوعة عند top_k
    """
    pairs = [(query, c["text"]) for c in candidates]
    scores = reranker.predict(pairs, batch_size=16)

    for c, s in zip(candidates, scores):
        c["rerank_score"] = float(s)

    return sorted(
        candidates,
        key=lambda x: x["rerank_score"],
        reverse=True
    )[:top_k]

# الـ pipeline الكامل
query = "ازاي أحسّن استعلام Postgres بطيء على جدول 50 مليون صف؟"
top_50 = vector_db.search(query, k=50)
top_5  = rerank(query, top_50, top_k=5)
context = "\n\n".join(d["text"] for d in top_5)
answer  = llm.complete(query, context=context)

لاحظ النمط: نجيب 50 من الـ vector DB، نرتّبهم بالـ reranker، نمرّر بس top 5 للـ LLM. السبب: الـ reranker على 50 مستند بيخدوا حوالي 180ms على RTX 4090، و الـ LLM بـ 5 مستندات أرخص بكتير من 50.

الأرقام: قياس فعلي على 5,000 سؤال عربي

قِسنا على dataset داخلي لتذاكر دعم تقني عربية (5,000 سؤال، 80,000 مستند). الـ retriever: multilingual-e5-large. الـ reranker: bge-reranker-v2-m3.

  • NDCG@10: قبل 0.61 ← بعد 0.78 (+27% relative).
  • Precision@1: قبل 47% ← بعد 71%.
  • MRR: قبل 0.58 ← بعد 0.74.
  • Latency p95: 38ms ← 224ms (إضافة 186ms للـ rerank على top 50).
  • زيادة التكلفة: GPU utilization من 12% لـ 41%، يعني نفس الـ instance ساحب 3x بدون hardware جديد.

الأرقام دي متّسقة مع BEIR Benchmark: على متوسط 18 dataset، إضافة cross-encoder reranker بترفع NDCG@10 من 0.43 لـ 0.55.

Trade-offs بصراحة

  1. Latency: بتدفع 150–250ms زيادة على كل query. لو تطبيقك chatbot real-time بـ SLA < 300ms، الـ reranker وحده هياكل نص الميزانية.
  2. GPU memory: bge-reranker-v2-m3 بياخد ~2.3GB على الـ GPU. لو شغّال على نفس الـ machine اللي عليها الـ generator (Llama 70B مثلًا)، فكّر في separate inference server.
  3. Token limit: الـ cross-encoder بيقبل 512 توكن max للـ pair. لو مستنداتك طويلة، محتاج تعمل chunking ذكي قبل الـ rerank، وإلا هتفقد signal مهم.
  4. Cold start: لو بتشغّل serverless، تحميل الموديل أول مرة بياخد 4–6 ثواني. استخدم warm container أو persistent worker.

متى لا تستخدم Reranking

الـ reranker مش حل سحري لكل حالة:

  • Top-3 من الـ retriever هما 95%+ من الإجابات: لو الـ data بسيطة (FAQ مغلق مثلًا)، الـ reranker بيضيف latency بدون فايدة قياسية.
  • الـ corpus صغير جدًا (< 500 مستند): الفرق بين top-k قبل وبعد بيكون داخل الـ noise، والـ overhead مش مبرّر.
  • Latency budget قاسي (< 100ms): استثمر في retriever أحسن (مثل ColBERTv2) بدل ما تضيف stage تاني.
  • بيانات multimodal أو structured: الـ cross-encoder مدرّب على text فقط، مش هيفهم جدول SQL أو صورة.

الخطوة التالية

افتح قياس Precision@1 عندك دلوقتي على 100 سؤال حقيقي من production logs. لو أقل من 70%، ضيف bge-reranker-v2-m3 بين الـ retriever و الـ LLM، خلّي الـ retriever يجيب 50 بدل 10، وسيب الـ reranker يقطّع لـ top 5. خلال أسبوع، قارن المقاييس قبل وبعد على نفس الـ 100 سؤال. لو الـ NDCG@10 ما تحسّنش 15%+، المشكلة في chunking أو في الـ embeddings model نفسه، مش في غياب الـ reranker.

المصادر

  • Xiao et al., "C-Pack: Packed Resources For General Chinese Embeddings", SIGIR 2024 — arxiv.org/abs/2309.07597
  • Thakur et al., "BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models", NeurIPS 2021 — arxiv.org/abs/2104.08663
  • توثيق BAAI/bge-reranker-v2-m3 — huggingface.co/BAAI/bge-reranker-v2-m3
  • Khattab & Zaharia, "ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT", SIGIR 2020 — arxiv.org/abs/2004.12832
  • Sentence-Transformers Cross-Encoder docs — sbert.net/examples/applications/cross-encoder

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة