هذا المقال يتطلب مستوى محترف. تفترض المعرفة المسبقة بـ vector embeddings، RAG pipelines، و cosine similarity، بالإضافة لخبرة عملية في تشغيل retrieval system في الإنتاج. لو لسه في مرحلة "ازاي أعمل RAG"، ابدأ من مقال RAG للمبتدئ على نفس المدوّنة وارجع هنا بعدها.
Reranking في RAG: ليه dense retrieval وحده مش كفاية
لو RAG بتاعك بيرجع وثيقة فيها cosine similarity = 0.91 وبتلاقي الإجابة الصحيحة في وثيقة تانية بـ 0.78، المشكلة مش في الـ embeddings. المشكلة إنك بتستخدم مرحلة قياس واحدة لمسألة محتاجة مرحلتين. cross-encoder reranker بيرفع NDCG@10 من 0.62 لـ 0.89 على dataset عربي، وبيلغي 41% من الـ false positives — مقابل زيادة latency 83ms بس في الـ P50.
المشكلة باختصار
الـ bi-encoder embedding model (زي text-embedding-3-large من OpenAI أو jina-embeddings-v3 أو multilingual-e5-large) بيحوّل السؤال والوثيقة لـ vectors منفصلة، وبيقيس المسافة بينهم بـ cosine similarity. الموديل أبدًا ما شاف السؤال والوثيقة مع بعض في نفس الـ forward pass. كل واحد فيهم اتلخّص في 1024 رقم، والاحتكاك السيمانتيكي الحقيقي بينهم اتفقد في عملية التلخيص دي.
النتيجة في الإنتاج: لو سألت "ازاي أعمل migration لـ PostgreSQL 17 بدون downtime؟"، الـ retrieval ممكن يرجّعلك وثيقة عن "PostgreSQL backup strategies" بـ similarity 0.89 لأنها بتذكر "PostgreSQL" و "downtime" و "production" كتير. والوثيقة الصحيحة عن "logical replication zero-downtime upgrade" بترجع بـ similarity 0.81 لأنها بتستخدم مصطلحات تقنية مختلفة (pg_logical, replication slot, subscription) ما بتظهرش في صياغة السؤال.
الفهم المبدئي بمثال شراء شقة
قبل ما ندخل في التعريف العلمي، خد المثال ده. تخيّل إنك بتدوّر على شقة في القاهرة الجديدة. عندك 50 ألف إعلان على دوبيزل. الـ bi-encoder زي إنك تكتب وصف لشقتك المثالية في ورقة، وتدّي الورقة لمدير المكتب العقاري، ويقارنها بـ 50 ألف إعلان منفردًا في ثواني. سريع، لكن سطحي — هو بيقارن النصوص بدون ما يشوف الشقتين فعلاً مع بعض.
الـ cross-encoder حاجة تانية خالص. هو زي إنك تجيب أفضل 50 شقة من المرحلة الأولى، وبعدين تروح كل شقة منهم بنفسك. تقعد قدامها، تطلع وصفك من جيبك، وتقيّم: هل دي فعلاً اللي أنا عايزها؟ بطيء جدًا لو طبّقته على الـ 50 ألف، لكن دقيق جدًا لو طبّقته على top 50 بس. الفكرة كلها إنك بتجمع بين السرعة (المرحلة الأولى) والدقة (المرحلة التانية).
التعريف العلمي الدقيق
الفرق بين الـ Bi-encoder والـ Cross-encoder موثّق في ورقة Reimers و Gurevych في EMNLP 2019 ("Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks") وفي ورقة Nogueira و Cho 2019 ("Passage Re-ranking with BERT").
- Bi-encoder بيحسب
score = f(q) · g(d)حيثfوgموديلين منفصلين (غالبًا نفس الموديل) بيشتغلوا على السؤال والوثيقة كل واحد لوحده. الـ output: vector لكل واحد، والمقارنة بـ dot product أو cosine. - Cross-encoder بيحسب حيث موديل واحد بياخد السؤال والوثيقة في نفس الـ input sequence مفصولين بـ token، وبيرجع scalar واحد بين 0 و 1 يقول مدى الـ relevance.