المستوى: متوسط — مناسب لمن طبّق RAG بسيط قبل كده وعنده Vector DB شغّال (Pinecone، pgvector، Chroma، أو غيرهم).
Reranking في RAG: الطبقة المخفية اللي بتحوّل النتائج المتوسطة لإجابات دقيقة
لو الـ RAG بتاعك بيرجع 5 مستندات لكل سؤال والإجابة الصح فعلًا موجودة فيهم بس ترتيبها رقم 4، الموديل في الغالب بيتجاهلها ويرد من أول مستندين. الحل مش زيادة عدد المستندات، الحل طبقة اسمها Reranking بتيجي بين الاسترجاع والتوليد.
المشكلة باختصار
الـ Vector Search لوحده بيقيس قرب رياضي بين السؤال والمستند، مش جودة الإجابة. لو سألت "ازاي أحدّث PostgreSQL من 14 لـ 16 بدون downtime"، ممكن يرجع لك مستند فيه كلمات "Postgres" و"upgrade" متكررة 8 مرات بس بيتكلم عن الترقية لـ 13. الـ Cosine Similarity شافه قريب، لكن المحتوى نفسه مش مفيد. Reranking بيقرا السؤال والمستند مع بعض ويعطي درجة relevance حقيقية بدل بُعد رياضي تقريبي.
مثال للمبتدئ: السوبرماركت بموظفين بدل موظف واحد
تخيّل إنك طلبت من سوبرماركت "أوريو شوكولاتة 137 جرام". الموظف الأول بيجري على رف البسكويت ويرجع لك بأقرب 20 صنف بسرعة في 30 ثانية. ده الـ Vector Search: سريع، عام، بيشوف الشكل العام بس. الموظف التاني بياخد العشرين صنف، يقرا التفاصيل المكتوبة على كل علبة، ويرتّبهم لك من الأقرب للأبعد للوصف اللي قلته بالظبط. ده الـ Reranker: أبطأ شوية، لكن لو مكنش موجود ممكن تطلع بمنتج غلط. على المستندات بيحصل نفس الموقف بالظبط.
التعريف العلمي: Bi-Encoder مقابل Cross-Encoder
الـ Vector Search بيستخدم Bi-Encoder: موديل Transformer بيحوّل السؤال لمتجه ومستند لمتجه، وبيقاس بُعد الكوسين بين الاتنين. ده سريع جداً لأن إمبيدنق المستندات بيتعمل مرة واحدة وبيتخزّن في الـ Vector DB. عيبه إن السؤال والمستند ما بيشوفوش بعض جوّا الموديل — كل واحد فيهم بيتكوّد لوحده، فالعلاقات الدقيقة بينهم بتضيع.
الـ Reranker بيستخدم Cross-Encoder: موديل ياخد السؤال والمستند مع بعض كـ input واحد فاصل بينهم بتوكن خاص ([SEP])، وبيمرّرهم في كل طبقات الـ Transformer سوا. كده الـ self-attention بيشوف العلاقة بين كل توكن في السؤال وكل توكن في المستند. النتيجة: درجة جودة أعلى بكتير، لكن الثمن إنك لازم ترن الموديل لكل زوج (سؤال، مستند) على حدة بدل ما تخزّن إمبيدنج جاهز.
الحل التنفيذي في 3 خطوات
- هات أكتر من اللي محتاجه فعلاً من الـ Vector DB (مثلاً
k=20بدل 5). - مرّر الـ 20 مستند على Cross-Encoder عشان يدّيك score حقيقي لكل واحد.
- اختار أعلى 5 وابعتهم للموديل اللي هيرد على المستخدم.
from sentence_transformers import CrossEncoder
from anthropic import Anthropic
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3", max_length=512)
client = Anthropic()
def answer(question: str, vector_db) -> str:
# 1) استدعاء أوسع من الـ Vector DB
candidates = vector_db.search(question, k=20)
# 2) Reranking بـ Cross-Encoder
pairs = [[question, c.text] for c in candidates]
scores = reranker.predict(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
top5 = [c.text for c, _ in ranked[:5]]
# 3) تمرير المستندات المرتّبة للموديل
context = "\n\n".join(top5)
msg = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
messages=[{
"role": "user",
"content": f"اعتمد فقط على المصادر التالية:\n{context}\n\nالسؤال: {question}"
}],
)
return msg.content[0].text