Course Content
LangGraph Agents
7 sections · 49 lessons
How does a LangGraph-based agent differ from an “agent loop” implemented in plain code?
What you need to know
The plain loop
1messages = [{"role": "user", "content": "Refund order OD-88123"}]2while True:3 reply = llm_with_tools.invoke(messages)4 messages.append(reply)5 if not reply.tool_calls:6 break7 for call in reply.tool_calls:8 messages.append(run_tool(call)) # returns a ToolMessageTwelve lines, and it works. It has no memory across requests, no pause, no retry, no stream, and if the process dies mid-loop the work is lost.
The prebuilt agent
1from langchain.agents import create_agent2from langchain.agents.middleware import HumanInTheLoopMiddleware, ToolCallLimitMiddleware3from langgraph.checkpoint.memory import InMemorySaver45agent = create_agent(6 "anthropic:claude-sonnet-4-5",7 tools=[lookup_order, issue_refund],8 system_prompt="You handle refunds for an online store. Be brief.",9 middleware=[10 HumanInTheLoopMiddleware(interrupt_on={"issue_refund": True}),11 ToolCallLimitMiddleware(run_limit=8, exit_behavior="end"),12 ],13 checkpointer=InMemorySaver(),14)15agent.invoke({"messages": [{"role": "user", "content": "Refund OD-88123"}]},16 {"configurable": {"thread_id": "cust-17"}})create_agent replaced LangGraph's create_react_agent, which still runs in 1.x but is deprecated. Middleware hooks run before and after model and tool calls — approvals, summarisation, retries, call limits, PII redaction, model fallback.
Streaming comes free
stream_mode | You get |
|---|---|
"values" | Full state after each step |
"updates" | Only what each node changed |
"messages" | LLM tokens as (chunk, metadata) tuples |
"custom" | Your own events from get_stream_writer() |
"checkpoints", "tasks", "debug" | Detailed execution events |
Pass a list to combine them. With version="v2", every chunk has the same shape: {"type", "ns", "data"}.
A real-life example
A fintech team's refund bot started as the twelve-line loop. Three incidents pushed them to a graph: a deploy killed 40 in-flight conversations; a refund above policy was issued with no human check; and users stared at a blank screen for 9 seconds. Moving to create_agent with a Postgres checkpointer, HumanInTheLoopMiddleware on issue_refund, and stream_mode="messages" to the UI fixed all three in about a week. The loop logic itself did not change.
Follow-up questions to expect
- "When is the plain loop the right answer?" — Scripts, evals, one-off tools, or when you already have another durable runtime. Adding a framework there only adds dependencies.
- "When is
create_agentnot enough?" — When the flow has fixed stages around the loop, parallel branches, several agents, or approval rules that depend on more than one tool call. - "Is
create_agenta different engine?" — No. It returns a compiled LangGraph graph, so checkpointers, streaming andCommand(resume=...)work the same.