Agentic Architecture Pattern: Heuristic Based

Written by

in


    """
Pattern 49: Heuristic-Based Pattern

Makes decisions using domain-specific rules of thumb and shortcuts.
Features heuristic evaluation, greedy search, and practical approximations.
"""

from typing import Dict, Any, List, Optional, Callable, Tuple
from dataclasses import dataclass, field
from heapq import heappush, heappop
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


@dataclass
class SearchNode:
    """Node in search space."""
    state: Any
    cost: float
    heuristic: float
    parent: Optional['SearchNode'] = None
    
    def __lt__(self, other):
        """Compare by f-score for heap."""
        return (self.cost + self.heuristic)  List[Tuple[Any, float]]:
        """Evaluate options using heuristic."""
        if heuristic_name not in self.heuristics:
            print(f"   Warning: Heuristic '{heuristic_name}' not found")
            return [(opt, 0.0) for opt in options]
        
        heuristic = self.heuristics[heuristic_name]
        heuristic.usage_count += 1
        
        scored_options = []
        for option in options:
            score = heuristic.evaluate(option)
            scored_options.append((option, score))
        
        # Sort by score (lower is better)
        scored_options.sort(key=lambda x: x[1])
        return scored_options
    
    def greedy_selection(self, options: List[Any], 
                        heuristic_name: str) -> Any:
        """Select best option using greedy heuristic."""
        scored = self.evaluate_options(options, heuristic_name)
        if scored:
            best_option, best_score = scored[0]
            print(f"   {self.name}: Selected option with score {best_score:.2f}")
            return best_option
        return None
    
    def a_star_search(self, start_state: Any, goal_test: Callable[[Any], bool],
                     get_neighbors: Callable[[Any], List[Tuple[Any, float]]],
                     heuristic_name: str, max_iterations: int = 100) -> Optional[List[Any]]:
        """A* search using heuristic."""
        if heuristic_name not in self.heuristics:
            return None
        
        heuristic_fn = self.heuristics[heuristic_name]
        
        # Priority queue
        open_set = []
        start_node = SearchNode(
            state=start_state,
            cost=0,
            heuristic=heuristic_fn.evaluate(start_state)
        )
        heappush(open_set, start_node)
        
        closed_set = set()
        iterations = 0
        
        while open_set and iterations  Dict[str, Any]:
        """Execute heuristic-based decisions."""
        print("=== Heuristic-Based Pattern Demo ===\n")
        
        for scenario in scenarios:
            scenario_type = scenario.get("type", "greedy")
            print(f"\nScenario: {scenario.get('description', 'Unknown')}")
            
            for agent in self.agents.values():
                if scenario_type == "greedy":
                    options = scenario.get("options", [])
                    heuristic = scenario.get("heuristic", "default")
                    result = agent.greedy_selection(options, heuristic)
                    print(f"   Selected: {result}")
                
                elif scenario_type == "search":
                    start = scenario.get("start")
                    goal_test = scenario.get("goal_test")
                    neighbors = scenario.get("get_neighbors")
                    heuristic = scenario.get("heuristic", "default")
                    path = agent.a_star_search(start, goal_test, neighbors, heuristic)
                    if path:
                        print(f"   Path: {' -> '.join(map(str, path))}")
        
        # Statistics
        total_heuristics = sum(
            len(agent.heuristics) for agent in self.agents.values()
        )
        
        return {
            "pattern": "HeuristicBased",
            "total_agents": len(self.agents),
            "total_heuristics": total_heuristics,
            "scenarios": len(scenarios)
        }


# Example usage
if __name__ == "__main__":
    print("=== Heuristic-Based Pattern Demo ===\n")
    
    # Create pattern
    pattern = HeuristicBasedPattern()
    
    # Create agent
    print("1. Creating heuristic-based agent...")
    agent = HeuristicAgent("RouteFinderAgent")
    
    # Define heuristics
    print("\n2. Registering heuristics...")
    
    # Manhattan distance heuristic
    def manhattan_distance(state):
        """Manhattan distance to goal (5, 5)."""
        x, y = state
        return abs(x - 5) + abs(y - 5)
    
    agent.register_heuristic(Heuristic(
        name="manhattan",
        evaluate=manhattan_distance,
        description="Manhattan distance to goal"
    ))
    
    # Distance heuristic for travel
    def distance_heuristic(option):
        """Prefer shorter distances."""
        return option.get("distance", 0)
    
    agent.register_heuristic(Heuristic(
        name="distance",
        evaluate=distance_heuristic,
        description="Minimize distance"
    ))
    
    # Cost heuristic
    def cost_heuristic(option):
        """Prefer lower cost."""
        return option.get("cost", 0)
    
    agent.register_heuristic(Heuristic(
        name="cost",
        evaluate=cost_heuristic,
        description="Minimize cost"
    ))
    
    pattern.register_agent(agent)
    
    # Scenario 1: Greedy selection
    print("\n3. Running scenarios...")
    
    scenarios = [
        {
            "type": "greedy",
            "description": "Choose delivery route",
            "options": [
                {"route": "A", "distance": 10, "cost": 50},
                {"route": "B", "distance": 8, "cost": 60},
                {"route": "C", "distance": 12, "cost": 45},
            ],
            "heuristic": "distance"
        },
        {
            "type": "greedy",
            "description": "Choose service provider",
            "options": [
                {"provider": "FastCo", "distance": 5, "cost": 100},
                {"provider": "CheapCo", "distance": 15, "cost": 50},
                {"provider": "BalanceCo", "distance": 8, "cost": 75},
            ],
            "heuristic": "cost"
        },
        {
            "type": "search",
            "description": "Find path in grid",
            "start": (0, 0),
            "goal_test": lambda state: state == (5, 5),
            "get_neighbors": lambda state: [
                ((state[0]+1, state[1]), 1),
                ((state[0], state[1]+1), 1),
                ((state[0]-1, state[1]), 1),
                ((state[0], state[1]-1), 1),
            ],
            "heuristic": "manhattan"
        }
    ]
    
    summary = pattern.run(scenarios)
    
    print(f"\n4. Summary:")
    print(f"   Total Heuristics: {summary['total_heuristics']}")
    print(f"   Scenarios Executed: {summary['scenarios']}")
    
    print("\n5. Heuristic Usage:")
    for h_name, heuristic in agent.heuristics.items():
        print(f"   {h_name}: used {heuristic.usage_count} times")
    
    print("\nāœ“ Heuristics enabled fast approximate decisions!")

    

Agentic Architecture Pattern: Heuristic Based

Pattern 49: 49 Heuristic Based – Line-by-Line Explanation

Overview

Pattern 49: Heuristic-Based Pattern

Makes decisions using domain-specific rules of thumb and shortcuts. Features heuristic evaluation, greedy search, and practical approximations.


Code Structure Analysis

File Information

  • Pattern Number: 49
  • Pattern Name: 49 Heuristic Based
  • File Path: patterns\decision_making\49_heuristic_based.py
  • Total Lines: 286

Detailed Code Explanation

Module Docstring

"""
Pattern 49: Heuristic-Based Pattern

Makes decisions using domain-specific rules of thumb and shortcuts.
Features heuristic evaluation, greedy search, and practical approximations.
"""

Explanation: This module docstring describes the purpose and key features of the 49 Heuristic Based pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable, Tuple
from dataclasses import dataclass, field
from heapq import heappush, heappop
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, Tuple: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Classes

SearchNode (Line 16)

Description: Node in search space.

Methods:

__lt__ (Line 23)
def __lt__(self, other):
    """Compare by f-score for heap."""

Purpose: Compare by f-score for heap.

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

Heuristic (Line 29)

Description: Represents a heuristic function.

HeuristicAgent (Line 37)

Description: Agent that uses heuristics for decision-making.

Methods:

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

Purpose: No method docstring.

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

register_heuristic (Line 45)
def register_heuristic(self, heuristic):
    """Register a heuristic."""

Purpose: Register a heuristic.

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

evaluate_options (Line 49)
def evaluate_options(self, options, heuristic_name):
    """Evaluate options using heuristic."""

Purpose: Evaluate options using heuristic.

Parameters: – self: Instance reference – options: Parameter for options – heuristic_name: Parameter for heuristic_name

greedy_selection (Line 68)
def greedy_selection(self, options, heuristic_name):
    """Select best option using greedy heuristic."""

Purpose: Select best option using greedy heuristic.

Parameters: – self: Instance reference – options: Parameter for options – heuristic_name: Parameter for heuristic_name

a_star_search (Line 78)
def a_star_search(self, start_state, goal_test, get_neighbors, heuristic_name, max_iterations):
    """A* search using heuristic."""

Purpose: A* search using heuristic.

Parameters: – self: Instance reference – start_state: Parameter for start_state – goal_test: Parameter for goal_test – get_neighbors: Parameter for get_neighbors – heuristic_name: Parameter for heuristic_name – max_iterations: Parameter for max_iterations

HeuristicBasedPattern (Line 134)

Description: Heuristic-Based Pattern – Fast approximate decisions.

Agents use domain-specific heuristics for efficient decision-making. Supports greedy selection and A* search.

Methods:

__init__ (Line 142)
def __init__(self):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference

register_agent (Line 145)
def register_agent(self, agent):
    """Register heuristic agent."""

Purpose: Register heuristic agent.

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

run (Line 149)
def run(self, scenarios):
    """Execute heuristic-based decisions."""

Purpose: Execute heuristic-based decisions.

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


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