> ## Documentation Index
> Fetch the complete documentation index at: https://genai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# ReAct Agent

> Build a ReAct agent utilizing custom tools and conditional routing

In this section, we will build a ReAct (Reasoning and Acting) agent that utilizes mathematical tools to solve multi-step calculations.

## Objectives

1. Implement the ReAct agent design loop.
2. Bind custom tools to a ChatOpenAI model.
3. Route queries dynamically using a `ToolNode` and conditional routing.

## Agent III: ReAct Agent

#### Goal

Build a ReAct agent that binds arithmetic tools (`add`, `subtract`, `multiply`) and dynamically decides whether to execute a tool or finish.

#### Sample Input

```python theme={null}
{"messages": [("user", "Add 40 + 12 and then multiply the result by 6.")]}
```

#### Sample Output

Outputs performing addition first, then multiplication: `312`.

#### Plan

1. Define the state schema using `add_messages` to append messages to history.
2. Create tools and bind them to the LLM.
3. Define the LLM call node, the `should_continue` conditional router, and the `ToolNode` tool runner.
4. Build the graph connecting START -> agent, agent -> conditional edge (tools or END), and tools -> agent.

### Code Implementation

#### 1. Define the State Schema

We define a state schema using `add_messages` annotation:

```python theme={null}
from typing import Annotated, Sequence, TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
```

#### 2. Define Custom Tools

We define three custom functions decorated with `@tool` and bind them to the model:

```python theme={null}
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

@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]
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)
```

#### 3. Define Nodes and Routing Logic

We write the model call node and the routing function:

```python theme={null}
from langchain_core.messages import SystemMessage

def model_call(state: AgentState) -> dict:
    system_prompt = SystemMessage(content="You are my AI assistant. Use tools when needed.")
    response = model.invoke([system_prompt] + list(state["messages"]))
    return {"messages": [response]}

def should_continue(state: AgentState) -> str: 
    messages = state["messages"]
    last_message = messages[-1]
    if not last_message.tool_calls: 
        return "end"
    else:
        return "continue"
```

#### 4. Build and Compile the Graph

```python theme={null}
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode

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()
```

#### 5. Invoke the Agent

We invoke the compiled app:

```python theme={null}
inputs = {"messages": [("user", "Add 40 + 12 and then multiply the result by 6.")]}
for step in app.stream(inputs, stream_mode="values"):
    step["messages"][-1].pretty_print()
```

## Exercise: Division and Power ReAct Agent ➗

#### Goal

Build a ReAct Agent that adds division (`divide`) and exponentiation (`power`) tools, and successfully handles mathematical queries requiring these functions.

#### Sample Input

```python theme={null}
{"messages": [("user", "Divide 100 by 4, and then raise the result to the power of 3.")]}
```

#### Sample Output

Outputs performing division, then exponentiation, returning the final answer: `15625.0`.

#### Plan

1. Create two tools `@tool` for `divide(a: float, b: float)` and `power(base: float, exponent: float)`.
2. Bind the new tools list to the model.
3. Set up the ReAct `StateGraph` with the agent node, the conditional router `should_continue`, and the tool node.
4. Verify step-by-step tool execution.

<Accordion title="Solution">
  ```python theme={null}
  from typing import Annotated, Sequence, TypedDict
  from langchain_core.messages import BaseMessage, SystemMessage
  from langchain_openai import ChatOpenAI
  from langchain_core.tools import tool
  from langgraph.graph.message import add_messages
  from langgraph.graph import StateGraph, START, END
  from langgraph.prebuilt import ToolNode

  # 1. State
  class AgentState(TypedDict):
      messages: Annotated[Sequence[BaseMessage], add_messages]

  # 2. Define tools
  @tool
  def divide(a: float, b: float) -> float:
      """Divide function that divides a by b."""
      return a / b

  @tool
  def power(base: float, exponent: float) -> float:
      """Power function that raises base to the power of exponent."""
      return base ** exponent

  tools = [divide, power]
  model = ChatOpenAI(model="gpt-4o").bind_tools(tools)

  # 3. Nodes and Router
  def model_call(state: AgentState) -> dict:
      sys = SystemMessage(content="You are an AI assistant. Use tools when needed.")
      response = model.invoke([sys] + list(state["messages"]))
      return {"messages": [response]}

  def should_continue(state: AgentState) -> str:
      last_message = state["messages"][-1]
      if not last_message.tool_calls:
          return "end"
      return "continue"

  # 4. Build Graph
  graph = StateGraph(AgentState)
  graph.add_node("our_agent", model_call)
  graph.add_node("tools", ToolNode(tools))
  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()

  # 5. Test
  for step in app.stream({"messages": [("user", "Divide 100 by 4, and raise the result to the power of 3.")]}, stream_mode="values"):
      step["messages"][-1].pretty_print()
  ```
</Accordion>

## Practice & Exercises

To reinforce what you've learned in this section, practice with the interactive notebook:

<CardGroup cols={1}>
  <Card title="Practice & Exercises" icon="laptop-code">
    Practice binding custom tools, routing conditional edges based on tool\_calls, and handling multi-step reasoning.

    [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/GenAI%20With%20Python/public/notebooks/langgraph/ReAct_Agent.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/master/public/notebooks/langgraph/ReAct_Agent.ipynb) | <a href="/public/notebooks/langgraph/ReAct_Agent.ipynb" download>📥 Download</a>
  </Card>
</CardGroup>
