المستوى: متوسط — يفترض إنك جرّبت RAG بسيط من قبل، عارف الفرق بين embedding و token، ومش راضي عن دقّة النتائج.
لو RAG بتاعك بيرجّع أعلى 10 chunks وفيهم 3 مفيدين والباقي ضجيج، المشكلة مش في الـ embeddings. ركز: الـ Bi-Encoder بيقيس تشابه vectors بسرعة لكنه بيخمّن، والـ Cross-Encoder بيقرأ السؤال والـ chunk جنب بعض ويحكم بدقّة. 18 سطر Python يرفعوا Recall@10 من 71.2% لـ 93.8% على corpus عربي 24,000 chunk، بزيادة latency 180ms فقط.
Reranking: الطبقة الناقصة بين Vector Search وإجابة دقيقة
المشكلة باختصار
Vector Search لوحده بيرجّع نتائج "قريبة دلاليًا" مش "إجابة فعلية". لمّا المستخدم بيسأل "ازاي أرجّع منتج بعد 45 يوم من الشراء"، الـ embeddings بترجّع أي chunk بيتكلم عن "إرجاع منتجات" — حتى لو سياسته فترة الإرجاع 14 يوم بس. المستخدم بياخد إجابة قريبة لكنها غلط، وانت بتدفع tokens على chunks مش مفيدة في الـ context.
اللي بيحصل فعلاً إن Bi-Encoder (نموذج الـ embedding اللي بنحب نستخدمه) بيحوّل النص لـ vector واحد قبل ما يشوف السؤال أصلاً. مفيش حوار بين السؤال والوثيقة. كل واحد بيتشفّر منفصل، وبنقيس cosine distance بينهم. ده سريع جدًا لكن دقّته محدودة في الحالات اللي السؤال فيها فيه شرط دقيق زي "بعد 45 يوم" أو "للأطفال تحت 12 سنة".
المثال للمبتدئ: المكتبي ضد الفهرس الإلكتروني
عندك مكتبة فيها 50,000 كتاب. حد سأل عن "تأثير الحروب الصليبية على القاهرة في القرن الثاني عشر". الفهرس الإلكتروني (Vector Search) بيرجّعلك أول 50 كتاب فيهم كلمات قريبة من سؤالك: "حروب صليبية"، "القاهرة"، "القرن 12". تمام، بس فيهم كتب عن صلاح الدين، كتب عن المماليك، كتب عن العمارة الفاطمية، وكتب عن القرن 14 ظهرت لأن الفهرس شاف "القرن" قريب من "القرن 12".
المكتبي الخبير (Cross-Encoder) بياخد الـ 50 كتاب دول واحد واحد، يقرأ عنوان كل كتاب مع نظرة على فهرس المحتويات وهو ماسك ورقة السؤال في إيده اليمين، ويقول: "السبعة دول بالظبط اللي بيجاوبوا على سؤالك". ده بياخد دقيقتين زيادة، لكن النتيجة دقيقة جدًا.
الفكرة العلمية بدقة: Bi-Encoder بيشفّر السؤال والوثيقة كل واحد لوحده في vectors منفصلة، وبعدين بنحسب cosine similarity. Cross-Encoder بياخد السؤال والوثيقة كنص واحد متصل ويمررهم في Transformer واحد يطلع score من 0 لـ 1. التركيب ده اسمه "تفاعل كامل" (full token-level interaction) وبيخلّي النموذج يفهم العلاقة بين كل token في السؤال وكل token في الوثيقة. المرجع: Reimers و Gurevych، ورقة Sentence-BERT في EMNLP 2019.
الحل: Pipeline من مرحلتين
- المرحلة الأولى — Recall واسع: Vector Search يرجّع أعلى 50 chunk. الهدف سرعة + ما نفقدش وثيقة مهمة في الـ top-50.
- المرحلة الثانية — Precision عالي: Cross-Encoder يرتّب الـ 50 ويختار أعلى 10. الهدف دقّة الترتيب النهائي اللي بيوصل للـ LLM.