Langgraph Agentic Chatbot


Agentic Chatbot with LangGraph

This project demonstrates how to build a modular, extensible chatbot using LangGraph, LangChain, and Ollama. The notebook walks through building a basic chatbot, adding tools, memory, streaming, and human-in-the-loop capabilities.


Table of Contents


Basic Chatbot

Define the agent state and a simple chatbot node:

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_ollama import ChatOllama

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]

model = ChatOllama(model="granite3.3:2b", temperature=0)

def chatbot(state: AgentState) -> AgentState:
    response = model.invoke(state["messages"])
    return {"messages": [response]}

graph = StateGraph(AgentState)
graph.add_node("chatbot", chatbot)
graph.set_entry_point("chatbot")
graph.add_edge("chatbot", END)
app = graph.compile()

Visualizing the Graph

You can visualize the graph structure:

from IPython.display import Image, display

try:
    display(Image(app.get_graph().draw_mermaid_png()))
except Exception:
    pass

Chatbot with Tools

Add tools such as search and math functions:

from dotenv import load_dotenv
load_dotenv()

from langchain_tavily import TavilySearch

def multiply(a: int, b: int) -> int:
    """Multiply a and b"""
    return a * b

tools = [TavilySearch(max_results=2), multiply]
model_with_tools = model.bind_tools(tools)

def tool_calling_llm(state: AgentState) -> AgentState:
    return {"messages": model_with_tools.invoke(state["messages"])}

from langgraph.prebuilt import ToolNode, tools_condition

graph = StateGraph(AgentState)
graph.add_node("tool_calling_llm", tool_calling_llm)
graph.add_node("tools", ToolNode(tools))
graph.add_edge(START, "tool_calling_llm")
graph.add_conditional_edges("tool_calling_llm", tools_condition)
graph.add_edge("tools", "tool_calling_llm")
app = graph.compile()

Adding Memory

Enable memory to persist conversation context:

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()
app = graph.compile(checkpointer=memory)

Streaming

Stream responses for real-time feedback:

config = {"configurable": {"thread_id": "2"}}

for chunk in app.stream({"messages": "Hello, my name is Rikki and I like software"}, config, stream_mode="updates"):
    print(chunk)

Human in the Loop

Allow the agent to request human assistance:

from langgraph.types import Command, interrupt
from langchain_core.tools import tool

@tool
def human_assistance(query: str) -> str:
    """Request assistance from a Human"""
    human_response = interrupt({"query": query})
    return human_response["data"]

search = TavilySearch(max_results=2)
tools = [search, human_assistance]
model_with_tools = model.bind_tools(tools)

graph = StateGraph(AgentState)
graph.add_node("chatbot", tool_calling_llm)
graph.add_node("tools", ToolNode(tools))
graph.add_conditional_edges("chatbot", tools_condition)
graph.add_edge("tools", "chatbot")
graph.add_edge(START, "chatbot")
app = graph.compile(checkpointer=memory)

Requirements

Install dependencies:

pip install langgraph langchain langchain-ollama langchain-tavily python-dotenv

Usage

Open and run the notebook chatbot/chatbot.ipynb for interactive examples and to explore each feature step by step.

Tip:

  • Configure your .env file with the necessary API keys for Ollama and Tavily.
  • Extend the