Tutorial
LangGraph Tutorial for Beginners
A hands-on LangGraph tutorial for beginners: build a graph from plain Python functions, add a chat model, give it tools, then add memory, human approval and streaming — with runnable code at every step. Written for students in Pakistan and India who know basic Python and have seen an agent loop before.
By Abdul Rahman Azam, founder of AI Season · Updated
Key facts
| You need | Basic Python, a terminal and an API key for a chat model that supports tool calling |
|---|---|
| LangGraph version | 1.x — every snippet tested with LangGraph 1.2 and LangChain 1.4 |
| You will build | A tool-using agent with memory, human approval and streaming |
| Time | About 90 minutes |
| Before this | LangChain tutorial or an agent loop in plain Python |
What LangGraph is, and why agents need it
LangGraph is an open-source library from the LangChain team for building AI agents as graphs. You describe your agent as a set of nodes (Python functions), edges (which node runs next) and a shared state that every node reads and updates. LangGraph runs the graph, saves the state after every step, and lets you pause, resume, branch and retry.
A plain agent loop — call the model, run a tool, repeat — is enough for small agents. LangGraph matters when the loop gets real: different paths for different requests, retries when a step fails, conversations that must survive a server restart, and a human who must approve risky actions. LangChain's own create_agent runs on LangGraph underneath.
- State — a typed dictionary that holds everything the agent knows: messages, intermediate results, flags.
- Node — a function that takes the state and returns the fields it wants to update.
- Edge — a fixed connection from one node to the next. A conditional edge picks the next node at run time.
- Checkpointer — saves the state after each step, which gives you memory, pause/resume and time travel.
- Interrupt — pauses the graph mid-run so a human can review or edit before it continues.
Step 1: Install LangGraph
Create a virtual environment and install LangGraph with LangChain's model integrations. This tutorial uses OpenAI; for other providers install their package instead (for example langchain-anthropic or langchain-google-genai).
mkdir langgraph-tutorial && cd langgraph-tutorial
python -m venv .venv
# Windows: .venv\Scripts\activate · macOS/Linux: source .venv/bin/activate
pip install -U langgraph langchain langchain-openai python-dotenvOPENAI_API_KEY=your-key-hereStep 2: Your first graph — no LLM yet
Start without a model so the mechanics are clear. Each node returns only the keys it changes; LangGraph merges them into the state.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
text: str
word_count: int
def clean(state: State) -> dict:
return {"text": state["text"].strip().lower()}
def count(state: State) -> dict:
return {"word_count": len(state["text"].split())}
builder = StateGraph(State)
builder.add_node("clean", clean)
builder.add_node("count", count)
builder.add_edge(START, "clean")
builder.add_edge("clean", "count")
builder.add_edge("count", END)
graph = builder.compile()
print(graph.invoke({"text": " LangGraph Makes Agents Explicit ", "word_count": 0}))
# {'text': 'langgraph makes agents explicit', 'word_count': 4}START and END are special nodes. Every graph needs a path from START, and every run must eventually reach END — or it stops at the recursion limit (25 steps by default).
Step 3: Add a chat model
MessagesState is a ready-made state with a messages list. Its reducer appends new messages instead of overwriting them, so the conversation grows as nodes run.
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, MessagesState, START, END
load_dotenv()
model = init_chat_model("openai:gpt-4o-mini", temperature=0)
def chatbot(state: MessagesState) -> dict:
return {"messages": [model.invoke(state["messages"])]}
builder = StateGraph(MessagesState)
builder.add_node("chatbot", chatbot)
builder.add_edge(START, "chatbot")
builder.add_edge("chatbot", END)
graph = builder.compile()
result = graph.invoke({"messages": [{"role": "user", "content": "What is LangGraph in one sentence?"}]})
print(result["messages"][-1].content)Step 4: Give it tools — the agent loop as a graph
An agent is a loop: the model either answers or asks for a tool; if it asks, run the tool and go back to the model. In LangGraph that loop is two nodes and a conditional edge. ToolNode runs whatever tools the model requested, and tools_condition routes to "tools" when there is a tool call and to END when there is not.
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.prebuilt import ToolNode, tools_condition
load_dotenv()
# Demo data so the tutorial runs offline. Swap in a real weather API later.
DEMO_WEATHER = {"karachi": "33°C, humid", "lahore": "36°C, sunny", "delhi": "35°C, hazy", "mumbai": "30°C, rain"}
@tool
def get_weather(city: str) -> str:
"""Get the current weather for a city in Pakistan or India."""
return DEMO_WEATHER.get(city.lower(), f"No data for {city}.")
tools = [get_weather]
model = init_chat_model("openai:gpt-4o-mini", temperature=0).bind_tools(tools)
def agent(state: MessagesState) -> dict:
return {"messages": [model.invoke(state["messages"])]}
builder = StateGraph(MessagesState)
builder.add_node("agent", agent)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition) # tool call -> "tools", otherwise -> END
builder.add_edge("tools", "agent")
graph = builder.compile()
result = graph.invoke({"messages": [{"role": "user", "content": "Is it hotter in Karachi or Delhi right now?"}]})
print(result["messages"][-1].content)Print the graph to check its shape — paste the output into any Mermaid viewer:
print(graph.get_graph().draw_mermaid())Step 5: Add memory with a checkpointer
Compile the graph with a checkpointer and pass a thread_id. LangGraph saves the state after every step under that thread, so the next call continues the same conversation. InMemorySaver is for learning; in production use a database-backed checkpointer such as the Postgres or SQLite ones.
from langgraph.checkpoint.memory import InMemorySaver
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "student-42"}}
graph.invoke({"messages": [{"role": "user", "content": "I'm Ayesha and I live in Lahore."}]}, config)
result = graph.invoke({"messages": [{"role": "user", "content": "What's the weather where I live?"}]}, config)
print(result["messages"][-1].content) # the agent remembers Lahore and calls the toolA different thread_id is a different conversation. One thread per user (or per chat) is the usual pattern.
Step 6: Ask a human before risky actions
Some tools should never run on the model's word alone — sending email, paying, deleting. Call interrupt() inside the tool: the graph pauses, saves its state, and returns what it wants approved. Resume it with Command(resume=...). Interrupts need a checkpointer.
from langchain.tools import tool
from langgraph.types import Command, interrupt
@tool
def send_email(to: str, subject: str) -> str:
"""Send an email. A human must approve it first."""
decision = interrupt({"action": "send_email", "to": to, "subject": subject})
if decision != "approve":
return "The user rejected this email. Do not retry."
return f"Email sent to {to}." # demo: plug in a real email API here
# Rebuild the graph from Step 4 with tools = [get_weather, send_email],
# then compile it with checkpointer=InMemorySaver().
config = {"configurable": {"thread_id": "approval-demo"}}
result = graph.invoke({"messages": [{"role": "user", "content": "Email ali@example.com the subject 'Class moved to 7pm'."}]}, config)
print(result["__interrupt__"]) # what the agent wants approved
result = graph.invoke(Command(resume="approve"), config)
print(result["messages"][-1].content)Step 7: Stream progress and cap the steps
Users should not stare at a blank screen while an agent works. stream yields each node's update as it happens. Set recursion_limit so a confused agent cannot loop forever and burn your API budget.
config = {"configurable": {"thread_id": "stream-demo"}, "recursion_limit": 10}
inputs = {"messages": [{"role": "user", "content": "Compare the weather in Mumbai and Lahore."}]}
for update in graph.stream(inputs, config, stream_mode="updates"):
for node, data in update.items():
print(f"[{node}]", data["messages"][-1].content or data["messages"][-1].tool_calls)LangGraph vs LangChain's create_agent
| You need | Use | Why |
|---|---|---|
| A standard tool-calling agent, fast | create_agent (LangChain) | One function call; it builds the graph for you |
| Custom routing, several specialised steps | LangGraph StateGraph | You decide every node and edge |
| Retries, fallbacks, validation steps | LangGraph | Conditional edges make failure paths explicit |
| Human approval mid-run | LangGraph interrupt | Pause, inspect, edit, resume from a checkpoint |
| Multi-agent systems | LangGraph | Each agent can be a node or a subgraph |
More detail: LangChain vs LangGraph and AI agent frameworks compared.
Common beginner mistakes
- Returning the whole state from a node — return only the keys you changed. With
MessagesState, return a list of new messages, not the old list plus new ones. - Forgetting the edge back from tools — without
tools -> agent, the model never sees the tool result. - Memory without a thread_id — a checkpointer does nothing useful until every call passes
configurable.thread_id. - Interrupts without a checkpointer — the graph has nowhere to save the paused state.
- Copying pre-1.0 tutorials — older posts import from paths that have moved. Check the version in any tutorial you copy.
- No evaluation — keep a small set of test questions and re-run them after every prompt, model or graph change.
Where to go next
| Next skill | Why it matters | Guide |
|---|---|---|
| RAG | Let the agent answer from your own documents | RAG for AI agents |
| Vector databases | Store and search embeddings for retrieval | What is a vector database? |
| MCP | Connect agents to tools and data through a standard protocol | MCP guide |
| The full path | Every skill in order, with a project per stage | Agentic AI roadmap |
Want to learn this live? The AI Season bootcamp teaches LangChain, LangGraph, RAG, MCP, guardrails and deployment in 12 live sessions over 6 weeks — explained in Urdu, easy to follow for Hindi speakers, coded in English. Cohort 02 starts 1st January 2027; early-bird PKR 3,000.
Frequently asked questions
What is LangGraph used for?
LangGraph is used to build AI agents and multi-step LLM workflows as graphs of nodes and edges with shared state. It adds what a simple agent loop lacks: branching, retries, persistent memory, human-in-the-loop approval, streaming and multi-agent coordination.
Is LangGraph hard for beginners?
Not if you learn it in order. Know basic Python, build one agent loop by hand or with LangChain's create_agent, then learn LangGraph's three ideas — state, nodes and edges. Most beginners have a working tool-using graph within an hour.
Should I learn LangChain or LangGraph first?
Learn enough LangChain to call a model and define a tool, then move to LangGraph for anything beyond a standard agent. LangChain's agents run on LangGraph, so the two are complementary rather than competing.
Is LangGraph free?
Yes. The LangGraph library is open source under the MIT licence. You pay only for the model API you call; LangSmith and LangGraph's hosted deployment platform are separate, optional paid products.
Can I use LangGraph without OpenAI?
Yes. LangGraph works with any chat model LangChain supports — Anthropic, Google Gemini, Groq, Mistral or local models through Ollama — as long as the model supports tool calling for agent graphs.
Is there a LangGraph course in Urdu or Hindi?
AI Season teaches LangGraph live as part of a 6 weeks AI agents bootcamp explained in Urdu (easy to follow for Hindi speakers), with English code and a project in every module. Cohort 02 starts 1st January 2027.