ראשי chevron_left מדריכים chevron_left RAG Systems
database ארכיטקטורת AI

RAG Systems —
מ-0 לProduction

RAG (Retrieval Augmented Generation) הוא הדרך להפוך LLM לכלי שעונה על שאלות לפי המסמכים שלך. כך בונים Chatbot שיודע הכל על החברה שלך — ולא ממציא תשובות.

schedule 45 דקות קריאה
code Python
layers LangChain + Pinecone
signal_cellular_alt מפתחים

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

LLMs כמו GPT-4 ו-Claude אימנו על מידע כללי מהאינטרנט עד לתאריך cut-off מסוים. הם לא יודעים כלום על המסמכים הפנימיים של החברה שלך, על הנהלים העדכניים, על בסיס הידע של התמיכה שלך — ובגלל זה הם "ממציאים" תשובות כשהם לא יודעים, תופעה שנקראת Hallucination.

RAG פותר את הבעיה הזאת: במקום לסמוך על הידע המאומן של המודל, אנחנו שולפים בזמן אמת את המידע הרלוונטי ממסמכים שלנו, מכניסים אותו ל-Prompt, ומבקשים מהמודל לענות על בסיס המידע הזה בלבד. התוצאה היא מערכת שעונה על שאלות בצורה מדויקת, מבוססת מקורות, ועדכנית.

cancel בלי RAG
  • המודל לא יודע על מסמכי החברה
  • Hallucinations — תשובות ממוצאות
  • ידע קפוא לתאריך אימון
  • אין מקורות לאימות
check_circle עם RAG
  • מבוסס על המסמכים שלך בדיוק
  • מקורות לכל תשובה
  • עדכני כמו ה-Index שלך
  • "אני לא יודע" במקום ניחוש
ה-RAG Pipeline בתמצות
שאלה → [Retrieve relevant docs] → [Augment prompt with docs] → [Generate answer]

ל-RAG Pipeline יש שני שלבים מרכזיים:

1 Ingestion (Offline)

טען מסמכים ← חתוך לחלקים (chunks) ← צור Embeddings ← שמור ב-Vector DB

2 Retrieval (Online)

שאלה ← צור Embedding ← חפש similarity ← קבל context ← גנרט תשובה

ארכיטקטורה מלאה

לפני שנגיע לקוד, חשוב להבין את כל הרכיבים ואיך הם מתחברים. הדיאגרמה הבאה מציגה את ה-Pipeline המלא — משלב טעינת המסמכים ועד ליצירת התשובה הסופית למשתמש.

[Documents: PDF / Word / Web / CSV]
         |
         v
   [Document Loader]         <-- LangChain loaders
         |
         v
     [Raw Text]
         |
         v
  [Text Splitter]            <-- RecursiveCharacterTextSplitter
   (chunks ~1000 chars)
         |
         v
     [Chunks]
         |
         v
  [Embedding Model]          <-- OpenAI / HuggingFace
  (each chunk → vector)
         |
         v
  [Vector Store (Index)]     <-- Pinecone / Chroma / FAISS

  ============ Online Phase ============

  [User Question]
         |
         v
  [Same Embedding Model]
         |
         v
   [Query Vector]
         |
         v
  [Similarity Search top-k]  <-- cosine similarity
         |
         v
  [Relevant Chunks (context)]
         |
         v
  [Prompt Template]          <-- question + context
         |
         v
   [LLM: GPT-4o / Claude]
         |
         v
     [Final Answer + Sources]

כל שלב בארכיטקטורה הזאת הוא נקודה שאפשר לשפר ולאופטימיזציה. החלטות על גודל ה-chunk, מודל ה-Embedding שנבחר, ומספר ה-chunks שמוחזרים (top-k) — כולן משפיעות ישירות על איכות התוצאות הסופיות.

Document Loaders — טעינת מסמכים

LangChain מגיע עם עשרות Document Loaders שתומכים בפורמטים שונים. הנה הנפוצים ביותר:

terminal התקנה מקדימה
pip install langchain langchain-community langchain-openai langchain-pinecone pypdf chromadb sentence-transformers
document_loaders.py
# התקן: pip install langchain-community pypdf
from langchain_community.document_loaders import (
    PyPDFLoader,
    DirectoryLoader,
    WebBaseLoader,
    CSVLoader,
    JSONLoader
)

# PDF יחיד
loader = PyPDFLoader("company_policy.pdf")
docs = loader.load()
print(f"Loaded {len(docs)} pages")

# תיקייה מלאה של PDFs
loader = DirectoryLoader(
    "./docs/",
    glob="**/*.pdf",
    loader_cls=PyPDFLoader
)
docs = loader.load()
print(f"Loaded {len(docs)} pages")

# טעינה מ-URL
loader = WebBaseLoader("https://docs.example.com/api")
docs = loader.load()

# Google Drive (מצריך הגדרת OAuth)
from langchain_google_community import GoogleDriveLoader
loader = GoogleDriveLoader(
    folder_id="your_folder_id",
    file_types=["document", "pdf"]
)
docs = loader.load()

Text Splitting — חיתוך חכם

אחרי שטוענים את המסמכים, צריך לחתוך אותם ל-chunks קטנים יותר. זה קריטי — chunk גדול מדי מכיל יותר מדי מידע לא רלוונטי ומגדיל עלויות. chunk קטן מדי לא נותן context מספיק. הערכים המומלצים לרוב המקרים: chunk_size=1000, chunk_overlap=200.

text_splitting.py
from langchain.text_splitter import (
    RecursiveCharacterTextSplitter,
    MarkdownHeaderTextSplitter,
    TokenTextSplitter
)

# Recursive — הכי מומלץ לרוב המקרים
splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,        # תווים לכל chunk
    chunk_overlap=200,      # חפיפה בין chunks — שומרת context
    separators=["\n\n", "\n", ".", " "]  # סדר עדיפות לחיתוך
)
chunks = splitter.split_documents(docs)
print(f"Created {len(chunks)} chunks")

# לפי Headers (Markdown) — מושלם לתיעוד טכני
headers = [("#", "H1"), ("##", "H2"), ("###", "H3")]
md_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=headers
)
md_docs = md_splitter.split_text(markdown_text)

# לפי Tokens — מדויק יותר לחישוב עלויות API
token_splitter = TokenTextSplitter(
    chunk_size=250,
    chunk_overlap=30
)
token_chunks = token_splitter.split_documents(docs)
lightbulb

טיפ: chunk_overlap חשוב

ה-overlap מוודא שמידע שנמצא על גבול שני chunks לא יאבד. לטקסטים עם משפטים ארוכים, תגדיל ל-300-400. לקוד, תוכל להוריד ל-50.

Embeddings — הלב של RAG

Embedding הוא המרת טקסט לוקטור מספרי (רשימה של מאות מספרים). הרעיון הגאוני הוא שטקסטים עם משמעות דומה יקבלו וקטורים קרובים זה לזה במרחב. כך, כשמחפשים "מה שעות הפעילות?", המערכת מוצאת chunk עם "שעות פתיחה של השירות" — גם בלי התאמת מילים מדויקת.

יש שתי אפשרויות עיקריות: מודלי OpenAI שמצריכים תשלום לפי שימוש אבל מצוינים באיכות, ומודלי HuggingFace שרצים לוקאלית ובחינם. לשפה העברית, מומלץ במיוחד מודל multilingual.

embeddings.py
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import HuggingFaceEmbeddings
import numpy as np

# OpenAI (מומלץ לאיכות ולדיוק)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 1536 dimensions, ~$0.02 per 1M tokens

# text-embedding-3-large — איכות גבוהה יותר, פי 2 יקר
embeddings_large = OpenAIEmbeddings(model="text-embedding-3-large")
# 3072 dimensions

# HuggingFace (חינמי, רץ לוקאלית, תומך עברית!)
embeddings_hf = HuggingFaceEmbeddings(
    model_name="sentence-transformers/paraphrase-multilingual-mpnet-base-v2"
)
# 768 dimensions, תומך ב-50+ שפות כולל עברית

# בדיקת Cosine Similarity
v1 = embeddings.embed_query("AI Agent")
v2 = embeddings.embed_query("Autonomous AI System")
similarity = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
print(f"Cosine similarity: {similarity:.3f}")  # ~0.85

# Embedding של batch
texts = ["מה ימי הפעילות?", "how to reset password", "price list"]
vectors = embeddings.embed_documents(texts)
print(f"Shape: {len(vectors)} x {len(vectors[0])}")
text-embedding-3-small
1536 dimensions
$0.02 / 1M tokens
מאוזן ומומלץ לרוב
text-embedding-3-large
3072 dimensions
$0.13 / 1M tokens
איכות גבוהה ביותר
multilingual-mpnet
768 dimensions
חינם, לוקאלי
תומך עברית מעולה

Vector Stores ו-Retrieval

ה-Vector Store הוא מסד הנתונים שמאחסן את הוקטורים ומאפשר חיפוש מהיר של הוקטורים הדומים ביותר. לפיתוח מקומי ממליצים על Chroma, לפרודקשיין — Pinecone הוא הבחירה הנפוצה ביותר בשוק.

vector_stores.py
from langchain_pinecone import PineconeVectorStore
from langchain_community.vectorstores import Chroma
import os

# === Pinecone (Production) ===
os.environ["PINECONE_API_KEY"] = "your-api-key"

vectorstore = PineconeVectorStore.from_documents(
    documents=chunks,
    embedding=embeddings,
    index_name="my-rag-index",
    namespace="v1"
)

# חיבור ל-index קיים
vectorstore = PineconeVectorStore(
    index_name="my-rag-index",
    embedding=embeddings,
    namespace="v1"
)

# === Chroma (Development / Local) ===
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"
)

# טעינה מ-disk
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings
)

# === Retriever ===
retriever = vectorstore.as_retriever(
    search_type="similarity",  # similarity / mmr / similarity_score_threshold
    search_kwargs={"k": 4}     # מספר chunks להחזיר
)

# MMR — גיוון תוצאות (מניע כפילויות)
retriever_mmr = vectorstore.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 6, "fetch_k": 20, "lambda_mult": 0.5}
)

# חיפוש ידני
results = vectorstore.similarity_search("מה מדיניות ההחזרות?", k=3)
for doc in results:
    print(doc.page_content[:200])
    print(f"Source: {doc.metadata.get('source', 'N/A')}")
info

MMR vs Similarity Search

Similarity Search מחזיר את ה-k הדומים ביותר — אבל לעיתים הם כמעט זהים. MMR (Maximal Marginal Relevance) מאזן בין רלוונטיות לגיוון, כך שהתוצאות מכסות יותר זוויות של הנושא.

RAG Chain מלא — הכל ביחד

עכשיו מחברים את כל הרכיבים. ה-Prompt Template הוא אחד הדברים הכי חשובים — הנחיות ברורות ל-LLM על מה לעשות עם ה-context שסיפקנו, ובמיוחד ההוראה לומר "אני לא יודע" כשהמידע לא קיים.

rag_chain.py
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_core.prompts import PromptTemplate

# Prompt Template — ה-Prompt שמגדיר איך המודל יתנהג
TEMPLATE = """השתמש בחלקי ה-Context הבאים כדי לענות על השאלה בסוף.
אם אינך יודע את התשובה, אמור "אני לא יודע" — אל תמציא תשובות.
ענה תמיד בעברית, בצורה ברורה ומסודרת.

Context:
{context}

שאלה: {question}

תשובה:"""

prompt = PromptTemplate(
    template=TEMPLATE,
    input_variables=["context", "question"]
)

# LLM
llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0  # 0 = עקבי ודטרמיניסטי
)

# RAG Chain
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",             # stuff = מכניס הכל ל-prompt
    retriever=retriever,
    return_source_documents=True,   # מחזיר מקורות
    chain_type_kwargs={"prompt": prompt}
)

# שאלה!
result = qa_chain.invoke({"query": "מה מדיניות ההחזרות שלנו?"})
print(result["result"])
print("\nמקורות:")
for doc in result["source_documents"]:
    print(f"  - {doc.metadata.get('source', 'unknown')}")

גרסה עם LCEL (LangChain Expression Language)

ה-LCEL הוא הגישה המודרנית ב-LangChain — הרכבה של רכיבים עם pipe operator. גמיש ומומלץ לפרויקטים חדשים.

rag_lcel.py
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# LCEL chain — קריא ונקי
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# שאלה
answer = rag_chain.invoke("מה כתוב בחוזה לגבי ביטול?")
print(answer)

# Streaming
for chunk in rag_chain.stream("תסביר את תנאי השירות"):
    print(chunk, end="", flush=True)

Advanced RAG — שיפור ביצועים

ה-Basic RAG מגיע לאיכות טובה, אבל יש כמה טכניקות מתקדמות שיכולות לשפר את הדיוק משמעותית, במיוחד כשהמאגר גדול או השאלות מורכבות.

Reranking — מיון חוזר לפי רלוונטיות

Similarity Search מוצא chunks קרובים מבחינה וקטורית, אבל לא תמיד הכי רלוונטיים לשאלה הספציפית. Reranker הוא מודל נפרד שמדרג מחדש את התוצאות לפי רלוונטיות אמיתית — ומשפר recall ב-20-40%.

reranking.py
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder

# Cross-Encoder Reranker — מדרג results לפי רלוונטיות
model = HuggingFaceCrossEncoder(
    model_name="cross-encoder/ms-marco-MiniLM-L-6-v2"
)
compressor = CrossEncoderReranker(model=model, top_n=3)

# Retriever עם Reranking
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=retriever
)

# שימוש
docs = compression_retriever.invoke("מה עלויות המנוי?")
print(f"Got {len(docs)} reranked docs")

HyDE — Hypothetical Document Embeddings

רעיון חכם: במקום לחפש את הוקטור של השאלה, ה-LLM קודם מייצר תשובה היפותטית, ואז מחפשים לפי הוקטור של התשובה הזו. שאלה ומסמך לרוב לא דומים וקטורית, אבל שתי תשובות לאותו נושא — כן. זה משפר משמעותית את ה-recall.

hyde.py
from langchain.chains import HypotheticalDocumentEmbedder
from langchain_core.prompts import PromptTemplate

# Prompt ליצירת מסמך היפותטי
hyde_prompt = PromptTemplate.from_template(
    "Write a detailed paragraph that would answer: {question}"
)

# HyDE Retriever
hyde_embeddings = HypotheticalDocumentEmbedder.from_llm(
    llm=llm,
    base_embeddings=embeddings,
    custom_instructions=hyde_prompt
)

# שילוב ב-vectorstore
hyde_retriever = vectorstore.as_retriever(
    search_kwargs={"k": 4}
)
# לחיפוש משתמשים ב-hyde_embeddings.embed_query

Multi-Query Retriever — גיוון השאלות

ה-LLM מייצר מספר וריאציות של השאלה המקורית, מבצע חיפוש לכל אחת מהן, ומאחד את התוצאות. מגדיל recall בצורה משמעותית לשאלות מורכבות.

from langchain.retrievers.multi_query import MultiQueryRetriever
import logging

logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)

multi_retriever = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(),
    llm=llm
)

# יכניס 3 וריאציות של השאלה ויאחד תוצאות
unique_docs = multi_retriever.invoke("מה הזכויות שלי כלקוח?")

Production Checklist

לפני שמעבירים מערכת RAG לפרודקשיין, יש נושאים קריטיים שחייבים לטפל בהם. מניסיון — רוב הבעיות בפרודקשיין נובעות מארבעה תחומים: הערכת איכות, ניטור, עלויות ואבטחה.

analytics

Evaluation — הערכת איכות

  • RAGAS — מסגרת הערכה אוטומטית: Faithfulness, Answer Relevancy, Context Recall
  • LangSmith — ניטור שיחות, tracing, A/B testing של prompts
  • בנה מאגר שאלות בדיקה לפני go-live
monitoring

Monitoring — ניטור שוטף

  • מדוד Query latency (p50, p95, p99)
  • עקוב אחר Retrieval quality — האם המקורות נכונים?
  • Feedback loop — אפשר למשתמשים לדרג תשובות
savings

Cost Optimization

  • GPTCache — Cache לשאלות חוזרות (חוסך 60-80%)
  • חשב tokens per query × מחיר × שאלות/יום
  • שקול gpt-4o-mini למקרים פשוטים
security

Security — אבטחה

  • לא לאחסן PII (מידע אישי) ב-Vector DB בלי הצפנה
  • הגנה מפני Prompt Injection — validate user input
  • namespace לפי permissions — כל משתמש רואה רק מה שמותר לו

טיפים מניסיון

lightbulb

Metadata Filtering: הוסף metadata לכל chunk (source, date, department) ואפשר סינון לפיהם. למשל: "ראה רק מסמכים מ-2024" או "רק מחלקת HR".

lightbulb

Incremental Indexing: אל תבנה מחדש את כל ה-index בכל עדכון. עקוב אחרי last_modified ועדכן רק chunks ששונו.

lightbulb

Parent-Child Chunking: שמור chunks קטנים ל-Embedding, אבל כשמוצאים match — החזר את ה-parent chunk הגדול יותר ל-LLM לצורך context מלא.

lightbulb

Chain of Thought בתשובות: הוסף להנחיה "חשוב שלב אחר שלב לפני שאתה עונה". מגדיל דיוק ב-15-25% לשאלות מורכבות הדורשות הסקה.

סיכום — מה הלאה?

בנית מערכת RAG מלאה — מטעינת מסמכים, דרך chunking ו-embedding, עד ל-retrieval ו-generation. זה הבסיס שעליו בנויות מאות מוצרי AI כיום. השלב הבא הוא לחבר את ה-RAG ל-Agent שיכול לנקוט פעולות על בסיס מה שמצא — ולכן מומלץ להמשיך למדריך AI Agents.

סיכום מהיר — החלטות ארכיטקטורה

נושא פיתוח פרודקשיין
Vector Store Chroma (local) Pinecone / pgvector
Embedding multilingual-mpnet text-embedding-3-small
LLM gpt-4o-mini gpt-4o / Claude
Chunk size 1000 / overlap 200 מותאם לתוכן
Retrieval similarity k=4 MMR + Reranking

קישורים שימושיים

מוכנים לבנות מערכת RAG אמיתית?

הצטרפו למסלול AI Agents Development — קורס מלא שמלמד LangChain, RAG, CrewAI ו-LangGraph עם 5 פרויקטים עסקיים אמיתיים.