Tutorial

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.

Anuj SainiAug 23, 20264 min read

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:

python
# %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: ")
python
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.

python
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.

python
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:

python
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.

python
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 .ipynb

Continue 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.

Anuj Saini

Written by

Anuj SainiFounder & Lead Instructor

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.