LangChain and LlamaIndex
🎯 Goal
After reading this chapter:
- You know the difference between LangChain and LlamaIndex frameworks
- You can build document loading, splitting, embedding pipelines
- You can work with chains and agents (LangChain)
- You build fast RAG with indexes (LlamaIndex)
- You also get familiar with modern alternatives (Pydantic AI, Instructor, raw API)
**Note:**In 2024-2026, industry sentiment is moving awayfrom LangChain (too complex, excessive abstraction). Modern approach: raw API + Instructor + minimal framework. But LangChain is still used in many projects — you should know it.
What to learn
- LangChain: chains, agents, memory, callbacks
- LangChain LCEL(LangChain Expression Language)
- LlamaIndex: indexes, retrievers, query engines
- Document loaders — PDF, HTML, Notion, GitHub
- Text splitters — RecursiveCharacter, Markdown, Code
- Modern alternatives — Pydantic AI, Instructor, raw API
- LangGraph — multi-agent workflows
- LangSmith — observability
Libraries
pip install langchain langchain-openai langchain-anthropic langchain-community
pip install llama-index llama-index-llms-openai
pip install pydantic-ai instructor
pip install unstructured pypdf # document loading
Framework comparison
| LangChain | LlamaIndex | Raw API + Instructor | |
|---|---|---|---|
| Learning curve | Steep | Medium | Low |
| RAG support | Good | Excellent | Manual |
| Agents | Complex | Good | Needs LangGraph |
| Production | Mixed reviews | Good | Best |
| Performance | Slow | OK | Fastest |
| Industry trend | ⬇️ | ⬆️ | ⬆️⬆️ |
| Code clarity | Abstract | Better | Clearest |
Recommendation:
- New project → raw API + Instructor + LlamaIndex(for RAG)
- Existing LangChain — keep, but migrate for new features
- Complex agent workflows → LangGraph
Code examples
LangChain — basic chain
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# LCEL syntax (new, recommended)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant. Language: {language}"),
("user", "{question}"),
])
chain = prompt | llm | StrOutputParser()
# Run
result = chain.invoke({"language": "uzbek", "question": "What is Python?"})
print(result)
LangChain — RAG (simple)
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 1. Load
loader = PyPDFLoader("document.pdf")
docs = loader.load()
# 2. Split
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
chunks = splitter.split_documents(docs)
# 3. Embed + store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings)
# 4. Retrieve + answer
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o-mini"),
retriever=retriever,
return_source_documents=True,
)
result = qa_chain.invoke({"query": "What is said about the document?"})
print(result["result"])
LlamaIndex — quickest RAG
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import Settings
# Settings (global)
Settings.llm = OpenAI(model="gpt-4o-mini")
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
# 1. Load (PDF, txt, markdown — various)
documents = SimpleDirectoryReader("data/").load_data()
# 2. Index (automatic embedding + chunk)
index = VectorStoreIndex.from_documents(documents)
# 3. Query
query_engine = index.as_query_engine(similarity_top_k=5)
response = query_engine.query("About this document")
print(response)
print(response.source_nodes) # which chunks it came from
LlamaIndex — Chat engine (multi-turn)
from llama_index.core.memory import ChatMemoryBuffer
memory = ChatMemoryBuffer.from_defaults(token_limit=4000)
chat_engine = index.as_chat_engine(
chat_mode="context",
memory=memory,
system_prompt="You are an experienced assistant. Answer only based on the given context.",
)
response = chat_engine.chat("Explain about this project")
print(response.response)
# Follow-up
response = chat_engine.chat("What are the main challenges?")
Pydantic AI — modern alternative
from pydantic_ai import Agent
from pydantic import BaseModel
class WeatherInfo(BaseModel):
temperature: float
condition: str
humidity: int
weather_agent = Agent(
model="openai:gpt-4o-mini",
result_type=WeatherInfo,
system_prompt="You are a weather agent. Estimate information for the given city.",
)
result = weather_agent.run_sync("Weather in Toshkent")
print(result.data) # Type-safe WeatherInfo object
Instructor — fully type-safe
import instructor
from openai import OpenAI
from pydantic import BaseModel
client = instructor.from_openai(OpenAI())
class Person(BaseModel):
name: str
age: int
occupation: str
# Guaranteed structured output (retries on failure)
person = client.chat.completions.create(
model="gpt-4o-mini",
response_model=Person,
messages=[{"role": "user", "content": "My name is Ali, I am 30 years old, a developer"}],
)
print(person) # Person(name="Ali", age=30, occupation="developer")
LangGraph — multi-agent
from langgraph.graph import StateGraph, END
from typing import TypedDict
class AgentState(TypedDict):
question: str
research: str
answer: str
# Node functions
def researcher(state):
# Search/research
return {"research": "Information found..."}
def writer(state):
# Generate answer
return {"answer": f"Answer:{state['research']}"}
def reviewer(state):
# Review
if len(state["answer"]) < 50:
return {"answer": state["answer"], "needs_revision": True}
return {"answer": state["answer"], "needs_revision": False}
# Build graph
workflow = StateGraph(AgentState)
workflow.add_node("researcher", researcher)
workflow.add_node("writer", writer)
workflow.add_node("reviewer", reviewer)
workflow.set_entry_point("researcher")
workflow.add_edge("researcher", "writer")
workflow.add_edge("writer", "reviewer")
workflow.add_conditional_edges(
"reviewer",
lambda x: "writer" if x.get("needs_revision") else END,
)
app = workflow.compile()
result = app.invoke({"question": "What is Python?"})
Document loaders — variety
# PDF
from langchain_community.document_loaders import PyPDFLoader
docs = PyPDFLoader("file.pdf").load()
# HTML / Website
from langchain_community.document_loaders import WebBaseLoader
docs = WebBaseLoader("https://example.com").load()
# YouTube transcript
from langchain_community.document_loaders import YoutubeLoader
docs = YoutubeLoader.from_youtube_url("https://...", add_video_info=True).load()
# Notion
from langchain_community.document_loaders import NotionDirectoryLoader
docs = NotionDirectoryLoader("notion_export/").load()
# GitHub
from langchain_community.document_loaders import GitHubIssuesLoader
docs = GitHubIssuesLoader("owner/repo", access_token="...").load()
# CSV/Excel
from langchain_community.document_loaders import CSVLoader
docs = CSVLoader("data.csv").load()
Text splitters
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter,
CharacterTextSplitter,
)
# Recursive — most common (with various separators)
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""],
)
# Markdown — by header
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
])
# Code — semantic splitting
from langchain.text_splitter import Language, RecursiveCharacterTextSplitter
py_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, chunk_size=1000, chunk_overlap=100
)
Backend integration
FastAPI + LlamaIndex RAG service
from fastapi import FastAPI
from llama_index.core import VectorStoreIndex, StorageContext, load_index_from_storage
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
# Load persisted index
storage_context = StorageContext.from_defaults(persist_dir="./index_storage")
app.state.index = load_index_from_storage(storage_context)
app.state.query_engine = app.state.index.as_query_engine(similarity_top_k=5)
yield
app = FastAPI(lifespan=lifespan)
class QueryRequest(BaseModel):
question: str
@app.post("/query")
async def query(req: QueryRequest):
response = await app.state.query_engine.aquery(req.question)
return {
"answer": str(response),
"sources": [
{"text": node.text[:200], "score": node.score}
for node in response.source_nodes
],
}
Production architecture
User → FastAPI → LlamaIndex (in-memory) → Qdrant (vectors) → LLM API
↓
Redis (cache)
↓
Postgres (history, logs)
↓
Langfuse (observability)
Resources
LangChain
- docs: python.langchain.com
- LangChain Academy — free course
- LangGraph docs — multi-agent
LlamaIndex
- docs: docs.llamaindex.ai
- LlamaIndex bootcamp — free
Modern alternatives
- Pydantic AI — ai.pydantic.dev — type-safe agents
- Instructor — python.useinstructor.com — guaranteed JSON
- Haystack — haystack.deepset.ai — production RAG framework
Observability
- Langfuse — langfuse.com (open source)
- LangSmith — from LangChain
- Phoenix (Arize) — open source
🏋️ Exercises
🟢 Easy
- Simple chain with LangChain LCEL (prompt | llm | parser).
- RAG a PDF in 5 lines with LlamaIndex.
- Resume → structured Pydantic with Instructor.
🟡 Medium
- Multi-source RAG: PDF + website + YouTube transcript — combined index.
- Conversational RAG: multi-turn with chat history.
- LangGraph workflow: 3-agent pipeline (researcher → writer → reviewer).
🔴 Hard
- Production RAG service: LlamaIndex + Qdrant + FastAPI + Langfuse. 100+ documents, async query, source citations, monitoring.
- Framework comparison: Write the same RAG in LangChain, LlamaIndex, and raw API; compare time and accuracy.
- Migration: Move existing LangChain code to Pydantic AI or raw API.
Capstone
notebooks/month-05/04_langchain_llamaindex.md:
- 50+ documents in Uzbek (PDFs, websites)
- RAG index with LlamaIndex
- Multi-turn chat engine
- Source citations
- FastAPI + Streamlit UI
✅ Checklist
- LangChain LCEL syntax
- LlamaIndex basic RAG
- Pydantic AI / Instructor (modern)
- Document loaders and text splitters
- Multi-source RAG
- Chat engine memory
- LangGraph multi-agent
- Production observability (Langfuse)
Moving on to Vector Databases.