LangGraph Tutorial (Python): testing agents locally for beginners
This tutorial shows you how to run and test a LangGraph agent locally in Python, without wiring it into a web app or production stack. You need this when you want fast feedback on agent behavior, deterministic tests, and a clean way to debug tool calls before you expose the graph to users.
What You'll Need
- •Python 3.10+
- •
langgraph - •
langchain-openai - •
python-dotenv - •An OpenAI API key in your environment as
OPENAI_API_KEY - •Optional but useful:
- •
pytestfor automated tests - •
ipythonor Jupyter for interactive inspection
- •
Install the packages:
pip install langgraph langchain-openai python-dotenv pytest
Step-by-Step
- •Start with a small graph that has one model node and one tool node.
Keep it minimal so you can test the wiring first, then expand later.
from typing import Annotated
from dotenv import load_dotenv
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.graph.message import add_messages
load_dotenv()
@tool
def get_policy_status(policy_id: str) -> str:
"""Return a fake policy status for local testing."""
return f"Policy {policy_id} is active and paid up."
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools([get_policy_status])
def call_model(state: MessagesState):
messages = [SystemMessage(content="You are a helpful insurance assistant.")] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
- •Add a router that decides whether the model should call a tool or end the conversation.
This is the part most beginners get wrong: the graph needs to inspect the last AI message for tool calls.
def should_continue(state: MessagesState):
last_message = state["messages"][-1]
if getattr(last_message, "tool_calls", None):
return "tools"
return END
graph_builder = StateGraph(MessagesState)
graph_builder.add_node("agent", call_model)
graph_builder.add_node("tools", lambda state: {"messages": []})
graph_builder.add_edge(START, "agent")
graph_builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph_builder.add_edge("tools", "agent")
app = graph_builder.compile()
- •Replace the placeholder tool node with LangGraph's built-in tool executor.
This gives you real tool execution instead of hand-rolled logic.
from langgraph.prebuilt import ToolNode
tool_node = ToolNode([get_policy_status])
graph_builder = StateGraph(MessagesState)
graph_builder.add_node("agent", call_model)
graph_builder.add_node("tools", tool_node)
graph_builder.add_edge(START, "agent")
graph_builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph_builder.add_edge("tools", "agent")
app = graph_builder.compile()
- •Run the graph locally with a simple input and print every message.
This is your first local test loop: if this works, your graph wiring is correct.
if __name__ == "__main__":
result = app.invoke(
{"messages": [HumanMessage(content="Check policy 12345 status")]}
)
for message in result["messages"]:
print(f"{message.__class__.__name__}: {message.content}")
if hasattr(message, "tool_calls") and message.tool_calls:
print(f"Tool calls: {message.tool_calls}")
- •Add a repeatable test so you can verify behavior without manual inspection every time.
For beginners, this is where local testing becomes useful instead of just running ad hoc scripts.
def test_policy_lookup():
result = app.invoke(
{"messages": [HumanMessage(content="What is the status of policy 12345?")]}
)
final_message = result["messages"][-1].content.lower()
assert "policy 12345" in final_message
assert "active" in final_message
if __name__ == "__main__":
test_policy_lookup()
print("Test passed.")
Testing It
Run the script from your terminal and confirm two things: the agent responds in natural language, and it actually uses the tool when asked about a policy ID. If you see a tool call followed by a final answer that includes the returned status, your graph is working.
Then run pytest if you added the test function to a file named like test_agent.py. If the test fails, inspect whether your model supports tool calling and whether OPENAI_API_KEY is loaded correctly from the environment.
A good local debugging habit is to print each message in the returned state. That makes it obvious whether failure happened at routing, tool execution, or final response generation.
Next Steps
- •Add more tools and route between them with structured conditions.
- •Persist state with a checkpointer so you can resume conversations across runs.
- •Write integration tests for edge cases like missing policy IDs or malformed user input.
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