Agentic Architecture Pattern: Retry

Written by

in


    """
Pattern 181: Retry Pattern

Automatically retry failed operations with configurable strategies (exponential backoff,
fixed delay, etc.). Essential for handling transient failures.
"""

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import time
import random
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


class RetryStrategy(Enum):
    """Retry strategies."""
    FIXED_DELAY = "fixed_delay"
    EXPONENTIAL_BACKOFF = "exponential_backoff"
    LINEAR_BACKOFF = "linear_backoff"
    RANDOM_JITTER = "random_jitter"


@dataclass
class RetryAttempt:
    """Track retry attempts."""
    attempt_number: int
    timestamp: datetime
    error: Optional[str] = None
    success: bool = False
    delay: float = 0.0


@dataclass
class RetryConfig:
    """Retry configuration."""
    max_attempts: int = 3
    initial_delay: float = 1.0
    max_delay: float = 60.0
    multiplier: float = 2.0
    strategy: RetryStrategy = RetryStrategy.EXPONENTIAL_BACKOFF


class RetryPattern:
    """
    Retry Pattern - Automatic retry with configurable strategies.
    """
    
    def __init__(self, config: Optional[RetryConfig] = None, model_name: str = "gpt-4"):
        self.config = config or RetryConfig()
        self.llm = ChatOpenAI(model=model_name, temperature=0.5)
        self.retry_history: List[RetryAttempt] = []
    
    def calculate_delay(self, attempt: int) -> float:
        """Calculate delay based on retry strategy."""
        if self.config.strategy == RetryStrategy.FIXED_DELAY:
            return self.config.initial_delay
        
        elif self.config.strategy == RetryStrategy.EXPONENTIAL_BACKOFF:
            delay = self.config.initial_delay * (self.config.multiplier ** (attempt - 1))
            return min(delay, self.config.max_delay)
        
        elif self.config.strategy == RetryStrategy.LINEAR_BACKOFF:
            delay = self.config.initial_delay * attempt
            return min(delay, self.config.max_delay)
        
        elif self.config.strategy == RetryStrategy.RANDOM_JITTER:
            base_delay = self.config.initial_delay * (self.config.multiplier ** (attempt - 1))
            jitter = random.uniform(0, base_delay * 0.1)
            return min(base_delay + jitter, self.config.max_delay)
        
        return self.config.initial_delay
    
    def execute_with_retry(self, operation: Callable, *args, **kwargs) -> Dict[str, Any]:
        """Execute operation with retry logic."""
        last_error = None
        
        for attempt in range(1, self.config.max_attempts + 1):
            try:
                # Execute operation
                result = operation(*args, **kwargs)
                
                # Record successful attempt
                self.retry_history.append(RetryAttempt(
                    attempt_number=attempt,
                    timestamp=datetime.now(),
                    success=True
                ))
                
                return {
                    "success": True,
                    "result": result,
                    "attempts": attempt,
                    "total_attempts": self.config.max_attempts
                }
            
            except Exception as e:
                last_error = str(e)
                
                # Record failed attempt
                self.retry_history.append(RetryAttempt(
                    attempt_number=attempt,
                    timestamp=datetime.now(),
                    error=last_error,
                    success=False
                ))
                
                # Don't sleep on last attempt
                if attempt  Dict[str, Any]:
        """Execute with retry - simulates operation with potential failures."""
        
        def simulated_operation():
            """Simulate an operation that might fail."""
            if random.random()  Dict[str, Any]:
        """Get retry statistics."""
        total_attempts = len(self.retry_history)
        successful = sum(1 for a in self.retry_history if a.success)
        failed = total_attempts - successful
        
        return {
            "total_attempts": total_attempts,
            "successful": successful,
            "failed": failed,
            "success_rate": f"{(successful/total_attempts*100):.1f}%" if total_attempts > 0 else "0%"
        }


# Example usage
if __name__ == "__main__":
    print("=== Retry Pattern Demo ===\n")
    
    # Test with exponential backoff
    print("1. Exponential Backoff Strategy")
    config1 = RetryConfig(
        max_attempts=4,
        initial_delay=0.5,
        multiplier=2.0,
        strategy=RetryStrategy.EXPONENTIAL_BACKOFF
    )
    pattern1 = RetryPattern(config1)
    
    result1 = pattern1.run("Process customer payment", simulate_failure_rate=0.6)
    print(f"   Operation: {result1['operation']}")
    print(f"   Success: {result1['result']['success']}")
    print(f"   Attempts: {result1['result']['attempts']}/{result1['result']['total_attempts']}")
    if result1['result']['success']:
        print(f"   Result: {result1['result']['result'][:100]}...")
    else:
        print(f"   Error: {result1['result']['error']}")
    
    # Test with fixed delay
    print("\n\n2. Fixed Delay Strategy")
    config2 = RetryConfig(
        max_attempts=3,
        initial_delay=1.0,
        strategy=RetryStrategy.FIXED_DELAY
    )
    pattern2 = RetryPattern(config2)
    
    result2 = pattern2.run("Fetch external API data", simulate_failure_rate=0.4)
    print(f"   Operation: {result2['operation']}")
    print(f"   Success: {result2['result']['success']}")
    print(f"   Attempts: {result2['result']['attempts']}/{result2['result']['total_attempts']}")
    
    # Show statistics
    print("\n\n=== Statistics ===")
    stats1 = pattern1.get_statistics()
    print(f"Pattern 1: {stats1['successful']}/{stats1['total_attempts']} successful ({stats1['success_rate']})")
    stats2 = pattern2.get_statistics()
    print(f"Pattern 2: {stats2['successful']}/{stats2['total_attempts']} successful ({stats2['success_rate']})")

    

Agentic Architecture Pattern: Retry

Pattern 181: 181 Retry – Line-by-Line Explanation

Overview

Pattern 181: Retry Pattern

Automatically retry failed operations with configurable strategies (exponential backoff, fixed delay, etc.). Essential for handling transient failures.


Code Structure Analysis

File Information

  • Pattern Number: 181
  • Pattern Name: 181 Retry
  • File Path: patterns\error_handling\181_retry.py
  • Total Lines: 211

Detailed Code Explanation

Module Docstring

"""
Pattern 181: Retry Pattern

Automatically retry failed operations with configurable strategies (exponential backoff,
fixed delay, etc.). Essential for handling transient failures.
"""

Explanation: This module docstring describes the purpose and key features of the 181 Retry pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import time
import random
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI  # 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 dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime: Date and time utilities – from enum import Enum: Enumeration support for fixed sets of values – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Enumerations

RetryStrategy (Line 18)

class RetryStrategy(Enum):
    FIXED_DELAY
    EXPONENTIAL_BACKOFF
    LINEAR_BACKOFF
    RANDOM_JITTER

Purpose: Retry strategies.

Values: – FIXED_DELAY: Represents fixed delay – EXPONENTIAL_BACKOFF: Represents exponential backoff – LINEAR_BACKOFF: Represents linear backoff – RANDOM_JITTER: Represents random jitter


Classes

RetryStrategy (Line 18)

Description: Retry strategies.

RetryAttempt (Line 27)

Description: Track retry attempts.

RetryConfig (Line 37)

Description: Retry configuration.

RetryPattern (Line 46)

Description: Retry Pattern – Automatic retry with configurable strategies.

Methods:

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

Purpose: No method docstring.

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

calculate_delay (Line 56)
def calculate_delay(self, attempt):
    """Calculate delay based on retry strategy."""

Purpose: Calculate delay based on retry strategy.

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

execute_with_retry (Line 76)
def execute_with_retry(self, operation):
    """Execute operation with retry logic."""

Purpose: Execute operation with retry logic.

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

run (Line 124)
def run(self, operation_description, simulate_failure_rate):
    """Execute with retry - simulates operation with potential failures."""

Purpose: Execute with retry – simulates operation with potential failures.

Parameters: – self: Instance reference – operation_description: Parameter for operation_description – simulate_failure_rate: Parameter for simulate_failure_rate

get_statistics (Line 154)
def get_statistics(self):
    """Get retry statistics."""

Purpose: Get retry statistics.

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 = RetryStrategy(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 *