Agentic Architecture Pattern: React

Written by

in


    """
Pattern 77: ReAct (Reasoning + Acting) Pattern
Interleaves reasoning traces with task-specific actions.
"""

from typing import Dict, Any, List, Optional, Callable
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from enum import Enum


class StepType(str, Enum):
    THOUGHT = "thought"
    ACTION = "action"
    OBSERVATION = "observation"


class ReActStep(BaseModel):
    """Represents a step in the ReAct cycle."""
    type: StepType
    content: str
    step_number: int


class ReActPattern:
    """
    ReAct Pattern - Reasoning and Acting in an interleaved manner.
    The agent alternates between thinking about what to do and taking actions.
    """
    
    def __init__(self, model_name: str = "gpt-4"):
        self.llm = ChatOpenAI(model=model_name, temperature=0.7)
        self.available_actions = self._define_actions()
        self.trace: List[ReActStep] = []
    
    def _define_actions(self) -> Dict[str, Callable]:
        """Define available actions the agent can take."""
        return {
            "search": lambda query: f"Search results for: {query}",
            "calculate": lambda expr: f"Calculation result: {eval(expr)}",
            "lookup": lambda term: f"Definition of {term}: [simulated lookup]",
            "finish": lambda answer: f"Final Answer: {answer}"
        }
    
    def think(self, question: str, context: str = "") -> str:
        """Reasoning step - agent thinks about next action."""
        prompt = ChatPromptTemplate.from_messages([
            ("system", """You are using the ReAct (Reasoning + Acting) pattern.
            
            Think step-by-step about how to answer this question.
            You can use these actions: {actions}
            
            Context so far:
            {context}
            
            Provide your thought process about what to do next."""),
            ("human", "Question: {question}\n\nThought:")
        ])
        
        chain = prompt | self.llm
        response = chain.invoke({
            "question": question,
            "actions": ", ".join(self.available_actions.keys()),
            "context": context or "No previous steps"
        })
        
        return response.content
    
    def act(self, thought: str) -> tuple[str, str]:
        """Action step - agent takes an action based on reasoning."""
        prompt = ChatPromptTemplate.from_messages([
            ("system", """Based on this thought, decide which action to take.
            
            Available actions: {actions}
            
            Thought: {thought}
            
            Respond with: Action: [action_name]([argument])
            For example: Action: search(quantum computing)"""),
            ("human", "What action should be taken?")
        ])
        
        chain = prompt | self.llm
        response = chain.invoke({
            "thought": thought,
            "actions": ", ".join(self.available_actions.keys())
        })
        
        # Parse action
        action_str = response.content
        action_name = "search"
        action_arg = thought[:30]
        
        if "Action:" in action_str:
            try:
                action_part = action_str.split("Action:")[1].strip()
                action_name = action_part.split("(")[0].strip()
                action_arg = action_part.split("(")[1].split(")")[0].strip()
            except:
                pass
        
        return action_name, action_arg
    
    def observe(self, action_name: str, action_arg: str) -> str:
        """Observation step - observe the result of the action."""
        if action_name in self.available_actions:
            try:
                result = self.available_actions[action_name](action_arg)
                return result
            except Exception as e:
                return f"Error executing {action_name}: {e}"
        return f"Unknown action: {action_name}"
    
    def run(self, question: str, max_steps: int = 5) -> Dict[str, Any]:
        """
        Execute the ReAct pattern: Think -> Act -> Observe -> Repeat.
        """
        self.trace = []
        context = ""
        final_answer = None
        
        for step_num in range(max_steps):
            # Step 1: Think (Reasoning)
            thought = self.think(question, context)
            self.trace.append(ReActStep(
                type=StepType.THOUGHT,
                content=thought,
                step_number=step_num + 1
            ))
            
            # Step 2: Act
            action_name, action_arg = self.act(thought)
            action_str = f"{action_name}({action_arg})"
            self.trace.append(ReActStep(
                type=StepType.ACTION,
                content=action_str,
                step_number=step_num + 1
            ))
            
            # Check if we're done
            if action_name == "finish":
                final_answer = action_arg
                break
            
            # Step 3: Observe
            observation = self.observe(action_name, action_arg)
            self.trace.append(ReActStep(
                type=StepType.OBSERVATION,
                content=observation,
                step_number=step_num + 1
            ))
            
            # Update context
            context += f"\nStep {step_num + 1}:\n  Thought: {thought}\n  Action: {action_str}\n  Observation: {observation}"
        
        return {
            "question": question,
            "trace": self.trace,
            "final_answer": final_answer,
            "total_steps": len(self.trace) // 3
        }
    
    def format_trace(self) -> str:
        """Format the trace for display."""
        output = []
        for step in self.trace:
            prefix = {
                StepType.THOUGHT: "💭 Thought",
                StepType.ACTION: "⚡ Action",
                StepType.OBSERVATION: "👁 Observation"
            }[step.type]
            output.append(f"{prefix} {step.step_number}: {step.content}")
        return "\n".join(output)


# Example usage
if __name__ == "__main__":
    agent = ReActPattern()
    
    question = "What is 15% of 240?"
    
    print("=== ReAct Pattern: Reasoning + Acting ===\n")
    print(f"Question: {question}\n")
    
    result = agent.run(question, max_steps=3)
    
    print("=== Execution Trace ===\n")
    print(agent.format_trace())
    
    if result["final_answer"]:
        print(f"\n=== Final Answer ===")
        print(result["final_answer"])

    

Agentic Architecture Pattern: React

Pattern 77: 77 React – Line-by-Line Explanation

Overview

Pattern 77: ReAct (Reasoning + Acting) Pattern Interleaves reasoning traces with task-specific actions.


Code Structure Analysis

File Information

  • Pattern Number: 77
  • Pattern Name: 77 React
  • File Path: patterns\execution_control\77_react.py
  • Total Lines: 194

Detailed Code Explanation

Module Docstring

"""
Pattern 77: ReAct (Reasoning + Acting) Pattern
Interleaves reasoning traces with task-specific actions.
"""

Explanation: This module docstring describes the purpose and key features of the 77 React pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
from enum import Enum  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Callable: Type hints for better code documentation – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration – from enum import Enum: Enumeration support for fixed sets of values


Enumerations

StepType (Line 13)

class StepType(Enum):
    THOUGHT
    ACTION
    OBSERVATION

Purpose: No enum docstring.

Values: – THOUGHT: Represents thought – ACTION: Represents action – OBSERVATION: Represents observation


Classes

StepType (Line 13)

Description: No class docstring.

ReActStep (Line 19)

Description: Represents a step in the ReAct cycle.

ReActPattern (Line 26)

Description: ReAct Pattern – Reasoning and Acting in an interleaved manner. The agent alternates between thinking about what to do and taking actions.

Methods:

__init__ (Line 32)
def __init__(self, model_name):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference – model_name: LLM model to use (e.g., “gpt-4”)

_define_actions (Line 37)
def _define_actions(self):
    """Define available actions the agent can take."""

Purpose: Define available actions the agent can take.

Parameters: – self: Instance reference

think (Line 46)
def think(self, question, context):
    """Reasoning step - agent thinks about next action."""

Purpose: Reasoning step – agent thinks about next action.

Parameters: – self: Instance reference – question: Parameter for question – context: Parameter for context

act (Line 70)
def act(self, thought):
    """Action step - agent takes an action based on reasoning."""

Purpose: Action step – agent takes an action based on reasoning.

Parameters: – self: Instance reference – thought: Parameter for thought

observe (Line 105)
def observe(self, action_name, action_arg):
    """Observation step - observe the result of the action."""

Purpose: Observation step – observe the result of the action.

Parameters: – self: Instance reference – action_name: Parameter for action_name – action_arg: Parameter for action_arg

run (Line 115)
def run(self, question, max_steps):
    """Execute the ReAct pattern: Think -> Act -> Observe -> Repeat."""

Purpose: Execute the ReAct pattern: Think -> Act -> Observe -> Repeat.

Parameters: – self: Instance reference – question: Parameter for question – max_steps: Parameter for max_steps

format_trace (Line 164)
def format_trace(self):
    """Format the trace for display."""

Purpose: Format the trace for display.

Parameters: – self: Instance reference


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = StepType(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *