LlamaIndex
Data framework for LLM apps — indexes, retrievers, query engines end-to-end; ingest/upsert, quality loops, synthesizers, ACL filters, debugging, and when to use vs LangChain/LangGraph.
What LlamaIndex is (plain English)
LlamaIndex is a data framework for LLM apps: connectors (readers), node parsers (chunking), indexes, retrievers, query engines, and agent abstractions aimed at “connect LLM ↔ private data.”
Complementary positioning:
| Library | Gravity |
|---|---|
| LlamaIndex | Indexes, retrieval, data connectors |
| LangChain / LangGraph | Chains, tools, agent graphs |
| Hand-rolled | Learning + full control (ShipAI default first) |
You can mix: LlamaIndex for retrieval, your own loop or LangGraph for agents.
Analogy: LlamaIndex is an ORM for retrieval — it maps documents → nodes → indexes the way an ORM maps rows → models. You still own the database (vector store), ACLs, and evals.
flowchart LR
Docs[Documents] --> Load[Readers / connectors]
Load --> Nodes[Nodes = chunks + metadata]
Nodes --> Idx[Index / vector store]
Idx --> Ret[Retriever]
Ret --> QE[Query engine]
QE --> LLM[LLM + citations]
Interview cue: Be ready to explain Document → Node → Index → Retriever without naming LlamaIndex. The framework should be optional clothing on that skeleton.
The problem it solves
RAG glue code explodes: load PDFs, chunk, embed, store, retrieve, pack citations, retry. LlamaIndex packages that pipeline so teams iterate on retrieval quality instead of rewriting loaders.
| Pain | LI-shaped help |
|---|---|
| Many file types / SaaS sources | Readers / connectors |
| Chunk + metadata boilerplate | Node parsers |
| Swap vector backends | Index abstractions |
| Query + synthesize | Query / chat engines |
| Retriever as agent tool | Tool wrappers |
It does not replace your vector DB ops, ACL model, or eval suite — it sits above them.
Architecture: core objects
| Object | Role |
|---|---|
| Document | Raw loaded content |
| Node | Chunk + metadata (the retrieval unit) |
| Index | Structure over nodes (vector, keyword, KG variants) |
| Retriever | Given query → nodes |
| Query engine | Retrieve + synthesize answer |
| Chat engine | Multi-turn wrapper over retrieval |
| Response synthesizer | How nodes become an answer (refine, compact, tree…) |
flowchart TB
Q[User query] --> Ret[Retriever top-k]
Ret --> Pack[Context packer]
Pack --> Synth[Response synthesizer]
Synth --> Out[Answer + sources]
VS[(Vector store: Chroma / pgvector / ...)] --> Ret
Index types you will actually meet
| Index | Use |
|---|---|
| VectorStoreIndex | Default semantic RAG |
| Keyword / BM25-ish | Exact tokens; often hybridized |
| Summary / tree | Hierarchical synthesize over large corpora |
| Knowledge graph | Entity/relation flavored retrieval (heavier) |
Start with vector + metadata filters. Add hybrid + rerank when recall@k plateaus — see Hybrid search and rerankers.
How it fits LLM apps
| Product surface | LlamaIndex role |
|---|---|
| Doc Q&A / wiki bot | Query engine over company corpus |
| Agent with memory | Retriever as a tool |
| Eval harness | Swap retrievers; measure hit rate |
| Ingest jobs | Readers + node parsers in batch workers |
| Multi-tenant SaaS | Metadata filters on every retrieve |
flowchart TB
Ingest[Batch ingest workers] --> LI[LlamaIndex readers + nodes]
LI --> Store[(Vector DB)]
API[Product API] --> QE[Query engine / retriever tool]
QE --> Store
QE --> LLM[LLM]
Eval[Golden set CI] --> QE
How to use (shape)
# Shape only — APIs evolve; pin versions
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
docs = SimpleDirectoryReader("./docs").load_data()
index = VectorStoreIndex.from_documents(docs)
qe = index.as_query_engine(similarity_top_k=5)
print(qe.query("What is our refund policy?"))Production-shaped additions you should still own:
- Metadata filters (
tenant_id,doc_type) - Stable node IDs for upserts
- Hybrid retriever + rerank (see Data track)
- Tracing spans around retrieve vs synthesize
- Explicit embed model id in the collection name
Retriever-as-tool for agents
# Shape — expose retrieval to ReAct / LangGraph without dumping full docs
def search_knowledge(query: str, tenant_id: str) -> str:
nodes = retriever.retrieve(query) # with where tenant_id=
# return ids + short excerpts, not novels
return format_excerpts(nodes, max_chars=1200)Pair with LangGraph and LangChain patterns or a hand-rolled loop from Agents and ReAct.
When to pick LlamaIndex
| Situation | Prefer |
|---|---|
| Learning RAG fundamentals | Hand-roll (guided RAG) |
| Many connectors + fast index experiments | LlamaIndex |
| Complex agent graphs / HITL | LangGraph (maybe LI retriever inside) |
| Tiny prototype, one folder | Either LI or raw Chroma |
| Extreme custom retrieval | Own pipeline; steal LI patterns |
Retrieval quality loop (what actually matters)
Framework speed is worthless without a measurement loop:
flowchart LR
Gold[Golden questions] --> Ret[Retriever]
Ret --> Hits[Hit rate / recall@k]
Hits --> Chunk[Tune chunk + metadata]
Chunk --> Ret
Hits --> Rank[Add hybrid + rerank]
Rank --> Ret
- Freeze a golden set (question → relevant
node_ids). - Log retrieved IDs every change.
- Only then tune synthesizer prompts.
Data track depth: Chunking and metadata, Hybrid search and rerankers, Vector databases.
Walkthrough: policy wiki bot
- Ingest Markdown policies with
tenant_id+doc_typemetadata. - Build VectorStoreIndex over Chroma or Postgres + pgvector.
- Query with
where tenant_id=…. - Return answer + source node ids.
- Eval: recall@5 on 50 golden questions before prompt fiddling.
Ingest and upsert discipline
| Practice | Why |
|---|---|
| Stable node IDs | Upsert without duplicates |
| Embed model in collection name | Survive model swaps |
| Content hash in metadata | Skip unchanged chunks |
| Soft-delete flags | Don’t leak retired docs |
| Batch embed | Cost control |
sequenceDiagram
participant Job as Ingest job
participant LI as LlamaIndex
participant VS as Vector store
Job->>LI: load + parse nodes
LI->>VS: upsert by node_id
Note over VS: same id replaces; new id duplicates
Alternatives
| Need | Prefer |
|---|---|
| Learn RAG fundamentals | Hand-roll (guided RAG) |
| LangChain ecosystem retrievers | LangChain; same skeleton |
| Pipeline-oriented NLP/RAG | Haystack |
| Max interview clarity | Raw pgvector / Chroma |
| Agent graphs / HITL | LangGraph + LI retriever as a tool |
Production gotchas
- Default chunking is rarely optimal for your corpus — measure recall@k
- Hidden prompts in synthesizers — version them like code (Weights & Biases / MLflow for LLMOps)
- Embed model changes without reindex → silent quality cliff
- ACL leaks if metadata filters are optional “nice to have”
- Framework upgrades — pin versions; LI APIs move
- Agent tool dump — returning full nodes into ReAct context blows budgets; return ids + excerpts (Context engineering)
- Chat engine memory — unbounded history without summarization
- Evaluating only answers — miss retrieval failures that cause hallucinations
flowchart TD
Bad[Wrong answer] --> Split{Split blame}
Split -->|Missed nodes| R[Fix retrieval / chunk / filters]
Split -->|Good nodes, bad prose| P[Fix synthesizer / model]
Split -->|Wrong tenant| A[Enforce metadata filters]
Failure modes checklist
- Golden questions never written — “vibes” shipping
tenant_idfilter omitted on one code path- Re-embed forgotten after model upgrade
- Query engine citations dropped in UI
- Agent receives 20 full pages per tool call
Hands-on next steps
- Index a Markdown folder; ask 10 golden questions; log retrieved node IDs.
- Rebuild the same index hand-rolled with Chroma; compare misses.
- Add metadata filters and a reranker from the Data track.
- Expose the retriever as a single tool inside a hand-rolled or LangGraph agent.
Micro-project
- Build a 30-question golden set with relevant node ids.
- Index with LlamaIndex defaults; measure recall@5.
- Tune chunk size once; remeasure.
- Hand-roll the same corpus in Chroma; compare miss lists.
- Write a one-pager: keep LI, hand-roll, or hybrid.
Interview whiteboard: Document → Node → Retriever
Draw the skeleton without framework names first. Then put LlamaIndex as a dashed box around readers/parsers/engines. Interviewers want to know you can rebuild it with raw embeddings + Chroma/pgvector.
Synthesizer modes (what “answer” means)
| Mode (conceptually) | Behavior | Risk |
|---|---|---|
| Stuff / compact | Pack nodes into one prompt | Truncation; lost citations |
| Refine | Iteratively update answer per node | Latency; drift |
| Tree / summarize | Hierarchical reduce | Cost; summary loss |
Pick based on corpus size and latency SLO. Always return source node ids to the product layer.
Metadata filter patterns
where tenant_id = X AND doc_type IN (...) AND not deletedOptional filters are ACL bugs waiting to happen. Make tenant filters mandatory in your wrapper — not a caller courtesy.
Observability split: retrieve vs synthesize
Log separately:
retrieve_ms,hit_ids,embed_modelsynthesize_ms,model_id,prompt_version- End-to-end answer score in evals
Otherwise you will “fix the prompt” when the retriever is blind. Pair with OpenTelemetry for LLMs.
Tradeoffs summary
| Use LlamaIndex when… | Hand-roll / other when… |
|---|---|
| Many connectors + fast experiments | Learning RAG fundamentals |
| Team wants shared retrieval abstractions | Extreme custom retrieval scoring |
| Retriever-as-tool inside agents | You need only a 50-line Chroma script |
Checklist
- Golden set with node ids
- recall@k measured before prompt changes
- Stable node ids + embed model in collection name
- Tenant filters mandatory in wrappers
- Synthesizer prompts versioned
- Agent tools return excerpts, not full nodes
Glossary
| Term | Meaning |
|---|---|
| Node | Chunk + metadata retrieval unit |
| Retriever | Query → ranked nodes |
| Query engine | Retrieve + synthesize |
| Response synthesizer | Strategy for turning nodes into text |
| Recall@k | Fraction of relevant nodes in top-k |
| Connector / reader | Loader for a source type |
Debugging playbook (first hour)
| Symptom | First checks | Fix direction |
|---|---|---|
| Confident wrong answer | Log hit_ids; empty / wrong tenant? |
Filters, chunking, hybrid |
| Good hits, bad prose | Compare synthesizer prompt + model | Prompt version / model pin |
| Flaky recall after deploy | Embed model / collection name drift? | Reindex; pin model id |
| Agent context blowups | Tool returns full nodes? | Excerpts + ids only |
| Duplicate chunks | Unstable node ids on re-ingest | Content-hash stable keys |
| Citations missing in UI | Query engine dropped sources | Force source node ids in API |
Ship rule: split retrieve vs synthesize before you touch prompts. Most “LLM regressions” are retrieval regressions.
Anti-patterns
- Framework = architecture — LI is clothing on Document → Node → Retriever.
- Eval only final answers — you cannot tell miss vs hallucination.
- Optional tenant filters — ACL bugs.
- Default chunk sizes forever — measure recall@k on your corpus.
- Dumping query engines into every agent step — budgets die.
- Upgrading LI without pinning — silent API / default changes.
- Chat engine as unbounded memory — summarize or truncate.
Security and privacy threat note
- Readers that pull SaaS sources can ingest secrets — scrub before index.
- Metadata often holds emails / ticket ids — treat as PII in logs.
- Multi-tenant: filter in the wrapper you own, not “hope the caller passes where.”
- Synthesizer prompts can leak system policy if logged raw — redact.
Interview prompts you should be able to answer
- Draw Document → Node → Index → Retriever → synthesizer without naming LlamaIndex.
- How do you measure retrieval quality separately from answer quality?
- What breaks when you swap embedding models?
- How would you expose retrieval to a ReAct agent without blowing the context window?
- When do you keep LlamaIndex vs hand-roll Chroma/pgvector?
End-to-end: from folder to gated release
flowchart LR
Src[Docs in git / drive] --> Ingest[Readers + node parsers]
Ingest --> VS[(Vector store)]
VS --> Ret[Retriever + filters]
Ret --> Synth[Synthesizer]
Synth --> API[Product API + citations]
Gold[Golden set CI] --> Ret
Gold --> Gate{recall@k OK?}
Gate -->|yes| Ship[Ship prompt/retriever version]
Gate -->|no| Tune[Chunk / hybrid / metadata]
Tune --> Ingest
Ship only when recall@k (and a small answer faithfulness sample) beat baseline with lineage: embed_model, chunk_config, prompt_version, git SHA.
Production readiness checklist
- Pin
llama-index*versions - Collection / index name encodes embed model + dim
- Stable node ids + soft-delete
- Mandatory tenant filter in app wrappers
- Golden set with node ids in CI
- Separate retrieve vs synthesize metrics
- Agent tools return excerpts, not full nodes
- Synthesizer prompts versioned as artifacts
How it works end-to-end (request lifecycle)
- Load documents via readers (local files, Notion, Slack, …).
- Parse into nodes (chunks) with metadata and stable ids.
- Embed nodes; upsert into the configured vector store.
- On query: embed question → retrieve top-k (filters applied).
- Synthesize with LLM (compact / refine / tree — pick deliberately).
- Return answer + source nodes; log retrieve vs synthesize metrics separately.
sequenceDiagram
participant App
participant LI as LlamaIndex
participant VS as Vector store
participant LLM
App->>LI: query
LI->>VS: retrieve + filters
VS-->>LI: nodes
LI->>LLM: synthesize with context
LLM-->>LI: answer
LI-->>App: response + citations
Interface contract (staff bar)
Whether you use LlamaIndex or not, keep these seams:
| Seam | Responsibility |
|---|---|
ingest(doc) -> nodes |
Chunk + metadata + ids |
upsert(nodes) |
Idempotent by id |
retrieve(query, filters) -> hits |
Ranked candidates |
synthesize(query, hits) -> answer |
LLM pack + generate |
Framework code should implement the seams — not own business authZ. Enforce tenant_id filters in your retrieve wrapper even if the index API is permissive.
Chunking and metadata (do not outsource judgment)
LlamaIndex node parsers are convenient; they are not a substitute for chunking and metadata strategy:
| Doc type | Typical approach |
|---|---|
| Policies / Markdown | Heading-aware splits |
| Code | Language-aware / symbol boundaries |
| Tables | Keep row groups; attach table title metadata |
| PDFs | Parser choice dominates; reparse triggers matter |
Ablate chunk sizes on a golden set before debating LlamaIndex vs LangChain.
Hybrid retrieval and rerank hooks
Production RAG often needs BM25 + vectors + rerank. LlamaIndex can compose retrievers; you can also retrieve in LI and rerank outside. Measure:
- recall@k vector-only vs hybrid
- nDCG after rerank
- added p95 latency
See Hybrid search and rerankers.
Agents vs query engines
| Abstraction | Use when |
|---|---|
| Query engine | Single-shot RAG Q&A |
| Chat engine | Multi-turn with memory |
| Agent + tools | Multi-step tool use |
Do not start with an agent if a query engine + citations solves the product. Agent loops multiply failure modes — pair with LangGraph when control flow needs cycles/HITL.
Observability fields
| Field | Why |
|---|---|
retriever_id / config hash |
Ablation lineage |
hit_ids |
Debug wrong context |
retrieve_ms / synthesize_ms |
Split latency |
embed_model + revision |
Index compatibility |
filter_json |
ACL bugs surface here |
citation_count |
Empty retrieval detection |
Worked walkthrough: internal wiki RAG
- Reader loads
docs/**/*.md. - Heading node parser; metadata
path,section,tenant=internal. - Upsert to Chroma or pgvector.
- Retriever top-8 with path prefix filter for space.
- Compact synthesizer; require citations.
- Golden 30 questions; gate release on recall@5 + faithfulness sample.
Memory, cost, and sizing intuition
| Lever | Effect |
|---|---|
| Top-k | Context cost + noise |
| Refine synthesizer | More LLM calls |
| Re-embed all | Expensive; version collections |
| Connector polling | Ingest lag vs API cost |
FAQ (LlamaIndex)
Is LlamaIndex required for RAG?
No. It accelerates glue. Hand-rolled teaches the skeleton first.
LlamaIndex or LangChain?
Retrieval-heavy → LI often. Agent graphs → LangGraph. Mixing is fine.
Will LI lock me to one vector DB?
Not if you keep the interface contract and stable node ids.
Deep dive: evaluating synthesizers
Compact packs context once; refine iteratively rewrites; tree summarize hierarchical nodes. On long policies, refine can help — and burn tokens. Always A/B on your corpus with cost caps.
Putting what / why / how together
| Lens | LlamaIndex |
|---|---|
| What | Data framework: load → node → index → retrieve → synthesize |
| Why | Shrink RAG glue; swap stores; iterate retrieval |
| How | Stable ids + filters + split metrics + eval gates |
Architecture that survives production
flowchart TB
subgraph ingest [Ingest workers]
R[Readers]
N[Node parsers]
U[Upsert jobs]
end
subgraph online [Online path]
Ret[Retriever wrapper + ACL]
Syn[Synthesizer]
GW[API gateway]
end
R --> N --> U --> VS[(Vector store)]
GW --> Ret --> VS
Ret --> Syn --> LLM[LLM provider]
Separate ingest from query processes. Nightly connector sync should not share thread pools with user Q&A. Version the node parser with the index; a silent parser bump without re-embed creates split-brain retrieval.
Index types you will actually touch
| Index | Role |
|---|---|
| Vector store index | Default ANN RAG |
| Keyword / BM25 table | Hybrid companion |
| Summary / tree index | Hierarchical corpora (use sparingly; cost) |
| Knowledge graph (advanced) | Entity-heavy domains — evaluate carefully |
Start with vector + metadata filters. Add complexity only when evals demand it.
Consistency and freshness
| Pattern | Tradeoff |
|---|---|
| Sync upsert on doc save | Simple; write amplification |
| Async queue ingest | Lag; need “indexing…” UX |
| Dual-write old/new collections | Safe embed upgrades |
| TTL deletes | Stale private docs risk |
SLA the lag: “searchable within N minutes” is a product promise.
Cost model for LlamaIndex stacks
Costs hide in: embedding calls on re-ingest, synthesizer multi-calls (refine), oversized top-k, and connector polling. Dashboard monthly $ by embed vs synthesize vs agent_tools. Cap refine iterations in config.
Interview whiteboard: replace LlamaIndex boxes
Draw Document → Node → Index → Retriever → Pack → LLM. Then redraw with LI class names. If you cannot erase the framework and keep the diagram, you do not understand the system yet.
Failure story bank
- Connector token expired → empty index → model invents policy.
- Tenant filter omitted in one query engine path → cross-tenant leak.
- Refine synthesizer × long context → latency SLO burn.
- Re-chunk without id scheme → duplicate nodes, weird citations.
End-to-end lab checklist (do this once)
- Index a Markdown folder; query with citations
- Ablate chunk size on 20 questions
- Enforce tenant filter in wrapper tests
- Split retrieve vs synthesize timings in logs
- Compare one hand-rolled retriever on the same corpus
Production readiness checklist (LlamaIndex)
- Node id scheme documented and tested for re-chunk
- Embed model revision pinned; collection/index named accordingly
- ACL/tenant filters enforced outside “happy path” demos
- Retrieve vs synthesize metrics and traces
- Synthesizer mode chosen deliberately (not default-by-accident)
- Eval suite gates releases (retrieval + answer quality)
- Ingest lag SLO defined if async
- Secrets for connectors rotated; least privilege
- Runbook: empty retrieval, embed outage, vector store down
What “good” looks like in a design doc
Name the corpus, chunker, embed model, vector backend, retriever config, synthesizer, citation UX, ACL model, eval suite, and rollback. “We’ll use LlamaIndex” is a library choice, not a design.
Common interview traps
- Claiming LlamaIndex replaces evals or vector DB ops
- Unable to explain nodes without framework jargon
- Ignoring metadata filters in multi-tenant designs
- Equating query engine with a safe agent
Security threat note (retrieval layer)
Retrieved text can contain prompt injection. Treat nodes as untrusted evidence. Bound tool use from RAG answers; cite sources; prefer read-only tools when context is untrusted HTML/email.
Glossary addendum
| Term | Meaning |
|---|---|
| Node | Chunk + metadata unit in LI |
| Query engine | Retrieve + synthesize facade |
| Retriever | Ranked node producer |
| Synthesizer | LLM packing/answering strategy |
| Response synthesizer mode | compact / refine / tree / … |
Micro-project stretch
After the base folder index: add a second embed model collection, shadow-query 20 questions, and write a one-pager on migration cost — without changing the public retrieve() interface.
Related
Core: RAG building blocks, Embeddings. Data: Vector databases, Chunking and metadata. Key tech: Chroma, LangGraph and LangChain patterns. Guided RAG.