התחלה טובה היא Vector search בלבד (FAISS) ואז להוסיף metadata filters. אם יש בעיה במונחים מדויקים - מוסיפים BM25 או reranker. המפתח: למדוד לפני שמוסיפים מורכבות.
RAG - Retrieval Augmented Generation
RAG הוא דפוס הנדסי שמחבר מודל שפה עם שכבת שליפה (Retrieval) ממאגר ידע, כך שהתשובה "נשענת" על מקורות אמיתיים במקום להסתמך על זיכרון המודל בלבד.
למה זה חשוב בפרודקשן? כי רוב הארגונים לא יכולים להכניס את כל הידע שלהם למודל, הידע משתנה, ויש צורך בהסבריות ובבקרה. RAG מאפשר grounding, שליטה במקורות, וצמצום hallucinations.
1) התמונה הגדולה: איפה RAG יושב בארכיטקטורה
RAG הוא לא "ספריה" אחת, אלא שרשרת רכיבים: אינג'סט מסמכים, עיבוד מקדים, יצירת embeddings, אחסון בוקטור-דאטאבייס, שליפה בזמן שאלה, והרכבת פרומפט שמזין את ה-LLM עם הקונטקסט הרלוונטי.
2) Chunking - האמנות שמחליטה אם RAG עובד
אם ה-chunks גדולים מדי, השליפה תהיה לא מדויקת והקונטקסט יתמלא רעש. אם הם קטנים מדי, מאבדים משמעות ונוצר מצב שבו צריך להרכיב עשרות chunks כדי לענות. לכן chunking הוא פרמטר קריטי.
כללי אצבע פרקטיים
- גודל chunk טיפוסי: 200-800 tokens, עם overlap של 10%-20%.
- שומרים מבנה: כותרות, סעיפים, טבלאות, קוד.
- מוסיפים metadata: מקור, עמוד, כותרת פרק, תאריך, הרשאות.
היכן זה נשבר
- PDFים סרוקים בלי OCR - הטקסט "מפורק".
- מסמכים עם הרבה קוד - צריך split שמכבד בלוקים.
- מידע מבוזר - צריך גם ריראנקר או hybrid search.
3) Retrieval - וקטורים, היברידי, וריראנק
בשליפה וקטורית אנחנו ממירים טקסט לווקטור במרחב סמנטי. אחר כך אנחנו עושים nearest neighbors כדי למצוא chunks דומים. אבל similarity לא תמיד מספיק, ולכן מערכות מתקדמות משלבות:
- Vector search: טוב למשמעות, פרפרזות, ושאלות "רכות".
- Lexical (BM25): טוב למונחים מדויקים, קודים, שמות, מספרים.
- Reranker: מודל קטן (cross-encoder) שמדרג מחדש את top-k לפי התאמה מדויקת לשאלה.
מה בוחרים בפרויקט סטודנטים?
4) Prompt Assembly - איך "מכניסים" את המקורות למודל
המודל מקבל קונטקסט מוגבל. לכן בונים פרומפט שמפריד בין ההוראות לבין המקורות, ומחייב תשובה שמבוססת רק עליהם. דפוס נפוץ:
System:
You are a helpful assistant. Answer ONLY from the provided sources.
If the answer is not in the sources, say "I don't know".
User:
Question: {question}
Sources:
[1] {chunk_1_text}
[2] {chunk_2_text}
...
הוספת citations היא חלק מהחוזה. זה מאפשר בדיקה, אמון, והפחתת טעויות. במוצרים ארגוניים, זה גם מאפשר compliance.
5) RAG ב-FastAPI - Endpoint לדוגמה
הדוגמה הבאה מראה שירות מינימלי: מקבלים שאלה, מבצעים retrieval, מרכיבים prompt, ושולחים ל-LLM. בפועל תחליף את הפונקציות placeholder למימוש אמיתי (FAISS/pgvector + ספק מודל).
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(title="RAG API", version="1.0")
class AskRequest(BaseModel):
question: str = Field(min_length=5, max_length=2000)
top_k: int = Field(default=5, ge=1, le=20)
class Source(BaseModel):
id: str
text: str
title: str | None = None
page: int | None = None
class AskResponse(BaseModel):
answer: str
sources: list[Source]
def embed_text(text: str) -> list[float]:
# TODO: use embedding model (local or API)
raise NotImplementedError
def retrieve(query_vec: list[float], top_k: int) -> list[Source]:
# TODO: vector search + metadata filters + optional rerank
raise NotImplementedError
def call_llm(prompt: str) -> str:
# TODO: call your LLM provider
raise NotImplementedError
@app.post("/api/v1/ask", response_model=AskResponse)
def ask(payload: AskRequest):
qvec = embed_text(payload.question)
sources = retrieve(qvec, payload.top_k)
sources_block = "\n".join(
[f"[{i+1}] {s.text}" for i, s in enumerate(sources)]
)
prompt = f\"\"\"You are a helpful assistant. Answer ONLY from the provided sources.
If the answer is not in the sources, say: I don't know.
Question: {payload.question}
Sources:
{sources_block}
\"\"\"
answer = call_llm(prompt)
return AskResponse(answer=answer, sources=sources)
שדרוגים פרודקשניים שכדאי להכיר
- Cache: לשאלות שחוזרות, או לשכבת embeddings של queries.
- Access control: פילטרים על metadata לפי הרשאות משתמש.
- Observability: לשמור trace: question, retrieved ids, token usage, latency.
- Safety: הגנה מפני prompt injection בתוך המסמכים (הוראות זדוניות במקור).
6) מדידה והערכה - איך יודעים שזה עובד
RAG נופל בדרך כלל על "שליפה". לכן ההערכה מתחלקת לשניים:
Retrieval metrics
- Recall@k - האם המקור הנכון נמצא בטופ-k
- MRR - כמה גבוה הוא מופיע
- Coverage - כמה שאלות מצליחות לקבל מקור
Answer quality
- Correctness - תשובה נכונה
- Groundedness - נשענת על המקורות
- Faithfulness - לא מוסיפה עובדות
תרגול
שאלות בדיקה (10)
- מה ההבדל בין "ידע המודל" לבין "מקורות" ב-RAG?
- למה chunking משפיע יותר מהמודל עצמו במקרים רבים?
- מה היתרון של metadata filters ב-RAG ארגוני?
- באיזה מצב BM25 מנצח vector search?
- מה עושה reranker ולמה הוא יקר יותר?
- מה זה prompt injection בתוך מסמך, ואיך מצמצמים את הסיכון?
- מה ההבדל בין Recall@k לבין MRR?
- מהי מדיניות תשובה מומלצת כשאין מקור מתאים?
- איך היית מוסיף cache בלי לשבור עקביות?
- איזה log או trace היית שומר כדי לדבג מערכת RAG?
משימת בית (Mini RAG)
בנה שירות FastAPI עם endpoint:
POST /api/v1/ask
{
"question": "string",
"top_k": 5
}
Response:
{
"answer": "string",
"sources": [{"id":"...", "title":"...", "page":1, "text":"..."}]
}
דרישות:
- המסמכים מגיעים מתיקיה מקומית (למשל markdown או txt) ונכנסים ל-index בהפעלה.
- יש chunking עם overlap, ושמירה של metadata (שם קובץ, מספר chunk).
- שליפה של top-k לפי similarity (אפשר להתחיל בלי reranker).
- תשובה מחזירה citations כ-id של chunks.
בדיקת פתרון (Checklist + בדיקות)
Checklist:
- ה-API מחזיר 200, וכולל answer + sources.
- בכל תשובה יש לפחות מקור אחד או "I don't know".
- ה-sources מתאימים לשאלה (בדיקה ידנית על 5 שאלות).
- המערכת לא קורסת על מסמך גדול או שאלה ארוכה (ולידציה).
בדיקת smoke עם curl:
curl -X POST http://localhost:8000/api/v1/ask \
-H "Content-Type: application/json" \
-d '{"question":"מה זה RAG ולמה הוא שימושי?","top_k":5}'
בדיקות יחידה (pytest) - דוגמה:
from fastapi.testclient import TestClient
from rag_api import app
client = TestClient(app)
def test_ask_returns_sources():
r = client.post("/api/v1/ask", json={"question":"define RAG", "top_k":3})
assert r.status_code == 200
data = r.json()
assert "answer" in data
assert "sources" in data
assert isinstance(data["sources"], list)
בעמוד הבא נוכל לבנות את "הצד התשתיתי" של RAG: בסיס נתונים, ולידציה, ומבנה דאטה נקי. כשתכתוב "המשך", אכין את העמוד הבא בסדרה.