מדריך LlamaIndex
מסגרת הנתונים ל-RAG — ומתי היא עדיפה על LangChain
LangChain בנוי סביב שרשור צעדים. LlamaIndex בנוי סביב שאלה אחת: איך מארגנים נתונים כך שמודל ימצא בהם את התשובה. ההבדל הזה נשמע קטן, והוא מכתיב אילו בעיות כל אחת מהן פותרת בקלות.
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 — נפוץ ועובד
סיכום מעשי
- חמש שורות מספיקות להתחלה; לייצור פרק את ברירות המחדל.
- פירוק היררכי עם AutoMergingRetriever הוא היתרון המובהק — דיוק בשליפה עם הקשר מלא.
- שאלות כמותיות → SQL, לא שליפה סמנטית. עם משתמש לקריאה בלבד.
- אכוף הרשאות בפילטרי מטא-דאטה, לא בהוראות לפרומפט.
- מדוד נאמנות ורלוונטיות בנפרד — הן מצביעות על תקלות שונות.
המשך באשכול ה-RAG
LlamaIndex היא המימוש. המדריכים הבאים מכסים את העקרונות ואת השכבות שמסביב.