مستوى المقال: متوسط. الافتراض إنك عامل RAG شغّال وفاهم يعني إيه embeddings وبحث متجهات (cosine similarity). مش مطلوب تكون خبير في الترانسفورمرز.
لو الـ RAG بتاعك بيرجّع للنموذج مستندات قريبة من السؤال بس مش أدقّها، المشكلة غالباً مش في الـ embeddings. المشكلة إنك واقف عند خطوة واحدة، والمفروض تحط خطوة تانية اسمها reranking. الخطوة دي بترفع دقة المستندات اللي بتوصل للنموذج بشكل ملموس، وهنقيسها بالأرقام تحت.
المشكلة باختصار
بحث المتجهات (vector search) سريع جداً وبيدوّر في ملايين المستندات في ملّي ثانية. لكنه بيقيس تشابه تقريبي، مش صلة حقيقية بالسؤال. النتيجة إن أول 50 مستند بيرجّعهم فيهم الإجابة الصح، بس مش بالضرورة في أول 5. ولمّا تبعت أول 5 بس للنموذج، بيفوّت الإجابة أو يهلوس على مستند قريب في المعنى وغلط في التفاصيل.
المفهوم بمثال: أمين المكتبة السريع مقابل الخبير
تخيّل مكتبة فيها مليون كتاب، وجاي تسأل عن سؤال دقيق. لو رحت للخبير المتخصص وقلتله اقرا المليون كتاب ورتّبهم لي حسب سؤالي، هيخلّص بعد سنة. مش عملي.
الحل بيبقى على مرحلتين. الأول تروح لأمين مكتبة سريع، بيبصّ على عناوين الكتب بسرعة ويطلّعلك 50 كتاب موضوعهم قريب. هو سريع لكن سطحي، فممكن يحط كتاب مش مظبوط في أول القايمة. بعدها تاخد الـ 50 دول وتديهم للخبير، اللي بيقرا كل واحد وهو حاطط سؤالك في دماغه بالظبط، ويعيد ترتيبهم حسب الصلة الحقيقية. انت ما طلبتش من الخبير يقرا مليون كتاب، طلبت منه يرتّب 50 بس. ده معنى retrieve ثم rerank.
نفس المثال بشكل علمي: bi-encoder مقابل cross-encoder
أمين المكتبة السريع هو الـ bi-encoder. بيحوّل السؤال لمتجه، وبيحوّل كل مستند لمتجه بشكل منفصل، وبعدين يقيس المسافة بينهم (cosine similarity). لإن متجهات المستندات بتتحسب مرة واحدة وتتخزن في فهرس زي FAISS أو pgvector، البحث بيبقى سريع جداً حتى في الملايين. الثمن: السؤال والمستند عمرهم ما شافوا بعض جوّه النموذج، فالفروق الدقيقة بتضيع.
الخبير هو الـ cross-encoder. بياخد السؤال والمستند مع بعض في مدخل واحد، ويمرّرهم في الترانسفورمر بـ attention كاملة بين كل كلمة في السؤال وكل كلمة في المستند، ويطلّع رقم واحد: درجة الصلة. الدقة أعلى بكتير، لكن مفيش فهرس تحسبه مقدّماً، لإن الدرجة بتعتمد على السؤال نفسه. عشان كده مينفعش تشغّله على مليون مستند لكل استعلام، بس ينفع جداً على 50.
القاعدة: الـ bi-encoder بيرشّح بسرعة، والـ cross-encoder بيحكم بدقة. الاتنين مع بعض، مش واحد بدل التاني.
الحل عملياً: أضف reranker فوق بحثك الحالي
مش هتغيّر بحث المتجهات. هتضيف خطوة بعده. الخطوات:
- سيب بحث المتجهات يرجّع top-k أكبر شوية من اللي محتاجه، مثلاً 50 بدل 5.
- مرّر الـ 50 مرشح على cross-encoder مع السؤال، وخُد درجة صلة لكل واحد.
- رتّب تنازلياً بالدرجة، وخُد أول 5 بس اللي تبعتهم للنموذج.
ده كود Python شغّال على مكتبة sentence-transformers ونموذج reranker صغير (حوالي 80 ميجا). بتحمّل نموذج cross-encoder، وتبني أزواج (سؤال، مستند) لكل المرشحين، وتطلب predict عشان يدّيك درجة صلة لكل زوج، وبعدين ترتّب تنازلياً وتاخد أول 5: