Build AI Agents with LangGraph: From State to Tool-Calling Graph
Build autonomous AI agents with LangGraph and Gemini — state, nodes, edges, tool integration, and a compiled graph you can run locally.
LLM calls are easy. Reliable agents are a graph. This notebook builds one with LangGraph + Gemini — state, nodes, edges, tool integration — that you can run, trace, and extend without rewriting the loop.
What does the course build?
A runnable agent: State (TypedDict) -> Nodes (LLM + tools) -> Conditional edges -> Compiled app that holds a conversation, calls a search tool when needed, and streams answers. Uses free Gemini (or OpenAI via one swap). Complement with API Masterclass for tool-side HTTP and Python fundamentals for TypedDict mechanics.
Ingredients: langgraph, langchain-google-genai, langchain-openai, duckduckgo-search, and a Gemini API key.
How do you configure the LLM brain?
Setup — keep keys out of code:
# %pip install -U langgraph langchain-google-genai langchain-openai langchain-community python-dotenv duckduckgo-search
import os, getpass
if "GOOGLE_API_KEY" not in os.environ:
os.environ["GOOGLE_API_KEY"] = getpass.getpass("Enter your Google API Key: ")from langchain_google_genai import ChatGoogleGenerativeAI
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0)
print(llm.invoke("Say hi in one sentence.").content[:120])Rendered output: a one-sentence greeting — proves the key and model string before graphite.
What is AgentState and why operator.add?
State is the agent's memory; operator.add appends messages instead of overwriting.
from typing import TypedDict, Annotated, List
from langchain_core.messages import BaseMessage
import operator
class AgentState(TypedDict):
messages: Annotated[List[BaseMessage], operator.add]
print("State structure defined.")If you omit operator.add, each node would replace the full history with its single output — conversation vanishes after one turn.
How do you build and compile the graph?
One node, linear edge — the smallest useful topology.
from langgraph.graph import StateGraph, END
def call_model(state: AgentState):
messages = state['messages']
response = llm.invoke(messages)
return {"messages": [response]}
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.set_entry_point("agent")
workflow.add_edge("agent", END)
app = workflow.compile()
print("Graph compiled successfully.")Run it:
from langchain_core.messages import HumanMessage
result = app.invoke({"messages": [HumanMessage(content="What is LangGraph?")]})
print(result['messages'][-1].content[:400])Rendered output: a paragraph defining LangGraph as a stateful orchestration layer over LangChain, citing nodes/edges.
How do you add tools and conditional routing?
Bind a search tool and route when the LLM emits tool_calls.
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import tool
search = DuckDuckGoSearchRun()
tools = [search]
llm_with_tools = llm.bind_tools(tools)
def call_model_with_tools(state: AgentState):
resp = llm_with_tools.invoke(state['messages'])
return {"messages": [resp]}
from langgraph.prebuilt import ToolNode
tool_node = ToolNode(tools)
def should_continue(state: AgentState):
last = state['messages'][-1]
if getattr(last, 'tool_calls', None):
return "tools"
return END
workflow2 = StateGraph(AgentState)
workflow2.add_node("agent", call_model_with_tools)
workflow2.add_node("tools", tool_node)
workflow2.set_entry_point("agent")
workflow2.add_conditional_edges("agent", should_continue, {"tools":"tools", END:END})
workflow2.add_edge("tools","agent")
app2 = workflow2.compile()
print("Tool graph compiled")| Feature / Criteria |
|---|
Gotcha: Passing Secrets Into the State
Putting GOOGLE_API_KEY inside AgentState leaks it into every trace and checkpoint. Read the key once at import time from os.environ or getpass, never include it as a state field.
What next after the graph runs?
Persist conversations to SQLite/Postgres per database guide, add a second tool (fetch via requests), and deploy behind FastAPI. Practise the tool contract on Topfolio Practice.
Download the Notebook and Practise
This article is a walkthrough of a runnable Jupyter notebook. Download the original .ipynb and run it locally or on Colab — every code block above appears in order.
Download the Ultimate Ai Agents Langgraph Course Notebook
Get the complete .ipynb with outputs — runs on any Python 3.10+ environment with pandas, numpy, and the libraries listed in setup.
Download .ipynbContinue your track: Data Analyst Roadmap · Python and Pandas Guide · SQL NULL Handbook · SQL JOIN Fan-Out · Topfolio Practice · Data Analyst vs Engineer
Dataset generators where applicable are in courses/workbooks/generators/ — see citations atop for the exact *.py source for this notebook.
Frequently Asked Questions
What is LangGraph and why not just call the LLM directly?
LangGraph models an agent as a StateGraph: state is shared memory, nodes are functions (LLM call, tool), edges are routing. It gives you loops, branching, and tool observability that a single llm.invoke cannot.
What is AgentState in LangGraph?
A TypedDict with messages: Annotated[List[BaseMessage], operator.add] so each node appends rather than overwrites history. Every node receives and returns a state patch.
How do tools connect to the graph?
Bind tools via llm.bind_tools(tools), add a tool node, and a conditional edge that routes to tools when the LLM emits tool_calls, otherwise to END.
Can you swap Gemini for OpenAI?
Yes — replace ChatGoogleGenerativeAI with ChatOpenAI; the graph (StateGraph, nodes, edges, compile) stays identical. Use getpass for either API key.
Frequently Asked Questions
What is LangGraph and why not just call the LLM directly?
LangGraph models an agent as a StateGraph: state is shared memory, nodes are functions (LLM call, tool), edges are routing. It gives you loops, branching, and tool observability that a single llm.invoke cannot.
What is AgentState in LangGraph?
A TypedDict with messages: Annotated[List[BaseMessage], operator.add] so each node appends rather than overwrites history. Every node receives and returns a state patch.
How do tools connect to the graph?
Bind tools via llm.bind_tools(tools), add a tool node, and a conditional edge that routes to tools when the LLM emits tool_calls, otherwise to END.
Can you swap Gemini for OpenAI?
Yes — replace ChatGoogleGenerativeAI with ChatOpenAI; the graph (StateGraph, nodes, edges, compile) stays identical. Use getpass for either API key.

Written by
Founder at Topfolio with 6+ years in data & analytics across JPMC, Ultrahuman, and high-growth startups. Sat on hiring panels, reviewed 500+ resumes, and writes practical SQL & data guides.
Related Articles
Python for Data Analysis: The Complete Workflow Playbook (2026)
Master python data analysis with this complete playbook: pandas wrangling, exploratory data analysis, statistical cohorts, and production data pipelines.
Python Tutorial: The Complete Guide for Data Analysts (2026)
Master Python programming with this comprehensive python tutorial for data analysts: variables, data structures, control flow, functions, NumPy, Pandas, and real-world projects.
Python Pandas for Data Analysis: Getting Started Guide (2026)
Learn Python Pandas for data analysis from scratch. DataFrames, filtering, groupby, merging, data cleaning, and 5 one-liners every data analyst should know.