LangGraph vs LangChain: when to use which
"LangGraph vs LangChain" is the question almost everyone trips over when they first walk into agent territory. No surprise: the names rhyme, the team is the same, the docs are intertwined. But these are not competitors and not two versions of one thing. LangChain is the broad toolkit. LangGraph is the narrow state-orchestration layer that sits on top of it. Let me draw the line honestly, with no marketing, and tell you what to reach for and when.
The short answer in two sentences
LangChain is a set of building blocks: model wrappers, document loaders, retrievers, parsers, integrations, and chains. It answers "how do I assemble an LLM app from standard parts". LangGraph is a separate library from the same team that answers "how do I orchestrate an agent that has loops, state, and pauses". The first is about the parts; the second is about coordinating complex behavior.
If you have already read when LangGraph beats the vanilla SDK → — that one covered what a StateGraph is and when the graph beats raw SDK code. I will not repeat it here: the focus is squarely on the LangChain vs LangGraph pair and exactly where the line between them falls.
What LangChain actually is
LangChain showed up as the answer to an early-2023 pain: everyone was hand-writing the same thing — prompt templates, a model call, response parsing, pulling context out of documents. LangChain gathered those patterns into one ecosystem. Today it is, first and foremost:
- Integrations. Hundreds of ready connectors — models (OpenAI, Anthropic, local), vector stores, loaders for PDFs, websites, Notion, databases. This is the strongest part: not writing the wrappers yourself.
- Chains. Linear sequences of steps: prompt → model → parser. Through LCEL (LangChain Expression Language) they compose into one pipeline with the
|operator. - Retrievers and RAG. Ready bricks for searching documents and stuffing context into the prompt. A classic RAG on LangChain comes together in an evening.
- Parsers and schemas. Extracting structured output (JSON, Pydantic models) from an LLM response without hand-rolled regex.
The key point: LangChain is excellent for linear, lightly branched scenarios. A chain is a pipe — data goes in one end and comes out the other. As long as the logic flows one way, LangChain saves you weeks.
Where LangChain starts to crack
The problems surface the moment an agent needs to loop back. A classic chain cannot elegantly do "try → check → redo". In the original LangChain that job fell to AgentExecutor — a black box that spun its own reasoning loop inside. On simple cases it worked, but the moment you needed to step into the transition logic, add a human pause, or persist state, the hacks began.
That is exactly why the LangChain team shipped LangGraph as a separate thing. It was an honest admission: the chain model does not carry complex agent behavior. They needed an explicit graph where transitions are visible and state is controlled. So the clean split appeared: LangChain stayed the toolkit of parts, LangGraph became the orchestration layer.
What LangGraph is and how it differs
LangGraph describes an agent as a state graph: nodes do the work, edges decide where to go next, and state flows between them. Unlike a chain, the graph can do three things a pipe cannot:
- Loops. An edge can point a node back at itself — the agent retries until it passes validation.
- Persistence. A checkpointer writes state to Postgres or Redis. The process survives a server restart and resumes from where it stopped.
- Human-in-the-loop. The graph pauses on a node, waits for a person to approve, then continues from the same state.
The most important thing — and the one that surprises people: LangGraph does not require LangChain. Inside a node you can call the vanilla OpenAI or Anthropic SDK and never touch a chain. The reverse works too: you can use LangChain integrations (loaders, retrievers) inside LangGraph nodes. They compose, but they are not hard-wired to each other.
The difference in code: chain vs StateGraph
The shortest way to feel the difference is to see the same task in two styles. First a simple LangChain chain: prompt → model → parser. A one-way pipe.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template(
"Explain briefly: {question}"
)
model = ChatOpenAI(model="gpt-4o-mini")
# LCEL: steps compose with the | operator
chain = prompt | model | StrOutputParser()
answer = chain.invoke({"question": "What is LangGraph?"})Clean, readable, zero overhead. But there is no loop, branch, or pause here — data just passes straight through. Now the same task, but with a "if the answer is weak, try again" condition, on LangGraph:
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class State(TypedDict):
question: str
answer: str
tries: int
def call_model(state: State) -> dict:
reply = model.invoke(state["question"]).content
return {"answer": reply, "tries": state["tries"] + 1}
def is_good(state: State) -> str:
# conditional edge: loop back or exit
if "i don't know" in state["answer"] and state["tries"] < 3:
return "retry"
return "done"
graph = StateGraph(State)
graph.add_node("call_model", call_model)
graph.add_edge(START, "call_model")
graph.add_conditional_edges(
"call_model", is_good, {"retry": "call_model", "done": END}
)
app = graph.compile()
result = app.invoke({"question": "What is LangGraph?", "tries": 0})See the difference? The chain is an expression that flows left to right. The graph is a state machine: retry points the node at itself, done goes to END. If your logic fits in one pass, the first version is shorter and more honest. If retries, branches, or pauses appear, the second one does not collapse into a wall of if statements.
LangChain vs LangGraph: the table
LangChain wins
- RAG and document search
- Linear pipelines (prompt → model → parser)
- You need ready integrations and loaders
- Structured output against a schema
- Prototype where build speed matters
LangGraph wins
- Loops, branches, retries
- Human-in-the-loop with pauses
- State persisted for days
- Multi-agent work handoff
- You need replay and transparent debug
Note: the rows in the two columns do not contradict each other. This is not "either-or", it is "whose tool goes where". RAG lives in LangChain. Orchestrating the agent that calls that RAG lives in LangGraph.
When to combine them
The most common real scenario is not a choice but a composition. LangGraph runs the process at the top level, and inside individual nodes you call LangChain bricks. A typical setup for a support agent with a knowledge base:
- A "classify request" node — a simple LangChain chain with structured output.
- A "find context" node — a LangChain retriever over a vector store (this is the RAG part).
- An "answer" node — a model call with the pulled context.
- Conditional edges, a "clarify if context is thin" loop, a pause for a manager on hard cases — all of that is orchestrated by LangGraph.
So the line is simple: LangChain is "what to build a node out of", LangGraph is "how to wire nodes into a stateful process". Once the task grows into several agents handing work to each other, this composition becomes mandatory — I broke that down in the multi-agent systems article →
Honestly: most simple apps do not need LangGraph
I will say it straight, because it saves clients money. If your product is a chat over documents, a bot that answers FAQs, or a template-driven text generator, LangChain is enough (and often the vanilla SDK is). The graph here is ceremony for the sake of ceremony: you pay in complexity for capabilities you never use.
LangGraph starts to pay off once at least one of three things appears in the process: a loop that goes back; a pause for a human decision; state that has to survive a restart. Until none of that exists, a chain is the more honest answer. A framework should show up because of pain, not "just in case".
Common selection mistakes
- Thinking LangGraph replaces LangChain. It does not. It sits on top of it (or beside it). You still pull integrations and retrievers from LangChain more often than not.
- Dragging LangGraph into a simple RAG. Document search is a linear flow. The graph adds nothing here but moving parts.
- Being afraid to use LangGraph without LangChain. You can write nodes on the vanilla SDK. If you do not want someone else's abstractions, skip them — the graph is fine without them.
- Staying on the old AgentExecutor for complex agents. If you have loops and branches bolted onto chains, that is the very pain the LangChain team itself walked away from into the graph. Do not repeat that path.
How to choose: the short test
Choose LangChain if you have RAG, a linear pipeline, or you need ready integrations and loaders, and the logic flows one way with no returns. In 70% of product cases that is plenty.
Choose LangGraph if loops, branches, human pauses, state persistence, or several agents handing off work have appeared. Then the graph pays for itself on the very first serious debug.
Combine both if the agent runs a complex process (LangGraph) but leans on RAG and integrations inside its nodes (LangChain). This is the most typical production setup.
That exact architecture — a graph for orchestration plus the right bricks inside the nodes — is what I design and build as part of multi-agent system development → from stack choice to deploy and monitoring.
If you are not sure which side of the line your case is on, message me. 30 minutes on a call and I will tell you honestly whether you need a graph at all or a chain is enough.