LangGraph Tutorial (Python): building conditional routing for beginners
This tutorial shows you how to build a LangGraph workflow in Python that routes requests down different paths based on simple conditions. You need this when one agent flow is not enough and you want the graph to choose between different handlers, like billing, support, or fallback logic.
What You'll Need
- •Python 3.10+
- •
langgraph - •
langchain-core - •An OpenAI-compatible LLM package if you want to extend this later
- •No API key is required for the routing example below because it uses pure Python logic
- •Basic familiarity with LangGraph nodes, edges, and state
Step-by-Step
- •Start by defining the graph state and the node functions.
For beginners, keep the state small: one input field and one output field is enough to understand how routing changes execution.
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
class RouteState(TypedDict):
text: str
route: str
response: str
def classify_route(state: RouteState) -> dict:
text = state["text"].lower()
if "billing" in text or "invoice" in text:
return {"route": "billing"}
elif "refund" in text or "cancel" in text:
return {"route": "support"}
else:
return {"route": "fallback"}
- •Add one node for each branch your graph can take.
Each node should return a partial update to state, not the whole object. That keeps your graph easy to reason about and avoids accidental overwrites.
def billing_node(state: RouteState) -> dict:
return {"response": "Billing team handles invoices, charges, and payment issues."}
def support_node(state: RouteState) -> dict:
return {"response": "Support team handles refunds, cancellations, and account help."}
def fallback_node(state: RouteState) -> dict:
return {"response": "I could not classify this request. Please rephrase it."}
- •Wire the nodes into a LangGraph workflow with conditional routing.
The key piece isadd_conditional_edges, which maps the classifier output to the next node name.
def route_decision(state: RouteState) -> Literal["billing", "support", "fallback"]:
return state["route"]
builder = StateGraph(RouteState)
builder.add_node("classifier", classify_route)
builder.add_node("billing", billing_node)
builder.add_node("support", support_node)
builder.add_node("fallback", fallback_node)
builder.add_edge(START, "classifier")
builder.add_conditional_edges(
"classifier",
route_decision,
{
"billing": "billing",
"support": "support",
"fallback": "fallback",
},
)
builder.add_edge("billing", END)
builder.add_edge("support", END)
builder.add_edge("fallback", END)
graph = builder.compile()
- •Run the graph with different inputs and inspect the results.
This is where conditional routing becomes visible: same graph, different path depending on the input text.
tests = [
{"text": "I have a billing issue with my invoice."},
{"text": "I want a refund for my subscription."},
{"text": "Can you help me with something else?"}
]
for test in tests:
result = graph.invoke(test)
print(f"Input: {test['text']}")
print(f"Route: {result['route']}")
print(f"Response: {result['response']}")
print("-" * 40)
- •Make the routing logic more realistic by separating classification from handling.
In production, you usually replace keyword checks with an LLM classifier or rules engine, but the graph structure stays the same.
def route_decision_v2(state: RouteState) -> Literal["billing", "support", "fallback"]:
mapping = {
"billing": "billing",
"support": "support",
"fallback": "fallback",
}
return mapping.get(state["route"], "fallback")
Testing It
Run the script and confirm each test input produces a different route value when expected. The billing example should go to billing, refund-related text should go to support, and unrelated text should hit fallback. If you want extra confidence, add more test cases with overlapping keywords like “invoice refund” and verify your classifier priority is what you expect. You can also call graph.invoke() directly in a Python REPL while tweaking the classifier logic to see how each change affects routing.
Next Steps
- •Replace keyword routing with an LLM-based classifier node using
ChatOpenAIor another chat model. - •Add memory or conversation state so routing can consider prior messages.
- •Extend the graph with nested conditional branches for multi-step workflows like claims triage or fraud screening.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit