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.
One: a tool that only looks things up
Safe to run at any time, so it just runs.
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.
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
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.
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 ENDFive: the loop, with memory
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))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
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
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.
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.
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
InMemorySaverforSqliteSaver, 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 out | What it is |
|---|---|
| The functional API | @entrypoint and @task, a second way to write the same graphs without the wiring. |
| Deployment | Running a graph as a server, with langgraph dev and a langgraph.json file. |
| LangSmith | Recording every run so you can see what the model actually did. Set two environment variables and it starts working. |
| Middleware | Hooks around the agent loop, named in lesson 29. |
| Node caching, timeouts, custom channels | Tuning for graphs that are already working. |
- 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.