LangGraph vs the OpenAI Agents SDK
If you are choosing between LangGraph and the OpenAI Agents SDK, the short answer is this: LangGraph vs OpenAI Agents SDK is really a choice between explicit control and implicit convenience. LangGraph makes you draw the graph of your agent's behavior, node by node, edge by edge, and in exchange gives you fine-grained control over state, retries, branching, and persistence. The OpenAI Agents SDK gives you a lightweight agent loop, handoffs, and guardrails out of the box, and in exchange asks you to give up some control over exactly how the loop runs. Neither is "better." They optimize for different failure modes, and picking wrong shows up months later as either an unmaintainable pile of custom Python or a framework you keep fighting.
This article walks through both frameworks with runnable code, compares them on the axes that actually matter in production (state management, control flow, observability, multi-agent patterns, deployment), and ends with a decision framework you can apply to your own project.
What each framework actually is
LangGraph is a low-level orchestration library built by the LangChain team. It represents your agent as a directed graph: nodes are functions (usually calling an LLM, a tool, or some business logic), edges define how control flows between nodes, and a shared state object gets passed and mutated as execution moves through the graph. LangGraph does not assume a particular agent architecture. You can build a single ReAct-style loop, a hierarchical multi-agent system, or a fixed pipeline with occasional LLM calls, all in the same abstraction.
The OpenAI Agents SDK is a much thinner library, originally shipped as an evolution of an earlier experimental "Swarm" project, and designed around a small number of primitives: an Agent (instructions plus tools plus optional structured output), a Runner that executes the agent loop until it produces a final answer, handoffs for passing control between agents, and guardrails for validating input and output. It is intentionally minimal. The mental model is "a while loop that calls the model, runs any tool calls, and repeats until done," with just enough structure layered on top to make multi-agent handoff and safety checks easy to express.
Both frameworks are provider-agnostic in principle: LangGraph works with any chat model LangChain supports, and the OpenAI Agents SDK works with any model that speaks the Chat Completions or Responses API shape, including via a lightweight LiteLLM-style adapter for non-OpenAI models. So "OpenAI Agents SDK" describes the framework, not a hard lock-in to OpenAI models, though it is obviously tuned for and best documented against OpenAI's own models and APIs.
Installing and writing a minimal agent in each
Start with LangGraph. Install it alongside a model integration:
pip install langgraph langchain-openaiA minimal single-node ReAct agent looks like this:
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
def get_weather(city: str) -> str:
"""Return the current weather for a city."""
return f"It is sunny in {city}."
model = ChatOpenAI(model="gpt-4.1")
agent = create_react_agent(model, tools=[get_weather])
result = agent.invoke({"messages": [{"role": "user", "content": "Weather in Austin?"}]})
print(result["messages"][-1].content)That create_react_agent helper hides the graph, but the graph is still there. You can inspect it, replace it, or build your own from scratch with StateGraph:
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
def call_model(state: AgentState):
response = model.invoke(state["messages"])
return {"messages": [response]}
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.set_entry_point("agent")
graph.add_edge("agent", END)
app = graph.compile()Now compare the OpenAI Agents SDK:
pip install openai-agentsfrom agents import Agent, Runner, function_tool
@function_tool
def get_weather(city: str) -> str:
"""Return the current weather for a city."""
return f"It is sunny in {city}."
agent = Agent(
name="Weather Assistant",
instructions="Answer questions about weather using the tool provided.",
tools=[get_weather],
)
result = Runner.run_sync(agent, "Weather in Austin?")
print(result.final_output)Notice the size difference. The Agents SDK version has no explicit graph, no state schema, no node wiring. Runner.run_sync handles the loop: call the model, execute any tool calls the model requests, feed results back, repeat until the model returns a final answer without further tool calls. For a large class of agents, that loop is all you need, and writing it yourself in LangGraph (even with the create_react_agent shortcut) is strictly more code to read and reason about.
State management: the real dividing line
The single biggest practical difference is how each framework thinks about state.
LangGraph treats state as a first-class, typed object that flows through the graph and can be checkpointed at every step. You define a schema (a TypedDict, a Pydantic model, or a dataclass), and every node receives the current state and returns a partial update, which LangGraph merges in using reducer functions you control (like add_messages above, which appends rather than overwrites). This matters enormously once your agent state includes more than a message list: retrieved documents, a running plan, tool call budgets, user preferences, partial form data. LangGraph lets you model all of that explicitly and gives you built-in checkpointing (via MemorySaver, or a Postgres/SQLite/Redis-backed checkpointer for production) so you can pause a run, inspect state, resume it later, or replay from any prior step for debugging.
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "user-42"}}
app.invoke({"messages": [{"role": "user", "content": "hi"}]}, config)
# later, same thread_id resumes with full history
app.invoke({"messages": [{"role": "user", "content": "what did I just say?"}]}, config)That thread-based persistence is what makes LangGraph a natural fit for long-running, resumable, human-in-the-loop workflows: approval steps, multi-day workflows, or anything where you need to stop execution, wait for a human, and continue exactly where you left off with full state intact.
The OpenAI Agents SDK's state model is much simpler: a RunResult carries the conversation history for a single run, and you pass result.to_input_list() into the next Runner.run call to continue a conversation. There is a Session abstraction for automatic conversation history management across turns, but it is scoped to message history, not arbitrary application state. If your agent needs to track a structured object that is not just "the conversation so far," you are responsible for threading it through yourself, usually via a context object passed into Runner.run(agent, input, context=my_context) that tools can read but the framework does not persist or checkpoint for you.
In short: LangGraph gives you a durable, inspectable state machine. The Agents SDK gives you a conversation and a loop. If your use case is "answer questions, call some tools, produce an answer," the Agents SDK's simplicity wins. If your use case is "run a multi-step business process with branches, retries, and a human approval gate that might not resolve for hours," LangGraph's explicit state and checkpointing earn their complexity.
Control flow: graphs vs loops
LangGraph's graph model lets you express control flow that a simple loop cannot: conditional edges that route to different nodes based on state, parallel fan-out where multiple nodes run concurrently and their results merge, cycles with explicit exit conditions, and subgraphs that nest one graph inside a node of another.
def route(state: AgentState) -> str:
last = state["messages"][-1]
if getattr(last, "tool_calls", None):
return "tools"
return END
graph.add_conditional_edges("agent", route, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")This is useful when the "shape" of your workflow is not just "keep looping until the model stops calling tools" but something with real branches: classify the request, then route to one of five different specialized subflows, each with its own tools and possibly its own model.
The Agents SDK expresses branching differently, through handoffs. Instead of a conditional edge, you give a triage agent a list of other agents it can hand off to, and the model itself decides which specialist to invoke:
from agents import Agent, Runner
billing_agent = Agent(name="Billing", instructions="Handle billing questions.")
tech_agent = Agent(name="Tech Support", instructions="Handle technical issues.")
triage_agent = Agent(
name="Triage",
instructions="Route the user to billing or tech support based on their question.",
handoffs=[billing_agent, tech_agent],
)
result = Runner.run_sync(triage_agent, "My invoice looks wrong")This is elegant for the common "router plus specialists" pattern, and it reads closer to how you would describe the system in English. But it is fundamentally model-driven control flow: the LLM decides the route based on its instructions, not a deterministic function you wrote. For workflows where you need guaranteed, auditable branching (say, a compliance-sensitive approval routing where "the model decided to route it here" is not an acceptable answer), LangGraph's explicit conditional edges, backed by code you control, are the safer choice.
Multi-agent patterns
Both frameworks support multi-agent systems, but with different topologies in mind.
LangGraph's answer is the supervisor pattern (or any custom topology you assemble): a supervisor node decides which of several worker subgraphs to invoke next, workers report back into shared or scoped state, and the supervisor loops until done. Because everything is just nodes and edges, you can build hierarchies of arbitrary depth: a supervisor of supervisors, workers that spawn their own sub-graphs, and so on. The LangGraph ecosystem also ships prebuilt multi-agent scaffolding (langgraph-supervisor and similar packages) so you are not always wiring this from scratch.
The Agents SDK's answer is handoffs plus agents-as-tools. Handoffs transfer the entire conversation to another agent (the specialist takes over completely). Agents-as-tools is the other pattern: wrap an agent as a callable tool that a parent agent invokes and gets a result back from, without losing control of the conversation.
from agents import Agent
researcher = Agent(name="Researcher", instructions="Research the topic and summarize.")
writer = Agent(
name="Writer",
instructions="Write a blog post. Use the researcher tool for background first.",
tools=[researcher.as_tool(tool_name="research", tool_description="Research a topic")],
)That distinction, handoff (transfer control) vs agent-as-tool (delegate and return), covers a surprising amount of ground and is easier to reason about than a hand-rolled LangGraph supervisor for teams that just want "orchestrator calls specialists." But it is also less flexible: you cannot easily express a workflow where three agents need to run in parallel and their outputs get merged by a fourth, the way you can with LangGraph's fan-out/fan-in graph edges.
Observability, tracing, and guardrails
The OpenAI Agents SDK ships built-in tracing that captures every agent run, tool call, handoff, and guardrail check, viewable in the OpenAI platform dashboard with no extra setup, and exportable to other backends through a processor interface. Guardrails are also first-class: you attach input and output guardrail functions to an agent, and the SDK runs them concurrently with the main generation, letting you abort a run early (and cheaply) if a guardrail fails.
from agents import Agent, GuardrailFunctionOutput, input_guardrail
@input_guardrail
async def block_prohibited_topics(ctx, agent, input_text):
is_flagged = "competitor pricing" in input_text.lower()
return GuardrailFunctionOutput(output_info={"flagged": is_flagged}, tripwire_triggered=is_flagged)
agent = Agent(name="Support", instructions="Help the user.", input_guardrails=[block_prohibited_topics])LangGraph does not ship an equivalent guardrail primitive; you implement input and output validation as ordinary nodes in your graph, which is more code but also more flexible (a guardrail node can do anything any other node can do, including calling a completely different model or a rules engine). For tracing, LangGraph's natural pairing is LangSmith, which gives you full run traces, token counts, latency breakdowns, and dataset-based evaluation, but it is a separate product with its own setup and, for the hosted version, its own billing. If you are already invested in the LangChain ecosystem this is a non-issue; if you are trying to avoid adding another vendor to your stack, factor that in.
Deployment
LangGraph graphs can run anywhere Python runs, but the LangChain team also offers LangGraph Platform, a managed deployment target purpose-built for long-running, stateful graphs, including built-in support for pausing on human-in-the-loop steps and resuming asynchronously (useful when a workflow needs to wait on a Slack approval or an external webhook for hours).
The OpenAI Agents SDK has no equivalent managed platform; you deploy it like any other Python service, typically behind FastAPI or similar, and you own the process lifecycle, retries, and scaling. That is a feature if you want no vendor lock-in beyond the OpenAI API itself, and a gap if you wanted a managed durable-execution story out of the box.
Head-to-head comparison
- Learning curve: Agents SDK is faster to a working prototype; LangGraph has more concepts to learn up front (state schema, reducers, edges, checkpointers) but pays off as complexity grows.
- State and persistence: LangGraph has native, typed, checkpointed state built for resumable and human-in-the-loop workflows. Agents SDK has conversation-scoped sessions and an unmanaged context object.
- Control flow: LangGraph gives you deterministic, code-defined branching and parallel fan-out. Agents SDK gives you model-driven handoffs, simpler to write, less deterministic.
- Multi-agent: Both support it well. LangGraph scales to arbitrary custom topologies; Agents SDK covers the common handoff and agent-as-tool patterns cleanly.
- Observability: Agents SDK has built-in tracing and guardrails with almost no setup. LangGraph relies on LangSmith (or your own instrumentation) for the same depth.
- Model flexibility: LangGraph is provider-agnostic by design across the whole LangChain ecosystem. Agents SDK is provider-agnostic in practice but most polished with OpenAI models.
- Deployment: LangGraph Platform offers a managed, durable-execution deployment target. Agents SDK deploys as a plain Python service you own end to end.
A decision framework
Pick the OpenAI Agents SDK when your agent fits the "instructions plus tools plus maybe a couple of specialists" shape, you want to ship fast, you are already using OpenAI models as your primary provider, and you do not need custom, code-guaranteed branching or long-lived pausable workflows. It is also a strong choice for teams that find LangChain's abstractions heavier than they want and would rather read a small, well-documented core.
Pick LangGraph when your workflow has real branching logic that must be deterministic and auditable, when you need durable state that survives process restarts and supports human-in-the-loop pauses measured in hours or days, when you are building a multi-agent system with a topology beyond "router plus specialists," or when you are already in the LangChain ecosystem and want tracing, evaluation, and deployment to come from the same vendor.
It is also completely reasonable to use both in the same organization for different projects, or even prototype in the Agents SDK and "graduate" a workflow into LangGraph once it outgrows a simple loop. Neither choice is permanent, and the code you write in either framework, tool definitions, prompt instructions, evaluation harnesses, mostly transfers if you switch orchestration layers later. The orchestration layer is real work, but it is not usually the hardest part of building a good agent. Get the tools, prompts, and evaluation loop right first, and the choice between LangGraph and the OpenAI Agents SDK becomes a much lower-stakes decision than it feels like upfront.
FAQ
Can I use LangGraph with OpenAI models and the Agents SDK with non-OpenAI models? Yes to both. LangGraph works with any chat model that has a LangChain integration, including OpenAI's models. The Agents SDK can call non-OpenAI models through a compatible Chat Completions-style endpoint or a LiteLLM-based adapter, though its tracing and guardrail tooling are most polished against OpenAI's own APIs.
Do I need LangChain to use LangGraph? No. LangGraph is a standalone graph orchestration library. You will typically still pull in a LangChain model integration package (like langchain-openai) for convenience, but the graph, state, and checkpointing APIs do not require the rest of the LangChain framework.
Which one is faster to prototype with? The OpenAI Agents SDK, in almost every case. A working tool-calling agent is a dozen lines of code with almost no setup. LangGraph requires defining a state schema and wiring nodes and edges even for a simple case, though the create_react_agent prebuilt helper closes most of that gap for basic use.
Can LangGraph do handoffs like the Agents SDK? Yes, via the supervisor pattern or a conditional edge that routes to a different subgraph based on classification. It takes more code to express than the Agents SDK's built-in handoffs parameter, but it is fully customizable, including parallel handoffs and merges that the Agents SDK does not natively support.
Is one of these more "production ready" than the other? Both are used in production today, at different scales. LangGraph has more built-in machinery for durability, checkpointing, and long-running workflows, which matters for certain production shapes (approval queues, multi-day workflows). The Agents SDK is lighter weight and easier to operate for straightforward request-response agents, and its native tracing and guardrails reduce the amount of custom observability code you need to write.
Can I migrate an agent from the OpenAI Agents SDK to LangGraph later? Generally yes, without throwing away much work. Your tool functions, system prompts, and evaluation datasets are framework-agnostic. What you rewrite is the orchestration glue: replacing Runner.run calls and handoffs with a StateGraph, nodes, and edges. Budget real time for this if your workflow already has nontrivial branching, since you will be making that branching explicit for the first time.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.