PythonTypeScriptHigh — roles/crews/agents out of the box
LlamaIndex
Ingesting, indexing, and querying private documents with LLMs — RAG as a first-class product rather than a chain you assemble.
Architecture
- Documents → Nodes: loaders (
SimpleDirectoryReader, LlamaHub connectors) produce documents; node parsers chunk them with metadata and relationships. - Indexes:
VectorStoreIndex, keyword, summary, knowledge-graph indexes over aStorageContextthat abstracts vector stores and doc stores. - Retrievers → Query engines → Chat engines: retrieval, optional node postprocessors (reranking, metadata filters), and a response synthesizer (
compact,refine,tree_summarize). - Agents and Workflows:
FunctionAgent/ReActAgentwrap tools (a query engine can be a tool);Workflowis an event-driven step system for custom orchestration. - Ingestion pipelines with transformations and caching for repeatable, incremental indexing.
Best use cases
- Document Q&A and knowledge assistants where retrieval quality is the product.
- Teams that want reranking, hybrid retrieval, and metadata filtering available as configuration.
- Structured data plus text (SQL + documents) routed through one query interface.
Weaknesses
- Defaults hide critical choices: chunk size 1024, top-k 2, a specific response synthesizer — good demos, surprising production behaviour until you read every default (RAG Evaluation).
- Response synthesizers (
refinein particular) issue multiple hidden LLM calls; latency and cost are non-obvious from the calling code. - The class hierarchy is deep; customising one step often means subclassing or reading source.
- Agent and workflow layers are newer and have churned (legacy
AgentRunnervsFunctionAgent), so docs and examples mix eras. - Heavy install with many optional extras;
llama-index-*package fragmentation makes version pinning fiddly.
When NOT to use it
- Your problem is not retrieval — a general agent runtime or plain SDK code fits better.
- You already have a search service (Elasticsearch, pgvector with your own queries) and only need to pass results to a model.
- You need to control exactly which chunks and how many tokens reach the prompt on every call.
Code example
Illustrative — APIs change between versions.
1from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, Settings2from llama_index.core.node_parser import SentenceSplitter3from llama_index.core.postprocessor import SimilarityPostprocessor4 5Settings.chunk_size = 512 # override the defaults deliberately6 7docs = SimpleDirectoryReader("./handbook").load_data()8index = VectorStoreIndex.from_documents(9 docs, transformations=[SentenceSplitter(chunk_size=512, chunk_overlap=64)]10)11 12query_engine = index.as_query_engine(13 similarity_top_k=6,14 node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.7)],15 response_mode="compact", # one synthesis call, not 'refine'16)17resp = query_engine.query("What is the on-call escalation policy?")18print(resp.response)19for n in resp.source_nodes: # citations come for free20 print(n.metadata.get("file_name"), round(n.score, 3))