*For other parts of the series : Part 0 , Part 1 , Part 2 , *Part 3
What this article is:Parts 0–3 gave you the architecture, the memory patterns, and the human-in-the-loop toolkit. This is where you use all three together for the first time, in one project, built step by step. Every line of code is explained. Nothing is assumed. Still very easy to follow along without reading the other parts of the series.
What we’re building:A customer support agent for an e-commerce store calledShopBot. Customers can ask about their orders, request refunds, and escalate complaints. The agent remembers the conversation, uses real tools to look up order data, and s for human approval before processing any refund.
One of the biggest mistakes beginners make is opening a blank file and starting to type. Before you write code, you need to draw the graph on paper or at least in your head. LangGraph rewards planning.
Here is what ShopBot will do:
That’s four nodes, two conditional edges, one interrupt() call, and one summarization trigger. That's the whole agent. Write this down before you start coding. The code is just translating this picture into Python.
Now let’s build it module by module, exactly as you’ve learned.
Create a new file called shopbot.py. Or open a Colab notebook. Either works.
Install what you need:
pip install langgraph langchain langchain-openai langgraph-checkpoint-sqlite python-dotenv
**Create a **.env file in the same folder:
OPENAI_API_KEY=your-key-here
This is always the first section. Every import your entire file needs lives here. No importing things halfway down the file.
**Why **gpt-4o-mini? It's smart enough for support tasks, cheap enough to run frequently, and fast enough that users won't notice latency. For production you might upgrade to gpt-4o for harder reasoning — but start small.
The state is your agent’s working memory. Every piece of information that travels through your graph must live here. Think carefully about what your agent needs to know at each stage.
summary — you learned this in Part 2. When the conversation runs past 6 messages, the agent compresses history into this field and deletes old raw messages. Token costs stay flat.
customer_name — a practical field you'll see in almost every real support agent. Once the agent knows who it's talking to, it can greet them by name and personalize responses. Without it in state, each node would have to re-parse the conversation to find the name.
ticket_category — this is what makes the conditional routing possible. The agent sets this field, and the routing function reads it to decide whether to trigger the human approval flow.
Tools are the hands of your agent. Without tools, the agent can only talk. With tools, it can actually look things up and do things.
ShopBot gets three tools:
Look at the docstring for process_refund. It says "This tool actually issues the refund. It should only be called AFTER human approval." The LLM reads this. It informs the LLM's decision-making. Write docstrings as if you're giving instructions to a smart intern who doesn't know your business rules yet — because that's exactly what you're doing.
This is where the work happens. Each node receives state, does something, and returns a dictionary of updates. Nothing else.
ShopBot has three nodes you write yourself, plus tool_node from Module 3 (pre-built).
Routing functions are the decision-makers. They look at state and return a string. That’s all they do. They never modify state — that’s the nodes’ job.
ShopBot needs two routing functions:
This is construction day. All the pieces exist. Now you wire them together. Follow the five-step sequence every time: Initialize → Register → Entry → Edges → Compile.
If you’re in a Colab or Jupyter notebook, run this to see a diagram of your graph:
from IPython.display import display, Imagefrom langchain_core.runnables.graph import MermaidDrawMethoddisplay(Image( shopbot.get_graph().draw_mermaid_png( draw_method=MermaidDrawMethod.API )))
This is one of the best features of LangGraph for learners — you can see exactly what you built, drawn as a real flowchart. If something looks wrong in the diagram, something is wrong in your edges.
This is where you run the agent. For a script, it’s the if name == "main" block. For an API server, it's your route handler. The pattern is the same either way.
Here is a full example session so you know what to expect when you run this.
======================================================= ShopBot — Customer Support Agent Powered by LangGraph=======================================================Type your message below. Type 'exit' to quit.Type 'state' to inspect what ShopBot currently remembers.You: Hi, my name is Alex and I need help with order ORD-001ShopBot: Hi Alex! I'd be happy to help you with order ORD-001.I can see your order:- Product: Wireless Headphones- Status: Delivered on 2025-06-10- Amount: $89.99What can I help you with regarding this order?You: I want to return it and get a refundShopBot: I've checked your order ORD-001 and it's eligible for a refundsince it's been delivered. Let me process that for you right away.======================================================= HUMAN APPROVAL REQUIRED======================================================= Customer : Alex Action : process_refund Arguments : {'order_id': 'ORD-001', 'reason': 'Customer requested return'} Context : No summary yet=======================================================Options: [a] Approve [r] Reject [e] EscalateYour decision: aShopBot: Great news, Alex! Your refund has been successfully processed.Refund Reference: REF-ORD-001-143022Amount: $89.99 will be returned to your original payment method within3–5 business days.Is there anything else I can help you with?You: state[DEBUG] Current State: Messages in state : 6 Customer name : (not set) Ticket category : refund_request Summary : (none yet) Next node(s) : ()You: exitShopBot: Thank you for contacting support. Have a great day!
Let’s be explicit about what every part of this project maps to in the series:
From Part 1 (Architecture): The entire seven-module file structure. Every module in exactly the right order. StateGraph, add_node, add_conditional_edges, compile — all used correctly.
From Part 2 (Memory): The summary field in state. The summarize_node function. The rolling summary pattern — new messages get compressed, old ones deleted, token costs stay flat. The MemorySaver checkpointer keeping conversation history alive across turns.
From Part 3 (Human-in-the-Loop): The review_refund node using interrupt() to execution. The Command(resume=...) call in the entrypoint to resume. The while "interrupt" in result loop to handle cases where multiple approvals might be needed in a single turn. The three-option approval flow (approve / reject / escalate).
New in this project: Real tools with @tool. The ToolNode pre-built node. Binding tools to the LLM with bind_tools. The ReAct loop (tools → agent_node → tools → ...). Conditional routing that treats different tools differently (safe tools go straight to tool_node; sensitive tools go through review_refund first).
The project above is deliberately complete but minimal. Here are three specific things to add next, in order of difficulty:
Level 1 — Better category detection: Right now, ticket_category is set by keyword scanning in agent_node. A cleaner approach is to ask the LLM to explicitly categorize the ticket as part of its response, using structured output. Look up with_structured_output() in LangChain docs.
Level 2 — SQLite persistence: Swap MemorySaver() for SqliteSaver (one line change in Module 6). Add a simple loop that asks "same session or new?" at startup. Your conversations now survive restarts. You learned exactly how to do this in Part 2.
Level 3 — Add a complaint escalation path: Add a fourth node called escalate_node that fires when ticket_category == "complaint" and the customer_emotion is "FRUSTRATED". The node calls interrupt() to notify a human agent. Wire it in as a new branch off route_after_agent. Everything you need for this is already in Part 3.
Everything new this project introduced, in one place:
ToolNode(tools) — pre-built node that executes tool calls from the LLM's last message. Takes your tools list, handles everything else automatically.
llm.bind_tools(tools) — connects your tool list to the LLM. After binding, the LLM can choose to call any tool by returning a special tool_calls field in its response instead of plain text.
@tool — decorator that turns a Python function into an LLM-callable tool. The function's docstring is what the LLM reads to decide when to use it.
hasattr(message, "tool_calls") and message.tool_calls — the standard pattern for checking if the LLM's last message contains a tool call. Always check hasattr first before accessing the attribute.
while "interrupt" in result — the loop pattern in Module 7 that handles one or more pending interrupt() calls. Each Command(resume=...) answers one interrupt and potentially triggers the next.
shopbot.get_state(config) — inspect the full current state at any point. .values gives you the state dict. .next tells you what would run next. Invaluable for debugging.
Reading Parts 1, 2, and 3 gave you the map. This project is the first step into the territory.
The gap between understanding a concept and writing the code for it is real, and it’s filled by exactly this kind of project — something small enough to hold in your head completely, but complete enough to use every tool in your toolkit. You have a checkpointer. You have real tools. You have a human approval gate. You have a summarization trigger. Every major concept from the series, working together in one coherent agent.
Build this. Break it deliberately. Fix the break. Then extend it. That’s the only path from reading articles to writing production code.
Next in the series: Multi-Agent Systems — splitting ShopBot into specialist sub-agents (a Refund Agent, an Order Agent, a Complaints Agent) orchestrated by a Supervisor, and connecting them using LangGraph’s subgraph pattern.
*For other parts of the series : Part 0 , Part 1 , Part 2 , *Part 3
Your First Real LangGraph Project: was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.