Agentic Architecture Pattern: Constraint Based

Written by

in


    """
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

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *