יישום חיפוש היברידי (וקטורי + טקסט מלא) עבור RAG

כשמאגר הנתונים שלך מכיל אלפי מסמכים, חיפוש סמנטי לעיתים קרובות מפספס מספרים מדויקים או מונחים נדירים, בעוד ששאילתות full-text מאבדות מילים נרדפות וניסוחים שונים. אנו מיישמים hybrid search, המשלב גישות vector ו-full-text, כך שה-RAG שלך מוצא גם התאמות מדויקות וגם מסמכים קשורים סמנטית. הצוות שלנו מספק פתרון turnkey עם תמיכה מתמשכת, המבטיח שליפה אמינה על הנתונים שלך.

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

בעת יישום מערכות RAG, דילמה נפוצה היא כיצד למצוא מסמך גם לפי משמעות וגם לפי מספר מדויק. חיפוש היברידי—שילוב של אחזור וקטורי (dense) וטקסט מלא (sparse/BM25) ואחריו מיזוג תוצאות—פותר בעיה זו. בפועל, חיפוש היברידי משיג ביצועים טובים יותר באופן עקבי מאשר כל אחת מהשיטות בנפרד על רוב מערכי הנתונים הארגוניים. לדוגמה, בפרויקט אחד חיפוש היברידי (RRF) שיפר את MRR@5 ב-12% יחסית לחיפוש וקטורי טהור תוך שמירה על דיוק גבוה למונחים מדויקים. אנו מיישמים פתרונות כאלה במפתח פתוח, עם אחריות לאיכות אחזור על הנתונים שלך. עלות פרויקט נעה בדרך כלל בין $5,000 ל-$10,000 בהתאם לגודל מערך הנתונים ומורכבותו. בקש ייעוץ על יישום חיפוש היברידי וקבל הערכת פרויקט.

מדוע חיפוש וקטורי בלבד אינו מספיק

ייצוגים וקטוריים (embeddings) צפופים ממצעים את הסמנטיקה—זה גם חוזק וגם חולשה. שאילתה כמו "חוזה מס' DA-2023-451" תהיה בעלת דמיון קוסינוס גבוה לחוזים בכלל, אך לא למסמך הספציפי לפי מספר. BM25 מוצא את המחרוזת המדויקת "DA-2023-451" באופן מיידי.

  • חיפוש וקטורי מתפקד גרוע עבור: מספרים מדויקים (חוזה, SKU, מספר סידורי), קיצורים וראשי תיבות ספציפיים, מונחים טכניים נדירים, שאילתות לחיפוש ציטוט מדויק.
  • BM25 מתפקד גרוע עבור: שאילתות מנוסחות מחדש (מילים נרדפות), מושגים דומים סמנטית עם מילים שונות, שאילתות חוצות שפות, תיאורים מעורפלים ("משהו על תשלום לאחר משלוח").

מדוע חיפוש היברידי עדיף על פני dense או BM25 בלבד

שילוב שתי הגישות יוצר סינרגיה: dense מכסה סמנטיקה, BM25 מכסה התאמות מדויקות. המקרה האמיתי שלהלן מראה שחיפוש היברידי RRF (ללא reranker) עולה על dense+reranker ב-MRR@5 (0.83 לעומת 0.80) וב-NDCG@5 (0.81 לעומת 0.77). בינתיים, hybrid+reranker משיג 0.89/0.87. במילים אחרות, יישום חיפוש היברידי משיג איזון בין דמיון סמנטי להתאמת מילות מפתח מדויקת. לפי בדיקת A/B שלנו על 400 שאילתות, חיפוש היברידי RRF עולה על dense+reranker בפקטור של 1.04 ב-MRR@5. עבור משימות רבות, זה מבטל את הצורך ב-reranker יקר.

אלגוריתמי מיזוג

Reciprocal Rank Fusion (RRF)—השיטה החזקה ביותר. RRF היא שיטת מיזוג שהוצעה על ידי Cormack et al. (2009)—ראה עוד בויקיפדיה.

קוד RRF
from collections import defaultdict

def reciprocal_rank_fusion(
    dense_results: list[tuple],  # [(doc_id, score), ...]
    sparse_results: list[tuple],
    k: int = 60  # RRF константа (обычно 60)
) -> list[tuple]:
    """
    RRF score = sum(1 / (k + rank_i)) по всем спискам
    k=60 стандартное значение (Cormack et al.)
    """
    scores = defaultdict(float)
    for rank, (doc_id, _) in enumerate(dense_results, 1):
        scores[doc_id] += 1 / (k + rank)
    for rank, (doc_id, _) in enumerate(sparse_results, 1):
        scores[doc_id] += 1 / (k + rank)
    return sorted(scores.items(), key=lambda x: -x[1])

Relative Score Fusion (RSF)—שילוב מנורמל:

קוד RSF
def relative_score_fusion(
    dense_results: list[tuple],
    sparse_results: list[tuple],
    alpha: float = 0.5  # Вес dense
) -> list[tuple]:
    """Нормализует оценки в [0,1] и взвешивает"""
    scores = defaultdict(float)

    # Нормализация dense
    if dense_results:
        max_d = max(s for _, s in dense_results)
        min_d = min(s for _, s in dense_results)
        for doc_id, score in dense_results:
            norm = (score - min_d) / (max_d - min_d + 1e-8)
            scores[doc_id] += alpha * norm

    # Нормализация sparse
    if sparse_results:
        max_s = max(s for _, s in sparse_results)
        min_s = min(s for _, s in sparse_results)
        for doc_id, score in sparse_results:
            norm = (score - min_s) / (max_s - min_s + 1e-8)
            scores[doc_id] += (1 - alpha) * norm

    return sorted(scores.items(), key=lambda x: -x[1])

השוואת אלגוריתמי מיזוג

פרמטר RRF RSF
עיקרון סכום של דרגות הפוכות סכום משוקלל של ציונים מנורמלים
רגישות לקני מידה נמוכה (משתמש רק בדרגה) גבוהה (דורש נורמליזציה)
כוונון פרמטר אחד k פרמטר alpha
חוסן גבוה בינוני (תלוי ב-alpha)
k/alpha מומלץ k=60 (אמפירי) alpha=0.5 (ברירת מחדל)

SPLADE: מקודד ספארס מתקדם

SPLADE (Sparse Lexical and Expansion Model) מייצר וקטורים ספארסיים עם הרחבה לקסיקלית—המודל לומד "להרחיב" את השאילתה עם מילים נרדפות ומונחים קשורים. לפי מדד BEIR, SPLADE עולה על BM25 פי 1.2–1.5 ב-NDCG@10.

from fastembed import SparseTextEmbedding

sparse_model = SparseTextEmbedding(
    model_name="prithivida/Splade_PP_en_v1"
)


def encode_sparse(text: str) -> dict:
    """Возвращает sparse вектор {token_id: weight}"""
    output = list(sparse_model.embed([text]))[0]
    return {
        "indices": output.indices.tolist(),
        "values": output.values.tolist(),
    }

SPLADE עולה על BM25 ברוב מדדי BEIR. עבור רוסית, אנו ממליצים על המודל from collections import defaultdict def reciprocal_rank_fusion( dense_results: list[tuple], # [(doc_id, score), ...] sparse_results: list[tuple], k: int = 60 # RRF константа (обычно 60) ) -> list[tuple]: """ RRF score = sum(1 / (k + rank_i)) по всем спискам k=60 стандартное значение (Cormack et al.) """ scores = defaultdict(float) for rank, (doc_id, _) in enumerate(dense_results, 1): scores[doc_id] += 1 / (k + rank) for rank, (doc_id, _) in enumerate(sparse_results, 1): scores[doc_id] += 1 / (k + rank) return sorted(scores.items(), key=lambda x: -x[1]) או גרסאות רב-לשוניות.

יישום עם Qdrant (דוגמה מעשית)

from qdrant_client import QdrantClient
from qdrant_client.models import (
    SparseVector,
    Prefetch,
    FusionQuery,
    Fusion,
    NamedVector,
    NamedSparseVector,
)
from fastembed import TextEmbedding, SparseTextEmbedding

dense_model = TextEmbedding("BAAI/bge-m3")  # Multilingual dense
sparse_model = SparseTextEmbedding("prithivida/Splade_PP_en_v1")

client = QdrantClient(url="http://localhost:6333")


def hybrid_search(query: str, top_k: int = 5) -> list[dict]:
    # Dense embedding
    dense_vec = list(dense_model.embed([query]))[0].tolist()

    # Sparse embedding
    sparse_output = list(sparse_model.embed([query]))[0]
    sparse_vec = SparseVector(
        indices=sparse_output.indices.tolist(),
        values=sparse_output.values.tolist(),
    )

    results = client.query_points(
        collection_name="hybrid_docs",
        prefetch=[
            Prefetch(query=dense_vec, using="dense", limit=50),
            Prefetch(query=sparse_vec, using="sparse", limit=50),
        ],
        query=FusionQuery(fusion=Fusion.RRF),
        limit=top_k,
        with_payload=True,
    )

    return [
        {"text": r.payload["text"], "source": r.payload["source"], "score": r.score}
        for r in results.points
    ]

מקרה מעשי: השפעת alpha על איכות האחזור

מהניסיון שלנו: בפרויקט עם 12,000 מסמכים במאגר ידע ארגוני (חוזים, תקנות, שאלות נפוצות), בדקנו 400 שאילתות מסוגים שונים. תוצאות:

תצורה MRR@5 NDCG@5 דיוק מונחים מדויקים
dense בלבד (BGE-M3) 0.74 0.71 0.58
BM25 בלבד 0.67 0.63 0.91
חיפוש היברידי RRF (k=60) 0.83 0.81 0.84
חיפוש היברידי RSF (α=0.6) 0.81 0.79 0.81
dense + Reranker 0.80 0.77 0.61
חיפוש היברידי + Reranker 0.89 0.87 0.86

חיפוש היברידי RRF ללא reranker כבר עולה על dense+reranker. השילוב של חיפוש היברידי+reranker מניב את התוצאה הטובה ביותר. לשם השוואה, שימוש ב-SPLADE כמקודד ספארס נותן שיפור MRR@5 של כ-0.03–0.05 לעומת BM25 עם אותה שיטת מיזוג.

כיצד לכוונן מיזוג RRF על מערך הנתונים שלך?

k אופטימלי עבור RRF: k=60 הוא ערך חזק אמפירית. k קטן מדי (10–20) נותן משקל גדול למיקומים הראשונים. גדול מדי (100+) מאזן את ההבדלים בין מיקומים. על נתונים אמיתיים, בדוק k∈{20, 40, 60, 80} על קבוצת אימות. עבור RSF, כוונן alpha מ-0.3 עד 0.7 בצעדים של 0.1.

תהליך יישום חיפוש היברידי שלב אחר שלב

  1. ביקורת על תכנית האחזור הנוכחית: ניתוח ה-embeddings שבשימוש, מחסן הווקטורים (vector DB), ומדדי איכות.
  2. בחירה והגדרה של מקודד ספארס: התקנת SPLADE או מקודד ספארס אחר המתאים לשפה ולתחום שלך.
  3. שילוב חיפוש כפול: הגדרת אינדוקס של וקטורים צפופים וספארסיים ב-Qdrant/Pinecone/Weaviate.
  4. יישום מיזוג: פריסת RRF או RSF עם פרמטרים ראשוניים (k=60, alpha=0.5).
  5. בדיקה ואופטימיזציה: הרצת השאילתות שלך, כוונון פרמטרים באמצעות מדדי MRR/NDCG.
  6. תיעוד ומסירה: תיאור התהליך, הדרכת הצוות, מסירת קוד וקבצי הגדרות.

מה כלול בפרויקט

  • קוד אינטגרציה לחיפוש היברידי לתוך מערכת ה-RAG שלך.
  • קבצי הגדרות עבור Qdrant/Pinecone.
  • תיעוד על התקנה ותפעול.
  • הדרכת צוות (סמינר מקוון של שעתיים).
  • אחריות לאיכות אחזור (מדדים נמדדים לפני/אחרי).
  • תמיכה חודש לאחר סיום הפרויקט.

צור קשר לקבלת הערכת פרויקט. קבל ייעוץ על יישום חיפוש היברידי ושפר את איכות האחזור של מערכת ה-RAG שלך.