LangChain vs LlamaIndex for RAG Pipelines
The same RAG pipeline built in LangChain and in LlamaIndex, a side-by-side list of their prebuilt components, and which framework fits each common use case.
On this page 8 sections
LangChain and LlamaIndex can both build a production RAG pipeline. They start from different places. LlamaIndex is a data framework: its best parts are about getting documents in, split well and retrieved accurately. LangChain is an orchestration framework: its best parts are about chaining models, tools and steps, and with LangGraph, running agents that keep state.
So the short answer is:
- If the hard part of your project is retrieval quality over messy documents, start with LlamaIndex.
- If the hard part is what happens around retrieval (tools, multi-step logic, approvals, an agent that does more than answer questions), start with LangChain and LangGraph.
- If both are hard, use both. They work well together.
The rest of this guide shows why, with the same pipeline built in each, a component-by-component comparison and a recommendation for each common use case.
How each framework thinks about RAG
A RAG pipeline has two halves:
- Ingestion: load documents, parse them, split them into chunks, embed the chunks and store them in a vector database.
- Query: take a question, retrieve the relevant chunks, optionally rerank them, and have a model write an answer from them with sources.
LlamaIndex is built around this pipeline. Its core objects are the stages themselves: readers, node parsers, indexes, retrievers, postprocessors, response synthesizers and query engines. Defaults are chosen for you, so a working pipeline takes a few lines, and each stage can be swapped out when the defaults stop being good enough.
LangChain gives you the building blocks (loaders, splitters, embeddings, vector stores, retrievers) as interchangeable integrations, and leaves the shape of the pipeline to you. Since LangChain 1.0, the recommended way to put RAG in front of users is an agent built with create_agent, with retrieval exposed as a tool. Agents run on LangGraph, which handles state, memory, streaming and human-in-the-loop steps.
The example pipeline
An internal assistant that answers employee questions from a folder of HR handbook files, and cites the file each answer came from.
Built in LlamaIndex
from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
Settings.llm = OpenAI(model="gpt-5-mini")
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
# Ingestion: load, split, embed and index
documents = SimpleDirectoryReader("./handbook", recursive=True).load_data()
index = VectorStoreIndex.from_documents(
documents,
transformations=[SentenceSplitter(chunk_size=512, chunk_overlap=64)],
)
index.storage_context.persist("./storage") # or pass a vector store such as Qdrant or pgvector
# Query: retrieve 10 candidates, rerank to the best 3, then answer
query_engine = index.as_query_engine(
similarity_top_k=10,
node_postprocessors=[
SentenceTransformerRerank(model="cross-encoder/ms-marco-MiniLM-L-6-v2", top_n=3),
],
)
response = query_engine.query("How many days of parental leave do we offer?")
print(response)
for node in response.source_nodes:
print(node.metadata["file_name"], round(node.score, 2))Reranking, source tracking and the answer prompt are all built in. To make it a multi-turn chat, swap as_query_engine() for as_chat_engine(chat_mode="condense_plus_context").
Built in LangChain
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_chroma import Chroma
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Ingestion: load, split, embed and store
docs = DirectoryLoader("./handbook", glob="**/*.md", loader_cls=TextLoader).load()
chunks = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=200).split_documents(docs)
vector_store = Chroma.from_documents(
chunks,
OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./chroma",
)
# Query: retrieval is a tool the agent decides when to call
@tool(response_format="content_and_artifact")
def search_handbook(query: str):
"""Search the employee handbook for policies and procedures."""
results = vector_store.similarity_search(query, k=4)
text = "\n\n".join(f"Source: {doc.metadata['source']}\n{doc.page_content}" for doc in results)
return text, results
agent = create_agent(
model="openai:gpt-5-mini",
tools=[search_handbook],
system_prompt=(
"You answer questions about the employee handbook. Always search it first "
"and cite the source file. If the handbook doesn't cover it, say so."
),
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "How many days of parental leave do we offer?"}]}
)
print(result["messages"][-1].content)There is a bit more code, but the shape is different in a useful way: the agent can call search_handbook more than once, rephrase its query, or skip retrieval for "hello". Adding a second tool, such as get_leave_balance(employee_id), is one more function in the tools list. If you want retrieval to always run exactly once, a two-step chain (retrieve, then prompt the model) is simpler and cheaper than an agent.
One detail that catches people moving between the two: LlamaIndex's SentenceSplitter measures chunks in tokens, LangChain's RecursiveCharacterTextSplitter measures in characters unless you build it with from_tiktoken_encoder. That's why the chunk sizes above differ.
Prebuilt components compared
| Pipeline stage | LlamaIndex | LangChain |
|---|---|---|
| Loading documents | SimpleDirectoryReader handles PDFs, Word, Markdown and more out of the box, plus hundreds of readers on LlamaHub | Hundreds of document loaders in langchain-community and partner packages (PDF, web, Notion, Google Drive, S3 and so on) |
| Parsing complex files | LlamaParse (paid, via LlamaCloud) for tables, charts and scanned PDFs; MarkdownElementNodeParser for tables | Integrations such as Unstructured, Docling and PyMuPDF; no first-party parser |
| Chunking | SentenceSplitter, TokenTextSplitter, SemanticSplitterNodeParser, MarkdownNodeParser, HierarchicalNodeParser, SentenceWindowNodeParser | RecursiveCharacterTextSplitter, MarkdownHeaderTextSplitter, HTMLHeaderTextSplitter, code-aware splitters, token splitters |
| Ingestion pipeline | IngestionPipeline with caching and a docstore that skips unchanged documents on re-runs | The indexing API with a record manager to skip duplicates and remove deleted documents |
| Embeddings and vector stores | Integrations for all major providers and databases | Integrations for all major providers and databases |
| Index types | Vector, summary, keyword and property graph indexes | Vector stores only; graph RAG through separate integrations |
| Retrievers | Vector, BM25, QueryFusionRetriever (hybrid and multi-query), AutoMergingRetriever, RecursiveRetriever, router retriever | Vector store retriever, BM25, EnsembleRetriever (hybrid), MultiQueryRetriever, ParentDocumentRetriever, SelfQueryRetriever, MultiVectorRetriever |
| Reranking and filtering | Node postprocessors: cross-encoder, Cohere and LLM rerankers, similarity cutoff, metadata replacement for sentence windows | ContextualCompressionRetriever with Cohere, cross-encoder or LLM-based compressors |
| Answer generation | Response synthesizers (compact, refine, tree_summarize), CitationQueryEngine | Prompt templates and chat models you compose yourself, or the agent's final answer |
| Multi-source questions | RouterQueryEngine, SubQuestionQueryEngine, SQL and pandas query engines | Agent with several retrieval tools, SQL toolkit |
| Chat and memory | Chat engines with built-in condensing of follow-up questions, memory modules | LangGraph checkpointers for conversation state, long-term memory stores |
| Agents and orchestration | FunctionAgent, AgentWorkflow, event-driven Workflows | create_agent on LangGraph, with middleware, human-in-the-loop interrupts and durable execution |
| Evaluation | Built-in faithfulness, relevancy, correctness and retriever (hit rate, MRR) evaluators | LangSmith datasets and evaluators (commercial, with a free tier), or open tools such as Ragas |
| Tracing | One-line integrations with Arize Phoenix, Langfuse and others | LangSmith, plus Langfuse and other integrations |
| Managed platform | LlamaCloud: hosted parsing, ingestion and retrieval | LangSmith: tracing, evaluation and agent deployment |
Note that in LangChain 1.0, several older chains and retriever helpers moved to the langchain-classic package. They still work, but newer tutorials build the same things with agents and plain functions, so check the date on any example you copy.
Advanced retrieval techniques out of the box
These are the techniques that usually separate a demo from a system people trust. Both frameworks cover most of them. The difference is how much you write yourself.
| Technique | What it fixes | LlamaIndex | LangChain |
|---|---|---|---|
| Hybrid search (keyword + vector) | Missed exact terms such as product codes and names | QueryFusionRetriever over vector and BM25 retrievers, or the vector database's own hybrid mode | EnsembleRetriever over vector and BM25 retrievers, or the database's hybrid mode |
| Reranking | Right chunk retrieved but ranked too low | One postprocessor | Compression retriever wrapper |
| Small-to-big retrieval | Small chunks match well but lack context | SentenceWindowNodeParser, AutoMergingRetriever | ParentDocumentRetriever |
| Multi-query / query rewriting | Vague or badly worded questions | QueryFusionRetriever with num_queries, HyDE transform | MultiQueryRetriever, or let the agent rephrase |
| Metadata filtering | Answers from the wrong department, year or customer | Metadata filters on retrievers, auto-retrieval | Filters on the vector store, SelfQueryRetriever |
| Questions that span documents | "Compare our 2025 and 2026 policies" | SubQuestionQueryEngine | An agent calling the search tool once per document |
| Tables in PDFs | Numbers lost or scrambled in chunking | LlamaParse plus recursive retrieval over table summaries | MultiVectorRetriever with table summaries, built yourself |
| Knowledge graphs | Questions about relationships between entities | PropertyGraphIndex | Graph database integrations, more assembly required |
In general, LlamaIndex has a named component for each technique, and LangChain gives you the pieces to build it. Neither choice fixes bad source documents: clean parsing and sensible chunking matter more than any retriever on this list.
Head-to-head comparison
| LlamaIndex | LangChain | |
|---|---|---|
| Main strength | Ingestion and retrieval quality | Orchestration, tools and agents |
| Time to first working RAG | Minutes, with good defaults | A little longer, with more choices to make |
| Abstraction level | Higher: query engines hide the plumbing | Lower: you see and control each step |
| Customizing the middle of the pipeline | Subclass or replace a stage | Rewrite the function |
| Agents and multi-step workflows | Capable (Workflows, AgentWorkflow) | The strongest part of the ecosystem (LangGraph) |
| Human-in-the-loop and long-running tasks | Possible with Workflows | Built into LangGraph (interrupts, checkpoints, resuming) |
| Built-in RAG evaluation | Included in the library | Mainly through LangSmith |
| Integrations | Very wide, through LlamaHub | The widest of any LLM framework |
| Debugging | Clear for standard pipelines, harder once defaults are deep | Clear once you trace with LangSmith or Langfuse |
| Upgrade churn | Moderate | Historically high, steadier since 1.0 |
| Community and hiring | Large | Larger |
Which to choose for each use case
| Use case | Example | Choose | Why |
|---|---|---|---|
| Document Q&A over a knowledge base | HR handbook assistant, support docs search, internal wiki bot | LlamaIndex | It's the case LlamaIndex is designed for. Good defaults, built-in citations, reranking in one line |
| Complex PDFs with tables | Financial reports, insurance policies, lab results | LlamaIndex | LlamaParse and table-aware retrieval save weeks of custom parsing work |
| Comparing many documents | "How did vendor A's contract terms change from 2024 to 2026?" | LlamaIndex | SubQuestionQueryEngine splits the question per document and combines the answers |
| Support agent that also takes actions | Answers from the help center, then checks order status or opens a ticket | LangChain | Retrieval is one tool among several, and LangGraph handles the multi-step logic |
| Workflows with approvals | Draft a contract clause from past contracts, wait for legal review, then file it | LangChain | LangGraph's interrupts and checkpoints let a run pause for a person and resume later |
| Research assistant | Searches internal docs and the web, reads results, searches again, writes a report | LangChain | Looping, tool-heavy agents are where LangGraph is strongest |
| Quick proof of concept | "Can AI answer questions over our 500 policy PDFs?" in a day | LlamaIndex | Fewest lines to a result you can show, and easy to grow into a real pipeline |
| Product that already uses LangChain | A SaaS app with LangChain agents adding a "chat with your files" feature | LangChain | One framework, one tracing setup, one set of upgrades to manage |
| SQL plus documents | "What did our top 5 customers by revenue say in their last support ticket?" | Either | LlamaIndex's router and SQL query engines and a LangChain agent with SQL and search tools both work. Pick based on the rest of your stack |
| Simple, latency-critical search | Autocomplete-style answers on a high-traffic site | Neither | Call the embedding API and the vector database client directly. Fewer layers, lower latency, nothing to upgrade |
Using both together
The two frameworks aren't mutually exclusive, and a common production setup uses each for what it does best: LlamaIndex for ingestion and retrieval, LangChain or LangGraph for the agent.
from langchain.agents import create_agent
from langchain.tools import tool
# Built with LlamaIndex as in the first example: parsing, chunking, hybrid search, reranking
query_engine = index.as_query_engine(similarity_top_k=10, node_postprocessors=[reranker])
@tool
def search_policies(question: str) -> str:
"""Answer a question from the company policy documents, with sources."""
response = query_engine.query(question)
sources = ", ".join({node.metadata["file_name"] for node in response.source_nodes})
return f"{response}\n\nSources: {sources}"
agent = create_agent(
model="openai:gpt-5-mini",
tools=[search_policies, get_leave_balance, create_hr_ticket],
system_prompt="You are the HR assistant...",
)The retrieval layer can be tuned and evaluated on its own, and the agent can change without touching it.
Mistakes to avoid with either framework
- Choosing the framework before checking the documents. Scanned PDFs, tables and slide decks need a parsing plan first. The framework comes second.
- Skipping evaluation. Write 30 to 50 real questions with known answers before tuning anything. Without them, every change to chunking or retrieval is a guess.
- Copying old tutorials. Both frameworks have changed a lot. Check that an example matches the version you've installed.
- Using an agent where a pipeline will do. If every question needs exactly one retrieval, a fixed pipeline is faster, cheaper and easier to debug than an agent deciding when to search.
- Over-abstracting a simple system. If you only need embed, search and answer, a hundred lines of plain Python may be the most maintainable option.
For what a RAG system costs to build and run once you've chosen the stack, see how much RAG development costs.
If you're planning a RAG system and aren't sure which way to build it, tell me about your documents and users. The first step of every RAG project I take on is a short evaluation on your real documents, and the framework choice follows from what it shows.
