"""
Pattern 47: Constraint-Based Pattern
Solves problems by satisfying constraints rather than optimizing objectives.
Features constraint propagation, backtracking, and feasibility checking.
"""
from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
class ConstraintType(Enum):
"""Types of constraints."""
EQUALITY = "equality"
INEQUALITY = "inequality"
MEMBERSHIP = "membership"
CUSTOM = "custom"
@dataclass
class Constraint:
"""Represents a constraint on variables."""
name: str
variables: List[str]
constraint_type: ConstraintType
check_function: Callable[[Dict[str, Any]], bool]
description: str = ""
@dataclass
class Variable:
"""Decision variable with domain."""
name: str
domain: List[Any] # Possible values
value: Optional[Any] = None
class ConstraintSolver:
"""Solver for constraint satisfaction problems."""
def __init__(self):
self.variables: Dict[str, Variable] = {}
self.constraints: List[Constraint] = []
self.solutions: List[Dict[str, Any]] = []
def add_variable(self, variable: Variable):
"""Add variable to problem."""
self.variables[variable.name] = variable
def add_constraint(self, constraint: Constraint):
"""Add constraint to problem."""
self.constraints.append(constraint)
def check_constraints(self, assignment: Dict[str, Any]) -> bool:
"""Check if assignment satisfies all constraints."""
for constraint in self.constraints:
# Check only if all constraint variables are assigned
if all(var in assignment for var in constraint.variables):
if not constraint.check_function(assignment):
return False
return True
def backtrack(self, assignment: Dict[str, Any]) -> bool:
"""Backtracking search for solutions."""
# Check if complete assignment
if len(assignment) == len(self.variables):
if self.check_constraints(assignment):
self.solutions.append(assignment.copy())
return True
return False
# Select unassigned variable
unassigned = [v for v in self.variables.keys() if v not in assignment]
if not unassigned:
return False
var_name = unassigned[0]
variable = self.variables[var_name]
# Try each value in domain
for value in variable.domain:
assignment[var_name] = value
# Check if consistent with constraints so far
if self.check_constraints(assignment):
if self.backtrack(assignment):
return True
# Backtrack
del assignment[var_name]
return False
def solve(self, find_all: bool = False) -> List[Dict[str, Any]]:
"""Solve constraint satisfaction problem."""
self.solutions = []
if find_all:
# Find all solutions
self.backtrack_all({})
else:
# Find one solution
self.backtrack({})
return self.solutions
def backtrack_all(self, assignment: Dict[str, Any]):
"""Find all solutions."""
if len(assignment) == len(self.variables):
if self.check_constraints(assignment):
self.solutions.append(assignment.copy())
return
unassigned = [v for v in self.variables.keys() if v not in assignment]
if not unassigned:
return
var_name = unassigned[0]
variable = self.variables[var_name]
for value in variable.domain:
assignment[var_name] = value
if self.check_constraints(assignment):
self.backtrack_all(assignment)
del assignment[var_name]
class ConstraintBasedAgent:
"""Agent that solves problems using constraint satisfaction."""
def __init__(self, name: str, model_name: str = "gpt-4"):
self.name = name
self.llm = ChatOpenAI(model=model_name, temperature=0.3)
self.solver = ConstraintSolver()
def solve_problem(self, find_all: bool = False) -> List[Dict[str, Any]]:
"""Solve the constraint problem."""
print(f"\n {self.name}: Solving constraint problem...")
print(f" Variables: {len(self.solver.variables)}")
print(f" Constraints: {len(self.solver.constraints)}")
solutions = self.solver.solve(find_all=find_all)
print(f" Found {len(solutions)} solution(s)")
return solutions
class ConstraintBasedPattern:
"""
Constraint-Based Pattern - Constraint satisfaction solving.
Solves problems by finding assignments that satisfy all constraints.
Uses backtracking search with constraint propagation.
"""
def __init__(self):
self.agents: Dict[str, ConstraintBasedAgent] = {}
def register_agent(self, agent: ConstraintBasedAgent):
"""Register constraint-based agent."""
self.agents[agent.name] = agent
def run(self) -> Dict[str, Any]:
"""Execute constraint solving."""
print("=== Constraint-Based Pattern Demo ===\n")
all_solutions = []
for agent in self.agents.values():
solutions = agent.solve_problem(find_all=True)
all_solutions.extend(solutions)
return {
"pattern": "ConstraintBased",
"total_agents": len(self.agents),
"total_solutions": len(all_solutions)
}
# Example usage
if __name__ == "__main__":
print("=== Constraint-Based Pattern Demo ===\n")
# Create pattern
pattern = ConstraintBasedPattern()
# Create agent
print("1. Creating constraint-based agent for scheduling...")
agent = ConstraintBasedAgent("SchedulerAgent")
# Define variables - meeting times
print("\n2. Defining variables (meeting time slots)...")
for meeting in ["meeting_A", "meeting_B", "meeting_C"]:
agent.solver.add_variable(Variable(
name=meeting,
domain=[9, 10, 11, 14, 15, 16] # Available hours
))
# Define constraints
print("\n3. Adding constraints...")
# All meetings at different times
agent.solver.add_constraint(Constraint(
name="all_different",
variables=["meeting_A", "meeting_B", "meeting_C"],
constraint_type=ConstraintType.CUSTOM,
check_function=lambda a: len(set(a.values())) == len(a),
description="All meetings at different times"
))
# Meeting A before lunch (before 12)
agent.solver.add_constraint(Constraint(
name="A_before_lunch",
variables=["meeting_A"],
constraint_type=ConstraintType.INEQUALITY,
check_function=lambda a: a.get("meeting_A", 0) = 14,
description="Meeting B after lunch"
))
# Meeting C not adjacent to Meeting A
agent.solver.add_constraint(Constraint(
name="C_not_adjacent_A",
variables=["meeting_A", "meeting_C"],
constraint_type=ConstraintType.CUSTOM,
check_function=lambda a: abs(a.get("meeting_A", 0) - a.get("meeting_C", 0)) > 1,
description="Meeting C not adjacent to A"
))
pattern.register_agent(agent)
# Solve
print("\n4. Solving...")
summary = pattern.run()
# Display solutions
print("\n5. Valid Schedules:")
for i, solution in enumerate(agent.solver.solutions, 1):
print(f" Solution {i}:")
for meeting, time in sorted(solution.items()):
print(f" {meeting}: {time}:00")
print(f"\n6. Summary:")
print(f" Total Solutions: {summary['total_solutions']}")
print("\nā Constraint satisfaction found valid assignments!")
Agentic Architecture Pattern: Constraint Based
Pattern 47: 47 Constraint Based – Line-by-Line Explanation
Overview
Pattern 47: Constraint-Based Pattern
Solves problems by satisfying constraints rather than optimizing objectives. Features constraint propagation, backtracking, and feasibility checking.
Code Structure Analysis
File Information
- Pattern Number: 47
- Pattern Name: 47 Constraint Based
- File Path:
patterns\decision_making\47_constraint_based.py - Total Lines: 257
Detailed Code Explanation
Module Docstring
"""
Pattern 47: Constraint-Based Pattern
Solves problems by satisfying constraints rather than optimizing objectives.
Features constraint propagation, backtracking, and feasibility checking.
"""
Explanation: This module docstring describes the purpose and key features of the 47 Constraint Based pattern.
Imports Section
from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass
from enum import Enum
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: Data class decorators for clean data structures
– 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
ConstraintType (Line 15)
class ConstraintType(Enum):
EQUALITY
INEQUALITY
MEMBERSHIP
CUSTOM
Purpose: Types of constraints.
Values:
– EQUALITY: Represents equality
– INEQUALITY: Represents inequality
– MEMBERSHIP: Represents membership
– CUSTOM: Represents custom
Classes
ConstraintType (Line 15)
Description: Types of constraints.
Constraint (Line 24)
Description: Represents a constraint on variables.
Variable (Line 34)
Description: Decision variable with domain.
ConstraintSolver (Line 41)
Description: Solver for constraint satisfaction problems.
Methods:
__init__ (Line 44)
def __init__(self):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
add_variable (Line 49)
def add_variable(self, variable):
"""Add variable to problem."""
Purpose: Add variable to problem.
Parameters:
– self: Instance reference
– variable: Parameter for variable
add_constraint (Line 53)
def add_constraint(self, constraint):
"""Add constraint to problem."""
Purpose: Add constraint to problem.
Parameters:
– self: Instance reference
– constraint: Parameter for constraint
check_constraints (Line 57)
def check_constraints(self, assignment):
"""Check if assignment satisfies all constraints."""
Purpose: Check if assignment satisfies all constraints.
Parameters:
– self: Instance reference
– assignment: Parameter for assignment
backtrack (Line 66)
def backtrack(self, assignment):
"""Backtracking search for solutions."""
Purpose: Backtracking search for solutions.
Parameters:
– self: Instance reference
– assignment: Parameter for assignment
solve (Line 97)
def solve(self, find_all):
"""Solve constraint satisfaction problem."""
Purpose: Solve constraint satisfaction problem.
Parameters:
– self: Instance reference
– find_all: Parameter for find_all
backtrack_all (Line 110)
def backtrack_all(self, assignment):
"""Find all solutions."""
Purpose: Find all solutions.
Parameters:
– self: Instance reference
– assignment: Parameter for assignment
ConstraintBasedAgent (Line 131)
Description: Agent that solves problems using constraint satisfaction.
Methods:
__init__ (Line 134)
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”)
solve_problem (Line 139)
def solve_problem(self, find_all):
"""Solve the constraint problem."""
Purpose: Solve the constraint problem.
Parameters:
– self: Instance reference
– find_all: Parameter for find_all
ConstraintBasedPattern (Line 151)
Description: Constraint-Based Pattern – Constraint satisfaction solving.
Solves problems by finding assignments that satisfy all constraints. Uses backtracking search with constraint propagation.
Methods:
__init__ (Line 159)
def __init__(self):
"""No method docstring."""
Purpose: No method docstring.
Parameters:
– self: Instance reference
register_agent (Line 162)
def register_agent(self, agent):
"""Register constraint-based agent."""
Purpose: Register constraint-based agent.
Parameters:
– self: Instance reference
– agent: Parameter for agent
run (Line 166)
def run(self):
"""Execute constraint solving."""
Purpose: Execute constraint solving.
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 = ConstraintType(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