Building an AI Agent With LangGraph
Most LangGraph agent tutorials start you at the hardest place. This one shows the one-line prebuilt path first — and exactly when to graduate to a hand-built graph.
from langgraph.prebuilt import create_react_agent
# tools are plain functions; the docstring becomes the description
agent = create_react_agent(model, tools=[search_docs, lookup_order])
result = agent.invoke(
{"messages": [{"role": "user", "content": "Where is order ORD-5?"}]}
)
print(result["messages"][-1].content)Here's a stat that should change how you approach this: the overwhelming majority of LangGraph agents that reach production for standard tool-calling never hand-build a graph at all — they use the prebuilt
create_react_agentLet me show it through a team that learned the ordering the hard way.
The Problem: A LangGraph Agent Tutorial That Skipped the Prebuilt Path
A three-person team wanted an agent that answered support questions using two tools: a docs search and an order lookup. Standard stuff — a model that calls tools in a loop until it can answer. They followed a popular tutorial that built everything manually: a
StateGraphIt took them three days and about two hundred lines. It worked, eventually. Then they discovered that LangGraph ships
create_react_agentEvery one of those lines was correct, which is what made it frustrating. They hadn't done anything wrong in the tutorial's terms. They'd just followed a tutorial that answered a question — how do LangGraph's primitives work — that wasn't the question their task was asking, which was simply how to ship a tool-calling agent.
The Wrong Approach
Their mistake wasn't using LangGraph. It was starting at the wrong level of the framework. LangGraph has a deliberate two-level design: high-level prebuilts for common patterns, and the low-level
StateGraphThe manual graph gave them nothing their task needed. A standard tool-calling agent is a solved pattern; reimplementing it by hand just meant more code to maintain and more places for their version to diverge from the well-tested one.
There's a deeper cost too. By hand-building the standard loop, they'd taken on maintenance of something LangGraph's team maintains for them. Every upgrade that improved the prebuilt agent passed them by, because they were effectively running their own fork of the pattern. Reimplementing a solved problem doesn't just cost the initial days — it opts you out of every future improvement to the thing you reimplemented.
⚡ Pro tip: For any standard tool-calling agent, start with
create_react_agentStateGraphThe Correct Pattern
They deleted the two hundred lines and replaced them with the prebuilt.
[object Object], langgraph.prebuilt ,[object Object], create_react_agent
,[object Object],
agent = create_react_agent(model, tools=[search_docs, lookup_order])
result = agent.invoke(
{,[object Object],: [{,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}]}
)
,[object Object],(result[,[object Object],][-,[object Object],].content)What this does: it builds a complete tool-calling agent — the model, the loop, the tool dispatch, the stop condition — in one call.
create_react_agentNotice what you still write: the tools.
create_react_agent⚡ Pro tip: In LangGraph, a tool is a normal Python function and its docstring becomes the description the model reads. Everything you know about writing sharp tool descriptions applies directly to the docstring — that text does the heavy lifting.
Results and What Changed
The rewrite cut their agent code by roughly 90% and made it more reliable, because the prebuilt is battle-tested across thousands of deployments while their hand-rolled loop was tested across three days.
The reliability gain is easy to underrate. A prebuilt used across thousands of production agents has had its edge cases found and fixed by other people's incidents. Your three-day version has had its edge cases found by your customers. Standing on well-worn code is a feature, not a compromise.
Then, weeks later, a real requirement arrived that finally justified the graph: before issuing any refund, a human had to approve. That's not the standard loop — it's a branch that pauses for outside input. This time, dropping to
StateGraph[object Object], langgraph.graph ,[object Object], StateGraph, START, END
builder = StateGraph(State)
builder.add_node(,[object Object],, decide_node)
builder.add_node(,[object Object],, approval_node)
builder.add_node(,[object Object],, execute_node)
builder.add_edge(START, ,[object Object],)
builder.add_conditional_edges(,[object Object],, route, {,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],})
builder.add_edge(,[object Object],, ,[object Object],)
builder.add_edge(,[object Object],, END)
graph = builder.,[object Object],()What this does: it builds a custom flow where a routing function sends risky actions through a human-approval node and safe ones straight to execution. This is exactly what the graph model is for — cycles, branches, and human-in-the-loop that the prebuilt can't express. The complexity is now buying something.
This is the payoff of understanding the two levels. The team didn't fight the framework or over-build up front; they used the simple thing until a real requirement demanded the powerful thing, then reached for exactly the powerful thing they needed. That sequencing — simple until proven insufficient — is the whole art of using LangGraph well.
⚡ Pro tip: The signal that you've genuinely outgrown
create_react_agentHow to Apply This to Your Situation
Start every LangGraph project with
create_react_agentConcretely, this ordering fits most teams. A startup shipping a support agent uses the prebuilt and moves on. A fintech team adds a
StateGraphWhen a requirement appears that the standard loop can't handle — approval gates, multi-step branching, resumable long-running flows — then drop to
StateGraphAnd when you do build a graph, keep the state definition small. The
StateA good habit: add a field to
State⚡ Pro tip: Keep your graph's
StateNext Steps
The right LangGraph agent tutorial ordering is prebuilt first, primitive second. Use
create_react_agentStateGraph⚠️ Common mistake: Hand-building a
StateGraphWhether you use the prebuilt or a custom graph, the tool docstrings and system instructions that steer the agent are the same reusable assets. PromptABCD keeps them versioned in one place, so when you graduate from
create_react_agentContinue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
