מעודכן לספטמבר 2026 20 דקות קריאה Python · RAG

מדריך LlamaIndex
מסגרת הנתונים ל-RAG — ומתי היא עדיפה על LangChain

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

Index
המושג המרכזי
160+
מקורות נתונים
5
שורות ל-RAG ראשון

LlamaIndex מול LangChain — ההבדל המעשי

שתיהן ספריות Python לבניית אפליקציות LLM, ויש חפיפה. אבל נקודת המוצא שונה, וזה מורגש:

LangChain    "יש לי רצף צעדים" — שרשור, סוכנים, כלים, זרימת בקרה
LlamaIndex   "יש לי נתונים"   — טעינה, אינדוקס, שליפה, שאילתה

ההשלכה: אם הבעיה שלך היא סוכן שמפעיל כלים ומקבל החלטות — LangChain נותן יותר. אם הבעיה היא 500 אלף מסמכים שצריך לענות עליהם נכון — LlamaIndex ייתן לך תוצאה טובה יותר בפחות קוד, כי כל המנגנונים של פירוק מסמכים, אינדוקסים היררכיים ושליפה מורכבת כבר בנויים בה.

ובפועל הן מסתדרות יחד: מקובל להשתמש ב-LlamaIndex כשכבת השליפה בתוך סוכן שנבנה ב-LangChain. ראה מדריך LangChain.

התחלה — RAG בחמש שורות

pip install llama-index
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("./docs").load_data()   # PDF, DOCX, MD, HTML...
index = VectorStoreIndex.from_documents(documents)
engine = index.as_query_engine()

print(engine.query("מה תקופת האחריות לפי החוזה?"))

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

שליטה בפירוק המסמכים

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

from llama_index.core.node_parser import (
    SentenceSplitter, SemanticSplitterNodeParser, HierarchicalNodeParser)
from llama_index.embeddings.openai import OpenAIEmbedding

# 1. גבולות משפט — ברירת מחדל סבירה לרוב המקרים
splitter = SentenceSplitter(chunk_size=512, chunk_overlap=50)

# 2. פירוק סמנטי — חותך היכן שהנושא משתנה, לא לפי ספירת תווים
semantic = SemanticSplitterNodeParser(
    buffer_size=1,
    breakpoint_percentile_threshold=95,
    embed_model=OpenAIEmbedding(),
)

# 3. היררכי — צ'אנקים בכמה רזולוציות בו-זמנית
hierarchical = HierarchicalNodeParser.from_defaults(
    chunk_sizes=[2048, 512, 128]
)

המפרק ההיררכי הוא היתרון הגדול של LlamaIndex. הוא שומר את המסמך בשלוש רמות ומאפשר שליפה קטנה עם החזרה גדולה: מוצאים התאמה בצ'אנק של 128 טוקנים — מדויק — ואז מחזירים למודל את הפסקה בת 2048 שמכילה אותו. מדויק בשליפה, עשיר בהקשר.

from llama_index.core.retrievers import AutoMergingRetriever
from llama_index.core.storage import StorageContext

nodes = hierarchical.get_nodes_from_documents(documents)
leaf_nodes = get_leaf_nodes(nodes)                 # רק העלים נכנסים לאינדקס

storage = StorageContext.from_defaults()
storage.docstore.add_documents(nodes)              # ההיררכיה כולה נשמרת

index = VectorStoreIndex(leaf_nodes, storage_context=storage)

# כשמספיק עלים מאותו הורה נשלפו — מחליף אותם בהורה עצמו
retriever = AutoMergingRetriever(
    index.as_retriever(similarity_top_k=12), storage, verbose=True)

מעבר לשליפה וקטורית פשוטה

דמיון וקטורי לבדו נכשל בשאלות מסוימות. שני מנועים שפותרים סוגים שונים:

Sub-question — שאלות מורכבות

"השווה בין מדיניות ההחזרים ב-2024 ל-2025" דורשת שתי שליפות נפרדות והשוואה. מנוע תת-שאלות מפרק אוטומטית:

from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool, ToolMetadata

tools = [
    QueryEngineTool(query_engine=index_2024.as_query_engine(),
        metadata=ToolMetadata(name="policy_2024", description="מדיניות 2024")),
    QueryEngineTool(query_engine=index_2025.as_query_engine(),
        metadata=ToolMetadata(name="policy_2025", description="מדיניות 2025")),
]

engine = SubQuestionQueryEngine.from_defaults(query_engine_tools=tools)
print(engine.query("מה השתנה במדיניות ההחזרים בין 2024 ל-2025?"))

שאילתה על נתונים טבלאיים

שאלות כמותיות — "כמה", "ממוצע", "הכי גבוה" — לא נענות היטב משליפה סמנטית. כאן מייצרים SQL:

from llama_index.core.query_engine import NLSQLTableQueryEngine
from sqlalchemy import create_engine
from llama_index.core import SQLDatabase

sql_db = SQLDatabase(create_engine("postgresql://..."), include_tables=["orders"])
engine = NLSQLTableQueryEngine(sql_database=sql_db, tables=["orders"])
print(engine.query("מה ההזמנה הממוצעת ברבעון האחרון?"))

SQL מיוצר על ידי מודל הוא משטח תקיפה. חבר תמיד עם משתמש לקריאה בלבד, הגבל ל-include_tables מפורש, והוסף timeout. משתמש שמנסח את השאלה שולט למעשה בקלט ליצירת ה-SQL — זה בדיוק LLM05 ב-OWASP LLM Top 10.

ייצור — אחסון מתמיד ומטא-דאטה

אינדוקס מחדש בכל הרצה יקר ואיטי. חבר מסד וקטורי אמיתי:

import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext, VectorStoreIndex

client = chromadb.PersistentClient(path="./chroma")
store = ChromaVectorStore(chroma_collection=client.get_or_create_collection("docs"))
storage = StorageContext.from_defaults(vector_store=store)

index = VectorStoreIndex.from_documents(documents, storage_context=storage)

# בהרצות הבאות — טעינה במקום אינדוקס מחדש
index = VectorStoreIndex.from_vector_store(store)

ומטא-דאטה היא מה שהופך RAG לשמיש בארגון — סינון לפני השליפה, כולל הרשאות:

from llama_index.core.vector_stores import MetadataFilters, MetadataFilter

for doc in documents:
    doc.metadata = {"department": "legal", "year": 2025, "clearance": "internal"}

engine = index.as_query_engine(
    filters=MetadataFilters(filters=[
        MetadataFilter(key="department", value="legal"),
        MetadataFilter(key="clearance",  value="internal"),
    ])
)

הרשאות נאכפות כאן, לא בפרומפט. "אל תזכיר מסמכים חסויים" הוא בקשה שאפשר לעקוף; פילטר שמונע מהמסמך להישלף מלכתחילה הוא גבול אמיתי.

מדידה

RAG נכשל בשני מקומות נפרדים, וצריך למדוד כל אחד לחוד — אחרת אתה מכוון את המרכיב הלא נכון:

from llama_index.core.evaluation import (
    FaithfulnessEvaluator, RelevancyEvaluator)

faithful = FaithfulnessEvaluator()   # האם התשובה נשענת על מה שנשלף? (הזיות)
relevant = RelevancyEvaluator()      # האם מה שנשלף בכלל רלוונטי לשאלה?

response = engine.query("מה תקופת האחריות?")
print("נאמנות:", faithful.evaluate_response(response=response).passing)
print("רלוונטיות:", relevant.evaluate_response(query="מה תקופת האחריות?",
                                                response=response).passing)

רלוונטיות נמוכה = בעיית שליפה (פירוק, embeddings, top_k). נאמנות נמוכה = המודל ממציא למרות הקשר תקין. להעמקה ראה מדריך Evals והזיות.

מתי לבחור במה

LlamaIndex  שאלות ותשובות על קורפוס גדול
            מסמכים מובנים למחצה (PDF, טבלאות, היררכיה)
            צריך אינדוקס מתוחכם בלי לבנות אותו בעצמך

LangChain   סוכנים שמפעילים כלים ומקבלים החלטות
            זרימת בקרה מורכבת עם ענפים ולולאות
            אורקסטרציה בין כמה מודלים ושירותים

שתיהן       LlamaIndex כשכבת שליפה בתוך סוכן LangChain — נפוץ ועובד

סיכום מעשי

database

המשך באשכול ה-RAG

LlamaIndex היא המימוש. המדריכים הבאים מכסים את העקרונות ואת השכבות שמסביב.