"""
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
Leave a Reply