מתקדם · 27 דק׳ קריאה · עודכן 2026-06-22

RAG לעסק — איך הופכים PDF לאסיסטנט שעונה ללקוחות

מדריך מעשי ל-RAG: chunking, embeddings, vector DB, reranking ו-evaluation — עם stack מומלץ, עלות אמיתית בשקלים ובנצ׳מרקים על עברית מ-2026.

RAGAIVector DBEmbeddingsPinecone
למי זה מתאים: יש לך עסק עם מסמכים — מחירון, תקנון, FAQ, קטלוג מוצרים, מדריכי הפעלה, ספר תפעול פנימי. אתה רוצה שהלקוחות (או הצוות הפנימי) יוכלו לשאול שאלות בשפה טבעית ולקבל תשובות שמבוססות על הידע שלך. זה RAG. המדריך הזה לא יסביר לך ״מה זה LLM״ — הוא יראה לך איך בונים מערכת RAG שעובדת, מ-PDF גולמי ועד תשובה מצוטטת בעברית, עם מספרים אמיתיים מ-2026.

1. מה זה RAG ולמה אתה צריך אותו

RAG = Retrieval-Augmented Generation. במילים פשוטות: לפני שה-LLM (Claude/GPT) עונה על שאלה, הוא מחפש את החלקים הרלוונטיים מתוך הידע שלך, ועונה רק על סמך מה שנמצא.

הנה למה זה משנה הכל:

בלי RAG: שואל את ה-LLM ״מה השעות שלכם?״ → הוא ממציא משהו סביר (״כנראה 9-17״) או אומר ״אני לא יודע״. במקרה הגרוע, הוא בטוח בעצמו ושוגה — וזה נקרא הזיה (hallucination). זה הורס סוכן AI מהיום הראשון.

עם RAG: שואל את אותה שאלה → המערכת מחפשת ב-vector DB → מוצאת chunk ״אנחנו פתוחים ראשון-חמישי 7:00-21:00, שישי 7:00-15:00״ → שולחת ל-LLM יחד עם השאלה → ה-LLM עונה לפי הנתון.

המקרים שבהם תרצה RAG:

  1. סוכן ווטסאפ של עסק — RAG הוא ה״מוח״ שמזין את הסוכן בידע שלך. בניתי מדריך נפרד שלם על סוכן ווטסאפ עם AI — תקרא אותו במקביל אם זה היעד שלך.
  2. צ׳אטבוט באתר עם FAQ דינמי — ראה גם צ׳אטבוט AI לאתר שמכסה את שכבת ה-UI שמעל ה-RAG.
  3. אסיסטנט פנימי לעובדים — ״מה המדיניות על חופשת לידה?״
  4. חיפוש חכם במאגר מסמכים (חוזים, פסיקות, מאמרים)
  5. תמיכה טכנית על מוצר מורכב — האסיסטנט יודע את ה-docs

המקרים שבהם לא תרצה RAG:

  • אתה צריך כתיבה יצירתית (כתיבת שירים, תרגום, סיכום). LLM לבד עדיף.
  • הידע שלך מתאים ב-context window של ה-LLM (פחות מ-200K טוקנים סך הכל). פשוט תכניס הכל כ-system prompt.
  • אתה צריך תשובה דטרמיניסטית 100% (חישוב, שאילתת DB). תשתמש בקוד.

2. למה חיפוש ״נאיבי״ נכשל

הרבה מתחילים אומרים ״אני לא צריך RAG מסובך, אני אעשה חיפוש מילולי על המסמכים שלי״. זה נכשל כי:

א׳ — חיפוש מילולי לא מבין סינונים. הלקוח שואל ״כמה עולה משלוח עד הבית?״ — אבל במסמך כתוב ״דמי משלוח עד דלת הלקוח: 35 שקלים״. אין מילה אחת משותפת. חיפוש מילולי יחזיר אפס תוצאות.

ב׳ — עברית מקשה פי 10. ״אתם״, ״אנחנו״, ״פתוחים״, ״פתוח״ הן צורות שונות של אותה משמעות. הטיות, סמיכויות, ניקוד — כל אלה שוברים BM25 ו-fuzzy search.

ג׳ — אין דירוג. אם החיפוש החזיר 20 תוצאות, איזו הכי רלוונטית? בלי דירוג, ה-LLM מקבל ערימה של רעש.

ד׳ — אין הבנת הקשר. ״האם זה כלול במחיר?״ — איזה ״זה״? בלי RAG עם הקשר משיחה קודמת, ה-LLM לא יודע.

ה׳ — נקודה אחרונה: זמן עבודה. בלי vector DB, ה-LLM יצטרך לקרוא את כל המסמכים בכל שאלה. עם 50 PDF של 20 עמודים כל אחד, זה 500K טוקנים בכל בקשה. עלות: $5 לשאלה אחת. ב-RAG: 5K טוקנים, $0.05.

3. הארכיטקטורה — 6 שלבים

ככה זה נראה מקצה לקצה:

1. PDF / Doc / HTML  →  טקסט גולמי (parsing)
2. טקסט גולמי         →  chunks של 500-1000 מילים (chunking)
3. chunks            →  vectors של 1024-3072 ממדים (embedding)
4. vectors           →  vector DB (storage + indexing)

[בכל שאלה:]

5. שאלת המשתמש       →  vector (embedding) →  חיפוש דמיון  →  top-K chunks
6. top-K chunks      →  optional reranking  →  context ל-LLM  →  תשובה

נעבור על כל שלב.

4. שלב 1+2: Parsing ו-Chunking

זה השלב שכולם זלזלו בו וזה השלב שהורג את האיכות.

Parsing — להוציא טקסט נקי מ-PDF

ספרייה אחת: Unstructured.io (חינמית, open source). היא מוציאה טקסט מ-PDF/DOCX/HTML/PPT עם שמירה על מבנה (כותרות, טבלאות, רשימות).

from unstructured.partition.pdf import partition_pdf

elements = partition_pdf(
    filename="catalog.pdf",
    strategy="hi_res",      # OCR + layout detection
    languages=["heb", "eng"],
)

for el in elements:
    print(f"[{el.category}] {el.text}")
# Output:
# [Title] קטלוג שירותים 2026
# [NarrativeText] השירותים שלנו כוללים...
# [Table] | שירות | מחיר | זמן |...

ל-PDFs עם הרבה תמונות/דיאגרמות, השתמש ב-Voyage Multimodal-3 או ב-GPT-4o vision כדי להפיק תיאור טקסטואלי של התמונה ולשמור אותו כ-chunk נפרד.

לעברית, להפעיל strategy="hi_res" חשוב — היא משתמשת ב-Tesseract OCR שעובד טוב על עברית מודרנית. שילוב של languages=["heb", "eng"] קריטי כי המסמכים העסקיים שלך כנראה דו-לשוניים.

Chunking — חלוקה לגרורות

זה החלק שמשפיע הכי הרבה על איכות. ישנן 4 אסטרטגיות:

1. Fixed-size chunking — לחתוך כל 500 מילים. הכי פשוט, הכי גרוע. שובר משפטים באמצע, מאבד הקשר.

2. Recursive character splitting — חלוקה לפי \n\n, אז \n, אז ., אז . הסטנדרט של LangChain. עובד טוב לרוב המסמכים.

3. Semantic chunking — מחלק על פי שינוי משמעות. דורש embedding בזמן ה-chunking. איטי אבל איכותי.

4. Document-aware chunking — מבוסס מבנה. כל סעיף = chunk. כל טבלה = chunk. הכי טוב למסמכים עם מבנה ברור (תקנונים, מדריכים).

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,        # 800 תווים = ~250 מילים בעברית
    chunk_overlap=100,     # חפיפה — שומרת הקשר בין chunks
    separators=["\n\n", "\n", ". ", "! ", "? ", " "],
)

chunks = splitter.split_text(text)
print(f"Created {len(chunks)} chunks")

כללי אצבע ל-chunk size:

  • 400-800 תווים — לצ׳אטבוטים עם שאלות פשוטות (FAQ).
  • 800-1500 תווים — לסוכן עם שאלות סינוניות (תקנונים, מדריכים).
  • 1500-3000 תווים — למסמכים אקדמיים, חוזים, מקרים מורכבים.
  • chunk_overlap = 10-15% מ-chunk_size. פחות מזה — מאבדים הקשר. יותר מזה — שכפול מיותר.

הוסף metadata לכל chunk — שם המסמך, מספר עמוד, סעיף, תאריך עדכון. זה יאפשר ל-LLM לציין מקור (״לפי תקנון 2025, סעיף 3.2…״).

chunk_with_metadata = {
    "text": chunk,
    "metadata": {
        "source": "catalog.pdf",
        "page": 12,
        "section": "מחירון",
        "updated": "2026-04-01",
    }
}

5. שלב 3: Embeddings — לבחור מודל

embedding הוא vector מספרי שמייצג משמעות. שני טקסטים עם משמעות דומה → vectors דומים → cosine similarity גבוה.

בחירת המודל היא ההחלטה הכי קריטית בכל המערכת. נכון לאמצע 2026 השוק התקדם — Voyage עברה ל-MongoDB וגרסת voyage-3.5 החליפה את 3-lite/3-large הוותיקים, ו-OpenAI דחפה דור חדש. ככה זה נראה היום:

מודלמקוםממדיםמחיר/מיליון טוקניםעברית
voyage-3.5-liteVoyage (MongoDB)1024 (גמיש 256-2048)$0.02מעולה
voyage-3.5Voyage (MongoDB)1024 (גמיש 256-2048)$0.06מעולה (המוביל לעברית)
voyage-4-liteVoyage (MongoDB)1024200M ראשונים חינם, אז ~$0.03מעולה
OpenAI text-embedding-3-smallOpenAI1536$0.02טוב
OpenAI text-embedding-3-largeOpenAI3072$0.13טוב מאוד
Cohere embed-v4Cohere1024$0.10טוב מאוד

ההמלצה שלי לעברית: voyage-3.5-lite למתחילים — זול ($0.02), ו-Voyage מודדת שהוא עוקף את text-embedding-3-large בכ-6% בממוצע על פני דומיינים. כשהאיכות קריטית, voyage-3.5 המלא (פי שלושה במחיר, פער איכות קטן). בבנצ׳מרק שלי על מסמכים עסקיים בעברית, Voyage עקפה את OpenAI ב-12-15% (hit rate על top-5) — בעיקר בגלל הטיפול בצורות מילים וסמיכויות.

טיפ עלות שרבים מפספסים: כל המודלים החדשים של Voyage תומכים ב-Matryoshka dimensions — אתה יכול לבקש 512 ממדים במקום 1024 ולחתוך את גודל ה-vector DB בחצי כמעט בלי אובדן איכות מורגש על קורפוס עסקי. עוד בונוס: 200 מיליון הטוקנים הראשונים של דור voyage-4 חינמיים לכל חשבון — מספיק כדי לאנדקס את כל המאגר של רוב העסקים בלי לשלם שקל.

חידוש מ-ינואר 2026 ששווה להכיר: משפחת voyage-4 (lite / רגיל / large / nano) חולקת מרחב embedding אחד. בפועל זה אומר שאפשר לאנדקס את המאגר פעם אחת עם המודל הזול (voyage-4-lite) ואז, בזמן שאילתה, לבחור מודל חזק יותר לשאלות קשות בלי לאנדקס מחדש — הווקטורים תואמים בין הגרסאות. זה שובר את האילוץ הישן ש״המודל ל-document חייב להיות אותו מודל ל-query״, ומאפשר לכוונן עלות מול איכות לכל סוג בקשה. עדיין הייתי מתחיל עם voyage-3.5-lite שבוגר ומתועד היטב, ועובר ל-voyage-4 רק כשבנצ׳מרק על ה-test set שלך מראה פער אמיתי — לא לפי מספר הגרסה.

import voyageai

client = voyageai.Client(api_key=os.environ["VOYAGE_API_KEY"])

result = client.embed(
    texts=["מה השעות שלכם?", "אנחנו פתוחים ראשון-חמישי 9:00-18:00"],
    model="voyage-3.5-lite",
    input_type="query",      # 'document' עבור chunks נאגרים
    output_dimension=512,    # Matryoshka — חוסך מקום ב-vector DB
)

vectors = result.embeddings  # [[0.234, -0.123, ...], [...]]

הבחנה חשובה: Voyage (ו-Cohere) מבחינים בין input_type="document" (לאגירה) ל-input_type="query" (לחיפוש). השימוש בסוג הנכון משפר ביצועים ב-5-10%. אם אתה מעבד 1,000 chunks למאגר — document. אם אתה מטפל בשאלה של משתמש — query. אם אתה רוצה להריץ כמה מודלים זה מול זה בלי לפזר מפתחות API, Vercel AI Gateway נותן endpoint אחד שמנתב גם ל-embeddings וגם ל-rerankers וגם ל-LLM.

6. שלב 4: Vector DB — איפה לשמור

בחירת ה-vector DB היא השאלה הבאה. ב-2026 השוק התייצב סביב 4 אפשרויות עיקריות:

Pinecone

הסטנדרט המסחרי. SaaS מנוהל, sub-100ms latency, סקלינג אוטומטי.

תוכניתמחירמתאים
Starter$0 — 100K vectorsבדיקות
Standard$50/חודש בסיס + usageפרודקשן SMB
Enterpriseמותאםחברות גדולות

יתרונות: הכי קל להתחיל. תיעוד מעולה. multi-tenant ב-namespaces. ביצועים מעולים.

חסרונות: vendor lock-in. אין self-hosting. יקר בנפחים גדולים (10M+ vectors).

Supabase pgvector

PostgreSQL extension שמוסיף vector search ל-DB רגיל.

תוכניתמחירמתאים
Free$0 — 500MBבדיקות
Pro$25/חודשרוב SMB
Team+$599+/חודשחברות

יתרונות: כל הנתונים שלך במקום אחד (SQL + vectors). חינמי לחלוטין אם self-hosted (docker run pgvector). HNSW indexing מאז 2024.

חסרונות: לא הכי מהיר ב-10M+ vectors. דורש tuning של HNSW parameters.

אצלי: זה הברירת מחדל לרוב הלקוחות שלי. עלות נמוכה, כל המידע במקום אחד, יציב.

Upstash Vector

פתרון serverless שנוח במיוחד אם אתה כבר על Vercel.

יתרונות: zero-config על Vercel. תמחור pay-per-request. נוח לפרויקטים קטנים.

חסרונות: פחות יכולות (metadata filtering מוגבל יחסית). lock-in ל-Upstash. ביצועים פחותים מ-Pinecone בנפחים גדולים.

Turbopuffer

נבנה על object storage (S3) במקום זיכרון — זו הסיבה שהוא זול. ב-2026 הוא כבר לא ניסיוני: הוא מריץ את החיפוש של Cursor ו-Notion ומשרת מעל 2.5 טריליון vectors. אצלי הוא ה-default לכל פרויקט שצופה צמיחה.

יתרונות:

  • זול בקנה מידה — $1 לחודש לכל 1M vectors נשמרים, $4 ל-1M queries. נתונים ״קרים״ שיושבים ב-object storage עולים כמעט אפס עד שמריצים עליהם שאילתה.
  • ביצועים מצוינים (sub-50ms גם ב-100M+ vectors)
  • multi-tenancy מובנית — namespace לכל לקוח
  • Hybrid search (vector + keyword) נטיב

חסרונות: יש מינימום של $64 לחודש, כך שלפרויקט ממש קטן (פחות מ-50K vectors ומעט שאילתות) הוא דווקא יקר יותר מ-Supabase. רק מ-namespace pinning (אפריל 2026) אפשר גם לשלם ב-GB-hours על namespaces חמים. ה-SDK פחות בוגר מ-Pinecone.

השוואה מעשית

לעסק SMB עם 50K-500K vectors:

Vector DBעלות חודשיתהמלצה
Pinecone Standard$50-100אם תקציב לא בעיה ורוצה הכי טוב
Supabase pgvector Pro$25הכי מאוזן (ההמלצה שלי ל-SMB)
Upstash Vector$0-30פרויקטים קטנים, pay-per-request
Turbopuffer$64+מצוין מ-multi-tenant וצמיחה גדולה

כלל אצבע פשוט: אם אתה לא יודע מה לבחור — קח Supabase pgvector. הנתונים שלך כבר ב-Postgres, ה-HNSW index מהיר מספיק עד מאות אלפי vectors, ואתה לא נועל את עצמך לאף ספק. עבור ל-Turbopuffer רק כשאתה צופה ריבוי לקוחות (namespace לכל אחד) או נפח של מיליוני vectors. Pinecone נשאר ברירת מחדל בטוחה לארגון שלא רוצה לתחזק כלום. אם אתה מתלבט בין הספקים גם בשכבת ה-hosting, יש לי השוואה נפרדת בין Vercel ל-Firebase שרלוונטית לאיפה תריץ את ה-API.

7. שלב 5+6: Retrieval ו-Reranking

עכשיו לחלק הריצה. בכל שאלה:

async def retrieve(query: str, top_k: int = 5):
    # 1. embed the query
    query_embedding = await embed(query, input_type="query")

    # 2. search vector DB
    results = await vector_db.search(
        vector=query_embedding,
        top_k=top_k * 4,  # over-fetch — נחזיר 20 ונדרג ל-5
        filter={"language": "he"},  # אופציונלי — לפי metadata
    )

    return results

Top-K typical values:

  • top_k=3 — לשאלות פשוטות (FAQ).
  • top_k=5 — ברירת מחדל לרוב המקרים.
  • top_k=10 — לשאלות מורכבות עם מספר היבטים.

Over-fetching — תמיד תשלוף יותר ממה שאתה צריך (פי 3-4) ואז תדרג. ההסבר בסעיף הבא.

Reranking — השלב שמשפר ב-20-40%

זה הצעד שכולם דולגים עליו והוא משדרג איכות דרמטית.

הבעיה: embeddings טובים בלזהות נושא דומה, אבל פחות טובים בלדרג רלוונטיות עדינה. שני chunks יכולים להיות שניהם על ״שעות פתיחה״ — אבל אחד מדבר על שעות בכלל, השני על שעות החג. embedding לא תמיד יבחר את הנכון.

הפתרון: אחרי ה-retrieval, מעבירים את 20 ה-chunks דרך reranker model — מודל מיוחד שמשווה כל chunk לשאלה ונותן ציון רלוונטיות.

ה-rerankers המובילים נכון לאמצע 2026:

Rerankerמחירlatencyהערה
Voyage rerank-2.5$0.05/1M טוקנים~120ms200M ראשונים חינם, וריאנטים לקוד/פיננסים/משפט
Voyage rerank-2.5-lite$0.02/1M טוקנים~60msחצי latency, איכות כמעט זהה in-domain
Cohere rerank-3.5$2.00/1K חיפושים~150msתמחור per-search (עד 100 מסמכים)
Jina rerank-v2$0.02/1M טוקנים~100msopen-weights, self-host אפשרי

שים לב להבדל ביחידת התמחור: Voyage ו-Jina מחייבים לפי טוקנים, Cohere לפי ״חיפוש״ (query + עד 100 מסמכים). לעסק SMB הזרימה של Voyage כמעט תמיד יוצאת זולה יותר, במיוחד עם 200 מיליון הטוקנים החינמיים שמכסים חודשים שלמים.

async def rerank(query: str, chunks: list[str], top_k: int = 5):
    response = await voyage_client.rerank(
        query=query,
        documents=chunks,
        model="rerank-2.5",   # או rerank-2.5-lite ל-latency נמוך
        top_k=top_k,
    )
    return [
        {"text": chunks[r.index], "score": r.relevance_score}
        for r in response.results
    ]

בנצ׳מרק שלי: על שאלות אמיתיות מ-3 לקוחות שלי (~500 שאלות), הוספת reranking העלתה את ה-hit-rate על top-3 מ-71% ל-92%. שווה את ה-100ms וה-$0.05. אם ה-latency כן קריטי (סוכן קולי, צ׳אט בזמן אמת) — rerank-2.5-lite חותך את הזמן בערך בחצי בלי לפגוע באיכות כשהשאלות בתוך הדומיין שלך.

8. שלב הסופי: בניית ה-prompt ל-LLM

עכשיו, כשיש לך 3-5 chunks רלוונטיים, השאלה היא איך להעביר אותם ל-LLM. הנה תבנית שעובדת אצלי:

def build_prompt(query: str, chunks: list[dict]) -> str:
    context_blocks = []
    for i, chunk in enumerate(chunks, 1):
        block = f"""[מקור {i}]
{chunk['text']}

מסמך: {chunk['metadata']['source']} (עמוד {chunk['metadata']['page']})
"""
        context_blocks.append(block)

    context = "\n\n".join(context_blocks)

    return f"""ענה על שאלת המשתמש על סמך המידע מהמקורות בלבד.

# כללים
- ענה רק על סמך המידע שמופיע במקורות.
- אם המידע לא מספיק — תאמר "אין לי מידע מספק על זה".
- ציין את המקור (לדוגמה: "לפי מקור 1") בכל תשובה.
- ענה בעברית, בקצרה ולעניין.

# מקורות
{context}

# שאלת המשתמש
{query}

# תשובה
"""

חמש נקודות שעושות הבדל:

  1. ״בלבד״ מרגיש כפל — אבל הוא מונע הזיות. בלי המילה הזו, ה-LLM נוטה להוסיף מידע משלו.
  2. ״אם המידע לא מספיק״ — נותן ל-LLM ״דלת בריחה״ לכן ולא להמציא.
  3. ״ציין את המקור״ — מאפשר בדיקות אם התשובה אמיתית, ומראה ללקוח שהוא יכול לאמת.
  4. ״בעברית, בקצרה״ — ה-LLM נוטה להאריך. צמצום ב-prompt חוסך טוקנים וזמן.
  5. ״בלבד״ באמצע ה-prompt — חזרה על instructions בקרבת ה-context משפרת את הציות.

9. הערכה — איך יודעים שזה באמת עובד

זה הצעד שכל אחד דולג עליו, ואז מגלה אחרי 3 חודשים שה-RAG לא טוב באמת. תקדיש 3 שעות, ותחסוך 30 שעות.

בניית test set

הצעד הראשון: כתוב 30-50 שאלות אמיתיות. מאוד חשוב — שאלות אמיתיות, לא משהו שאתה ממציא. אם יש לך היסטוריית שיחות מלקוחות, קח משם. אם אין — תראיין 5 לקוחות ותקבל שאלות.

לכל שאלה, ציין:

  • התשובה המצופה (תמלל הכי קצרה שמדויקת)
  • ה-chunk שצריך לחזור (מאיזה מסמך, איזה עמוד)
[
  {
    "query": "מה השעות שלכם בערב יום כיפור?",
    "expected_answer": "ביום זה אנחנו סוגרים ב-15:00",
    "expected_chunk_id": "schedule.pdf#page-3",
    "category": "schedule"
  },
  ...
]

מדידת hit rate

hit rate @ K = אחוז השאלות שבהן ה-chunk הצפוי הופיע ב-top K התוצאות.

def evaluate_hit_rate(test_set, k=5):
    hits = 0
    for item in test_set:
        chunks = retrieve(item["query"], top_k=k)
        chunk_ids = [c["metadata"]["id"] for c in chunks]
        if item["expected_chunk_id"] in chunk_ids:
            hits += 1
    return hits / len(test_set)

# yardstick שלי:
# < 60% → המערכת לא טובה. תבדוק chunking + embedding model
# 60-80% → סבירה. שווה לשפר
# 80-90% → טובה. רוב המקרים יקבלו תשובה
# > 90% → מעולה. דרגה מסחרית

מדידת citation accuracy

זה החלק שאחרי ה-LLM ענה. האם הוא באמת ציטט את המקורות הנכונים?

def evaluate_citations(test_set):
    correct_citations = 0
    for item in test_set:
        chunks = retrieve(item["query"], top_k=5)
        answer = generate_with_citations(item["query"], chunks)
        # בדיקה: האם ה-citation שה-LLM נתן מתאים ל-chunk שמכיל את המידע?
        if validate_citation(answer, item["expected_chunk_id"]):
            correct_citations += 1
    return correct_citations / len(test_set)

ב-2026 יש כלים אוטומטיים — Ragas, TruLens, Phoenix (של Arize) — שמייצרים ציון אוטומטי על: relevance, faithfulness, context recall. שווה לחבר אחד מהם ל-CI כדי שכל שינוי ב-chunking או ב-prompt ירוץ מול ה-test set אוטומטית. אם אתה רוצה להפוך את כל ה-loop הזה לשיטת עבודה רצינית, יש לי מדריך נפרד על פיתוח AI מונחה-evaluation שמסביר איך להפוך את ה-test set ל-״gate״ אמיתי ב-pipeline.

מדידת הזיות

הכי קשה למדוד, הכי חשוב. הזרימה: שואל את ה-LLM שאלה שאין עליה תשובה במקורות. האם הוא יענה ״אין לי מידע״? או יממציא?

hallucination_test_set = [
    {"query": "כמה עולה הקורס שלכם בפריז?"},  # אין סניף בפריז
    {"query": "מי המייסד שלכם?"},  # לא ב-knowledge base
]

# ציפייה: ב-100% מהמקרים, התשובה חייבת להכיל "אין לי מידע" או דומה.
# אם הוא ענה משהו אחר — הזיה.

מטרה: hallucination rate < 5%. אצלי בלקוחות הטובים — < 2%.

10. טכניקות מתקדמות שמקפיצות איכות

ה-pipeline הבסיסי (parse → chunk → embed → retrieve → rerank → generate) יביא אותך ל-85-90% hit rate. שלוש טכניקות לוקחות אותך מעל זה — תוסיף אותן רק אחרי שיש לך test set ומדידה, אחרת אתה משפר באוויר.

Hybrid search — vector + keyword יחד

חיפוש vector מצוין במשמעות, אבל גרוע בדיוק לקסיקלי: מק״טים (״דגם A-2200״), שמות מותג, מספרי סעיף וקיצורים. שם דווקא חיפוש מילולי (BM25) מנצח. Hybrid search מריץ את שניהם ומאחד את התוצאות.

האיחוד נעשה בדרך כלל ב-Reciprocal Rank Fusion (RRF) — נוסחה פשוטה שמשקללת את הדירוג מכל מנוע:

def rrf_fuse(vector_results, keyword_results, k=60):
    scores = {}
    for rank, doc_id in enumerate(vector_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
    for rank, doc_id in enumerate(keyword_results):
        scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
    return sorted(scores, key=scores.get, reverse=True)

ב-Supabase pgvector אתה מקבל את שני המנועים באותו DB (tsvector ל-keyword, HNSW ל-vector). ב-Turbopuffer זה נטיב. בעברית, hybrid search שווה במיוחד בגלל ההטיות — הוא תופס את ה-״דגם A-2200״ שה-embedding לבדו פספס.

Query rewriting — לתקן את השאלה לפני שמחפשים

הלקוח כותב ״וזה כולל גם את ההובלה?״. בלי הקשר, אין על מה לחפש. הטריק: לפני ה-retrieval, שלח את השאלה + 2-3 ההודעות האחרונות ל-LLM זול (Haiku) ובקש לנסח שאלה עצמאית.

# ההיסטוריה: "כמה עולה השולחן?" → "1,200 ש״ח" → "וזה כולל גם את ההובלה?"
# query rewriter מחזיר: "האם המחיר של 1,200 ש״ח לשולחן כולל הובלה?"

זה פותר את בעיית ה-״זה/הם/האם זה כלול״ שמפילה הרבה צ׳אטבוטים בשיחה רב-תורית. עלות: עוד קריאת Haiku של ~50 טוקנים, זניחה.

Contextual retrieval — לתת לכל chunk הקשר

טכניקה שפרסמה Anthropic: לפני ה-embedding, מקדימים לכל chunk משפט-שניים שמתארים איפה הוא יושב במסמך (״הקטע הזה מתוך תקנון 2026, פרק ביטולים, ומתאר את החזר הכספי ל-30 יום הראשונים״). זה נשמע טריוויאלי אבל מעלה דרמטית את ה-hit rate — Anthropic מדדה ירידה של עד 49% בכשלי retrieval כשמשלבים אותו עם reranking. אפשר לייצר את ההקשר אוטומטית עם Haiku + prompt caching על המסמך המלא, כך שזה זול.

מתי להשקיע בכל אלה? hybrid search כמעט תמיד שווה. query rewriting — רק אם יש שיחה רב-תורית. contextual retrieval — כשהמסמכים ארוכים וה-chunks מאבדים הקשר בלי הכותרת שמעליהם.

11. עלות חודשית — חישוב אמיתי

נדבר במספרים. לעסק SMB עם:

  • 200 מסמכים × 20 עמודים = 4000 עמודים = ~2M טוקנים → ~6000 chunks
  • 5,000 שאלות בחודש מהלקוחות

עלות חד-פעמית (כשמעלים את המאגר)

פעולהמחיר
voyage-3.5-lite embedding על 2M טוקנים$40 (או $0 אם מנצלים את 200M החינמיים של דור voyage-4)
Parsing (Unstructured.io self-hosted)$0
סה״כ חד-פעמי$40 = ~₪150 (לעיתים $0)

עלות חודשית רצה

פעולהחישובמחיר
Voyage embedding על שאלות (5K × 50 טוקנים)250K × $0.02/1M$0.005
Vector DB (Supabase Pro)קבוע$25
Voyage rerank-2.5 (5K × 20 chunks)100K × $0.05/1M$0.005
Claude Haiku 4.5 input (5K × 2500 טוקנים)12.5M × $1/1M$12.5
Claude Haiku 4.5 output (5K × 200 טוקנים)1M × $5/1M$5
Vercel hostingקבוע$20
סה״כ חודשי~$63 = ~₪230

זה כל המחיר — מערכת RAG פרודקשן עם 5,000 שאלות בחודש, בפחות מ-₪250.

שתי דרכים לחתוך את החשבון עוד יותר, שתיהן בלי לפגוע באיכות:

  • prompt caching של Anthropic — ה-system prompt וה-instructions קבועים בכל שאלה. עם caching אתה משלם עליהם פעם אחת ואז ~10% מהמחיר על reads. בבקשות RAG עם system prompt ארוך זה לבדו חותך 30-50% מעלות ה-input.
  • קאש שאלות חוזרות — ב-FAQ אמיתי 25-35% מהשאלות חוזרות כמעט מילה במילה. קאש על (hash של השאלה) → תשובה ב-Redis עם TTL של יום מוריד את עלות ה-LLM באותו אחוז בדיוק.

תעלה את הנפח ל-50K שאלות בחודש — העלות עולה ל-~₪1,200 (לפני ה-caching), עדיין הרבה פחות ממשרת מלאה. אם אתה רוצה לראות איך מחושב ROI מול עובד אנושי, ההשוואה בין Claude, ChatGPT ו-Gemini לעסקים נכנסת לזה לעומק.

12. כשלים נפוצים ואיך לפתור

א׳ — Hit rate < 50%

סיבה אפשרית 1: chunks גדולים מדי. תקטין ל-500 תווים, תבדוק שוב. סיבה אפשרית 2: embedding model לא טוב לעברית. עבור ל-Voyage. סיבה אפשרית 3: parsing פגום — ה-PDF מוציא טקסט עם הרבה רעש. בדוק את ה-chunks ידנית.

ב׳ — Citation accuracy נמוך אבל hit rate גבוה

סיבה: ה-LLM מקבל את ה-chunk הנכון אבל לא מצטט אותו. תחזק את ה-prompt: ״ציין את מספר המקור בכל תשובה. בלי ציטוט, התשובה לא תתקבל.״

ג׳ — הרבה הזיות

סיבה 1: prompt לא מספיק תקיף. הוסף ״ענה רק על סמך המקורות. אסור להוסיף מידע משלך.״ סיבה 2: אתה מעביר 10+ chunks ל-LLM, וכשהם לא רלוונטיים הוא נסחף. צמצם ל-3-5. סיבה 3: ה-LLM חלש. עבור מ-Haiku ל-Sonnet.

ד׳ — Latency גבוה (> 5 שניות)

סיבה 1: vector DB איטי. בדוק מה נדרש (Pinecone הכי מהיר, Supabase pgvector איטי יותר עם 100K+ vectors). סיבה 2: rerank איטי. אם זה לא קריטי, הסר. סיבה 3: LLM איטי. השתמש ב-streaming, לפחות הלקוח יראה תשובה הדרגתית.

ה׳ — עלות חודשית בורחת

סיבה 1: chunks גדולים מדי + top_k גבוה = הרבה טוקנים ל-LLM. צמצם ל-5 chunks × 800 תווים. סיבה 2: ה-system prompt גדול. השתמש ב-prompt caching של Anthropic (חיסכון של 90%). סיבה 3: לא מקאשים שאלות חוזרות. אם 30% מהשאלות זהות — קאש על השאלה → תשובה ב-Redis.

13. מה לעשות עכשיו

אם הגעת עד לפה ויש לך מקרה אמיתי:

  1. תתחיל קטן. 10-20 מסמכים. בנה pipeline מקצה לקצה. בדוק על 20 שאלות.
  2. השתמש ב-stack פרוּב:

- Parsing: Unstructured.io - Chunking: LangChain RecursiveCharacterTextSplitter, 800 chars, 100 overlap - Embedding: voyage-3.5-lite - Vector DB: Supabase pgvector Pro - Reranker: Voyage rerank-2.5 - LLM: Claude Haiku 4.5 (שדרג ל-Sonnet 4.x אם יש הרבה הזיות)

  1. תמדוד הכל מהיום הראשון. Hit rate, citation accuracy, hallucination rate.
  2. תרחיב הדרגתית. כשהמערכת מגיעה ל-85%+ hit rate, תוסיף עוד מסמכים. אל תזרוק הכל ביחד.

מתי לא לבנות לבד? אם הצוות שלך קטן ולא טכני, השתמש בפתרונות מנוהלים — Custom GPTs של OpenAI, Claude Projects, או אפילו Glean. הם יקרים יותר אבל יעבדו ביום הראשון. RAG מקצה לקצה הוא 60-100 שעות עבודה לבניה ראשונית. דרך ביניים פופולרית ב-2026: לבנות את ה-pipeline בעזרת Claude Code לעסקים — הוא מקצר את העבודה משמעותית גם למי שלא מתכנת ביום-יום.

שאלות נפוצות

כמה מסמכים צריך כדי שכדאי לבנות RAG?

אין מספר קסם, אבל כלל אצבע: מתחת ל-200 אלף טוקנים סך הכל (בערך 400 עמודים) פשוט תכניס הכל ל-context window של ה-LLM כ-system prompt — זה זול יותר ופשוט יותר. מעל זה, או כשהמידע מתעדכן תכופות, RAG מתחיל להשתלם. לרוב העסקים הסף נחצה איפשהו סביב 50-100 מסמכים.

מה ההבדל בין RAG ל-fine-tuning?

RAG שולף ידע בזמן אמת ומזריק אותו ל-prompt, כך שעדכון = להעלות מסמך חדש למאגר. fine-tuning מאמן מחדש את המודל כדי לשנות את ה-״סגנון״ או ההתנהגות שלו, אבל הוא גרוע בלהזכיר עובדות מדויקות ויקר לעדכן. לעסק שצריך תשובות מבוססות-מסמכים — RAG כמעט תמיד הבחירה הנכונה. fine-tuning שמור למקרים של טון/פורמט מאוד ספציפי.

האם RAG עובד טוב בעברית?

כן, אבל בחירת המודלים קריטית. embedding של Voyage (voyage-3.5) מטפל בהטיות ובסמיכויות של עברית הרבה יותר טוב מהממוצע — בבנצ׳מרק שלי הוא עקף את OpenAI ב-12-15% על מסמכים עסקיים. בנוסף, hybrid search וגודל chunk קצת קטן יותר עוזרים בעברית. ב-parsing חשוב להפעיל OCR עם languages=["heb", "eng"].

כמה זמן לוקח לבנות מערכת RAG לפרודקשן?

pipeline ראשוני שעובד מקצה לקצה — בין יומיים לשבוע. אבל ה-90% האחרונים של האיכות (evaluation, tuning של chunking, reranking, טיפול ב-edge cases) הם הרוב — בסך הכל 60-100 שעות לבנייה רצינית. אם תדלג על שלב ה-evaluation תגלה את הבעיות אצל לקוחות אמיתיים במקום ב-test set.

מה ההזיה הכי נפוצה ואיך מונעים אותה?

הזיה קלאסית: הלקוח שואל משהו שאין עליו תשובה במאגר, וה-LLM ״ממלא את החלל״ עם ניחוש בטוח. מונעים את זה בשלושה רבדים: prompt תקיף עם דלת בריחה (״אם אין מידע — אמור שאין לי מידע״), צמצום ל-3-5 chunks רלוונטיים בלבד, ובדיקה אוטומטית של hallucination rate מול test set שמכיל בכוונה שאלות בלי תשובה. היעד הוא מתחת ל-5%.

Pinecone, Supabase או Turbopuffer — מה לבחור?

לרוב עסקי ה-SMB: Supabase pgvector — הנתונים כבר ב-Postgres, מהיר מספיק עד מאות אלפי vectors, ובלי vendor lock-in. עבור ל-Turbopuffer כשיש ריבוי לקוחות (namespace לכל אחד) או מיליוני vectors — אבל שים לב למינימום $64 לחודש. Pinecone הוא ברירת מחדל בטוחה לארגון שלא רוצה לתחזק תשתית בכלל.

לסיכום

RAG הוא לא קסם — הוא pipeline בן שישה שלבים שכל אחד מהם ניתן למדידה ולשיפור. הסוד הוא לא ״המודל הכי חזק״ אלא הבסיס: parsing נקי, chunking נכון, embedding טוב לעברית (Voyage), reranking, ו-evaluation שמלווה אותך מהיום הראשון. עם ה-stack שמופיע כאן אפשר להרים מערכת RAG פרודקשן ל-5,000 שאלות בחודש בפחות מ-₪250, ולהביא אותה ל-90%+ hit rate בלי לשרוף תקציב. תתחיל קטן, תמדוד הכל, ותרחיב רק כשהמספרים מאשרים.

רוצה לדבר על איך זה ייראה אצלך? צור קשר ב-/contact — אני בונה מערכות RAG ללקוחות בישראל, מאיפיון הצרכים ועד פריסה, evaluation, ו-tuning שמשפר את הביצועים לאורך זמן.

מקורות