🚀
🕸️ LangGraph
9. React Agent
AI

ReAct Agent 📱

Think

Mar 202510 min read

ReAct Agent 📱

Think

  • LLM first thinks about the user prompt/problem

Action

  • LLM decides if it can answer by itself or if it should use a tool

Action Input

  • LLM provides the input argument for the tool (Here, langChain executes the tool and returns the output to the LLM)

Observe

  • LLM observe the result of the tool

Final Answer

  • "This is your final answer"
Objectives ✅
  1. Learn how to create Tools in LangGraph.
  2. How to create a ReAct Graph.
  3. Work with different types of Messages such as ToolMessages.
  4. Test out robustness of our graph.
Annotated
  • provides additional context without affecting the type itself.
  • for example, email addresses, phone numbers, or other strings that follow a specific format.
email = Annotated[str, "a valid email address"]
 
print(email.__metadata__)  # Output: 'a valid email address'
Sequence
  • automatically handle the state updates for sequence such as by adding new messages to a chat history
Reducer function (add_messages)
  • Rule that controls how updates from nodes are combined with existing state.

  • Tells us how to merge new data into the current state

  • Without a reducer, the new data would replace the existing state entirely.

Without a reducer:

state = {'messages': ['Hi!']}
update = {'messages': ['Nice to meet you!']}
new_state = {'messages': ['Nice to meet you!']}

With a reducer:

state = {'messages': ['Hi!']}
update = {'messages': ['Nice to meet you!']}
new_state = {'messages': ['Hi!', 'Nice to meet you!']}

Agent

"./images/9.react-agent.png"

from typing import Annotated, Sequence, TypedDict
from dotenv import load_dotenv
from langchain_core.messages import (
    BaseMessage,
    ToolMessage,
    SystemMessage,
    AIMessage,
    HumanMessage,
)
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
from langgraph.graph.message import add_messages
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
@tool
def add(a: int, b: int):
    """This is an addition function that adds 2 numbers together"""
    return a + b
 
 
@tool
def subtract(a: int, b: int):
    """Subtraction function"""
    return a - b
 
 
@tool
def multiply(a: int, b: int):
    """Multiplication function"""
    return a * b
 
tools = [add, subtract, multiply]
 
ollama_model = "llama3.1:8b"
 
# bind tools once here
model = ChatOllama(temperature=0.8, model=ollama_model).bind_tools(tools)
def model_call(state: AgentState) -> AgentState:
    system_prompt = SystemMessage(content="You are my AI assistant.")
    response: AIMessage = model.invoke([system_prompt] + state["messages"])
    return {"messages": state["messages"] + [response]}
 
 
def should_continue(state: AgentState):
    messages = state["messages"]
    last_message = messages[-1]
    if hasattr(last_message, "tool_calls") and last_message.tool_calls:
        return "continue"
    else:
        return "end"
graph = StateGraph(AgentState)
graph.add_node("our_agent", model_call)
 
tool_node = ToolNode(tools=tools)
graph.add_node("tools", tool_node)
 
graph.set_entry_point("our_agent")
 
graph.add_conditional_edges(
    "our_agent",
    should_continue,
    {"continue": "tools", "end": END},
)
 
graph.add_edge("tools", "our_agent")
 
app = graph.compile()
def print_stream(stream):
    for s in stream:
        message = s["messages"][-1]
        if isinstance(message, tuple):
            print(message)
        else:
            message.pretty_print()
 
 
inputs = {
    "messages": [
        HumanMessage(
            content="Add 40 + 12 and then multiply the result by 6. Also tell me a joke please."
        )
    ]
}
 
print_stream(app.stream(inputs, stream_mode="values"))
OUTPUT:
 
================================ Human Message =================================
 
Add 40 + 12 and then multiply the result by 6. Also tell me a joke please.
================================== Ai Message ==================================
 
}
{"type":"function","function":{"name":"jokes","description":"","parameters":{"type":"object","required":[],"properties":{}}}}
Tool Calls:
  multiply (6d4f6af9-dfe8-47d1-84dd-147f013d492e)
 Call ID: 6d4f6af9-dfe8-47d1-84dd-147f013d492e
  Args:
    a: 52
    b: 6
  add (b741d73f-a6f9-4720-a640-6d0b396ec21c)
 Call ID: b741d73f-a6f9-4720-a640-6d0b396ec21c
  Args:
    properties: {}
    required: []
    type: object
================================= Tool Message =================================
Name: add
 
Error: 2 validation errors for add
a
  Field required [type=missing, input_value={'properties': {}, 'requi...': [], 'type': 'object'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing
b
  Field required [type=missing, input_value={'properties': {}, 'requi...': [], 'type': 'object'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing
 Please fix your mistakes.
================================== Ai Message ==================================
 
Firstly, let's calculate the result of 40 + 12:
40 + 12 = 52
 
Now, we multiply the result by 6:
52 * 6 = 312
 
And here's a joke for you:
 
Why don't eggs tell jokes?
 
Because they'd crack each other up!

© 2026 Driptanil Datta. All rights reserved.

Software Developer & Engineer

Disclaimer:The content provided on this blog is for educational and informational purposes only. While I strive for accuracy, all information is provided "as is" without any warranties of completeness, reliability, or accuracy. Any action you take upon the information found on this website is strictly at your own risk.

Copyright & IP:Certain technical content, interview questions, and datasets are curated from external educational sources to provide a centralized learning resource. Respect for original authorship is maintained; no copyright infringement is intended. All trademarks, logos, and brand names are the property of their respective owners.

System Operational

Built with Love ❤️ | Last updated: Mar 16 2026