LangChain vs LangGraph: Complete Comparison Guide
A comprehensive comparison of two popular LLM application frameworks, helping developers make informed decisions for their projects.
Overview
What is LangChain?
LangChain is an open-source framework designed to simplify the development of applications powered by large language models (LLMs). It provides a standardized interface for chains, integrations with various tools, and end-to-end chains for common use cases.
What is LangGraph?
LangGraph is a library built on top of LangChain that enables the creation of stateful, multi-actor applications with LLMs. It extends LangChain with graph-based workflows, making it easier to build complex, cyclical applications.
Why Compare Them?
While both frameworks are designed for LLM application development, they serve different needs and excel in different scenarios. Understanding their differences helps you choose the right tool for your specific use case.
Core Concepts & Design Philosophy
LangChain: Chain-Based Thinking
LangChain follows a linear, chain-based approach where operations are sequenced one after another. It's built around the concept of "chains" - sequences of operations that can be linked together to create complex workflows.
Key concepts:
- Chains: Sequential operations linked together
- Prompts: Template-based input formatting
- LLMs: Language model integrations
- Memory: State persistence across calls
- Agents: Dynamic decision-making components
LangGraph: Graph-Based State Thinking
LangGraph introduces a graph-based approach where nodes represent actions and edges define the flow between them. This enables more complex, cyclical workflows with built-in state management.
Key concepts:
- Nodes: Individual actions or functions
- Edges: Connections between nodes
- State: Shared data across the graph
- Conditional Edges: Dynamic routing based on conditions
- Persistence: Built-in state checkpointing
Comparison Table
| Feature | LangChain | LangGraph |
|---|---|---|
| Learning Curve | Easier for beginners | Steeper but more consistent |
| State Management | Memory classes | Built-in state schema |
| Workflow Complexity | Good for linear chains | Excellent for complex graphs |
| Cycles & Loops | Limited support | Full support |
| Integrations | 100+ integrations | LangChain integrations |
| Debugging | Callbacks + LangSmith | State snapshots + LangSmith |
| Production Deployment | LangServe | Works with LangServe |
| Multi-Agent Systems | Possible but complex | Native support |
| Documentation | Extensive | Growing |
Learning Curve
LangChain Learning Path
LangChain has a relatively gentle learning curve for beginners. The chain-based concept is intuitive, and the documentation provides many examples. However, as applications become more complex, managing state and flow can become challenging.
Beginner: 2-3 days - Basic chains and prompts
Intermediate: 1-2 weeks - Agents, tools, and memory
Advanced: 2-4 weeks - Custom chains and complex integrations
LangGraph Learning Path
LangGraph has a steeper initial learning curve due to its graph-based paradigm. However, once understood, it provides a more consistent mental model for complex applications.
Beginner: 3-5 days - Basic graphs and state
Intermediate: 1-2 weeks - Conditional routing and persistence
Advanced: 2-3 weeks - Complex multi-agent systems
Code Comparison
Level 1: Simple Q&A Chain
Let's start with the simplest use case: a basic question-answering chain.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# Define the prompt template
prompt = ChatPromptTemplate.from_template(
"Answer the following question: {question}"
)
# Create the model
model = ChatOpenAI(model="gpt-4")
# Build the chain
chain = prompt | model | StrOutputParser()
# Invoke the chain
result = chain.invoke({"question": "What is LangChain?"})
print(result) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict
# Define state
class State(TypedDict):
question: str
answer: str
# Define nodes
def generate_answer(state: State) -> State:
model = ChatOpenAI(model="gpt-4")
response = model.invoke(state["question"])
return {"answer": response.content}
# Build the graph
graph = StateGraph(State)
graph.add_node("generate", generate_answer)
graph.set_entry_point("generate")
graph.add_edge("generate", END)
# Compile and run
app = graph.compile()
result = app.invoke({"question": "What is LangGraph?"})
print(result["answer"]) Level 2: Multi-turn Conversation
Adding conversation history to maintain context across multiple turns.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage, AIMessage
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
# Create model and memory
model = ChatOpenAI(model="gpt-4")
memory = ConversationBufferMemory(return_messages=True)
# Create conversation chain
chain = ConversationChain(llm=model, memory=memory)
# Multi-turn conversation
response1 = chain.predict(input="Hi, I'm Alice")
response2 = chain.predict(input="What's my name?")
print(response2) # Will remember "Alice" from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
from typing import TypedDict, List
class State(TypedDict):
messages: List
def chat(state: State) -> State:
model = ChatOpenAI(model="gpt-4")
response = model.invoke(state["messages"])
return {"messages": state["messages"] + [response]}
# Build graph
graph = StateGraph(State)
graph.add_node("chat", chat)
graph.set_entry_point("chat")
graph.add_edge("chat", END)
app = graph.compile()
# Multi-turn with state
result1 = app.invoke({
"messages": [HumanMessage("Hi, I'm Alice")]
})
result2 = app.invoke({
"messages": result1["messages"] + [HumanMessage("What's my name?")]
}) Level 3: RAG (Retrieval-Augmented Generation)
Implementing a RAG pipeline with document retrieval and generation.
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import TextLoader
# Load and split documents
loader = TextLoader("docs.txt")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)
texts = text_splitter.split_documents(documents)
# Create vector store
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(texts, embeddings)
# Create RAG chain
llm = ChatOpenAI(model="gpt-4")
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever()
)
# Query
result = qa_chain.invoke({"query": "What is the document about?"}) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from typing import TypedDict, List
class RAGState(TypedDict):
query: str
documents: List[str]
answer: str
def retrieve(state: RAGState) -> RAGState:
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(persist_directory="./chroma", embedding_function=embeddings)
docs = vectorstore.similarity_search(state["query"], k=3)
return {"documents": [d.page_content for d in docs]}
def generate(state: RAGState) -> RAGState:
llm = ChatOpenAI(model="gpt-4")
context = "\n\n".join(state["documents"])
prompt = f"Context: {context}\n\nQuestion: {state['query']}"
response = llm.invoke(prompt)
return {"answer": response.content}
# Build graph
graph = StateGraph(RAGState)
graph.add_node("retrieve", retrieve)
graph.add_node("generate", generate)
graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)
app = graph.compile()
result = app.invoke({"query": "What is the document about?"}) Level 4: Tool Calling & Function Binding
Enabling the LLM to use external tools and functions.
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
# Define custom tools
@tool
def get_weather(city: str) -> str:
"""Get weather for a city"""
return f"Weather in {city}: Sunny, 72Β°F"
@tool
def calculate(expression: str) -> str:
"""Evaluate a math expression"""
return str(eval(expression))
# Create tools list
tools = [get_weather, calculate]
# Create agent
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
# Execute
result = agent_executor.invoke({"input": "What's the weather in Tokyo?"}) from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from typing import TypedDict, List, Any
# Define tools
@tool
def get_weather(city: str) -> str:
"""Get weather for a city"""
return f"Weather in {city}: Sunny, 72Β°F"
@tool
def calculate(expression: str) -> str:
"""Evaluate a math expression"""
return str(eval(expression))
tools = [get_weather, calculate]
# Define state
class AgentState(TypedDict):
messages: List[Any]
# Create model with tools bound
llm = ChatOpenAI(model="gpt-4")
llm_with_tools = llm.bind_tools(tools)
# Define agent node
def agent(state: AgentState) -> AgentState:
response = llm_with_tools.invoke(state["messages"])
return {"messages": state["messages"] + [response]}
# Build graph
graph = StateGraph(AgentState)
graph.add_node("agent", agent)
graph.add_node("tools", ToolNode(tools))
graph.set_entry_point("agent")
# Conditional routing
def should_continue(state):
if state["messages"][-1].tool_calls:
return "tools"
return END
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")
app = graph.compile()
result = app.invoke({"messages": [{"role": "user", "content": "What's the weather in Tokyo?"}]}) Level 5: Multi-Agent Collaboration
Building a system where multiple agents collaborate to solve complex problems.
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain.tools import Tool
from langchain_core.prompts import ChatPromptTemplate
# Create specialized agents
def create_agent(role: str, expertise: str):
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", f"You are a {role}. {expertise}"),
("human", "{input}"),
])
return prompt | llm
# Create agents
researcher = create_agent("researcher", "You gather information.")
analyst = create_agent("analyst", "You analyze data.")
writer = create_agent("writer", "You write reports.")
# Sequential execution (simplified)
topic = "AI trends in 2024"
research = researcher.invoke({"input": f"Research {topic}"})
analysis = analyst.invoke({"input": f"Analyze: {research.content}"})
report = writer.invoke({"input": f"Write report based on: {analysis.content}"}) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, List, Optional
class MultiAgentState(TypedDict):
topic: str
research: Optional[str]
analysis: Optional[str]
report: Optional[str]
feedback: Optional[str]
llm = ChatOpenAI(model="gpt-4")
def researcher_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"Research this topic thoroughly: {state['topic']}"
response = llm.invoke(prompt)
return {"research": response.content}
def analyst_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"Analyze this research: {state['research']}"
response = llm.invoke(prompt)
return {"analysis": response.content}
def writer_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"Write a report based on this analysis: {state['analysis']}"
response = llm.invoke(prompt)
return {"report": response.content}
def reviewer_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"Review this report and provide feedback: {state['report']}"
response = llm.invoke(prompt)
return {"feedback": response.content}
# Conditional: revise if needed
def should_revise(state):
if "revise" in state["feedback"].lower():
return "writer"
return END
# Build graph
graph = StateGraph(MultiAgentState)
graph.add_node("researcher", researcher_node)
graph.add_node("analyst", analyst_node)
graph.add_node("writer", writer_node)
graph.add_node("reviewer", reviewer_node)
graph.set_entry_point("researcher")
graph.add_edge("researcher", "analyst")
graph.add_edge("analyst", "writer")
graph.add_edge("writer", "reviewer")
graph.add_conditional_edges("reviewer", should_revise)
app = graph.compile()
result = app.invoke({"topic": "AI trends in 2024"}) Level 6: Streaming Output
Implementing real-time streaming for better user experience.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# Create model with streaming
model = ChatOpenAI(model="gpt-4", streaming=True)
prompt = ChatPromptTemplate.from_template("Tell me a story about {topic}")
chain = prompt | model
# Stream output
for chunk in chain.stream({"topic": "a brave knight"}):
print(chunk.content, end="", flush=True) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict
class StreamState(TypedDict):
topic: str
story: str
async def generate_story(state: StreamState) -> StreamState:
model = ChatOpenAI(model="gpt-4")
prompt = f"Tell me a story about {state['topic']}"
story_parts = []
async for chunk in model.astream(prompt):
story_parts.append(chunk.content)
print(chunk.content, end="", flush=True)
return {"story": "".join(story_parts)}
# Build graph
graph = StateGraph(StreamState)
graph.add_node("generate", generate_story)
graph.set_entry_point("generate")
graph.add_edge("generate", END)
app = graph.compile()
# Stream execution
import asyncio
asyncio.run(app.ainvoke({"topic": "a brave knight"})) Level 7: Error Handling & Retry Logic
Implementing robust error handling with automatic retries.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from tenacity import retry, stop_after_attempt, wait_exponential
# Wrap with retry decorator
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def call_llm_with_retry(prompt: str) -> str:
model = ChatOpenAI(model="gpt-4")
try:
response = model.invoke(prompt)
return response.content
except Exception as e:
print(f"Error: {e}, retrying...")
raise
# Usage
try:
result = call_llm_with_retry("What is LangChain?")
except Exception as e:
print(f"Failed after retries: {e}") from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, Optional
class RetryState(TypedDict):
prompt: str
response: Optional[str]
error: Optional[str]
attempts: int
MAX_RETRIES = 3
def call_llm(state: RetryState) -> RetryState:
model = ChatOpenAI(model="gpt-4")
try:
response = model.invoke(state["prompt"])
return {"response": response.content, "error": None}
except Exception as e:
return {"error": str(e), "attempts": state.get("attempts", 0) + 1}
def handle_error(state: RetryState) -> RetryState:
print(f"Attempt {state['attempts']} failed: {state['error']}")
return state
def should_retry(state: RetryState):
if state.get("response"):
return END
if state.get("attempts", 0) >= MAX_RETRIES:
return END
return "call_llm"
# Build graph
graph = StateGraph(RetryState)
graph.add_node("call_llm", call_llm)
graph.add_node("handle_error", handle_error)
graph.set_entry_point("call_llm")
graph.add_conditional_edges("call_llm", should_retry)
graph.add_edge("handle_error", "call_llm")
app = graph.compile()
result = app.invoke({"prompt": "What is LangGraph?", "attempts": 0}) Architecture Deep Dive
LangChain Architecture
LangChain follows a layered architecture with three main layers:
- Integration Layer: Connections to external services (LLMs, vector stores, tools)
- Core Layer: Fundamental abstractions (prompts, chains, memory)
- Application Layer: Pre-built solutions for common use cases
The chain-based design promotes composition - smaller chains can be combined into larger ones. However, this can lead to deeply nested structures that are difficult to debug and maintain.
LangGraph Architecture
LangGraph introduces a graph-based architecture with these components:
- State Schema: TypedDict defining the shared state structure
- Nodes: Functions that receive and update state
- Edges: Define flow between nodes (static or conditional)
- Checkpointer: Persistence layer for state snapshots
The graph structure is more explicit about flow control, making complex applications easier to understand and debug. State management is built into the framework rather than being an add-on feature.
Performance Benchmarks
Based on community benchmarks and internal testing, here are typical performance characteristics:
| Metric | LangChain | LangGraph |
|---|---|---|
| Simple Chain Overhead | ~5ms | ~15ms |
| Complex Workflow (5+ steps) | ~50ms overhead | ~30ms overhead |
| State Serialization | Manual | Automatic (~10ms) |
| Memory Usage | Lower for simple cases | Slightly higher baseline |
| Streaming Latency | ~50ms first token | ~55ms first token |
Note: These are approximate values and will vary based on specific use cases, LLM provider, and infrastructure.
Use Case Analysis
Best Use Cases for LangChain
1. Simple Chatbots
If you're building a straightforward chatbot with basic conversation history, LangChain's conversation chains are quick to implement and sufficient for most needs.
2. Document Q&A Systems
For RAG applications where documents are loaded once and queried repeatedly, LangChain's retrieval chains work well and are easy to set up.
3. Prototyping & Experiments
When you need to quickly test an idea or build an MVP, LangChain's extensive templates and examples allow for rapid development.
Best Use Cases for LangGraph
1. Multi-Agent Systems
When multiple agents need to collaborate, pass information, or iterate on each other's work, LangGraph's graph structure provides the necessary control flow.
2. Complex Workflows with Branching
Applications that require dynamic routing based on intermediate results benefit from LangGraph's conditional edges and explicit state management.
3. Long-Running Processes
Workflows that may span hours or days need persistence and resumption capabilities, which LangGraph provides through its checkpointer system.
4. Human-in-the-Loop Systems
When human approval or intervention is needed at specific points, LangGraph's interrupt and resume capabilities make implementation straightforward.
Migration Guide
From LangChain to LangGraph
If you're considering migrating from LangChain to LangGraph, here's a general approach:
- Identify State: Define what data needs to persist between steps
- Map Chains to Nodes: Each chain becomes a node in the graph
- Define Edges: Connect nodes based on your workflow logic
- Handle Memory: Replace memory classes with state schema
- Test Incrementally: Migrate one component at a time
Using Both Together
Remember that LangGraph is built on LangChain, so you can continue using LangChain components (LLMs, tools, retrievers) within LangGraph nodes. This allows for gradual migration and best-of-both-worlds solutions.
Flexibility & Extensibility
LangChain Flexibility
- Extensive integration ecosystem (100+ integrations)
- Easy to swap components (LLMs, vector stores, etc.)
- Chain composition allows building complex workflows
- Custom chains can be created by extending base classes
LangGraph Flexibility
- Graph structure allows any workflow topology
- Conditional edges enable dynamic routing
- Built-in persistence and state management
- Better support for cycles and loops
State Management & Memory
LangChain State Management
LangChain uses memory classes to persist state across calls. Various memory types are available (ConversationBufferMemory, ConversationSummaryMemory, etc.), but they can be complex to configure and may not handle all scenarios well.
LangGraph State Management
LangGraph has built-in state management through its TypedDict-based state schema. State is passed between nodes and can be modified at each step. The checkpointer system allows for persistence and resumption of workflows.
Error Handling & Debugging
LangChain Error Handling
- Try-catch blocks around chain invocations
- Callbacks for logging and monitoring
- LangSmith integration for tracing
LangGraph Error Handling
- Error nodes for handling failures
- Retry logic built into graph execution
- Better visibility into execution flow
- State snapshots for debugging
Performance & Efficiency
Both frameworks have similar performance characteristics for basic operations. Key differences:
- LangChain: Lower overhead for simple chains; may become complex with state management
- LangGraph: Slightly higher initial overhead; better performance for complex, stateful workflows
Community & Documentation
LangChain Ecosystem
- Larger community and more resources
- Extensive documentation and examples
- More third-party integrations
- LangSmith for observability
LangGraph Ecosystem
- Growing community
- Well-documented core concepts
- Tighter integration with LangChain ecosystem
- LangSmith integration for tracing
Production Readiness
LangChain in Production
- Used by many production applications
- LangServe for deployment
- Good for simpler, linear workflows
- May require custom state management
LangGraph in Production
- Built for complex, stateful applications
- Built-in persistence and resumption
- Better for multi-agent systems
- Explicit error handling and retry logic
Conclusion & Recommendations
When to Choose LangChain
- Simple, linear workflows
- Quick prototyping and experimentation
- Need for extensive third-party integrations
- Team is new to LLM development
- Standard use cases (chatbots, Q&A, RAG)
When to Choose LangGraph
- Complex, multi-step workflows
- Applications requiring cycles or loops
- Multi-agent systems
- Need for explicit state management
- Long-running, resumable workflows
Hybrid Approach
It's worth noting that LangGraph is built on top of LangChain, so you can use both together. Many applications use LangChain for basic operations (LLM calls, retrievers) while using LangGraph for orchestration and state management.
Future Trends
Both frameworks are actively developed and evolving. LangGraph is becoming the recommended approach for complex applications within the LangChain ecosystem. Expect to see more convergence and better tooling for both in the future.