"""
Pattern 162: Horizontal Scaling Pattern
Purpose: Scale by adding more agent instances to distribute workload.
Load balancing across multiple identical agents.
"""
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import uuid
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
class AgentStatus(Enum):
"""Status of agent instance."""
IDLE = "idle"
BUSY = "busy"
FAILED = "failed"
@dataclass
class AgentInstance:
"""Individual agent instance in the pool."""
id: str
name: str
status: AgentStatus = AgentStatus.IDLE
tasks_completed: int = 0
current_load: float = 0.0
created_at: datetime = field(default_factory=datetime.now)
@dataclass
class Task:
"""Task to be processed."""
id: str
description: str
priority: int = 1
assigned_to: Optional[str] = None
status: str = "pending"
result: Optional[str] = None
created_at: datetime = field(default_factory=datetime.now)
class HorizontalScalingAgent:
"""Single agent instance."""
def __init__(self, instance: AgentInstance, model_name: str = "gpt-4"):
self.instance = instance
self.llm = ChatOpenAI(model=model_name, temperature=0.7)
def process(self, task: Task) -> str:
"""Process a task."""
self.instance.status = AgentStatus.BUSY
prompt = ChatPromptTemplate.from_messages([
("system", f"You are {self.instance.name} (ID: {self.instance.id})"),
("human", "{task}")
])
chain = prompt | self.llm
response = chain.invoke({"task": task.description})
self.instance.tasks_completed += 1
self.instance.status = AgentStatus.IDLE
return str(response.content)
class LoadBalancer:
"""Load balancing strategies."""
@staticmethod
def round_robin(instances: List[AgentInstance], last_index: int) -> int:
"""Round robin: cycle through instances."""
available = [i for i, inst in enumerate(instances)
if inst.status == AgentStatus.IDLE]
if not available:
return -1
# Find next available after last_index
for i in available:
if i > last_index:
return i
return available[0]
@staticmethod
def least_loaded(instances: List[AgentInstance]) -> int:
"""Least loaded: choose instance with lowest load."""
available = [(i, inst) for i, inst in enumerate(instances)
if inst.status == AgentStatus.IDLE]
if not available:
return -1
return min(available, key=lambda x: x[1].current_load)[0]
@staticmethod
def least_tasks(instances: List[AgentInstance]) -> int:
"""Least tasks: choose instance with fewest completed tasks."""
available = [(i, inst) for i, inst in enumerate(instances)
if inst.status == AgentStatus.IDLE]
if not available:
return -1
return min(available, key=lambda x: x[1].tasks_completed)[0]
class HorizontalScalingPattern:
"""
Horizontal Scaling Pattern - Scale by adding more agent instances.
Features:
- Dynamic instance creation/removal
- Load balancing strategies
- Task queue management
- Auto-scaling based on queue size
"""
def __init__(self, min_instances: int = 1, max_instances: int = 10,
model_name: str = "gpt-4"):
self.min_instances = min_instances
self.max_instances = max_instances
self.model_name = model_name
self.instances: List[AgentInstance] = []
self.agents: Dict[str, HorizontalScalingAgent] = {}
self.task_queue: List[Task] = []
self.completed_tasks: List[Task] = []
self.last_robin_index = -1
# Initialize minimum instances
for _ in range(min_instances):
self.add_instance()
def add_instance(self) -> Optional[str]:
"""Add a new agent instance."""
if len(self.instances) >= self.max_instances:
return None
instance_id = str(uuid.uuid4())[:8]
instance = AgentInstance(
id=instance_id,
name=f"Agent-{len(self.instances) + 1}"
)
self.instances.append(instance)
self.agents[instance_id] = HorizontalScalingAgent(instance, self.model_name)
return instance_id
def remove_instance(self) -> bool:
"""Remove an idle instance."""
if len(self.instances) idle_count * 2 and len(self.instances) 2 and queue_size == 0 and len(self.instances) > self.min_instances:
if self.remove_instance():
print(f"📉 Scaled down: Now {len(self.instances)} instances")
def process_queue(self, strategy: str = "round_robin"):
"""Process tasks from queue using load balancing strategy."""
while self.task_queue:
# Select instance based on strategy
if strategy == "round_robin":
idx = LoadBalancer.round_robin(self.instances, self.last_robin_index)
self.last_robin_index = idx
elif strategy == "least_loaded":
idx = LoadBalancer.least_loaded(self.instances)
elif strategy == "least_tasks":
idx = LoadBalancer.least_tasks(self.instances)
else:
idx = 0
if idx == -1:
break # No available instances
# Assign and process task
task = self.task_queue.pop(0)
instance = self.instances[idx]
agent = self.agents[instance.id]
task.assigned_to = instance.id
task.status = "processing"
result = agent.process(task)
task.result = result
task.status = "completed"
self.completed_tasks.append(task)
def get_statistics(self) -> Dict[str, Any]:
"""Get scaling statistics."""
return {
"total_instances": len(self.instances),
"idle_instances": sum(1 for i in self.instances if i.status == AgentStatus.IDLE),
"busy_instances": sum(1 for i in self.instances if i.status == AgentStatus.BUSY),
"queued_tasks": len(self.task_queue),
"completed_tasks": len(self.completed_tasks),
"total_tasks_processed": sum(i.tasks_completed for i in self.instances)
}
# Example usage
if __name__ == "__main__":
print("=== Horizontal Scaling Pattern Demo ===\n")
# Create pattern with 1-5 instances
pattern = HorizontalScalingPattern(min_instances=1, max_instances=5)
print(f"Initial instances: {len(pattern.instances)}\n")
# Add multiple tasks
tasks = [
Task(id=f"task-{i}", description=f"Analyze document {i}", priority=1)
for i in range(1, 11)
]
print("Adding 10 tasks to queue...")
for task in tasks:
pattern.add_task(task)
print(f"Queue size: {len(pattern.task_queue)}")
print(f"Instances after auto-scale: {len(pattern.instances)}\n")
# Process with round-robin
print("Processing with round-robin strategy...")
pattern.process_queue(strategy="round_robin")
# Statistics
print("\n=== Statistics ===")
stats = pattern.get_statistics()
for key, value in stats.items():
print(f"{key}: {value}")
# Show instance details
print("\n=== Instance Details ===")
for instance in pattern.instances:
print(f"{instance.name} (ID: {instance.id})")
print(f" Status: {instance.status.value}")
print(f" Tasks completed: {instance.tasks_completed}")
# Show completed tasks
print(f"\n=== Sample Completed Tasks ===")
for task in pattern.completed_tasks[:3]:
print(f"Task {task.id}: Assigned to {task.assigned_to}")
print(f" Result preview: {task.result[:100]}..." if task.result else " No result")
Agentic Architecture Pattern: Horizontal Scaling
Pattern 162: 162 Horizontal Scaling – Line-by-Line Explanation
Overview
Pattern 162: Horizontal Scaling Pattern
Purpose: Scale by adding more agent instances to distribute workload. Load balancing across multiple identical agents.
Code Structure Analysis
File Information
- Pattern Number: 162
- Pattern Name: 162 Horizontal Scaling
- File Path:
patterns\scaling\162_horizontal_scaling.py - Total Lines: 274
Detailed Code Explanation
Module Docstring
"""
Pattern 162: Horizontal Scaling Pattern
Purpose: Scale by adding more agent instances to distribute workload.
Load balancing across multiple identical agents.
"""
Explanation: This module docstring describes the purpose and key features of the 162 Horizontal Scaling pattern.
Imports Section
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import uuid
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: 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
AgentStatus (Line 17)
class AgentStatus(Enum):
IDLE
BUSY
FAILED
Purpose: Status of agent instance.
Values:
– IDLE: Not currently active
– BUSY: Represents busy
– FAILED: Execution failed
Classes
AgentStatus (Line 17)
Description: Status of agent instance.
AgentInstance (Line 25)
Description: Individual agent instance in the pool.
Task (Line 36)
Description: Task to be processed.
HorizontalScalingAgent (Line 47)
Description: Single agent instance.
Methods:
__init__ (Line 50)
def __init__(self, instance, model_name):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
– instance: Parameter for instance
– model_name: LLM model to use (e.g., “gpt-4”)
process (Line 54)
def process(self, task):
"""Process a task."""
Purpose: Process a task.
Parameters:
– self: Instance reference
– task: Parameter for task
LoadBalancer (Line 72)
Description: Load balancing strategies.
Methods:
round_robin (Line 76)
def round_robin(instances, last_index):
"""Round robin: cycle through instances."""
Purpose: Round robin: cycle through instances.
Parameters:
– instances: Parameter for instances
– last_index: Parameter for last_index
least_loaded (Line 90)
def least_loaded(instances):
"""Least loaded: choose instance with lowest load."""
Purpose: Least loaded: choose instance with lowest load.
Parameters:
– instances: Parameter for instances
least_tasks (Line 100)
def least_tasks(instances):
"""Least tasks: choose instance with fewest completed tasks."""
Purpose: Least tasks: choose instance with fewest completed tasks.
Parameters:
– instances: Parameter for instances
HorizontalScalingPattern (Line 110)
Description: Horizontal Scaling Pattern – Scale by adding more agent instances.
Features: – Dynamic instance creation/removal – Load balancing strategies – Task queue management – Auto-scaling based on queue size
Methods:
__init__ (Line 121)
def __init__(self, min_instances, max_instances, model_name):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
– min_instances: Parameter for min_instances
– max_instances: Parameter for max_instances
– model_name: LLM model to use (e.g., “gpt-4”)
add_instance (Line 136)
def add_instance(self):
"""Add a new agent instance."""
Purpose: Add a new agent instance.
Parameters:
– self: Instance reference
remove_instance (Line 151)
def remove_instance(self):
"""Remove an idle instance."""
Purpose: Remove an idle instance.
Parameters:
– self: Instance reference
add_task (Line 166)
def add_task(self, task):
"""Add task to queue."""
Purpose: Add task to queue.
Parameters:
– self: Instance reference
– task: Parameter for task
auto_scale (Line 171)
def auto_scale(self):
"""Auto-scale based on queue size."""
Purpose: Auto-scale based on queue size.
Parameters:
– self: Instance reference
process_queue (Line 187)
def process_queue(self, strategy):
"""Process tasks from queue using load balancing strategy."""
Purpose: Process tasks from queue using load balancing strategy.
Parameters:
– self: Instance reference
– strategy: Parameter for strategy
get_statistics (Line 218)
def get_statistics(self):
"""Get scaling statistics."""
Purpose: Get scaling 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 = AgentStatus(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
Leave a Reply