QA How-To
LangChain vs LangGraph: Which to Choose in 2026
Compare LangChain vs LangGraph in 2026 for agents, workflows, state, persistence, testing, observability, and production control, with runnable Python examples.
26 min read | 3,321 words
TL;DR
Use LangChain for the fastest path to a standard agent with models, tools, middleware, and structured output. Use LangGraph directly when the workflow itself needs explicit state, branches, loops, durable execution, checkpoints, interrupts, or custom recovery; LangChain's v1 agent runtime is already built on LangGraph, so many production systems use both.
Key Takeaways
- Start with LangChain `create_agent` when a standard model and tool loop solves the product requirement.
- Use LangGraph directly when explicit state, branching, cycles, persistence, interrupts, or recovery behavior is part of the design.
- LangChain agents run on LangGraph, so the practical choice is often the right abstraction layer, not one package replacing the other.
- Avoid the deprecated LangGraph prebuilt `create_react_agent` path for new v1 work, and use `langchain.agents.create_agent` for standard agents.
- Test deterministic routing, state reducers, tool contracts, and recovery separately from probabilistic answer quality.
- Persistence changes the test model because thread IDs, checkpoints, replay, concurrency, and side effects become first-class risks.
- Choose through a risk map and failure-injection prototype, not by counting integrations or copying an old tutorial.
LangChain vs LangGraph is a choice of abstraction, not a battle between unrelated frameworks. Start with LangChain when a standard agent loop, model integration, tools, middleware, and structured output cover the requirement. Use LangGraph directly when you must design the state machine itself, including branches, loops, parallel work, persistence, interrupts, and recovery.
In the current v1 architecture, LangChain's create_agent builds on LangGraph. That means a team can start high-level and move selected workflows to lower-level graph control without discarding the whole ecosystem. For QA and SDET engineers, the most important difference is what behavior becomes explicit and therefore testable.
TL;DR
| Need | LangChain starting point | LangGraph starting point |
|---|---|---|
| Standard tool-calling agent | create_agent |
Usually unnecessary at first |
| Provider and model abstraction | Built-in focus | Bring the model logic into nodes |
| Middleware and structured response | High-level agent APIs | Implement in graph nodes or composed components |
| Explicit branch and loop design | Agent runtime manages common loop | Graph edges and routing expose control |
| Durable state and checkpoints | Available through graph-backed runtime configuration | Core design surface |
| Human approval at a precise step | Possible through underlying runtime | Direct fit with interrupts and persistence |
| Deterministic workflow plus selected LLM steps | Can compose runnables | Strong direct fit |
| Fastest proof of a normal agent | Usually LangChain | More design work than needed |
A reliable production system often uses LangChain components inside LangGraph nodes. Choose the highest abstraction that still exposes the risks you need to control.
1. LangChain vs LangGraph: The Direct Answer
LangChain is the high-level application framework. Its current agent API provides a production-oriented model and tool loop, middleware, messages, structured output strategies, model configuration, and ecosystem integrations. create_agent returns a graph-backed runtime that supports invocation and streaming while hiding the ordinary agent loop.
LangGraph is the lower-level orchestration framework and runtime. You define state, nodes that return state updates, and edges that determine what runs next. You compile the graph before invocation. Checkpointing, persistence, human-in-the-loop, durable execution, streaming, and custom control flow are core reasons to use it directly.
For a support assistant that selects among three read-only tools and returns a typed response, begin with LangChain. For a claims workflow that must extract evidence, branch by amount, pause for human approval, resume after hours, retry only safe nodes, and compensate for a failed side effect, model it in LangGraph.
Do not start a new v1 agent from an outdated tutorial that imports create_react_agent from langgraph.prebuilt. Current guidance places the standard prebuilt agent at langchain.agents.create_agent. Direct LangGraph remains appropriate when you are intentionally creating custom orchestration rather than reaching for the old prebuilt.
2. Abstraction Layers and System Design
A useful mental model has three layers. At the top is product behavior, such as answer a policy question or approve a refund. In the middle are agent capabilities, including models, prompts, tools, middleware, and response schemas. At the bottom is execution control, including state transitions, concurrency, checkpoints, interruption, and recovery.
LangChain concentrates on the middle layer and common top-layer patterns. It helps initialize chat models, expose functions as tools, create agents, manage messages, and request structured results. The framework selects a sensible graph-based agent loop so the team does not hand-code model, tool, model repetition.
LangGraph exposes the bottom layer. A node is a function that receives state and returns updates. Edges connect steps, and conditional edges route from state. Reducers define how concurrent or repeated updates combine. The compiled graph validates structure and becomes invocable. This is closer to designing a stateful distributed workflow than chaining prompts.
Choose based on where product risk lives. If risk is primarily model selection, tool description, and answer format, a high-level agent is easier to maintain. If risk is a prohibited transition, duplicate side effect, approval bypass, lost resume, or non-terminating loop, graph structure deserves to be explicit.
Avoid using lower-level control merely to feel production-ready. Every custom edge, state field, and recovery rule creates tests and ownership. Also avoid hiding a complex regulated workflow in a single agent prompt. The correct abstraction makes critical policy visible in code.
3. Runnable LangChain create_agent Example
The current v1 agent entry point is langchain.agents.create_agent. The following example exposes a deterministic policy lookup tool and lets a configured chat model decide when to use it. Install LangChain and the integration package for your chosen provider, set that provider's credentials, and set MODEL to a supported provider-qualified model identifier.
import os
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def lookup_return_policy(product_type: str) -> str:
"""Return the approved return window for a product category."""
policies = {
"electronics": "14 days with receipt",
"books": "30 days in resalable condition",
}
return policies.get(product_type.lower(), "manual review required")
agent = create_agent(
model=os.environ["MODEL"],
tools=[lookup_return_policy],
system_prompt=(
"You answer return-policy questions. "
"Use the policy tool and do not invent a return window."
),
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "Can I return electronics after 10 days?"}
]
})
print(result["messages"][-1].content)
The code is simple because LangChain owns the common loop. A production test should not assert one exact paragraph from a nondeterministic model. Assert that the tool input is electronics, the tool result is preserved, the answer does not claim a window longer than 14 days, and the run terminates within a configured budget.
Use middleware for cross-cutting agent concerns supported by the current API, such as model selection, dynamic prompts, tool error handling, or guard behavior. Keep authorization inside tools and services too. A prompt instruction is not an access-control boundary.
4. Runnable LangGraph StateGraph Example
This deterministic graph triages support tickets without an LLM, which makes the orchestration API easy to see and the example fully runnable. Install current LangGraph with pip install -U langgraph.
from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import END, START, StateGraph
class TriageState(TypedDict):
ticket: str
severity: str
destination: str
def classify(state: TriageState) -> dict:
text = state["ticket"].lower()
urgent = any(word in text for word in ("outage", "breach", "data loss"))
return {"severity": "critical" if urgent else "normal"}
def choose_path(state: TriageState) -> Literal["escalate", "queue"]:
return "escalate" if state["severity"] == "critical" else "queue"
def escalate(state: TriageState) -> dict:
return {"destination": "on-call"}
def queue(state: TriageState) -> dict:
return {"destination": "support-queue"}
builder = StateGraph(TriageState)
builder.add_node("classify", classify)
builder.add_node("escalate", escalate)
builder.add_node("queue", queue)
builder.add_edge(START, "classify")
builder.add_conditional_edges("classify", choose_path)
builder.add_edge("escalate", END)
builder.add_edge("queue", END)
graph = builder.compile()
result = graph.invoke({
"ticket": "Production outage in checkout",
"severity": "",
"destination": "",
})
print(result)
Replace classify with a model-backed node only when semantic classification is needed. The route remains deterministic and testable. You can invoke the graph with data loss, ordinary billing text, empty input, and adversarial wording to test the boundary.
A graph must be compiled before use. Compilation checks the structure and is also where runtime capabilities such as a checkpointer are configured. Keep node functions small enough that a failure points to one responsibility.
5. State, Edges, Reducers, and Control Flow
LangChain agents include message state and execute a standard model and tool loop. The application can extend state and use middleware, but the high-level API intentionally keeps ordinary orchestration simple. This is helpful when product behavior is naturally receive request, reason, call tools, return result.
LangGraph state is an explicit schema, commonly a TypedDict, dataclass, or supported validated model. Nodes return partial updates rather than mutating global variables. When multiple updates target the same channel, a reducer defines how values combine. Message-oriented graphs often use the provided message reducer or state helpers so new messages append and identified messages can update correctly.
Edges create sequences, conditions create branches, and graph primitives support loops and fan-out patterns. This visibility is valuable for coverage. A QA engineer can enumerate every route from START to END, each conditional outcome, the maximum loop count, and state invariants at node boundaries.
State design affects compatibility. Renaming a field can invalidate old checkpoints. Changing a reducer can change replay results. Adding a new mandatory value can break resumed threads created by an earlier deployment. Treat the state schema like a persisted API when checkpointing is enabled. Version it, migrate it, and test mixed-version rollout.
Control flow should contain termination guards. A model can repeatedly call a tool or a router can cycle between nodes. Set an appropriate recursion or step budget, make the fallback explicit, and test the exact result when the budget is reached. A stack trace is not a user experience.
6. Persistence, Interrupts, and Durable Execution
LangGraph checkpointers save graph state as checkpoints organized into threads. This enables conversation memory, human approval, time-travel investigation, and recovery from failures. It also introduces distributed-systems risks that a stateless chat completion does not have.
Test thread identity first. Two users with different thread IDs must never see each other's messages, documents, approvals, or tool results. The same user resuming the same thread should receive the expected prior state. Missing, reused, excessively long, or unauthorized thread identifiers should fail according to policy.
For human-in-the-loop, test pause before the protected side effect, inspection of the proposed action, approve, reject, edit, timeout, and duplicate resume. Approval should bind to the exact state and action reviewed. If the underlying order changes while paused, the workflow may need revalidation rather than blindly executing stale approval.
Failure injection matters. Crash after a tool returns but before the next checkpoint, after a checkpoint but before an external write, and after the write but before recording success. A durable engine can resume computation, but it cannot automatically make an external payment idempotent. Use idempotency keys, transactional outboxes, or compensating actions at side-effect boundaries.
Checkpoint retention, encryption, deletion, tenant isolation, and sensitive data minimization belong in the test plan. Persistence is not free memory. It is stored application state with privacy, security, and lifecycle obligations.
7. Tools, Middleware, and Structured Output
LangChain tools turn callable capabilities into model-visible contracts with names, descriptions, and schemas. Middleware can modify or observe agent behavior around model and tool calls. Structured-output strategies can request a validated response type through provider-native or tool-based mechanisms according to model support. These abstractions reduce plumbing for standard agents.
Test tool contracts independently. Validate required and optional arguments, types, bounds, authorization context, timeouts, retries, and redaction. The tool description should help model selection, but the implementation must reject unauthorized or invalid inputs itself. Simulate timeouts and domain errors, then verify middleware produces a safe result rather than leaking a stack trace or looping.
In LangGraph, a node can call the same LangChain model or tool components. Direct graph control is useful when a tool must be preceded by policy validation, followed by deterministic verification, or routed to human review. Keep an external write in a named node instead of burying it inside a large model node, because named boundaries improve trace review and failure injection.
Structured output is not automatically truthful output. Schema validation proves shape, not factual support. Assert field provenance, allowed enum combinations, cross-field rules, and evidence links. When a provider rejects or repairs invalid output, test the retry budget and final failure contract.
For broader tool safety and test design, see agentic testing with tool calling.
8. Testing a LangChain Agent
Use a layered test pyramid. Unit-test deterministic tools, prompt builders, middleware, schema validators, and authorization without a live model. Contract-test model and provider behavior with a small set of representative calls. Evaluate end-to-end quality over a versioned dataset with ranges and categories rather than one golden sentence.
For tool selection, capture traces or instrument calls and assert allowed tool name, arguments, sequence constraints, and maximum count. Include cases where no tool should be called. Test prompt injection in user text and tool output. An agent must not obey untrusted instructions retrieved from a document when they conflict with system policy.
Control variability. Pin model identifier, framework versions, tool schema, system prompt, dataset revision, and relevant sampling settings. Even then, do not expect exact language. Use deterministic assertions first, then semantic or judge-based measures for qualities that rules cannot capture. Calibrate judge metrics with human-reviewed examples and monitor disagreement.
Test budgets as product behavior: maximum model calls, tool calls, tokens, elapsed time, and estimated cost according to your telemetry. Make timeout and budget exhaustion return a safe, useful state. A correct answer after an unbounded loop is an operational failure.
The testing an AI agent with LangSmith guide covers trace-driven investigation, while writing golden datasets for LLM evaluations explains dataset governance.
9. Testing a LangGraph Workflow
A graph adds structural coverage. Enumerate nodes, fixed edges, conditional outcomes, interrupts, error routes, loops, and END states. Unit-test every router with table-driven state. For the triage example, outage, breach, and data loss should route to escalation, while ordinary text should route to the support queue. Empty or malformed state needs an explicit contract.
Test state invariants after every node. A retrieval node may require nonempty document IDs, an approval node may require an immutable proposal hash, and a payment node may require an idempotency key. Property-based tests are useful for routers and reducers because unusual combinations can reveal unhandled branches.
With a checkpointer, add resume tests across process restarts. Verify the same thread continues, another tenant cannot access it, duplicate resume is safe, and a deployment with a compatible state schema can read older checkpoints. Test concurrent updates according to the chosen consistency model.
For loops, assert convergence or bounded termination. Inject a model that always chooses the same tool and verify the graph reaches its fallback. For parallel nodes, vary completion order and ensure reducers produce the same valid state when order should not matter.
Replay is excellent for diagnosis but dangerous around side effects. Mark or design external operations so replay does not resend email, charge a card, or create a second ticket. Use fakes in structural tests and a controlled sandbox for integration evidence.
10. Observability, Security, Latency, and Cost
Both abstraction levels need traceability. Capture run and thread identifiers, node or agent step, model identifier, tool name, sanitized arguments, response status, token use, latency, retry, and outcome. Redact credentials, personal data, retrieved secrets, and sensitive prompts according to policy. A trace platform is part of the data boundary and needs retention and access tests.
LangGraph's explicit nodes create natural latency and cost dimensions. You can see whether retrieval, approval wait, model reasoning, or a tool dominates. LangChain middleware and graph-backed traces provide similar visibility for standard agents. Choose bounded tag values. User text and document IDs should not become uncontrolled metric labels.
Security belongs below the model. Tools must authenticate and authorize using trusted runtime context. A model-produced user ID cannot override the authenticated principal. Graph routes that lead to privileged nodes should check policy in code. Human approval should be scoped to the action and expire appropriately.
Run chaos cases: model timeout, rate limit, invalid structured output, tool 500, tool partial success, checkpoint outage, duplicate delivery, and unavailable human reviewer. Define which cases retry, fall back, pause, compensate, or fail. Retry only operations known to be safe or protected by idempotency.
For systematic latency and budget checks, see measuring LLM latency and cost in tests.
11. Migration and Hybrid Architecture
If an older application uses langgraph.prebuilt.create_react_agent, migrate the standard agent path to langchain.agents.create_agent according to current v1 guidance. Review prompt parameter changes, middleware, custom state, structured output, runtime context, and streaming node names rather than making an import-only edit. Preserve trace-based behavior tests during migration.
Do not rewrite a working custom StateGraph as a high-level agent merely because create_agent is current. The deprecation applies to the prebuilt agent helper, not to the StateGraph API or direct orchestration. Graph nodes, edges, persistence, and execution remain the correct primitives for custom workflows.
A hybrid is often ideal. Use LangChain model initialization, tools, messages, middleware concepts, or structured outputs where they reduce integration work. Place those components in LangGraph nodes when workflow policy needs explicit routing and state. Keep deterministic transformations outside model calls.
Migrate one path at a time. Snapshot representative traces, run a versioned evaluation dataset, compare tool calls and structured fields, and inject failures at checkpoints and side effects. Exact natural-language output need not match, but safety, authorization, state transitions, business outcomes, latency, and cost must stay within objectives.
Document the abstraction boundary so future engineers know when a new requirement belongs in agent configuration, middleware, a tool, a graph node, or an external service.
12. LangChain vs LangGraph Decision Framework
Choose LangChain create_agent when the workflow is a conventional agent loop and your main work is selecting a model, defining tools, adding middleware, and shaping output. It provides the shortest supported path and already uses LangGraph underneath. Do not expose graph complexity before the product needs it.
Choose direct LangGraph when the product specification contains words such as branch, retry policy, wait for approval, resume, parallelize, compensate, replay, or persist. Those are workflow requirements. Encoding them as nodes, state, and edges makes safety and recovery reviewable.
Use both when integrations and orchestration are both important. A LangGraph node can call LangChain components, and a LangChain agent can participate inside a larger graph. The boundary should follow risk: probabilistic reasoning inside a node, deterministic policy and state transition around it.
Prototype the hardest failure, not the happy path. Pause before a side effect, restart the process, resume twice, deny approval, make a tool time out, and inspect the trace. If the high-level agent handles the product contract clearly, keep it. If critical behavior disappears inside the loop, move that workflow to explicit graph control.
Interview Questions and Answers
Q: Is LangGraph a replacement for LangChain?
No. LangGraph is the lower-level orchestration runtime, while LangChain provides higher-level agent and integration APIs. LangChain v1 agents run on LangGraph, and many systems use LangChain components inside custom LangGraph workflows.
Q: When should you use create_agent?
Use it for a standard model and tool loop where middleware and structured output cover the requirement. It reduces orchestration code and follows the current v1 path. Move lower only when custom state and control are product requirements.
Q: When is StateGraph the better choice?
Use StateGraph for explicit branches, cycles, parallel steps, persistence, interrupts, approvals, or recovery. It makes nodes, state updates, and transitions visible and independently testable.
Q: What changed about create_react_agent?
The old prebuilt helper in langgraph.prebuilt is deprecated for current v1 agent creation. New standard agents should use langchain.agents.create_agent. Direct StateGraph development remains valid and is not replaced by that migration.
Q: How do you test a LangGraph router?
Treat it as a deterministic function over state. Use a table that covers every destination, boundary, missing value, and invalid combination, then confirm every possible result maps to a real node or END.
Q: What new risks appear with checkpointing?
Thread isolation, stale state, schema migration, retention, duplicate resume, replayed side effects, concurrent updates, and checkpoint outages become relevant. I test process restarts and mixed deployment versions, not only in-memory invocation.
Q: How do you test an LLM agent without exact-string assertions?
Assert deterministic facts such as tool calls, argument schemas, authorization, structured fields, evidence, budgets, and termination. Use a versioned dataset and calibrated semantic evaluation for qualities that cannot be expressed as rules.
Q: Where should authorization be enforced?
In trusted tools, services, and deterministic workflow policy using authenticated runtime context. Prompts and model outputs can assist decisions but must not be the only access-control boundary.
Common Mistakes
- Treating LangChain and LangGraph as mutually exclusive competitors.
- Starting new v1 code from the deprecated
langgraph.prebuilt.create_react_agenthelper. - Building a custom graph for a standard agent loop without a product requirement.
- Hiding approvals, retries, or privileged transitions inside a prompt.
- Testing only final prose and ignoring tools, arguments, state, routing, and budgets.
- Persisting sensitive state without tenant isolation, retention, encryption, and deletion tests.
- Replaying a graph that repeats non-idempotent external side effects.
- Changing persisted state schemas without checkpoint migration tests.
- Allowing model-generated identity fields to override authenticated context.
- Retrying every tool failure without classifying safe and unsafe operations.
Conclusion
LangChain vs LangGraph is best answered by choosing the highest useful abstraction. Use LangChain create_agent for a standard agent with models, tools, middleware, and structured output. Use LangGraph directly when state transitions, persistence, human approval, recovery, or custom control flow are part of the product contract. Since the high-level agent already runs on LangGraph, moving between layers can be incremental.
Start with a risk map, then prototype the most dangerous failure path. Test tools and policy deterministically, evaluate model behavior over a versioned dataset, and inject failures around checkpoints and side effects. The right framework layer is the one that makes critical behavior explicit without creating unnecessary orchestration code.
Interview Questions and Answers
Is LangGraph a replacement for LangChain?
No. LangChain offers higher-level agent and integration abstractions, while LangGraph exposes stateful orchestration and runtime control. LangChain v1 agents are built on LangGraph, so the decision is often which layer to program directly.
When would you choose LangChain create_agent?
I choose it when the requirement is a standard model and tool loop with middleware or structured output. It minimizes custom orchestration and follows the supported v1 API. I move to direct graph control only for explicit workflow needs.
When would you choose LangGraph StateGraph?
I choose StateGraph when state, branches, loops, parallel steps, checkpoints, interrupts, approvals, or recovery are part of the product contract. These concerns should be visible as nodes and edges rather than hidden inside a prompt.
What happened to langgraph.prebuilt.create_react_agent?
The prebuilt helper is deprecated for the current v1 standard-agent path. New standard agents should use `langchain.agents.create_agent`. Direct StateGraph and its execution model remain supported for custom orchestration.
How do you test conditional edges in LangGraph?
I unit-test the routing function as a pure function over typed state. A table covers each destination, boundaries, missing values, and invalid combinations. I also verify every returned label maps to a node or terminal route.
How does persistence change the QA strategy?
It adds thread isolation, checkpoint retention, state-schema compatibility, restart, duplicate resume, concurrency, and replay risks. I test across process restarts and deployment versions. External side effects require their own idempotency or compensation design.
How do you test a LangChain agent reliably?
I assert deterministic behavior first: allowed tools, argument schemas, authorization, structured fields, evidence, call budgets, and termination. I use a versioned evaluation dataset for answer quality and calibrate semantic judges against human-reviewed cases.
Where should access control live in an agent system?
It should live in trusted application code, tools, services, and deterministic workflow gates using authenticated runtime context. A system prompt can guide the model but is not a security boundary. I test that model-supplied identity cannot override trusted context.
Frequently Asked Questions
What is the difference between LangChain and LangGraph?
LangChain provides high-level APIs for models, tools, agents, middleware, and structured output. LangGraph provides lower-level stateful orchestration with nodes, edges, persistence, interrupts, and durable execution.
Does LangChain use LangGraph?
Yes. The current LangChain `create_agent` runtime is graph-based and built on LangGraph. You can start with the high-level API and use direct LangGraph when you need custom orchestration.
Should I use LangChain create_agent or LangGraph StateGraph?
Use `create_agent` for a conventional model and tool loop. Use StateGraph when branches, loops, state schemas, checkpoints, approvals, parallel work, or custom recovery need to be explicit.
Is create_react_agent deprecated in LangGraph v1?
The old `langgraph.prebuilt.create_react_agent` helper is deprecated in favor of `langchain.agents.create_agent` for standard agents. Direct StateGraph APIs remain current for custom workflows.
Can LangChain and LangGraph be used together?
Yes. LangChain components can run inside LangGraph nodes, and LangChain agents already use the LangGraph runtime. A hybrid often keeps model integrations high-level and workflow policy explicit.
Which is better for human-in-the-loop AI workflows?
Direct LangGraph is usually the clearer fit when a workflow must pause at a precise state, persist, accept approval or edits, and resume safely. The surrounding nodes can still use LangChain models and tools.
How should LangGraph workflows be tested?
Test nodes, routers, reducers, every path to END, loop limits, interrupts, checkpoint resume, tenant isolation, concurrency, and side-effect idempotency. Evaluate model-backed nodes separately from deterministic graph structure.