LangChain vs LangGraph: When to Use Which in 2026
By 2026, the LangChain ecosystem has become the default toolkit for building LLM-powered applications in Python and TypeScript. Two libraries dominate the landscape: LangChain and LangGraph. They share common infrastructure (LangChain Core, LangSmith, LangServe) but solve fundamentally different problems. This tutorial clarifies what each library does, why the distinction matters, and how to choose the right one for your next project.
What is LangChain?
LangChain is a high-level framework for composing LLM applications using reusable components. Its core abstraction is the chain — a sequence of runnable steps that transform an input into an output. In 2026, LangChain’s standard interface is LangChain Expression Language (LCEL), which treats everything as a Runnable and uses the pipe operator (|) to compose logic.
Typical LangChain primitives include:
- Prompt templates for dynamic prompt construction
- Chat models (OpenAI, Anthropic, local models via Ollama)
- Output parsers that structure raw LLM text into typed data
- Retrievers and vector stores for RAG
- Tools and tool-calling wrappers
Here’s a minimal LangChain example using LCEL:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# Define a simple chain: prompt -> model -> string parser
prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
model = ChatOpenAI(model="gpt-4o", temperature=0.7)
output_parser = StrOutputParser()
chain = prompt | model | output_parser
result = chain.invoke({"topic": "programming"})
print(result)
LangChain shines when your workflow is a predictable sequence of steps — a single input, a single output, with optional intermediate formatting. It’s ideal for RAG pipelines, text summarization, structured extraction, and simple chatbots.
What is LangGraph?
LangGraph is a stateful orchestration framework built on top of LangChain Core. It models agentic workflows as directed graphs with cycles, where nodes represent computation steps and edges represent control flow — including conditional branching, loops, and human approvals. LangGraph introduces the concept of a StateGraph, a graph whose state schema (a typed dictionary or Pydantic model) flows through nodes and is updated at each step.
Key features in 2026:
- Arbitrary graph topologies (sequences, parallel fan-outs, loops)
- Built-in support for persistence and checkpointing
- Human-in-the-loop interrupts via
interrupt() - Streaming modes for real-time observability
- Integration with LangSmith for debugging and tracing
A minimal LangGraph agent looks like this:
from typing import TypedDict, Annotated, Sequence
import operator
from langgraph.graph import StateGraph, END
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_openai import ChatOpenAI
# Define the state schema
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
# other fields as needed
# Initialize the model
model = ChatOpenAI(model="gpt-4o", temperature=0)
def call_model(state: AgentState):
response = model.invoke(state["messages"])
return {"messages": [response]}
# Build the graph
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.set_entry_point("agent")
graph.add_edge("agent", END)
app = graph.compile()
# Run the agent
result = app.invoke({"messages": [HumanMessage(content="Hello, world!")]})
print(result["messages"][-1].content)
LangGraph excels at complex, multi-step reasoning where the flow is not linear — for example, agents that call tools, reflect on results, ask for human approval, and then loop back to re-evaluate. It’s the go-to choice for autonomous agents, multi-agent systems, and production-grade workflows that require reliability and state management.
Why It Matters in 2026
The line between “simple LLM call” and “agentic system” has blurred. Many developers start with LangChain for prototyping, but hit walls when they need:
- Persistent memory across multiple interactions
- Conditional execution based on LLM output (e.g., tool selection)
- Human approval gates before sensitive actions
- Parallel tool execution and result aggregation
- Retry loops and self-correction
In 2026, LangChain’s Runnable interface is still perfect for deterministic pipelines, but LangGraph has become the standard for agentic workflows. The two are not competitors — they are complementary layers. Using the wrong one leads to fragile code, lost state, or unnecessary complexity. Understanding when to use each is critical for building robust LLM applications.
When to Use LangChain
Choose LangChain (LCEL-based chains) when your use case fits these patterns:
- Linear, single-shot pipelines: The output of one step feeds directly into the next, with no branching or backtracking.
- RAG (Retrieval-Augmented Generation): Retrieve documents, inject them into a prompt, and generate an answer.
- Structured extraction: Parse unstructured text into typed JSON or Pydantic models.
- Text transformation: Summarization, translation, or formatting.
- Simple chatbots: A fixed sequence of prompt → model → post-processing, with no dynamic tool loops.
LangChain’s strengths are its simplicity, extensive integrations, and the ability to serve chains as REST APIs via LangServe with minimal code. If your logic can be drawn as a straight line with optional branching at the end (e.g., fallback answers), LangChain is the right tool.
When to Use LangGraph
Choose LangGraph when your application demands:
- Agentic loops: The LLM decides which tool to call, calls it, observes the result, and then decides the next step — potentially repeating until a final answer.
- Stateful, multi-turn reasoning: The system must remember context across multiple invocations and possibly roll back to earlier states.
- Human-in-the-loop: You need to pause execution, wait for human approval or input, and then resume from the exact same state.
- Parallelism: Execute multiple tools or branches concurrently, then merge results (e.g., gather data from several APIs).
- Complex control flow: Conditional edges that route to different nodes based on dynamic values, including cycles for retry or reflection.
LangGraph’s state machine model makes it straightforward to implement patterns like “plan → execute → verify → replan” or multi-agent collaboration where agents hand off tasks. It also provides production features like checkpointing to external storage (Postgres, Redis) and streaming updates to UIs.
Practical Code Examples
Example 1: Simple RAG with LangChain
This is a classic retrieval-augmented generation pipeline. It’s a linear chain: retrieve relevant documents, inject them into a prompt, and generate an answer. No loops, no state beyond the single query.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
# 1. Load a vector store (assume it exists locally)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = FAISS.load_local("my_index", embeddings, allow_dangerous_deserialization=True)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 2. Define the prompt
template = """Use the following context to answer the question.
Context: {context}
Question: {question}
Answer:"""
prompt = ChatPromptTemplate.from_template(template)
# 3. Build the chain
model = ChatOpenAI(model="gpt-4o", temperature=0)
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
# 4. Invoke
result = rag_chain.invoke("What is LangGraph used for?")
print(result)
This chain is completely stateless. Each invocation is independent. If you need conversation history, you’d manually accumulate messages and pass them as input. For simple Q&A, that’s fine. For a persistent agent that remembers previous turns and decides actions, it quickly becomes cumbersome.
Example 2: Multi-step Agent with LangGraph
Here we build a ReAct-style agent that can call a web search tool and a calculator. The graph has a node for the agent reasoning, a node for executing tools, a conditional edge to decide whether to continue or finish, and persistent memory via checkpointing.
from typing import TypedDict, Annotated, Sequence
import operator
import json
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
# Define tools
@tool
def web_search(query: str) -> str:
"""Search the web for information."""
# Stub: in production, use Tavily or similar
return f"Search results for '{query}': ..."
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression."""
return str(eval(expression)) # safe in controlled environments
tools = [web_search, calculator]
tool_by_name = {t.name: t for t in tools}
model = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
def call_agent(state: AgentState):
response = model.invoke(state["messages"])
return {"messages": [response]}
def execute_tools(state: AgentState):
last_message = state["messages"][-1]
tool_calls = last_message.tool_calls
results = []
for tc in tool_calls:
tool_name = tc["name"]
tool_args = tc["args"]
tool_func = tool_by_name[tool_name]
observation = tool_func.invoke(tool_args)
results.append(ToolMessage(content=observation, tool_call_id=tc["id"]))
return {"messages": results}
def should_continue(state: AgentState):
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools"
return END
# Build graph
graph = StateGraph(AgentState)
graph.add_node("agent", call_agent)
graph.add_node("tools", execute_tools)
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")
# Compile with checkpointing for memory
memory = MemorySaver()
app = graph.compile(checkpointer=memory)
# Run with a thread_id for session memory
config = {"configurable": {"thread_id": "user-123"}}
inputs = {"messages": [HumanMessage(content="What's the square root of 2026?")]}
for event in app.stream(inputs, config, stream_mode="values"):
message = event["messages"][-1]
if isinstance(message, AIMessage) and not message.tool_calls:
print("Final answer:", message.content)
This agent can loop indefinitely until the model decides to stop. The MemorySaver checkpointing stores the full state after each step, enabling seamless resumption. You can swap MemorySaver for a PostgresSaver to survive server restarts. Adding a human-in-the-loop interrupt is as simple as calling interrupt() inside a node and using app.invoke with a None input to resume.
Best Practices
- Start with LangChain for linear flows, graduate to LangGraph when you need cycles or state. Don’t force a complex agent into a chain — it will become unmaintainable.
- Combine them where beneficial. Use LangChain’s prompt templates and output parsers inside LangGraph nodes. LangGraph is built on LangChain Core, so you can freely mix
Runnablecomponents within nodes. - Leverage LangSmith for observability. Both libraries automatically trace runs. Use LangSmith to debug branching logic, inspect intermediate states, and monitor performance.
- Persist state early. In LangGraph, always attach a checkpointer for production agents. Even in-memory
MemorySaverhelps during development; usePostgresSaverorRedisSaverfor deployment. - Stream responses. LangGraph’s
streammethod withstream_mode="values"or"updates"gives real-time progress, which is essential for UIs. LangChain also supports streaming via.stream()on anyRunnable. - Test graph edges independently. LangGraph allows you to invoke subgraphs or individual nodes. Write unit tests for critical conditional edges and tool execution nodes.
- Keep state schemas minimal. Only include fields that genuinely need to persist across steps. Overly large states slow checkpointing and complicate debugging.
- Use LangGraph for multi-agent systems. In 2026, LangGraph’s subgraph composition lets you build hierarchical agents — one supervisor graph that delegates to worker graphs.
Conclusion
In 2026, LangChain and LangGraph are two sides of the same coin. LangChain provides the components and composition language for building LLM pipelines quickly and declaratively. LangGraph adds stateful orchestration for agentic and multi-step workflows that require dynamic control flow, memory, and human oversight. The decision is straightforward: if your logic is a straight line, use LangChain; if it involves loops, branching, or persistent state, reach for LangGraph. By mastering both, you’ll be equipped to handle any LLM application pattern — from a simple RAG endpoint to a resilient, self-correcting agent that collaborates with humans and other agents.