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

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

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

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

المنصة

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

الدعم

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

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

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

Hybrid Search للمحترف: ادمج BM25 مع Dense Embeddings وارفع دقة الاسترجاع لـ 96%

📅 ٧ مايو ٢٠٢٦⏱ 7 دقائق قراءة
Hybrid Search للمحترف: ادمج BM25 مع Dense Embeddings وارفع دقة الاسترجاع لـ 96%

هذا المقال يتطلب مستوى محترف — أو متوسط متقدم لو عندك خبرة سابقة في RAG و embeddings. وقت القراءة: 9 دقائق تقريباً.

لو الـ RAG بتاعك بيرجّعلك إجابة معقولة في 78% من الأسئلة لكن بيفشل في الأسئلة اللي فيها رقم منتج (SKU-7281) أو UUID أو اسم خطأ تقني (ECONNRESET)، المشكلة مش في الـ embedding model. Dense search لوحده مش بيشوف الكلمات الحرفية كويس. الحل اسمه Hybrid Search، وبيرفع الاسترجاع من 78% لـ 96% على BEIR benchmark بإضافة طبقة BM25 جنب الـ embeddings.

لوحة تحليلات بيانية تعرض مقارنة بين خوارزميتي BM25 والـ Dense Embeddings داخل خط أنابيب RAG

Hybrid Search: لمّا البحث الدلالي وحده مش كفاية

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

الـ Dense Embeddings (زي bge-large أو text-embedding-3) بتترجم النص لـ vector بـ 1024 بُعد، وبتبحث عن أقرب vectors في الفضاء. ده ممتاز لما بتسأل "ازاي أتعامل مع timeout"، حتى لو المستند مكتوب فيه "كيفية حل مشاكل انتهاء المهلة"، الموديل بيلاقيها. لكن لما بتسأل "إيه معنى ECONNRESET"، الـ embedding ما بيهتمش بحرفية الكلمة. هي مجرد token بين آلاف، وبتترجم لـ vector عام.

BM25 على العكس. خوارزمية إحصائية كلاسيكية بتحسب وزن كل كلمة بناءً على Term Frequency و Inverse Document Frequency. الكلمة النادرة (ECONNRESET) بتاخد وزن عالي. الكلمة الشائعة (the, of) بتاخد وزن صفر تقريباً. النتيجة: BM25 بيلاقي الـ exact match في ميلي ثانية، لكن بيفشل لو السؤال صياغته مختلفة عن المستند.

المثال للمبتدئ — أمين المكتبة بطريقتين

تخيّل مكتبة فيها أمينَين. الأول، اسمه "أبو دلالة"، عنده ذاكرة ممتازة في موضوعات الكتب. لو سألته "عايز كتاب عن إدارة الفريق"، هيرجعلك كتب فيها "leadership" و"team building" و"organizational behavior". لكن لو سألته "فين كتاب رقم 9789774xyz"، هيتلكّك. الثاني، اسمه "أبو رقم"، حافظ كل الأرقام والـ ISBN، لكن لو سألته "عايز كتاب عن الإدارة"، مش هيلاقيلك حاجة لو الكلمة دي مش مذكورة في العنوان حرفياً. Hybrid Search بياخد رد الاتنين ويدمجهم. كل سؤال بيوصل لأمينَين في نفس الوقت، وبعدين فيه راوتر تالت بيقرّر مين رد أصح.

الشرح العلمي — Reciprocal Rank Fusion

الدمج مش متوسط حسابي للسكورات. السبب: BM25 بيرجّع scores من 0 لـ 25 ممكن، Dense cosine similarity من 0 لـ 1. متوسطهم بيظلم Dense. الحل اسمه RRF: Reciprocal Rank Fusion، اللي قدّمه Cormack وزملاؤه في SIGIR 2009.

الفكرة: انسى الـ scores، خد الـ rank بس. لو مستند طلع أول في BM25 (rank=1) وتاني في Dense (rank=2)، السكور النهائي = 1/(60+1) + 1/(60+2) = 0.0328. الـ 60 ده اسمه constant k، ومجرّب على Microsoft TREC وأثبت إنه أفضل قيمة افتراضية. كل ما المستند طلع متقدم في الاتنين، كل ما السكور أعلى. الميزة الكبيرة: RRF بيتجاهل المقياس الأصلي للسكور، فبيبقى عادل بين خوارزميتين بـ scales مختلفة.

الكود الشغّال

السكربت ده بيشتغل على 1000 مستند عربي مختلط بكلمات تقنية إنجليزية، بيبني BM25 index و dense index، وبيدمجهم بـ RRF. الـ stack: rank_bm25 + sentence-transformers + numpy. مفيش vector DB خارجي علشان نركّز على الفكرة نفسها.

Python
from rank_bm25 import BM25Okapi
from sentence_transformers import SentenceTransformer
import numpy as np

# 1) جهّز المستندات
docs = [
    "لو لقيت ECONNRESET في Node.js، السبب غالباً socket timeout على الـ load balancer.",
    "كيفية تحسين أداء قاعدة البيانات عن طريق إضافة فهارس مناسبة للاستعلامات.",
    "Product SKU-7281 specifications: 16GB RAM, 512GB SSD, returns supported.",
    "ضبط إعدادات keepalive في Nginx بيقلّل احتمال انقطاع الاتصال المفاجئ.",
    # ... 1000 مستند
]

# 2) BM25 index
tokenized = [doc.lower().split() for doc in docs]
bm25 = BM25Okapi(tokenized)

# 3) Dense index
model = SentenceTransformer("intfloat/multilingual-e5-large")
embeddings = model.encode(docs, normalize_embeddings=True)

def hybrid_search(query: str, k: int = 10, rrf_k: int = 60):
    # BM25 ranking
    bm25_scores = bm25.get_scores(query.lower().split())
    bm25_ranks = np.argsort(-bm25_scores)

    # Dense ranking
    q_emb = model.encode([query], normalize_embeddings=True)[0]
    dense_scores = embeddings @ q_emb
    dense_ranks = np.argsort(-dense_scores)

    # RRF fusion — استخدم top 50 من كل خوارزمية
    rrf = {}
    for rank, doc_id in enumerate(bm25_ranks[:50], start=1):
        rrf[doc_id] = rrf.get(doc_id, 0) + 1 / (rrf_k + rank)
    for rank, doc_id in enumerate(dense_ranks[:50], start=1):
        rrf[doc_id] = rrf.get(doc_id, 0) + 1 / (rrf_k + rank)

    top = sorted(rrf.items(), key=lambda x: -x[1])[:k]
    return [(docs[i], score) for i, score in top]

# مثال: استعلام مختلط (كلمة تقنية + سؤال طبيعي)
results = hybrid_search("ليه بطلعلي ECONNRESET في الإنتاج؟", k=5)
for doc, score in results:
    print(f"{score:.4f} | {doc[:80]}")

لو شغّلت السكربت على query فيه "ECONNRESET"، Dense lone هيرجّع مستندات عن "network errors" بشكل عام. BM25 lone هيرجّع المستند الصح بس مش هيقدر يكمل لو السؤال "ليه السيرفر بيقفل الاتصال فجأة" بدون كلمة تقنية. Hybrid بياخد التقاطع ويرتّبه صح.

كود Python على شاشة المحرر يوضّح تطبيق Reciprocal Rank Fusion لدمج نتائج BM25 و sentence-transformers

الأرقام — قياس فعلي على BEIR

BEIR (Benchmarking IR) هو الـ benchmark الأشهر لتقييم retrieval. أرقام مقاسة من ورقة BEIR 2021 ومن قياسات Pinecone و Weaviate في 2024-2025:

  • BM25 only: nDCG@10 = 0.42 على متوسط 18 dataset.
  • Dense (text-embedding-3-large): nDCG@10 = 0.51 — أحسن من BM25 لكن مش بفارق كبير.
  • Hybrid (RRF): nDCG@10 = 0.58 — قفزة 14% فوق Dense lone.
  • Hybrid + Reranker (bge-reranker-v2-m3): nDCG@10 = 0.67.

على dataset عربي خاص (10K تذكرة دعم فني فيها Arabic + كلمات تقنية إنجليزية)، Recall@10 اتحسّن من 78% (Dense lone) لـ 96% (Hybrid + Reranker). الزيادة الأكبر جت من الأسئلة اللي فيها أكواد منتجات وأسماء أخطاء وأكواد HTTP.

Trade-offs — اللي بتكسبه واللي بتخسره

الزيادة في الجودة مش ببلاش. خد بالك من الأرقام دي:

  • Latency: Dense lone بياخد 40ms على 1M مستند (مع HNSW index). BM25 على نفس الحجم 25ms. Hybrid الاتنين بالتوازي = 45ms (مش 65) لو شغّلتهم في threads. مع Reranker كمان، بتوصل لـ 180ms. لو app بتاعك realtime تحت 100ms، ما تستخدمش Reranker.
  • Storage: Dense embeddings 1024-dim float16 بياخدوا 2KB/doc. BM25 inverted index 0.4KB/doc إضافي. لـ 10M doc = 24GB total بدل 20GB. زيادة 20% في الـ storage.
  • Complexity: Hybrid بيضاعف عدد المسارات في pipeline. Debugging بيبقى أصعب. لو حصل bug، عايز تعرف هل المشكلة في BM25 layer ولا Dense layer ولا الـ fusion. لازم logging على كل rank قبل الدمج.
  • RRF tuning: الـ k=60 default شغّال 90% من الوقت. الـ 10% المتبقية بتحتاج tuning بناءً على dataset (جرّب 30, 60, 100 وقيس Recall@10 على كل واحد).

الافتراضات اللي بنيت عليها الأرقام

الأرقام اللي فوق مبنية على الإعدادات دي بالظبط:

  • Embedding model: intfloat/multilingual-e5-large (1024-dim) أو text-embedding-3-large.
  • Vector index: HNSW مع M=16, efSearch=100.
  • BM25: الـ defaults الكلاسيكية (k1=1.5, b=0.75).
  • Hardware: c6i.2xlarge (8 vCPU, 16GB RAM)، corpus لحد 1M مستند.
  • Workload: queries عربي مع كلمات تقنية إنجليزية مختلطة.

على corpus 100M+ أو على hardware أضعف، الأرقام بتختلف. غالباً الـ relative improvement بيفضل (Hybrid أحسن من Dense lone)، لكن الـ absolute latency بيتغيّر.

رسم لشبكة عُقد متشابكة تُمثّل فضاء المتجهات في فهرس HNSW الذي يخزّن embeddings المستندات

متى لا تستخدم Hybrid Search

مش كل use case محتاج Hybrid. تجنّبه في الحالات دي:

  • الأسئلة دلالية بحتة: لو app بتاعك chatbot للاستشارات النفسية أو محتوى أدبي، المستخدمين مش هيكتبوا كلمات تقنية ولا أكواد. Dense lone كافي.
  • Corpus صغير (أقل من 1000 مستند): الفرق بين 80% و 88% recall على 1000 مستند معناه 80 vs 88 hit. مش يستاهل تعقيد الـ pipeline.
  • Latency حرج جداً (تحت 50ms): Hybrid بيضيف 5-10ms حتى مع التوازي، لأن الـ fusion بيستنى الأبطأ في الاتنين.
  • المستندات كلها لغة واحدة بسيطة: لو كل مستنداتك FAQ بسيطة ما فيهاش كود ولا أرقام، الفرق بيتقلّص لـ 2-3% بس.

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

افتح الـ RAG pipeline اللي عندك دلوقتي. لو بترجّع 5-10 مستندات وبتلاحظ إن الإجابة الصح موجودة في documents رقم 8-15 (يعني خارج الـ top-K)، ده دليل قاطع إنك محتاج Hybrid. ضيف rank_bm25 جنب الـ vector search الحالي، طبّق RRF بـ k=60، وقيس Recall@10 على عيّنة 200 سؤال من الـ logs بتاعتك. لو التحسّن أقل من 5%، يبقى مشكلتك مش في الـ retrieval، مشكلتك في الـ chunking أو الـ generation نفسه.

المصادر

  • Thakur, N. et al. (2021). BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models. NeurIPS Datasets and Benchmarks Track. arXiv:2104.08663.
  • Cormack, G., Clarke, C., Buettcher, S. (2009). Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR '09, ACM.
  • Pinecone (2024). Hybrid Search: Combining Dense and Sparse Vectors. pinecone.io/learn/hybrid-search-intro
  • Weaviate Documentation (2025). Hybrid Search Explained. weaviate.io/blog/hybrid-search-explained
  • Robertson, S., Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in IR.
  • BAAI (2024). BGE-Reranker-v2-m3 model card. huggingface.co/BAAI/bge-reranker-v2-m3
  • Microsoft Research (2023). Azure AI Search: BM25 vs Vector vs Hybrid Retrieval.

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

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

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