LangGraphLangGraph 1.2 · Python 3.10+
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson

Build the support agent

Lesson 0 promised an agent that reads a message, decides what to do, uses tools, remembers the conversation, and stops to ask you before doing anything it cannot undo. Here it is, in seven short pieces.

Nothing below is new. Every piece is something you have already built on its own, and this is the lesson where they meet.

The finished agent, and the lesson each piece came from
toolslookup_order, lesson 16refund, lesson 24the loopcall_model, lesson 15needs_a_tool, lesson 6ToolNode, lesson 18memorycheckpointer, lesson 20thread id, lesson 20safetyinterrupt inside a tool, lesson 24resume with an answer, lesson 23support agent

One: a tool that only looks things up

Safe to run at any time, so it just runs.

python
from langchain_core.tools import tool

@tool
def lookup_order(order_id: str) -> str:
    """Look up the status of an order by its id."""
    return f"Order {order_id} shipped on 3 March."

Two: a tool that spends money

This one asks first, using the pattern from lesson 24. The approval lives in the tool, so no graph has to remember to check.

python
from langgraph.types import interrupt

@tool
def refund(amount: int) -> str:
    """Refund an amount in rupees to the customer."""
    if interrupt(f"Approve a refund of {amount} rupees?") != "yes":
        return "The refund was not approved."
    return f"Refunded {amount} rupees."

If nobody approves it, the tool says so and the agent carries on with that as its answer. Nothing is refunded and nothing crashes.

Three: a model that knows about both

python
from pretend_model import PretendModel

tools = [lookup_order, refund]
model = PretendModel().bind_tools(tools)

Four: the two short functions

The model node from lesson 15, and the question from lesson 18.

python
from langgraph.graph import END

def call_model(state):
    return {"messages": [model.invoke(state["messages"])]}

def needs_a_tool(state):
    return "tools" if state["messages"][-1].tool_calls else END

Five: the loop, with memory

python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.prebuilt import ToolNode

builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_node("tools", ToolNode(tools))
python
builder.add_edge(START, "call_model")
builder.add_conditional_edges("call_model", needs_a_tool, ["tools", END])
builder.add_edge("tools", "call_model")

agent = builder.compile(checkpointer=InMemorySaver())

The checkpointer is doing both of its jobs at once here. It remembers the conversation between turns, and it holds the run still while a refund waits for approval.

Six: a first turn, which needs no permission

Example
from langchain_core.messages import HumanMessage

config = {"configurable": {"thread_id": "asha"}}
first = agent.invoke({"messages": [HumanMessage("Where is order A17?")]}, config)

print(first["messages"][-1].content)

The model asked for the lookup tool, the tool answered, and the model replied. Exactly lesson 18, and nobody was interrupted because nothing risky happened.

Seven: a second turn, which does

Example
second = agent.invoke({"messages": [HumanMessage("Please refund 500 rupees")]}, config)

print("waiting on:", second["__interrupt__"][0].value)

The run has stopped inside the refund tool. No money has moved. The state is saved under the same thread id, and it will sit there as long as it needs to.

Example
from langgraph.types import Command

done = agent.invoke(Command(resume="yes"), config)
print(done["messages"][-1].content)

What actually happened

Ask the thread for everything it holds and the whole story is there.

Example
for m in agent.get_state(config).values["messages"]:
    print(f"{m.type:<6} {m.content!r}")

Eight messages across two turns and one pause. Both questions, both times the model asked for a tool, both tool answers, and both replies. The second turn knew about the first because they share a thread id.

Every line of the promise in lesson 0 is in that output. It reads a message, decides what to do, uses tools, remembers, and stops to ask before spending money.

Where to take it

  • Make it real. Lesson 30, two lines, and the deciding stops being three rules.
  • Make it survive a restart. Swap InMemorySaver for SqliteSaver, from lesson 20.
  • Make it remember people. Add a store, from lesson 22, so it knows Asha next week.
  • Make it shorter. Trim the conversation, from lesson 21, before it gets expensive.
  • Make it smaller. If it grows past this, split it with subgraphs, from lesson 27.

What was left out, and where to find it

This course covered what you need to build something real. A few things were deliberately skipped, so you know they exist and know it was a choice.

Left outWhat it is
The functional API@entrypoint and @task, a second way to write the same graphs without the wiring.
DeploymentRunning a graph as a server, with langgraph dev and a langgraph.json file.
LangSmithRecording every run so you can see what the model actually did. Set two environment variables and it starts working.
MiddlewareHooks around the agent loop, named in lesson 29.
Node caching, timeouts, custom channelsTuning for graphs that are already working.
Where you have got to
You now know the whole of the LangGraph that people actually use daily: state, nodes, edges, branches, loops, reducers, tools, the agent loop, memory, interrupts, streaming and subgraphs. The rest of the documentation will read like a reference rather than a wall.
Try it yourself
  • Add a third tool of your own and ask for it.
  • Resume the refund with "no" and read what the agent says instead.
  • Give the refund tool a limit, so only refunds over 1000 stop to ask.

Little by little, you're building something great.