Take any large language model -- GPT, Claude, Gemini. It is trained on a huge slice of the public internet, so it answers general questions well. But ask it "what is the refund policy on t-shirts at my store?" and it has nothing, because it never saw your private data. Every framework below exists to close that gap, and then to manage the increasingly complex systems we build on top of it. Let us walk the stack from the bottom up.
01 Start With the Problem: RAG
The fix for the refund-policy question is RAG (Retrieval-Augmented Generation): you feed your own documents into a retrieval pipeline. Step by step, you load the documents, split them into chunks (to respect the model's context window), embed and store them in a vector database for semantic search, then at query time retrieve the relevant chunks and hand them to the LLM. You can write all of this by hand in Python. You usually do not have to -- which is exactly where LangChain enters.
02 LangChain: The Toolkit
LangChain is a framework that gives you ready-made building blocks for that pipeline: document loaders, text splitters, embedders, vector-store wrappers, retrievers. String them together and you have a chain -- a mostly linear flow where the LLM is one step among many.
In a chain, the LLM does not make autonomous decisions. It is handed a fixed job ("extract these fields from this text", "summarize this") and the rest is ordinary code. That is the sweet spot for LangChain: linear RAG and chatbot-style apps where input maps to output.
03 Workflows vs Agents: The Distinction That Matters
Anthropic's widely cited guide, Building Effective Agents, draws the line cleanly, and it is the backbone for understanding everything that follows.
Workflows
LLMs and tools orchestrated through predefined code paths. You decide the sequence; the LLM fills in slots.
Agents
The LLM dynamically directs its own process -- choosing which tools to call, in what order, and when the job is done.
That same guide argues that most teams should reach for a workflow -- or even a single LLM call -- before reaching for an agent. Agents add cost and unpredictability. Use them when the task genuinely needs open-ended planning, not because they sound impressive.
Picture a real agentic task: a customer says "I want to exchange my t-shirt for a different item." That is not one shot. You check the return policy, look up the order in a database, confirm the new item's availability, maybe call a shipping API to print a label -- and any step can fail and need a retry. The system plans sub-tasks, executes them, reacts to failures, and loops, much like a developer running code, hitting an error, fixing it, and running again. The knowledge (PDFs, databases) and the actions (APIs, services) it reaches for are its tools.
04 LangGraph: Orchestration for Agents
A popular design for agentic systems is a graph-based stateful workflow. You start at a node, make an LLM call, and the LLM decides the next step: branch to A, branch to B, call a different model, or loop back. The path is a graph, and state flows through it.
LangGraph is the framework for building exactly this. It is built by the LangChain team and sits on top of LangChain primitives. Its strengths:
Cycles
Retry, reflect, re-plan until a condition is met. This is a core primitive, not a workaround.
Conditional routing
Explicit branches you design and own -- "if confidence is below 0.7, route to a human."
Durable state
Checkpointers persist graph state, so long jobs and human-in-the-loop approvals can pause and resume days later.
Reproducibility
Deterministic, node-by-node execution you can measure and evaluate -- which matters enormously for production and for research.
A quick rule: write a simple linear flow → LangChain; build a complex, looping, stateful agent → LangGraph. It is not a hard wall (LangChain can build agents too, and LangGraph leans on LangChain), but that is the practical split. Companies run LangGraph in production at serious scale: Klarna's assistant serves 85M users, Uber automates test generation for large code migrations, and LinkedIn powers an AI recruiter with it.
05 LangSmith: The Observability Layer
LangSmith is not a framework for building -- it is for watching. Once your chain or graph is live, LangSmith gives you tracing, debugging, prompt management, evaluation, and cost and latency monitoring. You can inspect each call: what went into the vector database, what came back from the model, how many tokens it burned, how long it took. Build with LangChain or LangGraph; observe with LangSmith. Together the three form one ecosystem -- build, orchestrate, observe.
06 The OpenAI Agents SDK: The Other Philosophy
The OpenAI Agents SDK is a lightweight, code-first framework that picks the opposite tradeoff from LangGraph. Instead of you drawing an explicit graph, the LLM drives the loop. Its primitives:
Agents & Tools
An agent is an LLM plus instructions plus a set of tools -- your functions, hosted tools, or MCP servers.
Handoffs
One agent transfers control to a specialist (triage → refunds → billing). Multi-agent is first-class, not hand-wired.
Guardrails & Sessions
Validation that can block or pause before risky actions, plus conversation memory across turns.
The heart of it is the agent loop:
Tracing is built in, and the SDK abstracts the model and transport -- so you can point it at providers beyond OpenAI. Route it through OpenRouter, for example, and the same code can run Claude or GPT underneath while only the orchestration stays OpenAI's.
07 How the Agents SDK Differs From LangGraph
Same spectrum, opposite ends. One asks you to draw the path; the other asks the model to find it.
| Dimension | LangGraph | OpenAI Agents SDK |
|---|---|---|
| Who controls the path | You (explicit graph) | The LLM (agent loop) |
| Closer to | Anthropic's workflow | Anthropic's agent |
| Boilerplate | More | Minimal -- an agent is a few lines |
| Multi-agent handoffs | Hand-wired | First-class |
| Durable state / resume | Checkpointers (heavy-duty) | Sessions (lighter) |
| Reproducibility / eval | Strong, node-by-node | Harder -- path varies per run |
| Observability | LangSmith (separate) | Built in |
Neither is "better." LangGraph rewards you when you need to guarantee and measure the path. The Agents SDK rewards you when you want the model to figure the path out and you want to ship fast.
08 Best of Both Worlds
These are not either/or. A LangGraph node is just a function that takes state and returns state -- so that function can call the Agents SDK runtime inside it. You wrap the unpredictable part in a well-bounded box and keep the outer flow reproducible.
Use LangGraph for the parts you want to guarantee and measure (sequence, gates, retries, evaluation). Drop an Agents SDK agent into a node for the parts you want the model to figure out itself (which tool, which specialist, how many steps).
A concrete shape, competitor analysis: an outer LangGraph runs a fixed, auditable pipeline -- ingest, research, score, human-approval-if-risky, report -- and owns state, checkpointing, and the eval harness. Inside the research node, an Agents SDK agent autonomously decides which tools to hit and hands off to a pricing specialist when it finds a pricing page.
graph.py -- Agents SDK inside a LangGraph node# INNER: the OpenAI Agents SDK does the autonomous research
from agents import Agent, Runner, function_tool
@function_tool
def web_search(query: str) -> str:
"Search the web for competitor info."
...
pricing_specialist = Agent(
name="PricingAnalyst",
instructions="Extract and normalize pricing tiers into a table.",
tools=[fetch_pricing],
)
research_agent = Agent(
name="Researcher",
instructions="Build an evidence-backed competitor profile. "
"Hand off to PricingAnalyst when you find a pricing page.",
tools=[web_search],
handoffs=[pricing_specialist], # model decides when to hand off
)
# OUTER: LangGraph owns the deterministic, auditable pipeline
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
async def research_node(state):
# the whole tool-loop + handoff happens INSIDE this one node
result = await Runner.run(research_agent, f"Research {state['competitor']}")
return {"research": result.final_output}
def route_after_score(state):
# explicit conditional edge -- LangGraph's strength, fully auditable
return "human_review" if state["score"] > 0.7 else "report"
g = StateGraph(State)
g.add_node("research", research_node) # Agents SDK lives here
g.add_node("score", score_node)
g.add_node("human_review", human_review_node)
g.add_node("report", report_node)
g.add_edge(START, "research")
g.add_edge("research", "score")
g.add_conditional_edges("score", route_after_score)
g.add_edge("human_review", "report")
g.add_edge("report", END)
app = g.compile(checkpointer=MemorySaver()) # durable state + resume
The unpredictable work is contained; the overall flow stays deterministic and measurable. Other natural fits: customer support (LangGraph flow, Agents SDK triage-and-handoff inside), code migration (per-file LangGraph loop with test-pass checkpoints, an autonomous editing agent per file), and research report generation (planned outer graph, adaptive per-section research agent).
09 When to Use What
- Linear RAG or a simple chatbot → LangChain.
- Complex agent that loops, branches, and must be reproducible → LangGraph.
- Multi-agent system you want to ship fast, model-driven, OpenAI-leaning → OpenAI Agents SDK.
- Production observability for any of the above → LangSmith.
- You need both control and autonomy → LangGraph outside, Agents SDK inside the nodes that need it.
The frameworks are converging on the same truth Anthropic stated plainly: no single model or tool dominates -- the architecture is the bottleneck. Pick the one that lets you express, control, and measure the architecture your problem actually needs.