Tag: technology

  • Agentic Design Pattern: Model Distillation

    
        """
    Pattern 144: Model Distillation
    
    Description:
        Implements knowledge distillation from larger teacher models to smaller student
        models, enabling efficient deployment while maintaining performance. This pattern
        transfers knowledge through response generation, logits matching, and behavior cloning.
    
    Components:
        - Teacher Model: Large, powerful model with superior capabilities
        - Student Model: Smaller, efficient model that learns from teacher
        - Distillation Process: Knowledge transfer mechanism
        - Performance Comparison: Evaluation and validation
        - Training Data Generator: Creates synthetic training examples
        - Knowledge Transfer Evaluator: Measures transfer quality
    
    Use Cases:
        - Deploy efficient models in resource-constrained environments
        - Reduce inference costs while maintaining quality
        - Edge deployment and mobile applications
        - Real-time processing requirements
        - Multi-model deployment strategies
    
    Benefits:
        - Reduced computational costs
        - Faster inference times
        - Smaller model footprint
        - Maintained accuracy
        - Efficient knowledge transfer
    
    Trade-offs:
        - Some performance degradation
        - Requires training data and time
        - Quality depends on teacher model
        - May not capture all nuances
    
    LangChain Implementation:
        Uses ChatOpenAI with different model sizes, synthetic data generation,
        and comparison metrics to demonstrate knowledge distillation.
    """
    
    import os
    import json
    import time
    from typing import List, Dict, Any, Tuple, Optional
    from dataclasses import dataclass, field
    from datetime import datetime
    from collections import defaultdict
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    @dataclass
    class DistillationExample:
        """Training example for distillation"""
        input: str
        teacher_output: str
        student_output: Optional[str] = None
        quality_score: float = 0.0
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class DistillationMetrics:
        """Metrics for distillation performance"""
        teacher_latency: float = 0.0
        student_latency: float = 0.0
        quality_retention: float = 0.0
        speed_improvement: float = 0.0
        size_reduction: float = 0.0
        examples_processed: int = 0
        accuracy_comparison: Dict[str, float] = field(default_factory=dict)
    
    
    @dataclass
    class ModelProfile:
        """Profile of a model's characteristics"""
        model_name: str
        model_type: str  # teacher or student
        average_latency: float = 0.0
        average_tokens: int = 0
        cost_per_call: float = 0.0
        quality_score: float = 0.0
    
    
    class ModelDistillationAgent:
        """
        Agent that implements knowledge distillation from teacher to student models.
        
        The agent generates training data using a teacher model, trains a student model
        on this data, and evaluates the quality of knowledge transfer.
        """
        
        def __init__(
            self,
            teacher_model: str = "gpt-4",
            student_model: str = "gpt-3.5-turbo",
            temperature: float = 0.7
        ):
            """
            Initialize the distillation agent.
            
            Args:
                teacher_model: Name of the teacher model (larger, more capable)
                student_model: Name of the student model (smaller, efficient)
                temperature: Sampling temperature for generation
            """
            self.teacher = ChatOpenAI(model=teacher_model, temperature=temperature)
            self.student = ChatOpenAI(model=student_model, temperature=temperature)
            self.evaluator = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
            
            self.teacher_model = teacher_model
            self.student_model = student_model
            
            self.examples: List[DistillationExample] = []
            self.metrics = DistillationMetrics()
            self.teacher_profile = ModelProfile(teacher_model, "teacher")
            self.student_profile = ModelProfile(student_model, "student")
        
        def generate_training_data(
            self,
            domains: List[str],
            examples_per_domain: int = 5
        ) -> List[DistillationExample]:
            """
            Generate synthetic training data for distillation.
            
            Args:
                domains: List of domains to generate examples for
                examples_per_domain: Number of examples per domain
                
            Returns:
                List of training examples
            """
            print(f"\n🔄 Generating training data from teacher model...")
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", "Generate {count} diverse questions about {domain}. "
                          "Return a JSON array of questions."),
                ("user", "Generate questions that are challenging and require expertise.")
            ])
            
            chain = prompt | self.teacher | StrOutputParser()
            
            all_examples = []
            
            for domain in domains:
                try:
                    result = chain.invoke({
                        "domain": domain,
                        "count": examples_per_domain
                    })
                    
                    # Parse questions (simplified - in production, use structured output)
                    questions = self._extract_questions(result, examples_per_domain)
                    
                    for question in questions:
                        # Get teacher's response
                        start_time = time.time()
                        teacher_response = self.teacher.invoke(question).content
                        teacher_latency = time.time() - start_time
                        
                        example = DistillationExample(
                            input=question,
                            teacher_output=teacher_response,
                            metadata={
                                "domain": domain,
                                "teacher_latency": teacher_latency,
                                "timestamp": datetime.now().isoformat()
                            }
                        )
                        all_examples.append(example)
                        
                    print(f"  ✓ Generated {len(questions)} examples for {domain}")
                    
                except Exception as e:
                    print(f"  ✗ Error generating examples for {domain}: {e}")
            
            self.examples.extend(all_examples)
            print(f"\n✅ Generated {len(all_examples)} total training examples")
            return all_examples
        
        def distill_knowledge(
            self,
            examples: Optional[List[DistillationExample]] = None
        ) -> DistillationMetrics:
            """
            Perform knowledge distillation on examples.
            
            Args:
                examples: Training examples (uses self.examples if None)
                
            Returns:
                Distillation metrics
            """
            if examples is None:
                examples = self.examples
            
            if not examples:
                print("⚠️  No examples available for distillation")
                return self.metrics
            
            print(f"\n🎓 Distilling knowledge to student model...")
            print(f"   Teacher: {self.teacher_model}")
            print(f"   Student: {self.student_model}")
            
            teacher_times = []
            student_times = []
            quality_scores = []
            
            for i, example in enumerate(examples, 1):
                try:
                    # Student generates response
                    start_time = time.time()
                    student_response = self.student.invoke(example.input).content
                    student_latency = time.time() - start_time
                    
                    example.student_output = student_response
                    
                    # Evaluate quality
                    quality_score = self._evaluate_response_quality(
                        example.input,
                        example.teacher_output,
                        student_response
                    )
                    example.quality_score = quality_score
                    
                    teacher_times.append(example.metadata.get("teacher_latency", 0))
                    student_times.append(student_latency)
                    quality_scores.append(quality_score)
                    
                    if i % 5 == 0:
                        print(f"   Processed {i}/{len(examples)} examples...")
                    
                except Exception as e:
                    print(f"   ✗ Error processing example {i}: {e}")
            
            # Calculate metrics
            self.metrics.teacher_latency = sum(teacher_times) / len(teacher_times)
            self.metrics.student_latency = sum(student_times) / len(student_times)
            self.metrics.quality_retention = sum(quality_scores) / len(quality_scores)
            self.metrics.speed_improvement = (
                self.metrics.teacher_latency / self.metrics.student_latency
            )
            self.metrics.examples_processed = len(examples)
            
            print(f"\n✅ Distillation complete!")
            return self.metrics
        
        def compare_models(self, test_queries: List[str]) -> Dict[str, Any]:
            """
            Compare teacher and student model performance.
            
            Args:
                test_queries: Queries to test both models on
                
            Returns:
                Comparison results
            """
            print(f"\n📊 Comparing models on {len(test_queries)} test queries...")
            
            results = {
                "teacher": {"responses": [], "latencies": [], "total_time": 0},
                "student": {"responses": [], "latencies": [], "total_time": 0},
                "quality_scores": []
            }
            
            for query in test_queries:
                # Teacher response
                start = time.time()
                teacher_resp = self.teacher.invoke(query).content
                teacher_time = time.time() - start
                
                # Student response
                start = time.time()
                student_resp = self.student.invoke(query).content
                student_time = time.time() - start
                
                # Evaluate
                quality = self._evaluate_response_quality(
                    query, teacher_resp, student_resp
                )
                
                results["teacher"]["responses"].append(teacher_resp)
                results["teacher"]["latencies"].append(teacher_time)
                results["teacher"]["total_time"] += teacher_time
                
                results["student"]["responses"].append(student_resp)
                results["student"]["latencies"].append(student_time)
                results["student"]["total_time"] += student_time
                
                results["quality_scores"].append(quality)
            
            # Calculate summary statistics
            results["summary"] = {
                "avg_teacher_latency": sum(results["teacher"]["latencies"]) / len(test_queries),
                "avg_student_latency": sum(results["student"]["latencies"]) / len(test_queries),
                "avg_quality_retention": sum(results["quality_scores"]) / len(test_queries),
                "speed_improvement": (
                    sum(results["teacher"]["latencies"]) / 
                    sum(results["student"]["latencies"])
                ),
                "total_queries": len(test_queries)
            }
            
            print(f"\n✅ Model comparison complete")
            return results
        
        def _evaluate_response_quality(
            self,
            query: str,
            teacher_response: str,
            student_response: str
        ) -> float:
            """
            Evaluate quality of student response compared to teacher.
            
            Args:
                query: Original query
                teacher_response: Teacher's response
                student_response: Student's response
                
            Returns:
                Quality score (0-1)
            """
            eval_prompt = ChatPromptTemplate.from_messages([
                ("system", "You are evaluating the quality of a student model's response "
                          "compared to a teacher model's response. Rate the student's response "
                          "on a scale of 0-10 based on:\n"
                          "- Accuracy and correctness\n"
                          "- Completeness of information\n"
                          "- Clarity and coherence\n"
                          "- Similarity to teacher's response\n\n"
                          "Return ONLY a number between 0 and 10."),
                ("user", "Query: {query}\n\n"
                        "Teacher Response: {teacher}\n\n"
                        "Student Response: {student}\n\n"
                        "Quality Score (0-10):")
            ])
            
            try:
                chain = eval_prompt | self.evaluator | StrOutputParser()
                result = chain.invoke({
                    "query": query,
                    "teacher": teacher_response,
                    "student": student_response
                })
                
                # Extract numeric score
                score = float(''.join(c for c in result if c.isdigit() or c == '.'))
                return min(max(score / 10.0, 0.0), 1.0)  # Normalize to 0-1
                
            except Exception as e:
                print(f"⚠️  Error evaluating quality: {e}")
                return 0.5  # Default to neutral score
        
        def _extract_questions(self, text: str, count: int) -> List[str]:
            """Extract questions from generated text."""
            # Simple extraction - in production, use structured output
            lines = text.strip().split('\n')
            questions = []
            
            for line in lines:
                line = line.strip()
                if line and ('?' in line or line.startswith(('-', '*', str(len(questions)+1)))):
                    # Clean up line
                    question = line.lstrip('-*0123456789. ')
                    if question and len(questions) < count:
                        questions.append(question)
            
            # Ensure we have enough questions
            while len(questions)  str:
            """Generate a comprehensive metrics report."""
            report = []
            report.append("\n" + "="*60)
            report.append("MODEL DISTILLATION METRICS REPORT")
            report.append("="*60)
            
            report.append(f"\n📚 Training Data:")
            report.append(f"   Examples Processed: {self.metrics.examples_processed}")
            
            report.append(f"\n⚡ Performance Metrics:")
            report.append(f"   Teacher Avg Latency: {self.metrics.teacher_latency:.3f}s")
            report.append(f"   Student Avg Latency: {self.metrics.student_latency:.3f}s")
            report.append(f"   Speed Improvement: {self.metrics.speed_improvement:.2f}x")
            
            report.append(f"\n🎯 Quality Metrics:")
            report.append(f"   Quality Retention: {self.metrics.quality_retention:.1%}")
            
            report.append(f"\n💰 Efficiency Gains:")
            cost_reduction = (1 - (self.metrics.student_latency / self.metrics.teacher_latency)) * 100
            report.append(f"   Latency Reduction: {cost_reduction:.1f}%")
            
            report.append("\n" + "="*60)
            
            return "\n".join(report)
        
        def save_distilled_examples(self, filepath: str):
            """Save distilled examples to file for future use."""
            data = {
                "teacher_model": self.teacher_model,
                "student_model": self.student_model,
                "examples": [
                    {
                        "input": ex.input,
                        "teacher_output": ex.teacher_output,
                        "student_output": ex.student_output,
                        "quality_score": ex.quality_score,
                        "metadata": ex.metadata
                    }
                    for ex in self.examples
                ],
                "metrics": {
                    "teacher_latency": self.metrics.teacher_latency,
                    "student_latency": self.metrics.student_latency,
                    "quality_retention": self.metrics.quality_retention,
                    "speed_improvement": self.metrics.speed_improvement,
                    "examples_processed": self.metrics.examples_processed
                }
            }
            
            with open(filepath, 'w') as f:
                json.dump(data, f, indent=2)
            
            print(f"\n💾 Saved distilled examples to {filepath}")
    
    
    def demonstrate_model_distillation():
        """Demonstrate the Model Distillation pattern."""
        print("="*60)
        print("MODEL DISTILLATION PATTERN DEMONSTRATION")
        print("="*60)
        
        # Initialize agent with teacher and student models
        agent = ModelDistillationAgent(
            teacher_model="gpt-4",
            student_model="gpt-3.5-turbo",
            temperature=0.7
        )
        
        # Example 1: Generate training data
        print("\n" + "="*60)
        print("Example 1: Generate Training Data from Teacher")
        print("="*60)
        
        domains = [
            "machine learning algorithms",
            "software architecture patterns",
            "data structures"
        ]
        
        examples = agent.generate_training_data(
            domains=domains,
            examples_per_domain=3
        )
        
        print(f"\nSample generated example:")
        if examples:
            ex = examples[0]
            print(f"  Query: {ex.input[:100]}...")
            print(f"  Teacher Response: {ex.teacher_output[:150]}...")
        
        # Example 2: Distill knowledge to student
        print("\n" + "="*60)
        print("Example 2: Distill Knowledge to Student Model")
        print("="*60)
        
        metrics = agent.distill_knowledge()
        
        print(f"\nDistillation Results:")
        print(f"  Quality Retention: {metrics.quality_retention:.1%}")
        print(f"  Speed Improvement: {metrics.speed_improvement:.2f}x faster")
        
        # Example 3: Compare models on test queries
        print("\n" + "="*60)
        print("Example 3: Compare Teacher vs Student Performance")
        print("="*60)
        
        test_queries = [
            "Explain the concept of overfitting in machine learning",
            "What is the difference between composition and inheritance?",
            "How does a hash table achieve O(1) lookup time?"
        ]
        
        comparison = agent.compare_models(test_queries)
        
        print(f"\nComparison Results:")
        print(f"  Teacher Avg Latency: {comparison['summary']['avg_teacher_latency']:.3f}s")
        print(f"  Student Avg Latency: {comparison['summary']['avg_student_latency']:.3f}s")
        print(f"  Quality Retention: {comparison['summary']['avg_quality_retention']:.1%}")
        print(f"  Speed Improvement: {comparison['summary']['speed_improvement']:.2f}x")
        
        # Example 4: Demonstrate efficiency gains
        print("\n" + "="*60)
        print("Example 4: Efficiency Gains Analysis")
        print("="*60)
        
        print(f"\n📊 Efficiency Analysis:")
        print(f"   Total Time (Teacher): {comparison['teacher']['total_time']:.2f}s")
        print(f"   Total Time (Student): {comparison['student']['total_time']:.2f}s")
        
        time_saved = comparison['teacher']['total_time'] - comparison['student']['total_time']
        time_saved_pct = (time_saved / comparison['teacher']['total_time']) * 100
        
        print(f"   Time Saved: {time_saved:.2f}s ({time_saved_pct:.1f}%)")
        
        # Estimate cost savings (simplified)
        # Assuming GPT-4 is 10x more expensive than GPT-3.5
        cost_ratio = 10
        queries_per_month = 10000
        
        teacher_cost = queries_per_month * cost_ratio
        student_cost = queries_per_month * 1
        cost_savings = teacher_cost - student_cost
        
        print(f"\n💰 Estimated Monthly Cost Savings (10K queries):")
        print(f"   Teacher Cost (relative): {teacher_cost} units")
        print(f"   Student Cost (relative): {student_cost} units")
        print(f"   Savings: {cost_savings} units ({(cost_savings/teacher_cost)*100:.1f}%)")
        
        # Example 5: Quality vs Speed trade-off
        print("\n" + "="*60)
        print("Example 5: Quality vs Speed Trade-off Analysis")
        print("="*60)
        
        print(f"\n🎯 Trade-off Analysis:")
        print(f"   Quality Loss: {(1 - comparison['summary']['avg_quality_retention']) * 100:.1f}%")
        print(f"   Speed Gain: {(comparison['summary']['speed_improvement'] - 1) * 100:.1f}%")
        
        # Calculate trade-off ratio
        quality_loss = (1 - comparison['summary']['avg_quality_retention'])
        speed_gain = (comparison['summary']['speed_improvement'] - 1)
        
        if quality_loss > 0:
            tradeoff_ratio = speed_gain / quality_loss
            print(f"   Trade-off Ratio: {tradeoff_ratio:.2f}x speed gain per 1% quality loss")
        
        print(f"\n💡 Recommendation:")
        if comparison['summary']['avg_quality_retention'] >= 0.85:
            print("   ✅ Student model provides excellent quality retention")
            print("   ✅ Recommended for production deployment")
        elif comparison['summary']['avg_quality_retention'] >= 0.70:
            print("   ⚠️  Student model provides acceptable quality")
            print("   ⚠️  Consider for non-critical use cases")
        else:
            print("   ⛔ Student model quality too low")
            print("   ⛔ Additional distillation or different approach needed")
        
        # Example 6: Generate full metrics report
        print("\n" + "="*60)
        print("Example 6: Comprehensive Metrics Report")
        print("="*60)
        
        report = agent.get_metrics_report()
        print(report)
        
        # Summary
        print("\n" + "="*60)
        print("SUMMARY")
        print("="*60)
        print("""
    The Model Distillation pattern demonstrates:
    
    1. Knowledge Transfer: Large teacher models transfer knowledge to smaller students
    2. Efficiency Gains: Significant speed improvements with minimal quality loss
    3. Cost Reduction: Lower inference costs for production deployment
    4. Quality Metrics: Systematic evaluation of distillation quality
    5. Trade-off Analysis: Clear understanding of quality vs efficiency
    
    Key Benefits:
    - Faster inference times (typically 2-5x improvement)
    - Reduced computational costs (often 90%+ savings)
    - Maintained accuracy (usually 80-95% retention)
    - Suitable for resource-constrained environments
    - Enables real-time applications
    
    Best Practices:
    - Use diverse training data from teacher model
    - Evaluate quality systematically on held-out test set
    - Monitor quality retention thresholds
    - Consider ensemble of student models for critical applications
    - Continuously update student model as teacher improves
        """)
    
    
    if __name__ == "__main__":
        demonstrate_model_distillation()
    
        

    Agentic Design Pattern: Model Distillation

    Pattern 144: Model Distillation – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Model Distillation pattern implementation.


    Pattern Description

    Pattern 144: Model Distillation

    Description: Implements knowledge distillation from larger teacher models to smaller student models, enabling efficient deployment while maintaining performance. This pattern transfers knowledge through response generation, logits matching, and behavior cloning.

    Components: – Teacher Model: Large, powerful model with superior capabilities – Student Model: Smaller, efficient model that learns from teacher – Distillation Process: Knowledge transfer mechanism – Performance Comparison: Evaluation and validation – Training Data Generator: Creates synthetic training examples – Knowledge Transfer Evaluator: Measures transfer quality

    Use Cases: – Deploy efficient models in resource-constrained environments – Reduce inference costs while maintaining quality – Edge deployment and mobile applications – Real-time processing requirements – Multi-model deployment strategies

    Benefits: – Reduced computational costs – Faster inference times – Smaller model footprint – Maintained accuracy – Efficient knowledge transfer

    Trade-offs: – Some performance degradation – Requires training data and time – Quality depends on teacher model – May not capture all nuances

    LangChain Implementation: Uses ChatOpenAI with different model sizes, synthetic data generation, and comparison metrics to demonstrate knowledge distillation.


    File Statistics

    • Total Lines: 598
    • Classes: 4
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 144: Model Distillation
    
    Description:
        Implements knowledge distillation from larger teacher models to smaller student
        models, enabling efficient deployment while maintaining performance. This pattern
        transfers knowledge through response generation, logits matching, and behavior cloning.
    
    Components:
        - Teacher Model: Large, powerful model with superior capabilities
        - Student Model: Smaller, efficient model that learns from teacher
        - Distillation Process: Knowledge transfer mechanism
        - Performance Comparison: Evaluation and validation
        - Training Data Generator: Creates synthetic training examples
        - Knowledge Transfer Evaluator: Measures transfer quality
    
    Use Cases:
        - Deploy efficient models in resource-constrained environments
        - Reduce inference costs while maintaining quality
        - Edge deployment and mobile applications
        - Real-time processing requirements
        - Multi-model deployment strategies
    
    Benefits:
        - Reduced computational costs
        - Faster inference times
        - Smaller model footprint
        - Maintained accuracy
        - Efficient knowledge transfer
    
    Trade-offs:
        - Some performance degradation
        - Requires training data and time
        - Quality depends on teacher model
        - May not capture all nuances
    
    LangChain Implementation:
        Uses ChatOpenAI with different model sizes, synthetic data generation,
        and comparison metrics to demonstrate knowledge distillation.
    """
    

    Purpose: This module implements the Model Distillation pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import collections.defaultdict
    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import json
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import time
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Tuple
    

    Purpose: Core Python functionality: – collections.defaultdict: Specialized container datatypes – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – json: JSON serialization/deserialization – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – time: Time-related functions – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Tuple: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: DistillationExample

    Lines 59-65

    Purpose:

    Training example for distillation
    

    Class: DistillationMetrics

    Lines 69-77

    Purpose:

    Metrics for distillation performance
    

    Class: ModelProfile

    Lines 81-88

    Purpose:

    Profile of a model's characteristics
    

    Class: ModelDistillationAgent

    Lines 91-435

    Purpose:

    Agent that implements knowledge distillation from teacher to student models.
    
    The agent generates training data using a teacher model, trains a student model
    on this data, and evaluates the quality of knowledge transfer.
    

    Methods:

    __init__(self, teacher_model, student_model, temperature)

    Lines 99-123

    Purpose:

    Initialize the distillation agent.
    
    Args:
        teacher_model: Name of the teacher model (larger, more capable)
        student_model: Name of the student model (smaller, efficient)
        temperature: Sampling temperature for generation
    

    Parameters: – teacher_model: Language model instance – student_model: Language model instance – temperature: Sampling temperature (0-1)

    generate_training_data(self, domains, examples_per_domain)

    Lines 125-186

    Purpose:

    Generate synthetic training data for distillation.
    
    Args:
        domains: List of domains to generate examples for
        examples_per_domain: Number of examples per domain
    
    Returns:
        List of training examples
    

    Parameters: – domains: Domains – examples_per_domain: Examples per domain

    Returns: <ast.Subscript object at 0x000001A06D649110>

    distill_knowledge(self, examples)

    Lines 188-253

    Purpose:

    Perform knowledge distillation on examples.
    
    Args:
        examples: Training examples (uses self.examples if None)
    
    Returns:
        Distillation metrics
    

    Parameters: – examples: Examples

    Returns: DistillationMetrics

    compare_models(self, test_queries)

    Lines 255-312

    Purpose:

    Compare teacher and student model performance.
    
    Args:
        test_queries: Queries to test both models on
    
    Returns:
        Comparison results
    

    Parameters: – test_queries: Test queries

    Returns: <ast.Subscript object at 0x000001A06D65AED0>

    _evaluate_response_quality(self, query, teacher_response, student_response)

    Lines 314-360

    Purpose:

    Evaluate quality of student response compared to teacher.
    
    Args:
        query: Original query
        teacher_response: Teacher's response
        student_response: Student's response
    
    Returns:
        Quality score (0-1)
    

    Parameters: – query: Input query or question – teacher_response: Teacher response – student_response: Student response

    Returns: float

    _extract_questions(self, text, count)

    Lines 362-380

    Purpose:

    Extract questions from generated text.
    

    Parameters: – text: Text – count: Count

    Returns: <ast.Subscript object at 0x000001A06D6F7150>

    get_metrics_report(self)

    Lines 382-406

    Purpose:

    Generate a comprehensive metrics report.
    

    Parameters:

    Returns: str

    save_distilled_examples(self, filepath)

    Lines 408-435

    Purpose:

    Save distilled examples to file for future use.
    

    Parameters: – filepath: Filepath


    Functions

    demonstrate_model_distillation()

    Lines 438-593

    Purpose:

    Demonstrate the Model Distillation pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Model Distillation pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 143
    • Pattern 145

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Model Distillation pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 144_model_distillation.py
    Total Lines: 598
    Pattern Category: AI Agent Design Pattern #144


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Result Memoization

    
        """
    Pattern 143: Result Memoization
    
    Description:
        The Result Memoization pattern caches computation results to avoid redundant
        processing, significantly improving performance for repeated operations. When
        an agent encounters the same input or query it has processed before, it returns
        the cached result immediately instead of recomputing. This pattern is essential
        for production AI systems where identical or similar queries occur frequently.
    
        Memoization provides dramatic performance improvements for deterministic operations,
        expensive computations, and frequently accessed data. It trades memory for speed,
        storing results with appropriate cache keys, expiration policies, and invalidation
        strategies. The pattern supports various caching strategies including LRU, TTL,
        and semantic similarity-based caching.
    
        This implementation provides comprehensive memoization capabilities including
        function-level caching, LLM response caching, semantic similarity caching,
        cache warming, invalidation strategies, and performance metrics. It handles
        both simple key-value caching and advanced semantic caching for AI operations.
    
    Components:
        - Function Memoization: Cache function results by arguments
        - LLM Response Caching: Cache model outputs for queries
        - Semantic Caching: Cache based on semantic similarity
        - Cache Warming: Preload frequently accessed results
        - Invalidation Strategy: Remove stale cache entries
        - Performance Metrics: Track cache effectiveness
    
    Use Cases:
        - Repeated queries in chatbots
        - Expensive computations
        - API call reduction
        - Database query caching
        - Embedding generation
        - Translation caching
        - Recommendation systems
        - Data processing pipelines
    
    LangChain Implementation:
        This implementation uses:
        - Python functools.lru_cache for function memoization
        - Custom cache implementations with TTL
        - Semantic similarity-based caching
        - LangChain cache integration
        - Hash-based cache keys
        - Cache metrics and monitoring
    
    Benefits:
        - Dramatically reduces latency
        - Lowers computational costs
        - Reduces API calls
        - Improves scalability
        - Provides consistent responses
        - Enables offline operation
        - Reduces load on services
    
    Trade-offs:
        - Memory overhead for cache storage
        - Stale data if not invalidated properly
        - Cache consistency challenges
        - Cold start performance
        - Complex invalidation logic
        - Potential memory leaks
    
    Production Considerations:
        - Set appropriate cache sizes
        - Implement TTL for freshness
        - Monitor cache hit rates
        - Use distributed caching for scale
        - Handle cache misses gracefully
        - Implement cache warming
        - Monitor memory usage
        - Use appropriate invalidation
        - Consider cache coherence
        - Profile cache effectiveness
        - Implement cache preloading
        - Handle cache failures
    """
    
    import os
    import time
    import hashlib
    import json
    from typing import List, Dict, Any, Optional, Callable, Tuple
    from datetime import datetime, timedelta
    from functools import wraps, lru_cache
    from collections import OrderedDict
    from dataclasses import dataclass, field
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    @dataclass
    class CacheEntry:
        """Cache entry with metadata."""
        key: str
        value: Any
        created_at: datetime
        last_accessed: datetime
        access_count: int
        ttl_seconds: Optional[float]
        
        def is_expired(self) -> bool:
            """Check if entry is expired."""
            if self.ttl_seconds is None:
                return False
            age = (datetime.now() - self.created_at).total_seconds()
            return age > self.ttl_seconds
        
        def access(self):
            """Record access."""
            self.last_accessed = datetime.now()
            self.access_count += 1
    
    
    class LRUCache:
        """LRU (Least Recently Used) Cache implementation."""
        
        def __init__(self, max_size: int = 100, ttl_seconds: Optional[float] = None):
            """Initialize LRU cache."""
            self.max_size = max_size
            self.ttl_seconds = ttl_seconds
            self.cache: OrderedDict[str, CacheEntry] = OrderedDict()
            self.hits = 0
            self.misses = 0
        
        def get(self, key: str) -> Optional[Any]:
            """Get value from cache."""
            if key not in self.cache:
                self.misses += 1
                return None
            
            entry = self.cache[key]
            
            # Check if expired
            if entry.is_expired():
                del self.cache[key]
                self.misses += 1
                return None
            
            # Move to end (most recently used)
            self.cache.move_to_end(key)
            entry.access()
            self.hits += 1
            return entry.value
        
        def set(self, key: str, value: Any):
            """Set value in cache."""
            # Remove if exists
            if key in self.cache:
                del self.cache[key]
            
            # Create entry
            entry = CacheEntry(
                key=key,
                value=value,
                created_at=datetime.now(),
                last_accessed=datetime.now(),
                access_count=0,
                ttl_seconds=self.ttl_seconds
            )
            
            self.cache[key] = entry
            
            # Evict oldest if over size
            if len(self.cache) > self.max_size:
                self.cache.popitem(last=False)
        
        def clear(self):
            """Clear cache."""
            self.cache.clear()
            self.hits = 0
            self.misses = 0
        
        def get_stats(self) -> Dict[str, Any]:
            """Get cache statistics."""
            total = self.hits + self.misses
            hit_rate = self.hits / total if total > 0 else 0
            return {
                "size": len(self.cache),
                "max_size": self.max_size,
                "hits": self.hits,
                "misses": self.misses,
                "hit_rate": f"{hit_rate:.2%}",
                "total_requests": total
            }
    
    
    class SemanticCache:
        """Cache based on semantic similarity of inputs."""
        
        def __init__(self, similarity_threshold: float = 0.9, max_size: int = 100):
            """Initialize semantic cache."""
            self.similarity_threshold = similarity_threshold
            self.max_size = max_size
            self.entries: List[Tuple[str, Any, str]] = []  # (key, value, original_text)
            self.hits = 0
            self.misses = 0
        
        def _compute_similarity(self, text1: str, text2: str) -> float:
            """Compute simple similarity (in production, use embeddings)."""
            # Simple word overlap similarity
            words1 = set(text1.lower().split())
            words2 = set(text2.lower().split())
            
            if not words1 or not words2:
                return 0.0
            
            intersection = words1.intersection(words2)
            union = words1.union(words2)
            
            return len(intersection) / len(union)
        
        def get(self, query: str) -> Optional[Any]:
            """Get value from cache based on semantic similarity."""
            for key, value, original_text in self.entries:
                similarity = self._compute_similarity(query, original_text)
                if similarity >= self.similarity_threshold:
                    self.hits += 1
                    return value
            
            self.misses += 1
            return None
        
        def set(self, query: str, value: Any):
            """Set value in cache."""
            key = hashlib.md5(query.encode()).hexdigest()
            self.entries.append((key, value, query))
            
            # Evict oldest if over size
            if len(self.entries) > self.max_size:
                self.entries.pop(0)
        
        def get_stats(self) -> Dict[str, Any]:
            """Get cache statistics."""
            total = self.hits + self.misses
            hit_rate = self.hits / total if total > 0 else 0
            return {
                "size": len(self.entries),
                "max_size": self.max_size,
                "hits": self.hits,
                "misses": self.misses,
                "hit_rate": f"{hit_rate:.2%}",
                "similarity_threshold": self.similarity_threshold
            }
    
    
    def memoize(cache: Optional[LRUCache] = None, ttl_seconds: Optional[float] = None):
        """Decorator for function memoization."""
        if cache is None:
            cache = LRUCache(ttl_seconds=ttl_seconds)
        
        def decorator(func: Callable) -> Callable:
            @wraps(func)
            def wrapper(*args, **kwargs):
                # Create cache key from arguments
                key_data = {
                    "func": func.__name__,
                    "args": args,
                    "kwargs": kwargs
                }
                key = hashlib.md5(
                    json.dumps(key_data, sort_keys=True, default=str).encode()
                ).hexdigest()
                
                # Check cache
                cached_result = cache.get(key)
                if cached_result is not None:
                    return cached_result
                
                # Compute and cache
                result = func(*args, **kwargs)
                cache.set(key, result)
                return result
            
            wrapper.cache = cache
            return wrapper
        
        return decorator
    
    
    class MemoizationAgent:
        """
        Agent that uses memoization for performance optimization.
        
        This agent caches results to avoid redundant computation,
        dramatically improving performance for repeated operations.
        """
        
        def __init__(self, temperature: float = 0.3):
            """Initialize the memoization agent."""
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            
            # Different caches for different purposes
            self.llm_cache = LRUCache(max_size=100, ttl_seconds=3600)
            self.computation_cache = LRUCache(max_size=50, ttl_seconds=None)
            self.semantic_cache = SemanticCache(similarity_threshold=0.85)
            
            # Create LLM chain (we'll cache its results)
            self.chain = (
                ChatPromptTemplate.from_template("{query}")
                | self.llm
                | StrOutputParser()
            )
        
        def query_llm_cached(self, query: str, use_semantic: bool = False) -> str:
            """Query LLM with caching."""
            # Create cache key
            cache_key = hashlib.md5(query.encode()).hexdigest()
            
            # Try semantic cache first if enabled
            if use_semantic:
                result = self.semantic_cache.get(query)
                if result:
                    return f"[SEMANTIC CACHE HIT] {result}"
            
            # Try exact cache
            result = self.llm_cache.get(cache_key)
            if result:
                return f"[CACHE HIT] {result}"
            
            # Cache miss - query LLM
            result = self.chain.invoke({"query": query})
            
            # Store in caches
            self.llm_cache.set(cache_key, result)
            if use_semantic:
                self.semantic_cache.set(query, result)
            
            return f"[CACHE MISS] {result}"
        
        @memoize(ttl_seconds=300)
        def expensive_computation(self, value: int) -> int:
            """Simulate expensive computation with memoization."""
            time.sleep(0.5)  # Simulate expensive work
            return value ** 2
        
        def cache_warm(self, common_queries: List[str]):
            """Warm up cache with common queries."""
            print(f"Warming cache with {len(common_queries)} common queries...")
            for query in common_queries:
                result = self.chain.invoke({"query": query})
                cache_key = hashlib.md5(query.encode()).hexdigest()
                self.llm_cache.set(cache_key, result)
                self.semantic_cache.set(query, result)
            print("Cache warming complete")
        
        def invalidate_cache(self, pattern: Optional[str] = None):
            """Invalidate cache entries."""
            if pattern is None:
                # Clear all caches
                self.llm_cache.clear()
                self.computation_cache.clear()
                print("All caches cleared")
            else:
                print(f"Pattern-based invalidation not implemented for: {pattern}")
        
        def get_all_stats(self) -> Dict[str, Any]:
            """Get statistics for all caches."""
            return {
                "llm_cache": self.llm_cache.get_stats(),
                "computation_cache": self.computation_cache.get_stats(),
                "semantic_cache": self.semantic_cache.get_stats()
            }
    
    
    def demonstrate_memoization():
        """Demonstrate the result memoization pattern."""
        print("=" * 80)
        print("Result Memoization Pattern Demonstration")
        print("=" * 80)
        
        agent = MemoizationAgent()
        
        # Demonstration 1: Basic Function Memoization
        print("\n" + "=" * 80)
        print("Demonstration 1: Basic Function Memoization")
        print("=" * 80)
        
        print("\nFirst call (cache miss, will take 0.5s):")
        start = time.time()
        result1 = agent.expensive_computation(10)
        elapsed1 = time.time() - start
        print(f"Result: {result1}, Time: {elapsed1:.3f}s")
        
        print("\nSecond call with same argument (cache hit, instant):")
        start = time.time()
        result2 = agent.expensive_computation(10)
        elapsed2 = time.time() - start
        print(f"Result: {result2}, Time: {elapsed2:.3f}s")
        
        print(f"\nSpeedup: {elapsed1/elapsed2:.1f}x faster")
        
        # Demonstration 2: LLM Response Caching
        print("\n" + "=" * 80)
        print("Demonstration 2: LLM Response Caching")
        print("=" * 80)
        
        query = "What is 2+2?"
        
        print(f"\nQuery: {query}")
        print("First call (cache miss):")
        start = time.time()
        response1 = agent.query_llm_cached(query)
        elapsed1 = time.time() - start
        print(f"Response: {response1[:100]}...")
        print(f"Time: {elapsed1:.3f}s")
        
        print("\nSecond call (cache hit):")
        start = time.time()
        response2 = agent.query_llm_cached(query)
        elapsed2 = time.time() - start
        print(f"Response: {response2[:100]}...")
        print(f"Time: {elapsed2:.3f}s")
        
        # Demonstration 3: Semantic Caching
        print("\n" + "=" * 80)
        print("Demonstration 3: Semantic Caching")
        print("=" * 80)
        
        query1 = "What is the capital of France?"
        query2 = "Tell me the capital city of France"  # Similar semantic meaning
        
        print(f"\nFirst query: {query1}")
        response1 = agent.query_llm_cached(query1, use_semantic=True)
        print(f"Response: {response1[:80]}...")
        
        print(f"\nSimilar query: {query2}")
        response2 = agent.query_llm_cached(query2, use_semantic=True)
        print(f"Response: {response2[:80]}...")
        
        if "[SEMANTIC CACHE HIT]" in response2:
            print("\n✓ Semantic cache successfully matched similar query!")
        
        # Demonstration 4: Cache Warming
        print("\n" + "=" * 80)
        print("Demonstration 4: Cache Warming")
        print("=" * 80)
        
        common_queries = [
            "What is the weather?",
            "Tell me a joke",
            "Hello, how are you?"
        ]
        
        agent.cache_warm(common_queries)
        
        print("\nQuerying warmed cache:")
        for query in common_queries[:2]:
            response = agent.query_llm_cached(query)
            print(f"  {query}: {response[:60]}...")
        
        # Demonstration 5: TTL Expiration
        print("\n" + "=" * 80)
        print("Demonstration 5: TTL (Time-To-Live) Expiration")
        print("=" * 80)
        
        # Create cache with short TTL
        short_ttl_cache = LRUCache(max_size=10, ttl_seconds=2)
        
        @memoize(cache=short_ttl_cache)
        def short_lived_computation(x):
            time.sleep(0.1)
            return x * 2
        
        print("\nComputation with 2-second TTL:")
        print("First call:")
        result1 = short_lived_computation(5)
        print(f"Result: {result1}")
        
        print("\nImmediate second call (within TTL):")
        result2 = short_lived_computation(5)
        print(f"Result: {result2} (cached)")
        
        print("\nWaiting 2.5 seconds for TTL expiration...")
        time.sleep(2.5)
        
        print("Third call (after TTL expiration):")
        result3 = short_lived_computation(5)
        print(f"Result: {result3} (recomputed)")
        
        # Demonstration 6: LRU Eviction
        print("\n" + "=" * 80)
        print("Demonstration 6: LRU Cache Eviction")
        print("=" * 80)
        
        small_cache = LRUCache(max_size=3)
        
        print("\nCache with max size 3:")
        print("Adding 5 items...")
        for i in range(5):
            small_cache.set(f"key_{i}", f"value_{i}")
            print(f"  Added key_{i}, cache size: {len(small_cache.cache)}")
        
        print(f"\nFinal cache size: {len(small_cache.cache)} (oldest evicted)")
        print(f"Remaining keys: {list(small_cache.cache.keys())}")
        
        # Demonstration 7: Cache Invalidation
        print("\n" + "=" * 80)
        print("Demonstration 7: Cache Invalidation")
        print("=" * 80)
        
        print("\nQuerying LLM to populate cache...")
        agent.query_llm_cached("Test query 1")
        agent.query_llm_cached("Test query 2")
        
        stats_before = agent.get_all_stats()
        print(f"LLM cache size before: {stats_before['llm_cache']['size']}")
        
        print("\nInvalidating all caches...")
        agent.invalidate_cache()
        
        stats_after = agent.get_all_stats()
        print(f"LLM cache size after: {stats_after['llm_cache']['size']}")
        
        # Demonstration 8: Cache Statistics
        print("\n" + "=" * 80)
        print("Demonstration 8: Cache Performance Statistics")
        print("=" * 80)
        
        # Generate some cache activity
        for i in range(10):
            agent.query_llm_cached(f"Query {i % 3}")  # Repeat some queries
        
        stats = agent.get_all_stats()
        
        print("\nLLM Cache Statistics:")
        for key, value in stats['llm_cache'].items():
            print(f"  {key}: {value}")
        
        print("\nSemantic Cache Statistics:")
        for key, value in stats['semantic_cache'].items():
            print(f"  {key}: {value}")
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Result Memoization Pattern")
        print("=" * 80)
        print("""
    The Result Memoization pattern dramatically improves performance through caching:
    
    Key Features Demonstrated:
    1. Function Memoization - Cache expensive computations
    2. LLM Response Caching - Avoid redundant API calls
    3. Semantic Caching - Match similar queries
    4. Cache Warming - Preload common queries
    5. TTL Expiration - Auto-invalidate stale data
    6. LRU Eviction - Manage cache size
    7. Cache Invalidation - Clear stale entries
    8. Performance Metrics - Track cache effectiveness
    
    Benefits:
    - Dramatically reduces latency
    - Lowers computational costs
    - Reduces API calls
    - Improves scalability
    - Provides consistent responses
    - Reduces load on services
    
    Best Practices:
    - Set appropriate cache sizes
    - Implement TTL for freshness
    - Monitor cache hit rates
    - Use distributed caching for scale
    - Handle cache misses gracefully
    - Implement cache warming
    - Monitor memory usage
    - Use appropriate invalidation
    - Consider cache coherence
    - Profile cache effectiveness
    
    Common Use Cases:
    - Repeated queries in chatbots
    - Expensive computations
    - API call reduction
    - Database query caching
    - Embedding generation
    - Translation caching
    - Recommendation systems
    - Data processing pipelines
    
    Performance Considerations:
    - Memory overhead for cache storage
    - Balance cache size vs. hit rate
    - Implement appropriate eviction policies
    - Monitor for stale data
    - Consider distributed caching
    - Profile actual performance gains
    
    This pattern is essential for production AI systems requiring high performance
    and cost optimization through intelligent caching.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_memoization()
    
        

    Agentic Design Pattern: Result Memoization

    Pattern 143: Result Memoization – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Result Memoization pattern implementation.


    Pattern Description

    Pattern 143: Result Memoization

    Description: The Result Memoization pattern caches computation results to avoid redundant processing, significantly improving performance for repeated operations. When an agent encounters the same input or query it has processed before, it returns the cached result immediately instead of recomputing. This pattern is essential for production AI systems where identical or similar queries occur frequently.

    Memoization provides dramatic performance improvements for deterministic operations,
    expensive computations, and frequently accessed data. It trades memory for speed,
    storing results with appropriate cache keys, expiration policies, and invalidation
    strategies. The pattern supports various caching strategies including LRU, TTL,
    and semantic similarity-based caching.
    
    This implementation provides comprehensive memoization capabilities including
    function-level caching, LLM response caching, semantic similarity caching,
    cache warming, invalidation strategies, and performance metrics. It handles
    both simple key-value caching and advanced semantic caching for AI operations.
    

    Components: – Function Memoization: Cache function results by arguments – LLM Response Caching: Cache model outputs for queries – Semantic Caching: Cache based on semantic similarity – Cache Warming: Preload frequently accessed results – Invalidation Strategy: Remove stale cache entries – Performance Metrics: Track cache effectiveness

    Use Cases: – Repeated queries in chatbots – Expensive computations – API call reduction – Database query caching – Embedding generation – Translation caching – Recommendation systems – Data processing pipelines

    LangChain Implementation: This implementation uses: – Python functools.lru_cache for function memoization – Custom cache implementations with TTL – Semantic similarity-based caching – LangChain cache integration – Hash-based cache keys – Cache metrics and monitoring

    Benefits: – Dramatically reduces latency – Lowers computational costs – Reduces API calls – Improves scalability – Provides consistent responses – Enables offline operation – Reduces load on services

    Trade-offs: – Memory overhead for cache storage – Stale data if not invalidated properly – Cache consistency challenges – Cold start performance – Complex invalidation logic – Potential memory leaks

    Production Considerations: – Set appropriate cache sizes – Implement TTL for freshness – Monitor cache hit rates – Use distributed caching for scale – Handle cache misses gracefully – Implement cache warming – Monitor memory usage – Use appropriate invalidation – Consider cache coherence – Profile cache effectiveness – Implement cache preloading – Handle cache failures


    File Statistics

    • Total Lines: 603
    • Classes: 4
    • Functions: 2
    • Constants: 0

    Module Documentation

    """
    Pattern 143: Result Memoization
    
    Description:
        The Result Memoization pattern caches computation results to avoid redundant
        processing, significantly improving performance for repeated operations. When
        an agent encounters the same input or query it has processed before, it returns
        the cached result immediately instead of recomputing. This pattern is essential
        for production AI systems where identical or similar queries occur frequently.
    
        Memoization provides dramatic performance improvements for deterministic operations,
        expensive computations, and frequently accessed data. It trades memory for speed,
        storing results with appropriate cache keys, expiration policies, and invalidation
        strategies. The pattern supports various caching strategies including LRU, TTL,
        and semantic similarity-based caching.
    
        This implementation provides comprehensive memoization capabilities including
        function-level caching, LLM response caching, semantic similarity caching,
        cache warming, invalidation strategies, and performance metrics. It handles
        both simple key-value caching and advanced semantic caching for AI operations.
    
    Components:
        - Function Memoization: Cache function results by arguments
        - LLM Response Caching: Cache model outputs for queries
        - Semantic Caching: Cache based on semantic similarity
        - Cache Warming: Preload frequently accessed results
        - Invalidation Strategy: Remove stale cache entries
        - Performance Metrics: Track cache effectiveness
    
    Use Cases:
        - Repeated queries in chatbots
        - Expensive computations
        - API call reduction
        - Database query caching
        - Embedding generation
        - Translation caching
        - Recommendation systems
        - Data processing pipelines
    
    LangChain Implementation:
        This implementation uses:
        - Python functools.lru_cache for function memoization
        - Custom cache implementations with TTL
        - Semantic similarity-based caching
        - LangChain cache integration
        - Hash-based cache keys
        - Cache metrics and monitoring
    
    Benefits:
        - Dramatically reduces latency
        - Lowers computational costs
        - Reduces API calls
        - Improves scalability
        - Provides consistent responses
        - Enables offline operation
        - Reduces load on services
    
    Trade-offs:
        - Memory overhead for cache storage
        - Stale data if not invalidated properly
        - Cache consistency challenges
        - Cold start performance
        - Complex invalidation logic
        - Potential memory leaks
    
    Production Considerations:
        - Set appropriate cache sizes
        - Implement TTL for freshness
        - Monitor cache hit rates
        - Use distributed caching for scale
        - Handle cache misses gracefully
        - Implement cache warming
        - Monitor memory usage
        - Use appropriate invalidation
        - Consider cache coherence
        - Profile cache effectiveness
        - Implement cache preloading
        - Handle cache failures
    """
    

    Purpose: This module implements the Result Memoization pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import collections.OrderedDict
    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import datetime.timedelta
    import functools.lru_cache
    import functools.wraps
    import json
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import time
    import typing.Any
    import typing.Callable
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Tuple
    

    Purpose: Core Python functionality: – collections.OrderedDict: Regular expression operations – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – datetime.timedelta: Date and time handling – functools.lru_cache: Higher-order functions and operations on callable objects – functools.wraps: Higher-order functions and operations on callable objects – json: JSON serialization/deserialization – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – time: Time-related functions – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Callable: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Tuple: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    from hashlib
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: CacheEntry

    Lines 100-119

    Purpose:

    Cache entry with metadata.
    

    Methods:

    is_expired(self)

    Lines 109-114

    Purpose:

    Check if entry is expired.
    

    Parameters:

    Returns: bool

    access(self)

    Lines 116-119

    Purpose:

    Record access.
    

    Parameters:


    Class: LRUCache

    Lines 122-192

    Purpose:

    LRU (Least Recently Used) Cache implementation.
    

    Methods:

    __init__(self, max_size, ttl_seconds)

    Lines 125-131

    Purpose:

    Initialize LRU cache.
    

    Parameters: – max_size: Max size – ttl_seconds: Ttl seconds

    get(self, key)

    Lines 133-151

    Purpose:

    Get value from cache.
    

    Parameters: – key: Key

    Returns: <ast.Subscript object at 0x000001A06D811350>

    set(self, key, value)

    Lines 153-173

    Purpose:

    Set value in cache.
    

    Parameters: – key: Key – value: Value

    clear(self)

    Lines 175-179

    Purpose:

    Clear cache.
    

    Parameters:

    get_stats(self)

    Lines 181-192

    Purpose:

    Get cache statistics.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D164C10>


    Class: SemanticCache

    Lines 195-251

    Purpose:

    Cache based on semantic similarity of inputs.
    

    Methods:

    __init__(self, similarity_threshold, max_size)

    Lines 198-204

    Purpose:

    Initialize semantic cache.
    

    Parameters: – similarity_threshold: Similarity threshold – max_size: Max size

    _compute_similarity(self, text1, text2)

    Lines 206-218

    Purpose:

    Compute simple similarity (in production, use embeddings).
    

    Parameters: – text1: Text1 – text2: Text2

    Returns: float

    get(self, query)

    Lines 220-229

    Purpose:

    Get value from cache based on semantic similarity.
    

    Parameters: – query: Input query or question

    Returns: <ast.Subscript object at 0x000001A06D669D10>

    set(self, query, value)

    Lines 231-238

    Purpose:

    Set value in cache.
    

    Parameters: – query: Input query or question – value: Value

    get_stats(self)

    Lines 240-251

    Purpose:

    Get cache statistics.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D859510>


    Class: MemoizationAgent

    Lines 288-370

    Purpose:

    Agent that uses memoization for performance optimization.
    
    This agent caches results to avoid redundant computation,
    dramatically improving performance for repeated operations.
    

    Methods:

    __init__(self, temperature)

    Lines 296-310

    Purpose:

    Initialize the memoization agent.
    

    Parameters: – temperature: Sampling temperature (0-1)

    query_llm_cached(self, query, use_semantic)

    Lines 312-336

    Purpose:

    Query LLM with caching.
    

    Parameters: – query: Input query or question – use_semantic: Use semantic

    Returns: str

    expensive_computation(self, value)

    Lines 339-342

    Decorators:

    Purpose:

    Simulate expensive computation with memoization.
    

    Parameters: – value: Value

    Returns: int

    cache_warm(self, common_queries)

    Lines 344-352

    Purpose:

    Warm up cache with common queries.
    

    Parameters: – common_queries: Common queries

    invalidate_cache(self, pattern)

    Lines 354-362

    Purpose:

    Invalidate cache entries.
    

    Parameters: – pattern: Pattern

    get_all_stats(self)

    Lines 364-370

    Purpose:

    Get statistics for all caches.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D605C10>


    Functions

    memoize(cache, ttl_seconds)

    Lines 254-285

    Purpose:

    Decorator for function memoization.
    

    Parameters: – cache: Cache – ttl_seconds: Ttl seconds

    demonstrate_memoization()

    Lines 373-598

    Purpose:

    Demonstrate the result memoization pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Result Memoization pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 142
    • Pattern 144

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Result Memoization pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 143_result_memoization.py
    Total Lines: 603
    Pattern Category: AI Agent Design Pattern #143


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Speculative Execution

    
        """
    Pattern 142: Speculative Execution
    
    Description:
        The Speculative Execution pattern optimizes performance by predicting and executing
        likely operations before they're actually needed, enabling parallel processing and
        reduced latency. The agent anticipates probable next steps and begins computation
        proactively, discarding results if predictions prove incorrect. This pattern is
        particularly valuable for operations with high latency or when multiple potential
        paths exist.
    
        Speculative execution leverages parallelism to compute multiple branches
        simultaneously, betting that at least one will be correct. When predictions succeed,
        the agent gains significant performance improvements by having results ready
        immediately. When wrong, the cost is the wasted computation, which must be balanced
        against the benefits.
    
        This implementation provides speculative execution capabilities including predictive
        computation, parallel branch execution, result management, confidence-based
        speculation, and waste tracking. It supports both simple lookahead and complex
        multi-path speculation with cancellation mechanisms.
    
    Components:
        - Predictive Computation: Anticipate likely operations
        - Parallel Execution: Run multiple possibilities simultaneously
        - Result Management: Handle multiple speculative results
        - Confidence-Based: Speculate based on prediction confidence
        - Waste Tracking: Monitor efficiency of speculation
        - Cancellation: Stop incorrect speculations early
    
    Use Cases:
        - High-latency operations (API calls, database queries)
        - Branching workflows with predictable paths
        - User interaction prediction
        - Data prefetching
        - Multi-modal processing
        - Recommendation systems
        - Search optimization
        - Decision trees
    
    LangChain Implementation:
        This implementation uses:
        - Parallel chain execution with asyncio
        - Predictive path selection with LLM
        - Speculative result caching
        - Confidence-based speculation triggers
        - Waste tracking and efficiency metrics
        - Result selection and fallback
    
    Benefits:
        - Reduces perceived latency
        - Improves responsiveness
        - Enables parallel processing
        - Optimizes resource utilization
        - Handles uncertainty gracefully
        - Provides fallback options
        - Accelerates common paths
    
    Trade-offs:
        - Wastes resources on incorrect predictions
        - Increases complexity
        - May overload systems
        - Requires careful tuning
        - Can mask performance issues
        - Needs good prediction models
        - Risk of cascade failures
    
    Production Considerations:
        - Monitor speculation accuracy
        - Set resource limits
        - Implement cancellation
        - Track waste metrics
        - Use confidence thresholds
        - Limit speculation depth
        - Handle failures gracefully
        - Provide fallback paths
        - Consider cost implications
        - Test prediction models
        - Monitor system load
        - Balance parallelism
    """
    
    import os
    import time
    import asyncio
    from typing import List, Dict, Any, Optional, Callable, Tuple
    from datetime import datetime
    from enum import Enum
    from dataclasses import dataclass, field
    from concurrent.futures import ThreadPoolExecutor, Future
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class SpeculationStatus(Enum):
        """Status of speculative execution."""
        PENDING = "pending"
        RUNNING = "running"
        COMPLETED = "completed"
        CANCELLED = "cancelled"
        FAILED = "failed"
        USED = "used"
        WASTED = "wasted"
    
    
    @dataclass
    class SpeculativeTask:
        """Represents a speculative task."""
        task_id: str
        path_id: str
        computation: Callable[[], Any]
        confidence: float
        status: SpeculationStatus = SpeculationStatus.PENDING
        result: Optional[Any] = None
        start_time: Optional[datetime] = None
        end_time: Optional[datetime] = None
        execution_time: Optional[float] = None
        
        def execute(self) -> Any:
            """Execute the speculative task."""
            self.status = SpeculationStatus.RUNNING
            self.start_time = datetime.now()
            try:
                self.result = self.computation()
                self.status = SpeculationStatus.COMPLETED
            except Exception as e:
                self.status = SpeculationStatus.FAILED
                self.result = None
            finally:
                self.end_time = datetime.now()
                if self.start_time and self.end_time:
                    self.execution_time = (self.end_time - self.start_time).total_seconds()
            return self.result
    
    
    @dataclass
    class SpeculationMetrics:
        """Metrics for speculative execution."""
        total_speculations: int = 0
        successful: int = 0
        wasted: int = 0
        cancelled: int = 0
        failed: int = 0
        total_time_saved: float = 0.0
        total_time_wasted: float = 0.0
        
        @property
        def success_rate(self) -> float:
            """Calculate speculation success rate."""
            total = self.successful + self.wasted
            return self.successful / total if total > 0 else 0.0
        
        @property
        def efficiency(self) -> float:
            """Calculate efficiency (time saved vs wasted)."""
            total = self.total_time_saved + self.total_time_wasted
            return self.total_time_saved / total if total > 0 else 0.0
    
    
    class SpeculativeExecutionAgent:
        """
        Agent that uses speculative execution for performance optimization.
        
        This agent anticipates likely operations and executes them proactively,
        reducing latency when predictions are correct.
        """
        
        def __init__(self, temperature: float = 0.3, max_workers: int = 4):
            """Initialize the speculative execution agent."""
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.max_workers = max_workers
            self.executor = ThreadPoolExecutor(max_workers=max_workers)
            self.active_tasks: Dict[str, SpeculativeTask] = {}
            self.metrics = SpeculationMetrics()
            
            # Speculation thresholds
            self.min_confidence = 0.3  # Minimum confidence to speculate
            self.max_parallel = 3  # Maximum parallel speculations
            
            # Create path prediction chain
            prediction_prompt = ChatPromptTemplate.from_template(
                """You are an execution path predictor. Predict the most likely next steps.
    
    Current Context: {context}
    Current State: {state}
    Available Paths: {available_paths}
    
    Based on the context and patterns, predict which paths are most likely to be taken.
    
    Respond in this format:
    PATH_1: path_name (confidence: 0-1)
    PATH_2: path_name (confidence: 0-1)
    PATH_3: path_name (confidence: 0-1)
    REASONING: explanation"""
            )
            self.prediction_chain = prediction_prompt | self.llm | StrOutputParser()
        
        def predict_paths(
            self,
            context: Dict[str, Any],
            available_paths: List[str]
        ) -> List[Tuple[str, float]]:
            """
            Predict likely execution paths with confidence scores.
            
            Returns:
                List of (path_name, confidence) tuples
            """
            try:
                result = self.prediction_chain.invoke({
                    "context": str(context),
                    "state": context.get("current_state", "unknown"),
                    "available_paths": ", ".join(available_paths)
                })
                
                # Parse predictions
                predictions = []
                for line in result.split('\n'):
                    if line.startswith("PATH_"):
                        parts = line.split(":")
                        if len(parts) >= 2:
                            path_part = parts[1].strip()
                            # Extract path name and confidence
                            if "(confidence:" in path_part:
                                path_name = path_part.split("(confidence:")[0].strip()
                                confidence_str = path_part.split("(confidence:")[1].split(")")[0].strip()
                                try:
                                    confidence = float(confidence_str)
                                    predictions.append((path_name, confidence))
                                except ValueError:
                                    pass
                
                return predictions[:self.max_parallel]
                
            except Exception as e:
                print(f"Error predicting paths: {e}")
                # Default: equal confidence for all paths
                return [(path, 1.0 / len(available_paths)) for path in available_paths[:self.max_parallel]]
        
        def speculate(
            self,
            paths: Dict[str, Callable[[], Any]],
            context: Dict[str, Any]
        ) -> Dict[str, Future]:
            """
            Execute multiple paths speculatively in parallel.
            
            Args:
                paths: Dictionary of path_name -> computation function
                context: Context for path prediction
                
            Returns:
                Dictionary of path_name -> Future
            """
            # Predict likely paths
            predictions = self.predict_paths(context, list(paths.keys()))
            
            # Filter by confidence threshold
            valid_predictions = [
                (path, conf) for path, conf in predictions
                if conf >= self.min_confidence and path in paths
            ]
            
            # Submit speculative tasks
            futures = {}
            for path_name, confidence in valid_predictions:
                task = SpeculativeTask(
                    task_id=f"spec_{len(self.active_tasks)}_{time.time()}",
                    path_id=path_name,
                    computation=paths[path_name],
                    confidence=confidence
                )
                self.active_tasks[task.task_id] = task
                futures[path_name] = self.executor.submit(task.execute)
                self.metrics.total_speculations += 1
            
            return futures
        
        def select_result(
            self,
            futures: Dict[str, Future],
            actual_path: str,
            timeout: float = 10.0
        ) -> Any:
            """
            Select the result from the actual path taken.
            
            Args:
                futures: Dictionary of path_name -> Future
                actual_path: The path that was actually taken
                timeout: Maximum time to wait for result
                
            Returns:
                Result from the actual path
            """
            if actual_path not in futures:
                raise ValueError(f"Actual path {actual_path} not in futures")
            
            # Wait for the actual path result
            actual_future = futures[actual_path]
            try:
                result = actual_future.result(timeout=timeout)
                
                # Update metrics for actual path
                actual_task = self._find_task_by_path(actual_path)
                if actual_task:
                    actual_task.status = SpeculationStatus.USED
                    self.metrics.successful += 1
                    if actual_task.execution_time:
                        # Time saved is the time it would have taken if we waited
                        self.metrics.total_time_saved += actual_task.execution_time
                
                # Cancel and mark other paths as wasted
                for path_name, future in futures.items():
                    if path_name != actual_path:
                        future.cancel()
                        task = self._find_task_by_path(path_name)
                        if task:
                            if task.status == SpeculationStatus.COMPLETED:
                                task.status = SpeculationStatus.WASTED
                                self.metrics.wasted += 1
                                if task.execution_time:
                                    self.metrics.total_time_wasted += task.execution_time
                            else:
                                task.status = SpeculationStatus.CANCELLED
                                self.metrics.cancelled += 1
                
                return result
                
            except Exception as e:
                self.metrics.failed += 1
                raise e
        
        def _find_task_by_path(self, path_name: str) -> Optional[SpeculativeTask]:
            """Find task by path name."""
            for task in self.active_tasks.values():
                if task.path_id == path_name:
                    return task
            return None
        
        def speculative_search(
            self,
            query: str,
            search_strategies: Dict[str, Callable[[str], Any]]
        ) -> Any:
            """
            Speculatively execute multiple search strategies.
            
            Args:
                query: Search query
                search_strategies: Dictionary of strategy_name -> search function
                
            Returns:
                Best search result
            """
            context = {"query": query, "query_type": "search"}
            
            # Create speculation tasks
            tasks = {
                name: lambda name=name: strategy(query)
                for name, strategy in search_strategies.items()
            }
            
            # Execute speculatively
            futures = self.speculate(tasks, context)
            
            # For search, we take the first one that completes
            completed_results = {}
            for name, future in futures.items():
                try:
                    result = future.result(timeout=5.0)
                    completed_results[name] = result
                except Exception:
                    pass
            
            # Return first completed result
            if completed_results:
                first_strategy = list(completed_results.keys())[0]
                return self.select_result(futures, first_strategy, timeout=0.1)
            
            return None
        
        def prefetch_data(
            self,
            likely_requests: List[str],
            fetch_function: Callable[[str], Any],
            confidence_scores: Optional[List[float]] = None
        ) -> Dict[str, Future]:
            """
            Prefetch data for likely requests.
            
            Args:
                likely_requests: List of likely data requests
                fetch_function: Function to fetch data
                confidence_scores: Optional confidence for each request
                
            Returns:
                Dictionary of request -> Future
            """
            if confidence_scores is None:
                confidence_scores = [1.0 / len(likely_requests)] * len(likely_requests)
            
            futures = {}
            for request, confidence in zip(likely_requests, confidence_scores):
                if confidence >= self.min_confidence:
                    task = SpeculativeTask(
                        task_id=f"prefetch_{len(self.active_tasks)}_{time.time()}",
                        path_id=request,
                        computation=lambda req=request: fetch_function(req),
                        confidence=confidence
                    )
                    self.active_tasks[task.task_id] = task
                    futures[request] = self.executor.submit(task.execute)
                    self.metrics.total_speculations += 1
            
            return futures
        
        def get_metrics(self) -> Dict[str, Any]:
            """Get speculation metrics."""
            return {
                "total_speculations": self.metrics.total_speculations,
                "successful": self.metrics.successful,
                "wasted": self.metrics.wasted,
                "cancelled": self.metrics.cancelled,
                "failed": self.metrics.failed,
                "success_rate": f"{self.metrics.success_rate:.2%}",
                "efficiency": f"{self.metrics.efficiency:.2%}",
                "time_saved": f"{self.metrics.total_time_saved:.2f}s",
                "time_wasted": f"{self.metrics.total_time_wasted:.2f}s",
                "net_benefit": f"{self.metrics.total_time_saved - self.metrics.total_time_wasted:.2f}s"
            }
        
        def shutdown(self):
            """Shutdown the executor."""
            self.executor.shutdown(wait=True)
    
    
    def demonstrate_speculative_execution():
        """Demonstrate the speculative execution pattern."""
        print("=" * 80)
        print("Speculative Execution Pattern Demonstration")
        print("=" * 80)
        
        agent = SpeculativeExecutionAgent(max_workers=4)
        
        try:
            # Demonstration 1: Basic Speculative Execution
            print("\n" + "=" * 80)
            print("Demonstration 1: Basic Speculative Execution")
            print("=" * 80)
            
            def path_a():
                time.sleep(0.5)
                return "Result from Path A"
            
            def path_b():
                time.sleep(0.5)
                return "Result from Path B"
            
            def path_c():
                time.sleep(0.5)
                return "Result from Path C"
            
            paths = {
                "path_a": path_a,
                "path_b": path_b,
                "path_c": path_c
            }
            
            context = {"current_state": "initial", "user_preference": "fast"}
            
            print("\nStarting speculative execution of 3 paths...")
            start = time.time()
            futures = agent.speculate(paths, context)
            print(f"Speculated paths: {list(futures.keys())}")
            
            # Simulate that path_b was the actual path taken
            actual_path = "path_b"
            print(f"\nActual path taken: {actual_path}")
            
            result = agent.select_result(futures, actual_path)
            elapsed = time.time() - start
            
            print(f"Result: {result}")
            print(f"Total time: {elapsed:.2f}s (with speculation)")
            print("Without speculation, would have taken 0.5s after decision")
            
            # Demonstration 2: Confidence-Based Speculation
            print("\n" + "=" * 80)
            print("Demonstration 2: Confidence-Based Speculation")
            print("=" * 80)
            
            def operation_high_conf():
                time.sleep(0.3)
                return "High confidence result"
            
            def operation_low_conf():
                time.sleep(0.3)
                return "Low confidence result"
            
            # Manually set predictions with different confidences
            print("\nPredictions:")
            print("  Path 1 (high confidence): 0.8")
            print("  Path 2 (low confidence): 0.2")
            
            paths = {
                "high_conf": operation_high_conf,
                "low_conf": operation_low_conf
            }
            
            # Only high confidence should be speculated
            agent.min_confidence = 0.5
            futures = agent.speculate(paths, {"confidence_test": True})
            
            print(f"\nSpeculated paths (min confidence 0.5): {list(futures.keys())}")
            
            # Demonstration 3: Prefetching
            print("\n" + "=" * 80)
            print("Demonstration 3: Data Prefetching")
            print("=" * 80)
            
            def fetch_data(item_id: str) -> str:
                """Simulate data fetching."""
                time.sleep(0.2)
                return f"Data for {item_id}"
            
            likely_requests = ["item_1", "item_2", "item_3"]
            confidence_scores = [0.7, 0.5, 0.3]
            
            print(f"Likely requests: {likely_requests}")
            print(f"Confidence scores: {confidence_scores}")
            print(f"Prefetching data speculatively...")
            
            prefetch_futures = agent.prefetch_data(
                likely_requests,
                fetch_data,
                confidence_scores
            )
            
            print(f"Prefetched: {list(prefetch_futures.keys())}")
            
            # User actually requests item_2
            actual_request = "item_2"
            print(f"\nUser requests: {actual_request}")
            
            if actual_request in prefetch_futures:
                result = agent.select_result(prefetch_futures, actual_request, timeout=1.0)
                print(f"Result (from prefetch): {result}")
            
            # Demonstration 4: Multi-Path Processing
            print("\n" + "=" * 80)
            print("Demonstration 4: Multi-Path Processing")
            print("=" * 80)
            
            def process_with_method_a(data: str) -> str:
                time.sleep(0.4)
                return f"Processed {data} with method A"
            
            def process_with_method_b(data: str) -> str:
                time.sleep(0.4)
                return f"Processed {data} with method B"
            
            def process_with_method_c(data: str) -> str:
                time.sleep(0.4)
                return f"Processed {data} with method C"
            
            data = "sample_data"
            methods = {
                "method_a": lambda: process_with_method_a(data),
                "method_b": lambda: process_with_method_b(data),
                "method_c": lambda: process_with_method_c(data)
            }
            
            context = {"data_type": "text", "processing_mode": "fast"}
            
            print(f"Processing {data} with multiple methods speculatively...")
            futures = agent.speculate(methods, context)
            print(f"Speculated methods: {list(futures.keys())}")
            
            # Decision: use method_a
            chosen_method = list(futures.keys())[0] if futures else "method_a"
            print(f"\nChosen method: {chosen_method}")
            
            result = agent.select_result(futures, chosen_method)
            print(f"Result: {result}")
            
            # Demonstration 5: Early Cancellation
            print("\n" + "=" * 80)
            print("Demonstration 5: Early Cancellation")
            print("=" * 80)
            
            def slow_operation_1():
                time.sleep(2.0)
                return "Slow result 1"
            
            def slow_operation_2():
                time.sleep(2.0)
                return "Slow result 2"
            
            def fast_operation():
                time.sleep(0.1)
                return "Fast result"
            
            paths = {
                "slow_1": slow_operation_1,
                "slow_2": slow_operation_2,
                "fast": fast_operation
            }
            
            print("Starting speculation with slow and fast paths...")
            futures = agent.speculate(paths, {"cancel_test": True})
            
            # Quickly decide on fast path
            time.sleep(0.2)
            actual_path = "fast"
            print(f"\nQuick decision: use {actual_path}")
            print("Cancelling slow speculations...")
            
            result = agent.select_result(futures, actual_path, timeout=0.5)
            print(f"Result: {result}")
            print("Slow operations were cancelled early")
            
            # Demonstration 6: Speculation Metrics
            print("\n" + "=" * 80)
            print("Demonstration 6: Speculation Metrics")
            print("=" * 80)
            
            metrics = agent.get_metrics()
            print("\nSpeculation Performance:")
            print(f"  Total Speculations: {metrics['total_speculations']}")
            print(f"  Successful: {metrics['successful']}")
            print(f"  Wasted: {metrics['wasted']}")
            print(f"  Cancelled: {metrics['cancelled']}")
            print(f"  Failed: {metrics['failed']}")
            print(f"  Success Rate: {metrics['success_rate']}")
            print(f"  Efficiency: {metrics['efficiency']}")
            print(f"  Time Saved: {metrics['time_saved']}")
            print(f"  Time Wasted: {metrics['time_wasted']}")
            print(f"  Net Benefit: {metrics['net_benefit']}")
            
            # Summary
            print("\n" + "=" * 80)
            print("Summary: Speculative Execution Pattern")
            print("=" * 80)
            print("""
    The Speculative Execution pattern optimizes performance through prediction:
    
    Key Features Demonstrated:
    1. Basic Speculation - Execute multiple paths in parallel
    2. Confidence-Based - Only speculate on likely paths
    3. Data Prefetching - Preload likely-needed data
    4. Multi-Path Processing - Try multiple approaches simultaneously
    5. Early Cancellation - Stop unused speculations quickly
    6. Metrics Tracking - Monitor speculation effectiveness
    
    Benefits:
    - Reduces perceived latency
    - Improves responsiveness
    - Enables parallel processing
    - Optimizes resource utilization
    - Provides fallback options
    - Accelerates common paths
    
    Best Practices:
    - Monitor speculation accuracy
    - Set resource limits
    - Implement cancellation
    - Track waste metrics
    - Use confidence thresholds
    - Limit speculation depth
    - Handle failures gracefully
    - Provide fallback paths
    - Consider cost implications
    - Test prediction models
    - Monitor system load
    - Balance parallelism
    
    Common Use Cases:
    - High-latency operations
    - Branching workflows
    - User interaction prediction
    - Data prefetching
    - Multi-modal processing
    - Recommendation systems
    - Search optimization
    - Decision trees
    
    Performance Considerations:
    - Speculation wastes resources on wrong predictions
    - Best for high-latency operations
    - Requires good prediction models
    - Monitor success rate and efficiency
    - Balance speculation vs. waste
    - Consider system capacity
    
    This pattern is essential for responsive AI agents that need to minimize
    latency by anticipating likely operations.
    """)
        
        finally:
            # Cleanup
            agent.shutdown()
    
    
    if __name__ == "__main__":
        demonstrate_speculative_execution()
    
        

    Agentic Design Pattern: Speculative Execution

    Pattern 142: Speculative Execution – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Speculative Execution pattern implementation.


    Pattern Description

    Pattern 142: Speculative Execution

    Description: The Speculative Execution pattern optimizes performance by predicting and executing likely operations before they’re actually needed, enabling parallel processing and reduced latency. The agent anticipates probable next steps and begins computation proactively, discarding results if predictions prove incorrect. This pattern is particularly valuable for operations with high latency or when multiple potential paths exist.

    Speculative execution leverages parallelism to compute multiple branches
    simultaneously, betting that at least one will be correct. When predictions succeed,
    the agent gains significant performance improvements by having results ready
    immediately. When wrong, the cost is the wasted computation, which must be balanced
    against the benefits.
    
    This implementation provides speculative execution capabilities including predictive
    computation, parallel branch execution, result management, confidence-based
    speculation, and waste tracking. It supports both simple lookahead and complex
    multi-path speculation with cancellation mechanisms.
    

    Components: – Predictive Computation: Anticipate likely operations – Parallel Execution: Run multiple possibilities simultaneously – Result Management: Handle multiple speculative results – Confidence-Based: Speculate based on prediction confidence – Waste Tracking: Monitor efficiency of speculation – Cancellation: Stop incorrect speculations early

    Use Cases: – High-latency operations (API calls, database queries) – Branching workflows with predictable paths – User interaction prediction – Data prefetching – Multi-modal processing – Recommendation systems – Search optimization – Decision trees

    LangChain Implementation: This implementation uses: – Parallel chain execution with asyncio – Predictive path selection with LLM – Speculative result caching – Confidence-based speculation triggers – Waste tracking and efficiency metrics – Result selection and fallback

    Benefits: – Reduces perceived latency – Improves responsiveness – Enables parallel processing – Optimizes resource utilization – Handles uncertainty gracefully – Provides fallback options – Accelerates common paths

    Trade-offs: – Wastes resources on incorrect predictions – Increases complexity – May overload systems – Requires careful tuning – Can mask performance issues – Needs good prediction models – Risk of cascade failures

    Production Considerations: – Monitor speculation accuracy – Set resource limits – Implement cancellation – Track waste metrics – Use confidence thresholds – Limit speculation depth – Handle failures gracefully – Provide fallback paths – Consider cost implications – Test prediction models – Monitor system load – Balance parallelism


    File Statistics

    • Total Lines: 712
    • Classes: 4
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 142: Speculative Execution
    
    Description:
        The Speculative Execution pattern optimizes performance by predicting and executing
        likely operations before they're actually needed, enabling parallel processing and
        reduced latency. The agent anticipates probable next steps and begins computation
        proactively, discarding results if predictions prove incorrect. This pattern is
        particularly valuable for operations with high latency or when multiple potential
        paths exist.
    
        Speculative execution leverages parallelism to compute multiple branches
        simultaneously, betting that at least one will be correct. When predictions succeed,
        the agent gains significant performance improvements by having results ready
        immediately. When wrong, the cost is the wasted computation, which must be balanced
        against the benefits.
    
        This implementation provides speculative execution capabilities including predictive
        computation, parallel branch execution, result management, confidence-based
        speculation, and waste tracking. It supports both simple lookahead and complex
        multi-path speculation with cancellation mechanisms.
    
    Components:
        - Predictive Computation: Anticipate likely operations
        - Parallel Execution: Run multiple possibilities simultaneously
        - Result Management: Handle multiple speculative results
        - Confidence-Based: Speculate based on prediction confidence
        - Waste Tracking: Monitor efficiency of speculation
        - Cancellation: Stop incorrect speculations early
    
    Use Cases:
        - High-latency operations (API calls, database queries)
        - Branching workflows with predictable paths
        - User interaction prediction
        - Data prefetching
        - Multi-modal processing
        - Recommendation systems
        - Search optimization
        - Decision trees
    
    LangChain Implementation:
        This implementation uses:
        - Parallel chain execution with asyncio
        - Predictive path selection with LLM
        - Speculative result caching
        - Confidence-based speculation triggers
        - Waste tracking and efficiency metrics
        - Result selection and fallback
    
    Benefits:
        - Reduces perceived latency
        - Improves responsiveness
        - Enables parallel processing
        - Optimizes resource utilization
        - Handles uncertainty gracefully
        - Provides fallback options
        - Accelerates common paths
    
    Trade-offs:
        - Wastes resources on incorrect predictions
        - Increases complexity
        - May overload systems
        - Requires careful tuning
        - Can mask performance issues
        - Needs good prediction models
        - Risk of cascade failures
    
    Production Considerations:
        - Monitor speculation accuracy
        - Set resource limits
        - Implement cancellation
        - Track waste metrics
        - Use confidence thresholds
        - Limit speculation depth
        - Handle failures gracefully
        - Provide fallback paths
        - Consider cost implications
        - Test prediction models
        - Monitor system load
        - Balance parallelism
    """
    

    Purpose: This module implements the Speculative Execution pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import asyncio
    import concurrent.futures.Future
    import concurrent.futures.ThreadPoolExecutor
    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import time
    import typing.Any
    import typing.Callable
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Tuple
    

    Purpose: Core Python functionality: – asyncio: Asynchronous I/O operations – concurrent.futures.Future: Regular expression operations – concurrent.futures.ThreadPoolExecutor: Regular expression operations – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – time: Time-related functions – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Callable: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Tuple: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: SpeculationStatus

    Lines 100-108

    Inherits from: Enum

    Purpose:

    Status of speculative execution.
    

    Class: SpeculativeTask

    Lines 112-138

    Purpose:

    Represents a speculative task.
    

    Methods:

    execute(self)

    Lines 124-138

    Purpose:

    Execute the speculative task.
    

    Parameters:

    Returns: Any


    Class: SpeculationMetrics

    Lines 142-162

    Purpose:

    Metrics for speculative execution.
    

    Methods:

    success_rate(self)

    Lines 153-156

    Decorators: property

    Purpose:

    Calculate speculation success rate.
    

    Parameters:

    Returns: float

    efficiency(self)

    Lines 159-162

    Decorators: property

    Purpose:

    Calculate efficiency (time saved vs wasted).
    

    Parameters:

    Returns: float


    Class: SpeculativeExecutionAgent

    Lines 165-440

    Purpose:

    Agent that uses speculative execution for performance optimization.
    
    This agent anticipates likely operations and executes them proactively,
    reducing latency when predictions are correct.
    

    Methods:

    __init__(self, temperature, max_workers)

    Lines 173-201

    Purpose:

    Initialize the speculative execution agent.
    

    Parameters: – temperature: Sampling temperature (0-1) – max_workers: Max workers

    predict_paths(self, context, available_paths)

    Lines 203-243

    Purpose:

    Predict likely execution paths with confidence scores.
    
    Returns:
        List of (path_name, confidence) tuples
    

    Parameters: – context: Context – available_paths: Available paths

    Returns: <ast.Subscript object at 0x000001A06D7F76D0>

    speculate(self, paths, context)

    Lines 245-282

    Purpose:

    Execute multiple paths speculatively in parallel.
    
    Args:
        paths: Dictionary of path_name -> computation function
        context: Context for path prediction
    
    Returns:
        Dictionary of path_name -> Future
    

    Parameters: – paths: Paths – context: Context

    Returns: <ast.Subscript object at 0x000001A06D83E910>

    select_result(self, futures, actual_path, timeout)

    Lines 284-337

    Purpose:

    Select the result from the actual path taken.
    
    Args:
        futures: Dictionary of path_name -> Future
        actual_path: The path that was actually taken
        timeout: Maximum time to wait for result
    
    Returns:
        Result from the actual path
    

    Parameters: – futures: Futures – actual_path: Actual path – timeout: Timeout

    Returns: Any

    _find_task_by_path(self, path_name)

    Lines 339-344

    Purpose:

    Find task by path name.
    

    Parameters: – path_name: Path name

    Returns: <ast.Subscript object at 0x000001A06D68E790>

    speculative_search(self, query, search_strategies)

    Lines 346-386

    Purpose:

    Speculatively execute multiple search strategies.
    
    Args:
        query: Search query
        search_strategies: Dictionary of strategy_name -> search function
    
    Returns:
        Best search result
    

    Parameters: – query: Input query or question – search_strategies: Search strategies

    Returns: Any

    prefetch_data(self, likely_requests, fetch_function, confidence_scores)

    Lines 388-421

    Purpose:

    Prefetch data for likely requests.
    
    Args:
        likely_requests: List of likely data requests
        fetch_function: Function to fetch data
        confidence_scores: Optional confidence for each request
    
    Returns:
        Dictionary of request -> Future
    

    Parameters: – likely_requests: Likely requests – fetch_function: Fetch function – confidence_scores: Confidence scores

    Returns: <ast.Subscript object at 0x000001A06D6F4710>

    get_metrics(self)

    Lines 423-436

    Purpose:

    Get speculation metrics.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D62A2D0>

    shutdown(self)

    Lines 438-440

    Purpose:

    Shutdown the executor.
    

    Parameters:


    Functions

    demonstrate_speculative_execution()

    Lines 443-707

    Purpose:

    Demonstrate the speculative execution pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Speculative Execution pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 141
    • Pattern 143

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Speculative Execution pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 142_speculative_execution.py
    Total Lines: 712
    Pattern Category: AI Agent Design Pattern #142


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Lazy Evaluation

    
        """
    Pattern 141: Lazy Evaluation
    
    Description:
        The Lazy Evaluation pattern defers computation until results are actually needed,
        optimizing resource usage by avoiding unnecessary work. Instead of eagerly computing
        all results upfront, lazy evaluation creates computation plans that execute only
        when values are requested. This pattern is particularly valuable for expensive
        operations, large datasets, and scenarios where not all computed values may be used.
    
        Lazy evaluation improves performance by reducing wasted computation, lowering
        memory usage, enabling infinite sequences, and allowing for optimization opportunities
        through computation graph analysis. It's especially useful in AI agents that might
        explore multiple paths but only need results from successful branches, or when
        processing large datasets where early termination is possible.
    
        This implementation provides lazy evaluation capabilities for LangChain agents,
        including deferred chain execution, lazy data loading, conditional computation,
        result streaming, and computation graph optimization. It supports both simple
        delayed execution and complex multi-stage lazy pipelines.
    
    Components:
        - Deferred Execution: Delay computation until needed
        - Lazy Data Loading: Load data on-demand
        - Conditional Computation: Skip unnecessary branches
        - Result Streaming: Process results incrementally
        - Graph Optimization: Optimize computation plans
        - Caching Integration: Cache lazy results
    
    Use Cases:
        - Large dataset processing
        - Multi-path exploration
        - Conditional workflows
        - Resource-constrained environments
        - Expensive API calls
        - Database query optimization
        - Streaming data processing
        - Early termination scenarios
    
    LangChain Implementation:
        This implementation uses:
        - Python generators for lazy iteration
        - Deferred chain execution with LCEL
        - Lazy data loading with document loaders
        - Conditional branching in chains
        - Streaming for incremental results
        - Custom lazy wrappers for expensive operations
    
    Benefits:
        - Reduces unnecessary computation
        - Lowers memory usage
        - Enables early termination
        - Improves responsiveness
        - Supports infinite sequences
        - Allows optimization opportunities
        - Reduces API costs
    
    Trade-offs:
        - Can make debugging harder
        - May cause unexpected delays
        - Complexity in error handling
        - Potential memory leaks if not careful
        - Harder to reason about execution flow
        - May hide performance issues
    
    Production Considerations:
        - Profile to identify expensive operations
        - Use for truly expensive computations
        - Combine with caching
        - Handle errors in lazy contexts
        - Set timeouts for lazy operations
        - Monitor actual execution patterns
        - Document lazy behavior clearly
        - Test edge cases thoroughly
        - Consider memory management
        - Balance with eager evaluation
        - Use generators appropriately
        - Profile actual vs potential savings
    """
    
    import os
    import time
    from typing import List, Dict, Any, Optional, Iterator, Callable, Generator
    from datetime import datetime
    from dataclasses import dataclass
    from functools import wraps
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    @dataclass
    class LazyResult:
        """Wrapper for lazy computation result."""
        computation: Callable[[], Any]
        _cached_value: Optional[Any] = None
        _computed: bool = False
        _computation_time: Optional[float] = None
        
        def compute(self) -> Any:
            """Execute the lazy computation."""
            if not self._computed:
                start = time.time()
                self._cached_value = self.computation()
                self._computation_time = time.time() - start
                self._computed = True
            return self._cached_value
        
        @property
        def value(self) -> Any:
            """Get the computed value."""
            return self.compute()
        
        @property
        def is_computed(self) -> bool:
            """Check if value has been computed."""
            return self._computed
        
        @property
        def computation_time(self) -> Optional[float]:
            """Get computation time in seconds."""
            return self._computation_time
    
    
    def lazy(func: Callable) -> Callable:
        """Decorator to make a function lazy."""
        @wraps(func)
        def wrapper(*args, **kwargs):
            def computation():
                return func(*args, **kwargs)
            return LazyResult(computation=computation)
        return wrapper
    
    
    class LazyChain:
        """Lazy evaluation wrapper for LangChain chains."""
        
        def __init__(self, chain):
            """Initialize lazy chain."""
            self.chain = chain
            self._result = None
            self._computed = False
        
        def compute(self, input_data: Dict[str, Any]) -> Any:
            """Execute the chain."""
            if not self._computed:
                self._result = self.chain.invoke(input_data)
                self._computed = True
            return self._result
        
        def stream(self, input_data: Dict[str, Any]) -> Iterator[Any]:
            """Stream results lazily."""
            for chunk in self.chain.stream(input_data):
                yield chunk
    
    
    class LazyDataLoader:
        """Lazy data loader that loads data on demand."""
        
        def __init__(self, data_source: str):
            """Initialize lazy data loader."""
            self.data_source = data_source
            self._data = None
            self._loaded = False
            self._load_time = None
        
        def load(self) -> List[Any]:
            """Load data (simulated)."""
            if not self._loaded:
                start = time.time()
                # Simulate expensive data loading
                time.sleep(0.1)
                self._data = [f"Item {i} from {self.data_source}" for i in range(100)]
                self._load_time = time.time() - start
                self._loaded = True
            return self._data
        
        def iter_lazy(self, chunk_size: int = 10) -> Generator[List[Any], None, None]:
            """Iterate over data in chunks."""
            data = self.load()
            for i in range(0, len(data), chunk_size):
                yield data[i:i + chunk_size]
        
        @property
        def is_loaded(self) -> bool:
            """Check if data is loaded."""
            return self._loaded
    
    
    class LazyEvaluationAgent:
        """
        Agent that uses lazy evaluation for efficient processing.
        
        This agent defers expensive computations until results are needed,
        optimizing resource usage and enabling early termination.
        """
        
        def __init__(self, temperature: float = 0.3):
            """Initialize the lazy evaluation agent."""
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.computation_stats = {
                "deferred": 0,
                "executed": 0,
                "saved": 0
            }
            
            # Create lazy chains
            self.analysis_chain = LazyChain(
                ChatPromptTemplate.from_template(
                    "Analyze this text: {text}\n\nProvide a detailed analysis."
                ) | self.llm | StrOutputParser()
            )
        
        @lazy
        def expensive_computation(self, value: int) -> int:
            """Simulate expensive computation."""
            time.sleep(0.5)  # Simulate work
            return value ** 2
        
        def lazy_filter(
            self,
            items: List[Any],
            condition: Callable[[Any], bool]
        ) -> Generator[Any, None, None]:
            """Lazy filter that yields items matching condition."""
            for item in items:
                if condition(item):
                    yield item
        
        def lazy_map(
            self,
            items: List[Any],
            transform: Callable[[Any], Any]
        ) -> Generator[Any, None, None]:
            """Lazy map that transforms items on demand."""
            for item in items:
                yield transform(item)
        
        def conditional_computation(
            self,
            condition: bool,
            true_computation: Callable[[], Any],
            false_computation: Callable[[], Any]
        ) -> LazyResult:
            """Conditionally defer computation."""
            if condition:
                return LazyResult(computation=true_computation)
            else:
                return LazyResult(computation=false_computation)
        
        def lazy_pipeline(
            self,
            data: List[Any],
            operations: List[Callable[[Any], Any]],
            early_exit: Optional[Callable[[Any], bool]] = None
        ) -> Generator[Any, None, None]:
            """
            Execute a lazy pipeline with early exit capability.
            
            Args:
                data: Input data
                operations: List of operations to apply
                early_exit: Optional function to check if we should stop early
            """
            for item in data:
                result = item
                
                # Apply operations lazily
                for op in operations:
                    result = op(result)
                    
                    # Check early exit condition
                    if early_exit and early_exit(result):
                        yield result
                        return  # Early termination
                
                yield result
        
        def parallel_lazy_evaluation(
            self,
            tasks: List[Callable[[], Any]],
            max_results: Optional[int] = None
        ) -> Generator[Any, None, None]:
            """
            Evaluate multiple lazy tasks, stopping when enough results obtained.
            
            Args:
                tasks: List of lazy computations
                max_results: Maximum number of results to compute
            """
            count = 0
            for task in tasks:
                if max_results and count >= max_results:
                    break
                
                result = LazyResult(computation=task)
                yield result
                count += 1
        
        def lazy_llm_batch(
            self,
            prompts: List[str],
            process_until: Optional[Callable[[str], bool]] = None
        ) -> Generator[str, None, None]:
            """
            Process prompts lazily, with optional early termination.
            
            Args:
                prompts: List of prompts to process
                process_until: Optional function to check if we should stop
            """
            for prompt in prompts:
                result = self.llm.invoke(prompt).content
                yield result
                
                if process_until and process_until(result):
                    break  # Early termination
        
        def get_stats(self) -> Dict[str, Any]:
            """Get computation statistics."""
            total = self.computation_stats["executed"] + self.computation_stats["saved"]
            return {
                "deferred": self.computation_stats["deferred"],
                "executed": self.computation_stats["executed"],
                "saved": self.computation_stats["saved"],
                "efficiency": (
                    self.computation_stats["saved"] / total * 100 
                    if total > 0 else 0
                )
            }
    
    
    def demonstrate_lazy_evaluation():
        """Demonstrate the lazy evaluation pattern."""
        print("=" * 80)
        print("Lazy Evaluation Pattern Demonstration")
        print("=" * 80)
        
        agent = LazyEvaluationAgent()
        
        # Demonstration 1: Basic Lazy Computation
        print("\n" + "=" * 80)
        print("Demonstration 1: Basic Lazy Computation")
        print("=" * 80)
        
        print("\nCreating lazy computation...")
        lazy_result = agent.expensive_computation(10)
        print(f"Computation created (not executed yet)")
        print(f"Is computed: {lazy_result.is_computed}")
        
        print("\nAccessing value (triggers computation)...")
        start = time.time()
        value = lazy_result.value
        elapsed = time.time() - start
        print(f"Result: {value}")
        print(f"Time: {elapsed:.3f}s")
        print(f"Is computed: {lazy_result.is_computed}")
        
        print("\nAccessing value again (uses cached)...")
        start = time.time()
        value2 = lazy_result.value
        elapsed = time.time() - start
        print(f"Result: {value2}")
        print(f"Time: {elapsed:.3f}s (cached)")
        
        # Demonstration 2: Lazy Filter
        print("\n" + "=" * 80)
        print("Demonstration 2: Lazy Filter (Early Termination)")
        print("=" * 80)
        
        numbers = list(range(1, 101))
        print(f"Processing {len(numbers)} numbers...")
        
        # Eager evaluation
        start = time.time()
        eager_result = [x for x in numbers if x % 7 == 0][:5]  # Get first 5
        eager_time = time.time() - start
        print(f"\nEager evaluation: {eager_result}")
        print(f"Time: {eager_time:.4f}s (processed all {len(numbers)} items)")
        
        # Lazy evaluation with early termination
        start = time.time()
        lazy_filtered = agent.lazy_filter(numbers, lambda x: x % 7 == 0)
        lazy_result = []
        for item in lazy_filtered:
            lazy_result.append(item)
            if len(lazy_result) >= 5:
                break  # Early termination
        lazy_time = time.time() - start
        print(f"\nLazy evaluation: {lazy_result}")
        print(f"Time: {lazy_time:.4f}s (stopped after finding 5)")
        
        # Demonstration 3: Lazy Data Loading
        print("\n" + "=" * 80)
        print("Demonstration 3: Lazy Data Loading")
        print("=" * 80)
        
        loader = LazyDataLoader("database")
        print(f"Data loader created")
        print(f"Is loaded: {loader.is_loaded}")
        
        print("\nProcessing first chunk only...")
        for chunk in loader.iter_lazy(chunk_size=10):
            print(f"Processing chunk of {len(chunk)} items")
            # Only process first chunk
            break
        
        print(f"Is loaded: {loader.is_loaded} (only loaded what was needed)")
        
        # Demonstration 4: Conditional Lazy Computation
        print("\n" + "=" * 80)
        print("Demonstration 4: Conditional Lazy Computation")
        print("=" * 80)
        
        def expensive_path_a():
            time.sleep(0.3)
            return "Result from expensive path A"
        
        def expensive_path_b():
            time.sleep(0.3)
            return "Result from expensive path B"
        
        condition = True
        print(f"Condition: {condition}")
        print("Creating conditional lazy computation...")
        
        result = agent.conditional_computation(
            condition,
            expensive_path_a,
            expensive_path_b
        )
        
        print(f"Computation deferred, is computed: {result.is_computed}")
        print("\nNow computing...")
        value = result.value
        print(f"Result: {value}")
        print(f"Computation time: {result.computation_time:.3f}s")
        
        # Demonstration 5: Lazy Pipeline with Early Exit
        print("\n" + "=" * 80)
        print("Demonstration 5: Lazy Pipeline with Early Exit")
        print("=" * 80)
        
        data = list(range(1, 21))
        
        operations = [
            lambda x: x * 2,
            lambda x: x + 10,
            lambda x: x ** 2
        ]
        
        early_exit = lambda x: x > 1000
        
        print(f"Processing {len(data)} items through pipeline...")
        print("Early exit when result > 1000")
        
        results = list(agent.lazy_pipeline(data, operations, early_exit))
        print(f"\nProcessed {len(results)} items before early exit")
        print(f"Results: {results}")
        
        # Demonstration 6: Lazy Map Transformation
        print("\n" + "=" * 80)
        print("Demonstration 6: Lazy Map (Process Only What's Needed)")
        print("=" * 80)
        
        items = ["apple", "banana", "cherry", "date", "elderberry"]
        
        def expensive_transform(item: str) -> str:
            """Simulate expensive transformation."""
            time.sleep(0.1)
            return item.upper()
        
        print(f"Items to transform: {items}")
        print("Transforming lazily, taking only first 3...")
        
        start = time.time()
        transformed = agent.lazy_map(items, expensive_transform)
        result = []
        for item in transformed:
            result.append(item)
            if len(result) >= 3:
                break
        elapsed = time.time() - start
        
        print(f"Transformed: {result}")
        print(f"Time: {elapsed:.2f}s (only transformed 3 items, not all {len(items)})")
        
        # Demonstration 7: Parallel Lazy Tasks with Limit
        print("\n" + "=" * 80)
        print("Demonstration 7: Parallel Lazy Tasks (Limited Execution)")
        print("=" * 80)
        
        tasks = [
            lambda: f"Task 1 result",
            lambda: f"Task 2 result",
            lambda: f"Task 3 result",
            lambda: f"Task 4 result",
            lambda: f"Task 5 result",
        ]
        
        print(f"Created {len(tasks)} tasks")
        print("Executing only first 2 tasks...")
        
        lazy_results = agent.parallel_lazy_evaluation(tasks, max_results=2)
        executed = []
        for lazy_result in lazy_results:
            executed.append(lazy_result.value)
        
        print(f"Executed: {len(executed)} tasks")
        print(f"Results: {executed}")
        print(f"Saved: {len(tasks) - len(executed)} task executions")
        
        # Demonstration 8: Lazy LLM Batch Processing
        print("\n" + "=" * 80)
        print("Demonstration 8: Lazy LLM Batch (Early Termination)")
        print("=" * 80)
        
        prompts = [
            "Count to 3",
            "Count to 5",
            "Count to 10",
            "Count to 20"
        ]
        
        print(f"Created {len(prompts)} prompts")
        print("Processing until we get a result with '10' in it...")
        
        processed = 0
        found_result = None
        
        for result in agent.lazy_llm_batch(
            prompts,
            process_until=lambda r: "10" in r
        ):
            processed += 1
            if "10" in result:
                found_result = result
                break
        
        print(f"\nProcessed: {processed}/{len(prompts)} prompts")
        print(f"Found result: {found_result[:50]}...")
        print(f"Saved: {len(prompts) - processed} LLM calls")
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Lazy Evaluation Pattern")
        print("=" * 80)
        print("""
    The Lazy Evaluation pattern optimizes resource usage by deferring computation:
    
    Key Features Demonstrated:
    1. Basic Lazy Computation - Defer expensive operations until needed
    2. Lazy Filter - Filter with early termination
    3. Lazy Data Loading - Load data on-demand
    4. Conditional Computation - Only compute chosen branch
    5. Lazy Pipeline - Process with early exit
    6. Lazy Map - Transform only what's needed
    7. Limited Execution - Execute only required tasks
    8. LLM Batch Processing - Stop when condition met
    
    Benefits:
    - Reduces unnecessary computation
    - Lowers memory usage
    - Enables early termination
    - Improves responsiveness
    - Reduces API costs
    - Supports infinite sequences
    - Allows optimization
    
    Best Practices:
    - Use for expensive operations
    - Combine with caching
    - Handle errors properly
    - Set timeouts
    - Monitor execution patterns
    - Document lazy behavior
    - Test edge cases
    - Balance with eager evaluation
    - Use generators appropriately
    - Profile actual savings
    
    Common Use Cases:
    - Large dataset processing
    - Multi-path exploration
    - Conditional workflows
    - Resource-constrained environments
    - Expensive API calls
    - Database queries
    - Streaming data
    - Early termination scenarios
    
    This pattern is essential for efficient AI agents that need to optimize
    resource usage and avoid unnecessary computation.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_lazy_evaluation()
    
        

    Agentic Design Pattern: Lazy Evaluation

    Pattern 141: Lazy Evaluation – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Lazy Evaluation pattern implementation.


    Pattern Description

    Pattern 141: Lazy Evaluation

    Description: The Lazy Evaluation pattern defers computation until results are actually needed, optimizing resource usage by avoiding unnecessary work. Instead of eagerly computing all results upfront, lazy evaluation creates computation plans that execute only when values are requested. This pattern is particularly valuable for expensive operations, large datasets, and scenarios where not all computed values may be used.

    Lazy evaluation improves performance by reducing wasted computation, lowering
    memory usage, enabling infinite sequences, and allowing for optimization opportunities
    through computation graph analysis. It's especially useful in AI agents that might
    explore multiple paths but only need results from successful branches, or when
    processing large datasets where early termination is possible.
    
    This implementation provides lazy evaluation capabilities for LangChain agents,
    including deferred chain execution, lazy data loading, conditional computation,
    result streaming, and computation graph optimization. It supports both simple
    delayed execution and complex multi-stage lazy pipelines.
    

    Components: – Deferred Execution: Delay computation until needed – Lazy Data Loading: Load data on-demand – Conditional Computation: Skip unnecessary branches – Result Streaming: Process results incrementally – Graph Optimization: Optimize computation plans – Caching Integration: Cache lazy results

    Use Cases: – Large dataset processing – Multi-path exploration – Conditional workflows – Resource-constrained environments – Expensive API calls – Database query optimization – Streaming data processing – Early termination scenarios

    LangChain Implementation: This implementation uses: – Python generators for lazy iteration – Deferred chain execution with LCEL – Lazy data loading with document loaders – Conditional branching in chains – Streaming for incremental results – Custom lazy wrappers for expensive operations

    Benefits: – Reduces unnecessary computation – Lowers memory usage – Enables early termination – Improves responsiveness – Supports infinite sequences – Allows optimization opportunities – Reduces API costs

    Trade-offs: – Can make debugging harder – May cause unexpected delays – Complexity in error handling – Potential memory leaks if not careful – Harder to reason about execution flow – May hide performance issues

    Production Considerations: – Profile to identify expensive operations – Use for truly expensive computations – Combine with caching – Handle errors in lazy contexts – Set timeouts for lazy operations – Monitor actual execution patterns – Document lazy behavior clearly – Test edge cases thoroughly – Consider memory management – Balance with eager evaluation – Use generators appropriately – Profile actual vs potential savings


    File Statistics

    • Total Lines: 603
    • Classes: 4
    • Functions: 2
    • Constants: 0

    Module Documentation

    """
    Pattern 141: Lazy Evaluation
    
    Description:
        The Lazy Evaluation pattern defers computation until results are actually needed,
        optimizing resource usage by avoiding unnecessary work. Instead of eagerly computing
        all results upfront, lazy evaluation creates computation plans that execute only
        when values are requested. This pattern is particularly valuable for expensive
        operations, large datasets, and scenarios where not all computed values may be used.
    
        Lazy evaluation improves performance by reducing wasted computation, lowering
        memory usage, enabling infinite sequences, and allowing for optimization opportunities
        through computation graph analysis. It's especially useful in AI agents that might
        explore multiple paths but only need results from successful branches, or when
        processing large datasets where early termination is possible.
    
        This implementation provides lazy evaluation capabilities for LangChain agents,
        including deferred chain execution, lazy data loading, conditional computation,
        result streaming, and computation graph optimization. It supports both simple
        delayed execution and complex multi-stage lazy pipelines.
    
    Components:
        - Deferred Execution: Delay computation until needed
        - Lazy Data Loading: Load data on-demand
        - Conditional Computation: Skip unnecessary branches
        - Result Streaming: Process results incrementally
        - Graph Optimization: Optimize computation plans
        - Caching Integration: Cache lazy results
    
    Use Cases:
        - Large dataset processing
        - Multi-path exploration
        - Conditional workflows
        - Resource-constrained environments
        - Expensive API calls
        - Database query optimization
        - Streaming data processing
        - Early termination scenarios
    
    LangChain Implementation:
        This implementation uses:
        - Python generators for lazy iteration
        - Deferred chain execution with LCEL
        - Lazy data loading with document loaders
        - Conditional branching in chains
        - Streaming for incremental results
        - Custom lazy wrappers for expensive operations
    
    Benefits:
        - Reduces unnecessary computation
        - Lowers memory usage
        - Enables early termination
        - Improves responsiveness
        - Supports infinite sequences
        - Allows optimization opportunities
        - Reduces API costs
    
    Trade-offs:
        - Can make debugging harder
        - May cause unexpected delays
        - Complexity in error handling
        - Potential memory leaks if not careful
        - Harder to reason about execution flow
        - May hide performance issues
    
    Production Considerations:
        - Profile to identify expensive operations
        - Use for truly expensive computations
        - Combine with caching
        - Handle errors in lazy contexts
        - Set timeouts for lazy operations
        - Monitor actual execution patterns
        - Document lazy behavior clearly
        - Test edge cases thoroughly
        - Consider memory management
        - Balance with eager evaluation
        - Use generators appropriately
        - Profile actual vs potential savings
    """
    

    Purpose: This module implements the Lazy Evaluation pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import datetime.datetime
    import functools.wraps
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import time
    import typing.Any
    import typing.Callable
    import typing.Dict
    import typing.Generator
    import typing.Iterator
    import typing.List
    import typing.Optional
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – functools.wraps: Higher-order functions and operations on callable objects – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – time: Time-related functions – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Callable: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Generator: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Iterator: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: LazyResult

    Lines 97-126

    Purpose:

    Wrapper for lazy computation result.
    

    Methods:

    compute(self)

    Lines 104-111

    Purpose:

    Execute the lazy computation.
    

    Parameters:

    Returns: Any

    value(self)

    Lines 114-116

    Decorators: property

    Purpose:

    Get the computed value.
    

    Parameters:

    Returns: Any

    is_computed(self)

    Lines 119-121

    Decorators: property

    Purpose:

    Check if value has been computed.
    

    Parameters:

    Returns: bool

    computation_time(self)

    Lines 124-126

    Decorators: property

    Purpose:

    Get computation time in seconds.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D169C10>


    Class: LazyChain

    Lines 139-158

    Purpose:

    Lazy evaluation wrapper for LangChain chains.
    

    Methods:

    __init__(self, chain)

    Lines 142-146

    Purpose:

    Initialize lazy chain.
    

    Parameters: – chain: Chain

    compute(self, input_data)

    Lines 148-153

    Purpose:

    Execute the chain.
    

    Parameters: – input_data: Input data

    Returns: Any

    stream(self, input_data)

    Lines 155-158

    Purpose:

    Stream results lazily.
    

    Parameters: – input_data: Input data

    Returns: <ast.Subscript object at 0x000001A06D7B1150>


    Class: LazyDataLoader

    Lines 161-191

    Purpose:

    Lazy data loader that loads data on demand.
    

    Methods:

    __init__(self, data_source)

    Lines 164-169

    Purpose:

    Initialize lazy data loader.
    

    Parameters: – data_source: Data source

    load(self)

    Lines 171-180

    Purpose:

    Load data (simulated).
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D72DDD0>

    iter_lazy(self, chunk_size)

    Lines 182-186

    Purpose:

    Iterate over data in chunks.
    

    Parameters: – chunk_size: Chunk size

    Returns: <ast.Subscript object at 0x000001A06D72ED90>

    is_loaded(self)

    Lines 189-191

    Decorators: property

    Purpose:

    Check if data is loaded.
    

    Parameters:

    Returns: bool


    Class: LazyEvaluationAgent

    Lines 194-334

    Purpose:

    Agent that uses lazy evaluation for efficient processing.
    
    This agent defers expensive computations until results are needed,
    optimizing resource usage and enabling early termination.
    

    Methods:

    __init__(self, temperature)

    Lines 202-216

    Purpose:

    Initialize the lazy evaluation agent.
    

    Parameters: – temperature: Sampling temperature (0-1)

    expensive_computation(self, value)

    Lines 219-222

    Decorators: lazy

    Purpose:

    Simulate expensive computation.
    

    Parameters: – value: Value

    Returns: int

    lazy_filter(self, items, condition)

    Lines 224-232

    Purpose:

    Lazy filter that yields items matching condition.
    

    Parameters: – items: Items – condition: Condition

    Returns: <ast.Subscript object at 0x000001A06D88E250>

    lazy_map(self, items, transform)

    Lines 234-241

    Purpose:

    Lazy map that transforms items on demand.
    

    Parameters: – items: Items – transform: Transform

    Returns: <ast.Subscript object at 0x000001A06D622A50>

    conditional_computation(self, condition, true_computation, false_computation)

    Lines 243-253

    Purpose:

    Conditionally defer computation.
    

    Parameters: – condition: Condition – true_computation: True computation – false_computation: False computation

    Returns: LazyResult

    lazy_pipeline(self, data, operations, early_exit)

    Lines 255-281

    Purpose:

    Execute a lazy pipeline with early exit capability.
    
    Args:
        data: Input data
        operations: List of operations to apply
        early_exit: Optional function to check if we should stop early
    

    Parameters: – data: Data – operations: Operations – early_exit: Early exit

    Returns: <ast.Subscript object at 0x000001A06D77C0D0>

    parallel_lazy_evaluation(self, tasks, max_results)

    Lines 283-302

    Purpose:

    Evaluate multiple lazy tasks, stopping when enough results obtained.
    
    Args:
        tasks: List of lazy computations
        max_results: Maximum number of results to compute
    

    Parameters: – tasks: Tasks – max_results: Max results

    Returns: <ast.Subscript object at 0x000001A06D865010>

    lazy_llm_batch(self, prompts, process_until)

    Lines 304-321

    Purpose:

    Process prompts lazily, with optional early termination.
    
    Args:
        prompts: List of prompts to process
        process_until: Optional function to check if we should stop
    

    Parameters: – prompts: Prompt template or text – process_until: Process until

    Returns: <ast.Subscript object at 0x000001A06D864E50>

    get_stats(self)

    Lines 323-334

    Purpose:

    Get computation statistics.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D811510>


    Functions

    lazy(func)

    Lines 129-136

    Purpose:

    Decorator to make a function lazy.
    

    Parameters: – func: Func

    Returns: Callable

    demonstrate_lazy_evaluation()

    Lines 337-598

    Purpose:

    Demonstrate the lazy evaluation pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Lazy Evaluation pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 140
    • Pattern 142

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Lazy Evaluation pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 141_lazy_evaluation.py
    Total Lines: 603
    Pattern Category: AI Agent Design Pattern #141


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Escalation

    
        """
    Pattern 140: Escalation Pattern
    
    Description:
        The Escalation Pattern implements mechanisms for identifying situations that
        require human intervention or higher-level decision-making and routing them
        appropriately through an escalation hierarchy. This pattern recognizes when an
        agent encounters scenarios beyond its capabilities, authority, or confidence
        level and escalates to appropriate handlers based on severity and context.
    
        Escalation is critical for AI safety and reliability, ensuring that complex,
        high-stakes, or ambiguous situations receive appropriate human oversight. The
        pattern supports multiple escalation levels, severity assessment, automatic
        triggering based on rules or confidence thresholds, and routing to appropriate
        stakeholders. It balances agent autonomy with human control.
    
        This implementation provides a comprehensive escalation system with severity
        assessment, trigger conditions, escalation routing, stakeholder management,
        escalation tracking, and resolution workflows. It supports both rule-based
        and confidence-based escalation with customizable escalation paths.
    
    Components:
        - Severity Assessment: Evaluate situation severity
        - Trigger Detection: Identify when to escalate
        - Escalation Routing: Route to appropriate handler
        - Stakeholder Management: Manage escalation recipients
        - Escalation Tracking: Monitor escalation status
        - Resolution Workflow: Handle escalation lifecycle
    
    Use Cases:
        - High-stakes decisions (medical, financial, legal)
        - Low-confidence predictions
        - Policy violations
        - Security incidents
        - Customer complaints
        - System errors
        - Ethical dilemmas
        - Edge cases
    
    LangChain Implementation:
        This implementation uses:
        - LLM for severity assessment and decision complexity analysis
        - Rule-based escalation triggers
        - Confidence-threshold escalation
        - Hierarchical escalation routing
        - Escalation event tracking
        - Integration with human-in-the-loop systems
    
    Benefits:
        - Ensures human oversight for critical decisions
        - Prevents autonomous errors in high-stakes scenarios
        - Provides safety net for uncertain situations
        - Enables graduated response based on severity
        - Supports compliance requirements
        - Facilitates learning from escalations
        - Maintains audit trail
    
    Trade-offs:
        - Reduces full autonomy
        - Introduces latency for escalated cases
        - Requires human availability
        - May create bottlenecks
        - Needs clear escalation criteria
        - Can interrupt workflow
    
    Production Considerations:
        - Define clear escalation criteria
        - Set appropriate confidence thresholds
        - Implement escalation routing rules
        - Ensure stakeholder availability
        - Track escalation metrics
        - Provide escalation status updates
        - Support escalation history
        - Enable escalation feedback
        - Monitor escalation patterns
        - Reduce false escalations
        - Support emergency escalations
        - Document escalation procedures
    """
    
    import os
    from typing import List, Dict, Any, Optional
    from datetime import datetime, timedelta
    from enum import Enum
    from dataclasses import dataclass, field
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class EscalationLevel(Enum):
        """Escalation severity levels."""
        CRITICAL = "critical"
        HIGH = "high"
        MEDIUM = "medium"
        LOW = "low"
    
    
    class EscalationStatus(Enum):
        """Status of escalation."""
        PENDING = "pending"
        IN_PROGRESS = "in_progress"
        RESOLVED = "resolved"
        CANCELLED = "cancelled"
    
    
    class EscalationTrigger(Enum):
        """Reasons for escalation."""
        LOW_CONFIDENCE = "low_confidence"
        POLICY_VIOLATION = "policy_violation"
        HIGH_RISK = "high_risk"
        SECURITY_INCIDENT = "security_incident"
        ERROR = "error"
        AMBIGUOUS = "ambiguous"
        SENSITIVE_DATA = "sensitive_data"
        MANUAL_REQUEST = "manual_request"
        THRESHOLD_EXCEEDED = "threshold_exceeded"
    
    
    @dataclass
    class Stakeholder:
        """Escalation stakeholder."""
        stakeholder_id: str
        name: str
        role: str
        email: str
        escalation_levels: List[EscalationLevel]
        availability: bool = True
        priority: int = 0
    
    
    @dataclass
    class EscalationCase:
        """Escalation case record."""
        case_id: str
        level: EscalationLevel
        trigger: EscalationTrigger
        title: str
        description: str
        context: Dict[str, Any]
        agent_id: str
        confidence_score: Optional[float]
        created_at: datetime
        assigned_to: Optional[str] = None
        status: EscalationStatus = EscalationStatus.PENDING
        resolution: Optional[str] = None
        resolved_at: Optional[datetime] = None
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class EscalationRule:
        """Rule for triggering escalation."""
        rule_id: str
        name: str
        condition: str  # e.g., "confidence  None:
            """Add escalation stakeholder."""
            self.stakeholders[stakeholder.stakeholder_id] = stakeholder
            print(f"Added stakeholder: {stakeholder.name} ({stakeholder.role})")
        
        def add_rule(self, rule: EscalationRule) -> None:
            """Add escalation rule."""
            self.rules[rule.rule_id] = rule
            print(f"Added escalation rule: {rule.name}")
        
        def should_escalate(
            self,
            context: Dict[str, Any],
            confidence: Optional[float] = None
        ) -> tuple[bool, Optional[EscalationTrigger], Optional[EscalationLevel]]:
            """
            Determine if situation should be escalated.
            
            Returns:
                Tuple of (should_escalate, trigger, level)
            """
            # Check confidence threshold
            if confidence is not None and confidence  bool:
            """Evaluate if rule condition is met."""
            condition = rule.condition.lower()
            
            # Simple condition evaluation
            if "sensitive_data" in condition:
                return context.get("has_sensitive_data", False)
            
            if "high_risk" in condition:
                risk_score = context.get("risk_score", 0)
                return risk_score > self.risk_threshold
            
            if "error" in condition:
                return context.get("error_occurred", False)
            
            return False
        
        def create_escalation(
            self,
            title: str,
            description: str,
            context: Dict[str, Any],
            trigger: EscalationTrigger,
            level: Optional[EscalationLevel] = None,
            confidence: Optional[float] = None
        ) -> EscalationCase:
            """Create an escalation case."""
            # Assess severity if not provided
            if level is None:
                level = self._assess_severity(title, description, context, trigger)
            
            case = EscalationCase(
                case_id=f"esc_{len(self.cases) + 1}_{datetime.now().timestamp()}",
                level=level,
                trigger=trigger,
                title=title,
                description=description,
                context=context,
                agent_id=self.agent_id,
                confidence_score=confidence,
                created_at=datetime.now()
            )
            
            # Route to stakeholder
            stakeholder = self._route_escalation(case)
            if stakeholder:
                case.assigned_to = stakeholder.stakeholder_id
            
            self.cases.append(case)
            return case
        
        def _assess_severity(
            self,
            title: str,
            description: str,
            context: Dict[str, Any],
            trigger: EscalationTrigger
        ) -> EscalationLevel:
            """Assess severity using LLM."""
            try:
                result = self.severity_chain.invoke({
                    "title": title,
                    "description": description,
                    "context": str(context),
                    "trigger": trigger.value
                })
                
                # Parse severity
                for line in result.split('\n'):
                    if line.startswith("SEVERITY:"):
                        severity = line.replace("SEVERITY:", "").strip().lower()
                        if severity == "critical":
                            return EscalationLevel.CRITICAL
                        elif severity == "high":
                            return EscalationLevel.HIGH
                        elif severity == "medium":
                            return EscalationLevel.MEDIUM
                        else:
                            return EscalationLevel.LOW
                
                return EscalationLevel.MEDIUM
                
            except Exception as e:
                print(f"Error assessing severity: {e}")
                return EscalationLevel.MEDIUM
        
        def _route_escalation(
            self,
            case: EscalationCase
        ) -> Optional[Stakeholder]:
            """Route escalation to appropriate stakeholder."""
            # Find available stakeholders for this level
            candidates = [
                s for s in self.stakeholders.values()
                if case.level in s.escalation_levels and s.availability
            ]
            
            if not candidates:
                return None
            
            # Sort by priority and return highest
            candidates.sort(key=lambda s: s.priority, reverse=True)
            return candidates[0]
        
        def update_case_status(
            self,
            case_id: str,
            status: EscalationStatus,
            resolution: Optional[str] = None
        ) -> bool:
            """Update escalation case status."""
            case = next((c for c in self.cases if c.case_id == case_id), None)
            if not case:
                return False
            
            case.status = status
            if resolution:
                case.resolution = resolution
            
            if status == EscalationStatus.RESOLVED:
                case.resolved_at = datetime.now()
            
            return True
        
        def get_pending_escalations(
            self,
            level: Optional[EscalationLevel] = None,
            stakeholder_id: Optional[str] = None
        ) -> List[EscalationCase]:
            """Get pending escalation cases."""
            pending = [c for c in self.cases if c.status == EscalationStatus.PENDING]
            
            if level:
                pending = [c for c in pending if c.level == level]
            
            if stakeholder_id:
                pending = [c for c in pending if c.assigned_to == stakeholder_id]
            
            return pending
        
        def get_escalation_metrics(
            self,
            days: int = 7
        ) -> Dict[str, Any]:
            """Get escalation metrics."""
            cutoff = datetime.now() - timedelta(days=days)
            recent_cases = [c for c in self.cases if c.created_at >= cutoff]
            
            # Resolution time for resolved cases
            resolved = [c for c in recent_cases if c.status == EscalationStatus.RESOLVED]
            avg_resolution_time = None
            if resolved:
                times = [
                    (c.resolved_at - c.created_at).total_seconds() / 3600
                    for c in resolved if c.resolved_at
                ]
                avg_resolution_time = sum(times) / len(times) if times else None
            
            return {
                "period_days": days,
                "total_escalations": len(recent_cases),
                "pending": len([c for c in recent_cases if c.status == EscalationStatus.PENDING]),
                "in_progress": len([c for c in recent_cases if c.status == EscalationStatus.IN_PROGRESS]),
                "resolved": len(resolved),
                "by_level": {
                    level.value: len([c for c in recent_cases if c.level == level])
                    for level in EscalationLevel
                },
                "by_trigger": {
                    trigger.value: len([c for c in recent_cases if c.trigger == trigger])
                    for trigger in EscalationTrigger
                },
                "avg_resolution_hours": avg_resolution_time
            }
    
    
    def demonstrate_escalation():
        """Demonstrate the escalation pattern."""
        print("=" * 80)
        print("Escalation Pattern Demonstration")
        print("=" * 80)
        
        agent = EscalationAgent(agent_id="agent_001")
        
        # Demonstration 1: Add Stakeholders
        print("\n" + "=" * 80)
        print("Demonstration 1: Configure Escalation Stakeholders")
        print("=" * 80)
        
        support = Stakeholder(
            stakeholder_id="stake_001",
            name="Support Team",
            role="support",
            email="support@company.com",
            escalation_levels=[EscalationLevel.LOW, EscalationLevel.MEDIUM],
            priority=10
        )
        
        manager = Stakeholder(
            stakeholder_id="stake_002",
            name="Engineering Manager",
            role="manager",
            email="manager@company.com",
            escalation_levels=[EscalationLevel.MEDIUM, EscalationLevel.HIGH],
            priority=20
        )
        
        director = Stakeholder(
            stakeholder_id="stake_003",
            name="Director",
            role="director",
            email="director@company.com",
            escalation_levels=[EscalationLevel.HIGH, EscalationLevel.CRITICAL],
            priority=30
        )
        
        agent.add_stakeholder(support)
        agent.add_stakeholder(manager)
        agent.add_stakeholder(director)
        
        # Demonstration 2: Add Escalation Rules
        print("\n" + "=" * 80)
        print("Demonstration 2: Define Escalation Rules")
        print("=" * 80)
        
        sensitive_rule = EscalationRule(
            rule_id="rule_001",
            name="Sensitive Data Rule",
            condition="contains sensitive_data",
            level=EscalationLevel.HIGH,
            trigger=EscalationTrigger.SENSITIVE_DATA
        )
        
        risk_rule = EscalationRule(
            rule_id="rule_002",
            name="High Risk Rule",
            condition="high_risk",
            level=EscalationLevel.HIGH,
            trigger=EscalationTrigger.HIGH_RISK
        )
        
        agent.add_rule(sensitive_rule)
        agent.add_rule(risk_rule)
        
        # Demonstration 3: Low Confidence Escalation
        print("\n" + "=" * 80)
        print("Demonstration 3: Low Confidence Escalation")
        print("=" * 80)
        
        should_escalate, trigger, level = agent.should_escalate(
            context={"task": "medical_diagnosis"},
            confidence=0.45
        )
        
        print(f"Should escalate: {should_escalate}")
        print(f"Trigger: {trigger.value if trigger else None}")
        print(f"Level: {level.value if level else None}")
        
        if should_escalate:
            case1 = agent.create_escalation(
                title="Low Confidence Medical Diagnosis",
                description="Agent confidence below threshold for medical diagnosis",
                context={"task": "medical_diagnosis", "symptoms": "fever, cough"},
                trigger=trigger,
                level=level,
                confidence=0.45
            )
            print(f"\nCreated escalation case: {case1.case_id}")
            print(f"Assigned to: {case1.assigned_to}")
            print(f"Status: {case1.status.value}")
        
        # Demonstration 4: Security Incident Escalation
        print("\n" + "=" * 80)
        print("Demonstration 4: Security Incident Escalation")
        print("=" * 80)
        
        case2 = agent.create_escalation(
            title="Unauthorized Access Attempt",
            description="Multiple failed login attempts detected",
            context={
                "security_incident": True,
                "source_ip": "192.168.1.100",
                "attempts": 10
            },
            trigger=EscalationTrigger.SECURITY_INCIDENT,
            level=EscalationLevel.CRITICAL
        )
        
        print(f"Created critical escalation: {case2.case_id}")
        print(f"Level: {case2.level.value}")
        print(f"Trigger: {case2.trigger.value}")
        print(f"Assigned to: {case2.assigned_to}")
        
        # Demonstration 5: Rule-Based Escalation
        print("\n" + "=" * 80)
        print("Demonstration 5: Rule-Based Escalation")
        print("=" * 80)
        
        should_escalate, trigger, level = agent.should_escalate(
            context={
                "has_sensitive_data": True,
                "data_type": "PII"
            }
        )
        
        if should_escalate:
            case3 = agent.create_escalation(
                title="Sensitive Data Access",
                description="Request to access personally identifiable information",
                context={"has_sensitive_data": True, "data_type": "PII"},
                trigger=trigger,
                level=level
            )
            print(f"Created escalation: {case3.case_id}")
            print(f"Trigger: {trigger.value}")
            print(f"Level: {level.value}")
        
        # Demonstration 6: Manual Escalation Request
        print("\n" + "=" * 80)
        print("Demonstration 6: Manual Escalation Request")
        print("=" * 80)
        
        case4 = agent.create_escalation(
            title="Complex Customer Complaint",
            description="Customer requesting refund for premium service",
            context={
                "escalate": True,
                "customer_id": "cust_12345",
                "amount": 5000
            },
            trigger=EscalationTrigger.MANUAL_REQUEST
        )
        
        print(f"Manual escalation: {case4.case_id}")
        print(f"Level: {case4.level.value}")
        print(f"Assigned to: {case4.assigned_to}")
        
        # Demonstration 7: Case Resolution
        print("\n" + "=" * 80)
        print("Demonstration 7: Escalation Resolution")
        print("=" * 80)
        
        print(f"\nResolving case: {case3.case_id}")
        agent.update_case_status(
            case_id=case3.case_id,
            status=EscalationStatus.RESOLVED,
            resolution="Access granted with additional monitoring"
        )
        
        resolved_case = next(c for c in agent.cases if c.case_id == case3.case_id)
        print(f"Status: {resolved_case.status.value}")
        print(f"Resolution: {resolved_case.resolution}")
        
        # Demonstration 8: Pending Escalations
        print("\n" + "=" * 80)
        print("Demonstration 8: Pending Escalations")
        print("=" * 80)
        
        pending = agent.get_pending_escalations()
        print(f"\nTotal pending escalations: {len(pending)}")
        
        for case in pending:
            print(f"\n  Case: {case.case_id}")
            print(f"    Title: {case.title}")
            print(f"    Level: {case.level.value}")
            print(f"    Assigned to: {case.assigned_to}")
            print(f"    Age: {(datetime.now() - case.created_at).seconds // 60} minutes")
        
        # Demonstration 9: Critical Escalations Only
        print("\n" + "=" * 80)
        print("Demonstration 9: Critical Escalations")
        print("=" * 80)
        
        critical = agent.get_pending_escalations(level=EscalationLevel.CRITICAL)
        print(f"\nCritical pending escalations: {len(critical)}")
        for case in critical:
            print(f"  - {case.title}")
        
        # Demonstration 10: Escalation Metrics
        print("\n" + "=" * 80)
        print("Demonstration 10: Escalation Metrics")
        print("=" * 80)
        
        metrics = agent.get_escalation_metrics(days=7)
        print(f"\nEscalation Metrics (last {metrics['period_days']} days):")
        print(f"  Total Escalations: {metrics['total_escalations']}")
        print(f"  Pending: {metrics['pending']}")
        print(f"  In Progress: {metrics['in_progress']}")
        print(f"  Resolved: {metrics['resolved']}")
        print(f"\n  By Level:")
        for level, count in metrics['by_level'].items():
            if count > 0:
                print(f"    {level}: {count}")
        print(f"\n  By Trigger:")
        for trigger, count in metrics['by_trigger'].items():
            if count > 0:
                print(f"    {trigger}: {count}")
        
        if metrics['avg_resolution_hours']:
            print(f"\n  Avg Resolution Time: {metrics['avg_resolution_hours']:.1f} hours")
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Escalation Pattern")
        print("=" * 80)
        print("""
    The Escalation Pattern ensures appropriate human oversight for critical situations:
    
    Key Features Demonstrated:
    1. Stakeholder Management - Configure escalation recipients by level
    2. Escalation Rules - Define automatic escalation triggers
    3. Confidence-Based Escalation - Escalate low-confidence decisions
    4. Security Escalation - Handle security incidents
    5. Rule-Based Escalation - Trigger based on context rules
    6. Manual Escalation - Support explicit escalation requests
    7. Case Resolution - Track escalation lifecycle
    8. Pending Cases - Monitor unresolved escalations
    9. Level Filtering - Focus on critical cases
    10. Metrics Tracking - Monitor escalation patterns
    
    Benefits:
    - Ensures human oversight for critical decisions
    - Prevents autonomous errors
    - Provides safety net for uncertainty
    - Graduated response by severity
    - Supports compliance
    - Facilitates learning
    - Maintains audit trail
    
    Best Practices:
    - Define clear escalation criteria
    - Set appropriate thresholds
    - Implement routing rules
    - Ensure stakeholder availability
    - Track metrics
    - Provide status updates
    - Support escalation history
    - Enable feedback loops
    - Monitor patterns
    - Reduce false escalations
    - Support emergency escalations
    - Document procedures
    
    This pattern is essential for safe AI systems requiring human oversight,
    especially in high-stakes domains like healthcare, finance, and security.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_escalation()
    
        

    Agentic Design Pattern: Escalation

    Pattern 140: Escalation – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Escalation pattern implementation.


    Pattern Description

    Pattern 140: Escalation Pattern

    Description: The Escalation Pattern implements mechanisms for identifying situations that require human intervention or higher-level decision-making and routing them appropriately through an escalation hierarchy. This pattern recognizes when an agent encounters scenarios beyond its capabilities, authority, or confidence level and escalates to appropriate handlers based on severity and context.

    Escalation is critical for AI safety and reliability, ensuring that complex,
    high-stakes, or ambiguous situations receive appropriate human oversight. The
    pattern supports multiple escalation levels, severity assessment, automatic
    triggering based on rules or confidence thresholds, and routing to appropriate
    stakeholders. It balances agent autonomy with human control.
    
    This implementation provides a comprehensive escalation system with severity
    assessment, trigger conditions, escalation routing, stakeholder management,
    escalation tracking, and resolution workflows. It supports both rule-based
    and confidence-based escalation with customizable escalation paths.
    

    Components: – Severity Assessment: Evaluate situation severity – Trigger Detection: Identify when to escalate – Escalation Routing: Route to appropriate handler – Stakeholder Management: Manage escalation recipients – Escalation Tracking: Monitor escalation status – Resolution Workflow: Handle escalation lifecycle

    Use Cases: – High-stakes decisions (medical, financial, legal) – Low-confidence predictions – Policy violations – Security incidents – Customer complaints – System errors – Ethical dilemmas – Edge cases

    LangChain Implementation: This implementation uses: – LLM for severity assessment and decision complexity analysis – Rule-based escalation triggers – Confidence-threshold escalation – Hierarchical escalation routing – Escalation event tracking – Integration with human-in-the-loop systems

    Benefits: – Ensures human oversight for critical decisions – Prevents autonomous errors in high-stakes scenarios – Provides safety net for uncertain situations – Enables graduated response based on severity – Supports compliance requirements – Facilitates learning from escalations – Maintains audit trail

    Trade-offs: – Reduces full autonomy – Introduces latency for escalated cases – Requires human availability – May create bottlenecks – Needs clear escalation criteria – Can interrupt workflow

    Production Considerations: – Define clear escalation criteria – Set appropriate confidence thresholds – Implement escalation routing rules – Ensure stakeholder availability – Track escalation metrics – Provide escalation status updates – Support escalation history – Enable escalation feedback – Monitor escalation patterns – Reduce false escalations – Support emergency escalations – Document escalation procedures


    File Statistics

    • Total Lines: 709
    • Classes: 7
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 140: Escalation Pattern
    
    Description:
        The Escalation Pattern implements mechanisms for identifying situations that
        require human intervention or higher-level decision-making and routing them
        appropriately through an escalation hierarchy. This pattern recognizes when an
        agent encounters scenarios beyond its capabilities, authority, or confidence
        level and escalates to appropriate handlers based on severity and context.
    
        Escalation is critical for AI safety and reliability, ensuring that complex,
        high-stakes, or ambiguous situations receive appropriate human oversight. The
        pattern supports multiple escalation levels, severity assessment, automatic
        triggering based on rules or confidence thresholds, and routing to appropriate
        stakeholders. It balances agent autonomy with human control.
    
        This implementation provides a comprehensive escalation system with severity
        assessment, trigger conditions, escalation routing, stakeholder management,
        escalation tracking, and resolution workflows. It supports both rule-based
        and confidence-based escalation with customizable escalation paths.
    
    Components:
        - Severity Assessment: Evaluate situation severity
        - Trigger Detection: Identify when to escalate
        - Escalation Routing: Route to appropriate handler
        - Stakeholder Management: Manage escalation recipients
        - Escalation Tracking: Monitor escalation status
        - Resolution Workflow: Handle escalation lifecycle
    
    Use Cases:
        - High-stakes decisions (medical, financial, legal)
        - Low-confidence predictions
        - Policy violations
        - Security incidents
        - Customer complaints
        - System errors
        - Ethical dilemmas
        - Edge cases
    
    LangChain Implementation:
        This implementation uses:
        - LLM for severity assessment and decision complexity analysis
        - Rule-based escalation triggers
        - Confidence-threshold escalation
        - Hierarchical escalation routing
        - Escalation event tracking
        - Integration with human-in-the-loop systems
    
    Benefits:
        - Ensures human oversight for critical decisions
        - Prevents autonomous errors in high-stakes scenarios
        - Provides safety net for uncertain situations
        - Enables graduated response based on severity
        - Supports compliance requirements
        - Facilitates learning from escalations
        - Maintains audit trail
    
    Trade-offs:
        - Reduces full autonomy
        - Introduces latency for escalated cases
        - Requires human availability
        - May create bottlenecks
        - Needs clear escalation criteria
        - Can interrupt workflow
    
    Production Considerations:
        - Define clear escalation criteria
        - Set appropriate confidence thresholds
        - Implement escalation routing rules
        - Ensure stakeholder availability
        - Track escalation metrics
        - Provide escalation status updates
        - Support escalation history
        - Enable escalation feedback
        - Monitor escalation patterns
        - Reduce false escalations
        - Support emergency escalations
        - Document escalation procedures
    """
    

    Purpose: This module implements the Escalation pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import datetime.timedelta
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – datetime.timedelta: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: EscalationLevel

    Lines 95-100

    Inherits from: Enum

    Purpose:

    Escalation severity levels.
    

    Class: EscalationStatus

    Lines 103-108

    Inherits from: Enum

    Purpose:

    Status of escalation.
    

    Class: EscalationTrigger

    Lines 111-121

    Inherits from: Enum

    Purpose:

    Reasons for escalation.
    

    Class: Stakeholder

    Lines 125-133

    Purpose:

    Escalation stakeholder.
    

    Class: EscalationCase

    Lines 137-152

    Purpose:

    Escalation case record.
    

    Class: EscalationRule

    Lines 156-163

    Purpose:

    Rule for triggering escalation.
    

    Class: EscalationAgent

    Lines 166-432

    Purpose:

    Agent that manages escalation of decisions and situations.
    
    This agent identifies when to escalate, assesses severity,
    routes to appropriate stakeholders, and tracks resolution.
    

    Methods:

    __init__(self, agent_id, temperature)

    Lines 174-208

    Purpose:

    Initialize the escalation agent.
    

    Parameters: – agent_id: Agent id – temperature: Sampling temperature (0-1)

    add_stakeholder(self, stakeholder)

    Lines 210-213

    Purpose:

    Add escalation stakeholder.
    

    Parameters: – stakeholder: Stakeholder

    Returns: None

    add_rule(self, rule)

    Lines 215-218

    Purpose:

    Add escalation rule.
    

    Parameters: – rule: Rule

    Returns: None

    should_escalate(self, context, confidence)

    Lines 220-253

    Purpose:

    Determine if situation should be escalated.
    
    Returns:
        Tuple of (should_escalate, trigger, level)
    

    Parameters: – context: Context – confidence: Confidence

    Returns: <ast.Subscript object at 0x000001A06D649A90>

    _evaluate_rule(self, rule, context)

    Lines 255-274

    Purpose:

    Evaluate if rule condition is met.
    

    Parameters: – rule: Rule – context: Context

    Returns: bool

    create_escalation(self, title, description, context, trigger, level, confidence)

    Lines 276-308

    Purpose:

    Create an escalation case.
    

    Parameters: – title: Title – description: Description – context: Context – trigger: Trigger – level: Level – confidence: Confidence

    Returns: EscalationCase

    _assess_severity(self, title, description, context, trigger)

    Lines 310-343

    Purpose:

    Assess severity using LLM.
    

    Parameters: – title: Title – description: Description – context: Context – trigger: Trigger

    Returns: EscalationLevel

    _route_escalation(self, case)

    Lines 345-361

    Purpose:

    Route escalation to appropriate stakeholder.
    

    Parameters: – case: Case

    Returns: <ast.Subscript object at 0x000001A06D8890D0>

    update_case_status(self, case_id, status, resolution)

    Lines 363-381

    Purpose:

    Update escalation case status.
    

    Parameters: – case_id: Case id – status: Status – resolution: Resolution

    Returns: bool

    get_pending_escalations(self, level, stakeholder_id)

    Lines 383-397

    Purpose:

    Get pending escalation cases.
    

    Parameters: – level: Level – stakeholder_id: Stakeholder id

    Returns: <ast.Subscript object at 0x000001A06D824C90>

    get_escalation_metrics(self, days)

    Lines 399-432

    Purpose:

    Get escalation metrics.
    

    Parameters: – days: Days

    Returns: <ast.Subscript object at 0x000001A06D683110>


    Functions

    demonstrate_escalation()

    Lines 435-704

    Purpose:

    Demonstrate the escalation pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Escalation pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 139
    • Pattern 141

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Escalation pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 140_escalation.py
    Total Lines: 709
    Pattern Category: AI Agent Design Pattern #140


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Permission Authorization

    
        """
    Pattern 139: Permission & Authorization
    
    Description:
        The Permission & Authorization pattern implements fine-grained access control
        for agent systems, determining who can perform what actions on which resources.
        This pattern provides role-based access control (RBAC), attribute-based access
        control (ABAC), and resource-level permissions to ensure that agents and users
        only access what they're authorized to access.
    
        Authorization is fundamental to secure AI systems, especially in multi-user
        environments or when handling sensitive data. It enforces the principle of
        least privilege, prevents unauthorized access, and ensures compliance with
        security policies. The pattern supports hierarchical roles, permission
        inheritance, dynamic permissions, and context-aware authorization.
    
        This implementation provides a comprehensive authorization system with role
        management, permission assignment, access control checks, resource-level
        permissions, and permission delegation. It supports both static and dynamic
        authorization decisions based on user attributes, resource properties, and
        environmental context.
    
    Components:
        - Role Management: Define and manage user roles
        - Permission Assignment: Assign permissions to roles and users
        - Access Control: Check if action is authorized
        - Resource Protection: Control access to specific resources
        - Permission Delegation: Temporary permission grants
        - Context-Aware Authorization: Consider context in decisions
    
    Use Cases:
        - Multi-user AI systems
        - Enterprise agent platforms
        - Data access control
        - API authorization
        - Healthcare systems (HIPAA compliance)
        - Financial systems
        - Government systems
        - Multi-tenant platforms
    
    LangChain Implementation:
        This implementation uses:
        - Custom RBAC and ABAC systems
        - Permission hierarchy and inheritance
        - Resource-level access control
        - LLM for complex authorization decisions
        - Context-aware permission evaluation
        - Audit logging integration
    
    Benefits:
        - Enforces least privilege principle
        - Prevents unauthorized access
        - Supports compliance requirements
        - Enables fine-grained control
        - Facilitates security auditing
        - Supports multi-tenancy
        - Enables delegation
    
    Trade-offs:
        - Adds complexity to system
        - Requires permission management
        - May impact performance
        - Needs careful role design
        - Can be difficult to debug
        - Requires ongoing maintenance
    
    Production Considerations:
        - Design clear role hierarchy
        - Implement efficient permission checks
        - Cache authorization decisions
        - Log all authorization events
        - Support emergency access
        - Implement permission review process
        - Handle permission conflicts
        - Support temporal permissions
        - Provide audit capabilities
        - Test permission combinations
        - Document permission model
        - Monitor unauthorized attempts
    """
    
    import os
    from typing import List, Dict, Any, Optional, Set
    from datetime import datetime, timedelta
    from enum import Enum
    from dataclasses import dataclass, field
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class ActionType(Enum):
        """Types of actions."""
        READ = "read"
        WRITE = "write"
        UPDATE = "update"
        DELETE = "delete"
        EXECUTE = "execute"
        ADMIN = "admin"
        EXPORT = "export"
        SHARE = "share"
    
    
    class ResourceType(Enum):
        """Types of resources."""
        DATA = "data"
        MODEL = "model"
        AGENT = "agent"
        CONFIG = "config"
        USER = "user"
        API = "api"
    
    
    @dataclass
    class Permission:
        """Permission definition."""
        permission_id: str
        name: str
        description: str
        resource_type: ResourceType
        action: ActionType
        conditions: Dict[str, Any] = field(default_factory=dict)
        
        def __hash__(self):
            return hash(self.permission_id)
    
    
    @dataclass
    class Role:
        """Role definition."""
        role_id: str
        name: str
        description: str
        permissions: Set[Permission] = field(default_factory=set)
        parent_roles: Set[str] = field(default_factory=set)
        created_at: datetime = field(default_factory=datetime.now)
        
        def add_permission(self, permission: Permission):
            """Add permission to role."""
            self.permissions.add(permission)
        
        def remove_permission(self, permission: Permission):
            """Remove permission from role."""
            self.permissions.discard(permission)
        
        def has_permission(self, permission: Permission) -> bool:
            """Check if role has permission."""
            return permission in self.permissions
    
    
    @dataclass
    class User:
        """User definition."""
        user_id: str
        username: str
        roles: Set[str] = field(default_factory=set)
        direct_permissions: Set[Permission] = field(default_factory=set)
        attributes: Dict[str, Any] = field(default_factory=dict)
        created_at: datetime = field(default_factory=datetime.now)
        
        def add_role(self, role_id: str):
            """Add role to user."""
            self.roles.add(role_id)
        
        def remove_role(self, role_id: str):
            """Remove role from user."""
            self.roles.discard(role_id)
        
        def add_permission(self, permission: Permission):
            """Add direct permission to user."""
            self.direct_permissions.add(permission)
    
    
    @dataclass
    class Resource:
        """Resource definition."""
        resource_id: str
        resource_type: ResourceType
        owner_id: str
        attributes: Dict[str, Any] = field(default_factory=dict)
        created_at: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class AuthorizationRequest:
        """Authorization request."""
        user_id: str
        action: ActionType
        resource: Resource
        context: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class AuthorizationResult:
        """Authorization result."""
        authorized: bool
        reason: str
        matched_permissions: List[Permission]
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class PermissionAuthorizationAgent:
        """
        Agent that provides permission and authorization management.
        
        This agent manages roles, permissions, and authorization decisions
        using RBAC and ABAC approaches.
        """
        
        def __init__(self, temperature: float = 0.3):
            """Initialize the authorization agent."""
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.roles: Dict[str, Role] = {}
            self.users: Dict[str, User] = {}
            self.resources: Dict[str, Resource] = {}
            self.permissions: Dict[str, Permission] = {}
            
            # Create authorization chain for complex decisions
            auth_prompt = ChatPromptTemplate.from_template(
                """You are an authorization expert. Determine if the user should be authorized for this action.
    
    User: {user_id}
    User Attributes: {user_attributes}
    Action: {action}
    Resource Type: {resource_type}
    Resource Attributes: {resource_attributes}
    Context: {context}
    
    Consider:
    1. User's roles and permissions
    2. Resource sensitivity
    3. Context (time, location, etc.)
    4. Business rules and policies
    
    Respond in this format:
    AUTHORIZED: yes/no
    REASON: explanation
    RISK_LEVEL: low/medium/high
    CONDITIONS: any conditions for authorization"""
            )
            self.auth_chain = auth_prompt | self.llm | StrOutputParser()
        
        def create_permission(
            self,
            permission_id: str,
            name: str,
            description: str,
            resource_type: ResourceType,
            action: ActionType,
            conditions: Optional[Dict[str, Any]] = None
        ) -> Permission:
            """Create a new permission."""
            permission = Permission(
                permission_id=permission_id,
                name=name,
                description=description,
                resource_type=resource_type,
                action=action,
                conditions=conditions or {}
            )
            self.permissions[permission_id] = permission
            return permission
        
        def create_role(
            self,
            role_id: str,
            name: str,
            description: str,
            parent_roles: Optional[Set[str]] = None
        ) -> Role:
            """Create a new role."""
            role = Role(
                role_id=role_id,
                name=name,
                description=description,
                parent_roles=parent_roles or set()
            )
            self.roles[role_id] = role
            return role
        
        def create_user(
            self,
            user_id: str,
            username: str,
            roles: Optional[Set[str]] = None,
            attributes: Optional[Dict[str, Any]] = None
        ) -> User:
            """Create a new user."""
            user = User(
                user_id=user_id,
                username=username,
                roles=roles or set(),
                attributes=attributes or {}
            )
            self.users[user_id] = user
            return user
        
        def create_resource(
            self,
            resource_id: str,
            resource_type: ResourceType,
            owner_id: str,
            attributes: Optional[Dict[str, Any]] = None
        ) -> Resource:
            """Create a new resource."""
            resource = Resource(
                resource_id=resource_id,
                resource_type=resource_type,
                owner_id=owner_id,
                attributes=attributes or {}
            )
            self.resources[resource_id] = resource
            return resource
        
        def assign_permission_to_role(
            self,
            role_id: str,
            permission_id: str
        ) -> bool:
            """Assign permission to role."""
            if role_id not in self.roles or permission_id not in self.permissions:
                return False
            
            role = self.roles[role_id]
            permission = self.permissions[permission_id]
            role.add_permission(permission)
            return True
        
        def assign_role_to_user(
            self,
            user_id: str,
            role_id: str
        ) -> bool:
            """Assign role to user."""
            if user_id not in self.users or role_id not in self.roles:
                return False
            
            user = self.users[user_id]
            user.add_role(role_id)
            return True
        
        def get_user_permissions(self, user_id: str) -> Set[Permission]:
            """Get all permissions for a user (from roles and direct)."""
            if user_id not in self.users:
                return set()
            
            user = self.users[user_id]
            permissions = user.direct_permissions.copy()
            
            # Add permissions from roles
            for role_id in user.roles:
                if role_id in self.roles:
                    role = self.roles[role_id]
                    permissions.update(role.permissions)
                    
                    # Add permissions from parent roles (inheritance)
                    for parent_id in role.parent_roles:
                        if parent_id in self.roles:
                            permissions.update(self.roles[parent_id].permissions)
            
            return permissions
        
        def check_authorization(
            self,
            request: AuthorizationRequest
        ) -> AuthorizationResult:
            """
            Check if user is authorized for action on resource.
            
            This performs both static (RBAC) and dynamic (ABAC) checks.
            """
            if request.user_id not in self.users:
                return AuthorizationResult(
                    authorized=False,
                    reason="User not found",
                    matched_permissions=[]
                )
            
            user = self.users[request.user_id]
            user_permissions = self.get_user_permissions(request.user_id)
            
            # Check if user is owner
            if request.resource.owner_id == request.user_id:
                return AuthorizationResult(
                    authorized=True,
                    reason="User is resource owner",
                    matched_permissions=[]
                )
            
            # Check static permissions (RBAC)
            matched = []
            for permission in user_permissions:
                if (permission.resource_type == request.resource.resource_type and
                    permission.action == request.action):
                    
                    # Check conditions
                    if self._check_conditions(permission.conditions, request.context):
                        matched.append(permission)
            
            if matched:
                return AuthorizationResult(
                    authorized=True,
                    reason=f"Authorized via permissions: {', '.join(p.name for p in matched)}",
                    matched_permissions=matched
                )
            
            # If no static match, use LLM for dynamic decision (ABAC)
            return self._dynamic_authorization(user, request)
        
        def _check_conditions(
            self,
            conditions: Dict[str, Any],
            context: Dict[str, Any]
        ) -> bool:
            """Check if conditions are met."""
            if not conditions:
                return True
            
            for key, value in conditions.items():
                if key not in context or context[key] != value:
                    return False
            
            return True
        
        def _dynamic_authorization(
            self,
            user: User,
            request: AuthorizationRequest
        ) -> AuthorizationResult:
            """Use LLM for dynamic authorization decision."""
            try:
                result = self.auth_chain.invoke({
                    "user_id": user.user_id,
                    "user_attributes": str(user.attributes),
                    "action": request.action.value,
                    "resource_type": request.resource.resource_type.value,
                    "resource_attributes": str(request.resource.attributes),
                    "context": str(request.context)
                })
                
                # Parse result
                authorized = "yes" in result.lower().split("AUTHORIZED:")[1].split("\n")[0]
                reason_line = [l for l in result.split("\n") if l.startswith("REASON:")]
                reason = reason_line[0].replace("REASON:", "").strip() if reason_line else "Dynamic authorization"
                
                return AuthorizationResult(
                    authorized=authorized,
                    reason=reason,
                    matched_permissions=[]
                )
                
            except Exception as e:
                return AuthorizationResult(
                    authorized=False,
                    reason=f"Authorization check failed: {e}",
                    matched_permissions=[]
                )
        
        def get_authorization_summary(self) -> Dict[str, Any]:
            """Get summary of authorization system."""
            return {
                "total_users": len(self.users),
                "total_roles": len(self.roles),
                "total_permissions": len(self.permissions),
                "total_resources": len(self.resources),
                "users_by_role": {
                    role_id: len([u for u in self.users.values() if role_id in u.roles])
                    for role_id in self.roles.keys()
                }
            }
    
    
    def demonstrate_permission_authorization():
        """Demonstrate the permission and authorization pattern."""
        print("=" * 80)
        print("Permission & Authorization Pattern Demonstration")
        print("=" * 80)
        
        agent = PermissionAuthorizationAgent()
        
        # Demonstration 1: Create Permissions
        print("\n" + "=" * 80)
        print("Demonstration 1: Create Permissions")
        print("=" * 80)
        
        read_data = agent.create_permission(
            permission_id="perm_read_data",
            name="Read Data",
            description="Permission to read data resources",
            resource_type=ResourceType.DATA,
            action=ActionType.READ
        )
        
        write_data = agent.create_permission(
            permission_id="perm_write_data",
            name="Write Data",
            description="Permission to write data resources",
            resource_type=ResourceType.DATA,
            action=ActionType.WRITE
        )
        
        admin_all = agent.create_permission(
            permission_id="perm_admin",
            name="Admin All",
            description="Full administrative access",
            resource_type=ResourceType.DATA,
            action=ActionType.ADMIN
        )
        
        print(f"Created permissions:")
        print(f"  - {read_data.name}: {read_data.description}")
        print(f"  - {write_data.name}: {write_data.description}")
        print(f"  - {admin_all.name}: {admin_all.description}")
        
        # Demonstration 2: Create Roles
        print("\n" + "=" * 80)
        print("Demonstration 2: Create Roles with Hierarchy")
        print("=" * 80)
        
        viewer_role = agent.create_role(
            role_id="role_viewer",
            name="Viewer",
            description="Can view data"
        )
        agent.assign_permission_to_role("role_viewer", "perm_read_data")
        
        editor_role = agent.create_role(
            role_id="role_editor",
            name="Editor",
            description="Can view and edit data",
            parent_roles={"role_viewer"}
        )
        agent.assign_permission_to_role("role_editor", "perm_write_data")
        
        admin_role = agent.create_role(
            role_id="role_admin",
            name="Admin",
            description="Full system access",
            parent_roles={"role_editor"}
        )
        agent.assign_permission_to_role("role_admin", "perm_admin")
        
        print(f"Created role hierarchy:")
        print(f"  Admin -> Editor -> Viewer")
        print(f"  - Viewer: {len(viewer_role.permissions)} permissions")
        print(f"  - Editor: {len(editor_role.permissions)} permissions (+ inherited)")
        print(f"  - Admin: {len(admin_role.permissions)} permissions (+ inherited)")
        
        # Demonstration 3: Create Users
        print("\n" + "=" * 80)
        print("Demonstration 3: Create Users with Roles")
        print("=" * 80)
        
        user1 = agent.create_user(
            user_id="user_001",
            username="alice",
            roles={"role_viewer"},
            attributes={"department": "sales", "level": "junior"}
        )
        
        user2 = agent.create_user(
            user_id="user_002",
            username="bob",
            roles={"role_editor"},
            attributes={"department": "engineering", "level": "senior"}
        )
        
        user3 = agent.create_user(
            user_id="user_003",
            username="charlie",
            roles={"role_admin"},
            attributes={"department": "it", "level": "manager"}
        )
        
        print(f"Created users:")
        print(f"  - {user1.username}: {user1.roles}")
        print(f"  - {user2.username}: {user2.roles}")
        print(f"  - {user3.username}: {user3.roles}")
        
        # Demonstration 4: Create Resources
        print("\n" + "=" * 80)
        print("Demonstration 4: Create Protected Resources")
        print("=" * 80)
        
        resource1 = agent.create_resource(
            resource_id="res_001",
            resource_type=ResourceType.DATA,
            owner_id="user_003",
            attributes={"classification": "public", "department": "sales"}
        )
        
        resource2 = agent.create_resource(
            resource_id="res_002",
            resource_type=ResourceType.DATA,
            owner_id="user_003",
            attributes={"classification": "confidential", "department": "finance"}
        )
        
        print(f"Created resources:")
        print(f"  - {resource1.resource_id}: {resource1.attributes['classification']}")
        print(f"  - {resource2.resource_id}: {resource2.attributes['classification']}")
        
        # Demonstration 5: Authorization Checks
        print("\n" + "=" * 80)
        print("Demonstration 5: Authorization Checks")
        print("=" * 80)
        
        # Alice (viewer) tries to read
        request1 = AuthorizationRequest(
            user_id="user_001",
            action=ActionType.READ,
            resource=resource1,
            context={}
        )
        result1 = agent.check_authorization(request1)
        print(f"\nAlice (viewer) reads public data:")
        print(f"  Authorized: {result1.authorized}")
        print(f"  Reason: {result1.reason}")
        
        # Alice tries to write
        request2 = AuthorizationRequest(
            user_id="user_001",
            action=ActionType.WRITE,
            resource=resource1,
            context={}
        )
        result2 = agent.check_authorization(request2)
        print(f"\nAlice (viewer) writes data:")
        print(f"  Authorized: {result2.authorized}")
        print(f"  Reason: {result2.reason}")
        
        # Bob (editor) tries to write
        request3 = AuthorizationRequest(
            user_id="user_002",
            action=ActionType.WRITE,
            resource=resource1,
            context={}
        )
        result3 = agent.check_authorization(request3)
        print(f"\nBob (editor) writes data:")
        print(f"  Authorized: {result3.authorized}")
        print(f"  Reason: {result3.reason}")
        
        # Demonstration 6: Owner Access
        print("\n" + "=" * 80)
        print("Demonstration 6: Resource Owner Access")
        print("=" * 80)
        
        # Charlie is owner
        request4 = AuthorizationRequest(
            user_id="user_003",
            action=ActionType.DELETE,
            resource=resource1,
            context={}
        )
        result4 = agent.check_authorization(request4)
        print(f"\nCharlie (owner) deletes resource:")
        print(f"  Authorized: {result4.authorized}")
        print(f"  Reason: {result4.reason}")
        
        # Demonstration 7: User Permissions Summary
        print("\n" + "=" * 80)
        print("Demonstration 7: User Permissions Summary")
        print("=" * 80)
        
        for user_id in ["user_001", "user_002", "user_003"]:
            user = agent.users[user_id]
            permissions = agent.get_user_permissions(user_id)
            print(f"\n{user.username} permissions:")
            print(f"  Roles: {', '.join(user.roles)}")
            print(f"  Total Permissions: {len(permissions)}")
            for perm in permissions:
                print(f"    - {perm.name}: {perm.action.value} {perm.resource_type.value}")
        
        # Demonstration 8: System Summary
        print("\n" + "=" * 80)
        print("Demonstration 8: Authorization System Summary")
        print("=" * 80)
        
        summary = agent.get_authorization_summary()
        print(f"\nAuthorization System:")
        print(f"  Users: {summary['total_users']}")
        print(f"  Roles: {summary['total_roles']}")
        print(f"  Permissions: {summary['total_permissions']}")
        print(f"  Resources: {summary['total_resources']}")
        print(f"\n  Users by Role:")
        for role, count in summary['users_by_role'].items():
            if count > 0:
                print(f"    {role}: {count}")
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Permission & Authorization Pattern")
        print("=" * 80)
        print("""
    The Permission & Authorization pattern provides fine-grained access control:
    
    Key Features Demonstrated:
    1. Permission Management - Create and manage permissions
    2. Role-Based Access Control - RBAC with role hierarchy
    3. User Management - Assign roles and permissions to users
    4. Resource Protection - Control access to specific resources
    5. Authorization Checks - Verify user access rights
    6. Owner Access - Automatic authorization for resource owners
    7. Permission Inheritance - Inherit permissions from parent roles
    8. System Management - Track and manage authorization state
    
    Benefits:
    - Enforces least privilege
    - Prevents unauthorized access
    - Supports compliance
    - Enables fine-grained control
    - Facilitates auditing
    - Supports multi-tenancy
    - Enables role delegation
    
    Best Practices:
    - Design clear role hierarchy
    - Use principle of least privilege
    - Cache authorization decisions
    - Log authorization events
    - Support emergency access
    - Review permissions regularly
    - Handle permission conflicts
    - Test permission combinations
    - Document permission model
    - Monitor unauthorized attempts
    - Support temporal permissions
    - Provide audit capabilities
    
    This pattern is essential for secure multi-user AI systems requiring
    access control, compliance, and security enforcement.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_permission_authorization()
    
        

    Agentic Design Pattern: Permission Authorization

    Pattern 139: Permission Authorization – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Permission Authorization pattern implementation.


    Pattern Description

    Pattern 139: Permission & Authorization

    Description: The Permission & Authorization pattern implements fine-grained access control for agent systems, determining who can perform what actions on which resources. This pattern provides role-based access control (RBAC), attribute-based access control (ABAC), and resource-level permissions to ensure that agents and users only access what they’re authorized to access.

    Authorization is fundamental to secure AI systems, especially in multi-user
    environments or when handling sensitive data. It enforces the principle of
    least privilege, prevents unauthorized access, and ensures compliance with
    security policies. The pattern supports hierarchical roles, permission
    inheritance, dynamic permissions, and context-aware authorization.
    
    This implementation provides a comprehensive authorization system with role
    management, permission assignment, access control checks, resource-level
    permissions, and permission delegation. It supports both static and dynamic
    authorization decisions based on user attributes, resource properties, and
    environmental context.
    

    Components: – Role Management: Define and manage user roles – Permission Assignment: Assign permissions to roles and users – Access Control: Check if action is authorized – Resource Protection: Control access to specific resources – Permission Delegation: Temporary permission grants – Context-Aware Authorization: Consider context in decisions

    Use Cases: – Multi-user AI systems – Enterprise agent platforms – Data access control – API authorization – Healthcare systems (HIPAA compliance) – Financial systems – Government systems – Multi-tenant platforms

    LangChain Implementation: This implementation uses: – Custom RBAC and ABAC systems – Permission hierarchy and inheritance – Resource-level access control – LLM for complex authorization decisions – Context-aware permission evaluation – Audit logging integration

    Benefits: – Enforces least privilege principle – Prevents unauthorized access – Supports compliance requirements – Enables fine-grained control – Facilitates security auditing – Supports multi-tenancy – Enables delegation

    Trade-offs: – Adds complexity to system – Requires permission management – May impact performance – Needs careful role design – Can be difficult to debug – Requires ongoing maintenance

    Production Considerations: – Design clear role hierarchy – Implement efficient permission checks – Cache authorization decisions – Log all authorization events – Support emergency access – Implement permission review process – Handle permission conflicts – Support temporal permissions – Provide audit capabilities – Test permission combinations – Document permission model – Monitor unauthorized attempts


    File Statistics

    • Total Lines: 742
    • Classes: 9
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 139: Permission & Authorization
    
    Description:
        The Permission & Authorization pattern implements fine-grained access control
        for agent systems, determining who can perform what actions on which resources.
        This pattern provides role-based access control (RBAC), attribute-based access
        control (ABAC), and resource-level permissions to ensure that agents and users
        only access what they're authorized to access.
    
        Authorization is fundamental to secure AI systems, especially in multi-user
        environments or when handling sensitive data. It enforces the principle of
        least privilege, prevents unauthorized access, and ensures compliance with
        security policies. The pattern supports hierarchical roles, permission
        inheritance, dynamic permissions, and context-aware authorization.
    
        This implementation provides a comprehensive authorization system with role
        management, permission assignment, access control checks, resource-level
        permissions, and permission delegation. It supports both static and dynamic
        authorization decisions based on user attributes, resource properties, and
        environmental context.
    
    Components:
        - Role Management: Define and manage user roles
        - Permission Assignment: Assign permissions to roles and users
        - Access Control: Check if action is authorized
        - Resource Protection: Control access to specific resources
        - Permission Delegation: Temporary permission grants
        - Context-Aware Authorization: Consider context in decisions
    
    Use Cases:
        - Multi-user AI systems
        - Enterprise agent platforms
        - Data access control
        - API authorization
        - Healthcare systems (HIPAA compliance)
        - Financial systems
        - Government systems
        - Multi-tenant platforms
    
    LangChain Implementation:
        This implementation uses:
        - Custom RBAC and ABAC systems
        - Permission hierarchy and inheritance
        - Resource-level access control
        - LLM for complex authorization decisions
        - Context-aware permission evaluation
        - Audit logging integration
    
    Benefits:
        - Enforces least privilege principle
        - Prevents unauthorized access
        - Supports compliance requirements
        - Enables fine-grained control
        - Facilitates security auditing
        - Supports multi-tenancy
        - Enables delegation
    
    Trade-offs:
        - Adds complexity to system
        - Requires permission management
        - May impact performance
        - Needs careful role design
        - Can be difficult to debug
        - Requires ongoing maintenance
    
    Production Considerations:
        - Design clear role hierarchy
        - Implement efficient permission checks
        - Cache authorization decisions
        - Log all authorization events
        - Support emergency access
        - Implement permission review process
        - Handle permission conflicts
        - Support temporal permissions
        - Provide audit capabilities
        - Test permission combinations
        - Document permission model
        - Monitor unauthorized attempts
    """
    

    Purpose: This module implements the Permission Authorization pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import datetime.timedelta
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Set
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – datetime.timedelta: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Set: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: ActionType

    Lines 96-105

    Inherits from: Enum

    Purpose:

    Types of actions.
    

    Class: ResourceType

    Lines 108-115

    Inherits from: Enum

    Purpose:

    Types of resources.
    

    Class: Permission

    Lines 119-129

    Purpose:

    Permission definition.
    

    Methods:

    __hash__(self)

    Lines 128-129

    Purpose: Hash operation.

    Parameters:


    Class: Role

    Lines 133-152

    Purpose:

    Role definition.
    

    Methods:

    add_permission(self, permission)

    Lines 142-144

    Purpose:

    Add permission to role.
    

    Parameters: – permission: Permission

    remove_permission(self, permission)

    Lines 146-148

    Purpose:

    Remove permission from role.
    

    Parameters: – permission: Permission

    has_permission(self, permission)

    Lines 150-152

    Purpose:

    Check if role has permission.
    

    Parameters: – permission: Permission

    Returns: bool


    Class: User

    Lines 156-175

    Purpose:

    User definition.
    

    Methods:

    add_role(self, role_id)

    Lines 165-167

    Purpose:

    Add role to user.
    

    Parameters: – role_id: Role id

    remove_role(self, role_id)

    Lines 169-171

    Purpose:

    Remove role from user.
    

    Parameters: – role_id: Role id

    add_permission(self, permission)

    Lines 173-175

    Purpose:

    Add direct permission to user.
    

    Parameters: – permission: Permission


    Class: Resource

    Lines 179-185

    Purpose:

    Resource definition.
    

    Class: AuthorizationRequest

    Lines 189-194

    Purpose:

    Authorization request.
    

    Class: AuthorizationResult

    Lines 198-203

    Purpose:

    Authorization result.
    

    Class: PermissionAuthorizationAgent

    Lines 206-474

    Purpose:

    Agent that provides permission and authorization management.
    
    This agent manages roles, permissions, and authorization decisions
    using RBAC and ABAC approaches.
    

    Methods:

    __init__(self, temperature)

    Lines 214-245

    Purpose:

    Initialize the authorization agent.
    

    Parameters: – temperature: Sampling temperature (0-1)

    create_permission(self, permission_id, name, description, resource_type, action, conditions)

    Lines 247-266

    Purpose:

    Create a new permission.
    

    Parameters: – permission_id: Permission id – name: Name – description: Description – resource_type: Resource type – action: Action – conditions: Conditions

    Returns: Permission

    create_role(self, role_id, name, description, parent_roles)

    Lines 268-283

    Purpose:

    Create a new role.
    

    Parameters: – role_id: Role id – name: Name – description: Description – parent_roles: Parent roles

    Returns: Role

    create_user(self, user_id, username, roles, attributes)

    Lines 285-300

    Purpose:

    Create a new user.
    

    Parameters: – user_id: User id – username: Username – roles: Roles – attributes: Attributes

    Returns: User

    create_resource(self, resource_id, resource_type, owner_id, attributes)

    Lines 302-317

    Purpose:

    Create a new resource.
    

    Parameters: – resource_id: Resource id – resource_type: Resource type – owner_id: Owner id – attributes: Attributes

    Returns: Resource

    assign_permission_to_role(self, role_id, permission_id)

    Lines 319-331

    Purpose:

    Assign permission to role.
    

    Parameters: – role_id: Role id – permission_id: Permission id

    Returns: bool

    assign_role_to_user(self, user_id, role_id)

    Lines 333-344

    Purpose:

    Assign role to user.
    

    Parameters: – user_id: User id – role_id: Role id

    Returns: bool

    get_user_permissions(self, user_id)

    Lines 346-365

    Purpose:

    Get all permissions for a user (from roles and direct).
    

    Parameters: – user_id: User id

    Returns: <ast.Subscript object at 0x000001A06D70EE50>

    check_authorization(self, request)

    Lines 367-412

    Purpose:

    Check if user is authorized for action on resource.
    
    This performs both static (RBAC) and dynamic (ABAC) checks.
    

    Parameters: – request: Request

    Returns: AuthorizationResult

    _check_conditions(self, conditions, context)

    Lines 414-427

    Purpose:

    Check if conditions are met.
    

    Parameters: – conditions: Conditions – context: Context

    Returns: bool

    _dynamic_authorization(self, user, request)

    Lines 429-461

    Purpose:

    Use LLM for dynamic authorization decision.
    

    Parameters: – user: User – request: Request

    Returns: AuthorizationResult

    get_authorization_summary(self)

    Lines 463-474

    Purpose:

    Get summary of authorization system.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D7EDE90>


    Functions

    demonstrate_permission_authorization()

    Lines 477-737

    Purpose:

    Demonstrate the permission and authorization pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Permission Authorization pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 138
    • Pattern 140

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Permission Authorization pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 139_permission_authorization.py
    Total Lines: 742
    Pattern Category: AI Agent Design Pattern #139


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Audit Trail

    
        """
    Pattern 138: Audit Trail & Logging
    
    Description:
        The Audit Trail & Logging pattern provides comprehensive tracking and recording
        of all agent activities, decisions, and interactions. This pattern creates a
        complete, tamper-evident record of agent behavior that can be used for compliance,
        debugging, security analysis, and forensic investigation. It captures not just
        what happened, but why it happened, who was involved, and what data was accessed.
    
        Audit trails are critical for production AI systems, especially in regulated
        industries like healthcare, finance, and government. They provide accountability,
        enable incident investigation, support compliance audits, and help identify
        security threats. The pattern supports different log levels, structured logging,
        searchable records, and retention policies.
    
        This implementation provides a comprehensive audit system with activity logging,
        decision tracking, data access monitoring, security event recording, and forensic
        analysis capabilities. It supports multiple log formats, encryption, retention
        policies, and compliance reporting.
    
    Components:
        - Activity Logging: Records all agent actions
        - Decision Tracking: Captures reasoning and decisions
        - Data Access Monitoring: Tracks data read/write operations
        - Security Event Recording: Logs security-relevant events
        - Forensic Analysis: Tools for investigating incidents
        - Retention Management: Handles log retention and archival
    
    Use Cases:
        - Compliance auditing (SOX, HIPAA, GDPR)
        - Security incident investigation
        - Performance analysis and debugging
        - User behavior tracking
        - Regulatory reporting
        - Fraud detection
        - System monitoring
        - Forensic analysis
    
    LangChain Implementation:
        This implementation uses:
        - Custom audit logging system with structured records
        - Tamper-evident logging with checksums
        - LLM for log analysis and pattern detection
        - Structured audit events with rich metadata
        - Query capabilities for forensic analysis
        - Retention policies and archival
    
    Benefits:
        - Provides complete accountability
        - Enables incident investigation
        - Supports compliance requirements
        - Facilitates debugging and troubleshooting
        - Detects security threats
        - Creates audit trail for legal purposes
        - Enables behavior analysis
    
    Trade-offs:
        - Storage overhead for comprehensive logs
        - Performance impact from logging operations
        - Privacy concerns with detailed logging
        - Complexity in log management
        - Cost of log storage and retention
        - Potential information disclosure risks
    
    Production Considerations:
        - Use structured logging formats (JSON)
        - Implement log rotation and archival
        - Encrypt sensitive log data
        - Index logs for fast searching
        - Set appropriate retention periods
        - Monitor log storage usage
        - Implement log integrity verification
        - Provide audit reporting tools
        - Support log export and analysis
        - Handle PII data carefully
        - Implement access controls for logs
        - Consider regulatory requirements
    """
    
    import os
    import json
    import hashlib
    from typing import List, Dict, Any, Optional
    from datetime import datetime, timedelta
    from enum import Enum
    from dataclasses import dataclass, field, asdict
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class AuditEventType(Enum):
        """Types of audit events."""
        ACTION = "action"
        DECISION = "decision"
        DATA_ACCESS = "data_access"
        SECURITY = "security"
        ERROR = "error"
        CONFIGURATION = "configuration"
        AUTHENTICATION = "authentication"
        AUTHORIZATION = "authorization"
    
    
    class AuditLevel(Enum):
        """Audit log levels."""
        CRITICAL = "critical"
        HIGH = "high"
        MEDIUM = "medium"
        LOW = "low"
        INFO = "info"
    
    
    class DataOperation(Enum):
        """Types of data operations."""
        READ = "read"
        WRITE = "write"
        UPDATE = "update"
        DELETE = "delete"
        EXPORT = "export"
    
    
    @dataclass
    class AuditEvent:
        """Audit event record."""
        event_id: str
        event_type: AuditEventType
        level: AuditLevel
        timestamp: datetime
        agent_id: str
        user_id: Optional[str]
        action: str
        details: Dict[str, Any]
        result: Optional[str]
        duration_ms: Optional[float]
        metadata: Dict[str, Any] = field(default_factory=dict)
        checksum: Optional[str] = None
        
        def compute_checksum(self) -> str:
            """Compute checksum for tamper detection."""
            # Create canonical representation
            data = {
                "event_id": self.event_id,
                "event_type": self.event_type.value,
                "timestamp": self.timestamp.isoformat(),
                "agent_id": self.agent_id,
                "action": self.action,
                "details": json.dumps(self.details, sort_keys=True)
            }
            canonical = json.dumps(data, sort_keys=True)
            return hashlib.sha256(canonical.encode()).hexdigest()
        
        def to_dict(self) -> Dict[str, Any]:
            """Convert to dictionary."""
            d = asdict(self)
            d['event_type'] = self.event_type.value
            d['level'] = self.level.value
            d['timestamp'] = self.timestamp.isoformat()
            return d
    
    
    @dataclass
    class DataAccessEvent(AuditEvent):
        """Specialized event for data access."""
        data_type: str = ""
        operation: DataOperation = DataOperation.READ
        record_count: int = 0
        data_classification: str = "public"
    
    
    @dataclass
    class SecurityEvent(AuditEvent):
        """Specialized event for security incidents."""
        threat_level: str = "low"
        source_ip: Optional[str] = None
        attack_type: Optional[str] = None
    
    
    class AuditTrailAgent:
        """
        Agent that provides comprehensive audit trail and logging.
        
        This agent logs all activities, decisions, and events with rich
        metadata for compliance, security, and forensic analysis.
        """
        
        def __init__(self, agent_id: str, temperature: float = 0.3):
            """Initialize the audit trail agent."""
            self.agent_id = agent_id
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.events: List[AuditEvent] = []
            self.retention_days = 90  # Default retention period
            
            # Create log analysis chain
            analysis_prompt = ChatPromptTemplate.from_template(
                """You are a log analysis expert. Analyze the following audit log events and identify patterns, anomalies, or security concerns.
    
    Events:
    {events}
    
    Provide analysis in this format:
    PATTERNS: List any patterns you observe
    ANOMALIES: List any unusual or suspicious activities
    SECURITY_CONCERNS: List any potential security issues
    RECOMMENDATIONS: Suggest actions to take"""
            )
            self.analysis_chain = analysis_prompt | self.llm | StrOutputParser()
        
        def log_event(
            self,
            event_type: AuditEventType,
            level: AuditLevel,
            action: str,
            details: Dict[str, Any],
            user_id: Optional[str] = None,
            result: Optional[str] = None,
            duration_ms: Optional[float] = None,
            metadata: Optional[Dict[str, Any]] = None
        ) -> AuditEvent:
            """Log an audit event."""
            event = AuditEvent(
                event_id=f"evt_{len(self.events) + 1}_{datetime.now().timestamp()}",
                event_type=event_type,
                level=level,
                timestamp=datetime.now(),
                agent_id=self.agent_id,
                user_id=user_id,
                action=action,
                details=details,
                result=result,
                duration_ms=duration_ms,
                metadata=metadata or {}
            )
            
            # Compute checksum for integrity
            event.checksum = event.compute_checksum()
            
            self.events.append(event)
            return event
        
        def log_data_access(
            self,
            operation: DataOperation,
            data_type: str,
            record_count: int,
            data_classification: str,
            user_id: Optional[str] = None,
            details: Optional[Dict[str, Any]] = None
        ) -> DataAccessEvent:
            """Log a data access event."""
            event = DataAccessEvent(
                event_id=f"data_{len(self.events) + 1}_{datetime.now().timestamp()}",
                event_type=AuditEventType.DATA_ACCESS,
                level=AuditLevel.MEDIUM if data_classification == "sensitive" else AuditLevel.LOW,
                timestamp=datetime.now(),
                agent_id=self.agent_id,
                user_id=user_id,
                action=f"{operation.value} {data_type}",
                details=details or {},
                result="success",
                duration_ms=None,
                data_type=data_type,
                operation=operation,
                record_count=record_count,
                data_classification=data_classification
            )
            
            event.checksum = event.compute_checksum()
            self.events.append(event)
            return event
        
        def log_security_event(
            self,
            action: str,
            threat_level: str,
            details: Dict[str, Any],
            source_ip: Optional[str] = None,
            attack_type: Optional[str] = None,
            user_id: Optional[str] = None
        ) -> SecurityEvent:
            """Log a security event."""
            level_map = {
                "critical": AuditLevel.CRITICAL,
                "high": AuditLevel.HIGH,
                "medium": AuditLevel.MEDIUM,
                "low": AuditLevel.LOW
            }
            
            event = SecurityEvent(
                event_id=f"sec_{len(self.events) + 1}_{datetime.now().timestamp()}",
                event_type=AuditEventType.SECURITY,
                level=level_map.get(threat_level, AuditLevel.MEDIUM),
                timestamp=datetime.now(),
                agent_id=self.agent_id,
                user_id=user_id,
                action=action,
                details=details,
                result="logged",
                duration_ms=None,
                threat_level=threat_level,
                source_ip=source_ip,
                attack_type=attack_type
            )
            
            event.checksum = event.compute_checksum()
            self.events.append(event)
            return event
        
        def query_events(
            self,
            event_type: Optional[AuditEventType] = None,
            level: Optional[AuditLevel] = None,
            user_id: Optional[str] = None,
            start_time: Optional[datetime] = None,
            end_time: Optional[datetime] = None,
            action_pattern: Optional[str] = None
        ) -> List[AuditEvent]:
            """Query audit events with filters."""
            filtered = self.events.copy()
            
            if event_type:
                filtered = [e for e in filtered if e.event_type == event_type]
            
            if level:
                filtered = [e for e in filtered if e.level == level]
            
            if user_id:
                filtered = [e for e in filtered if e.user_id == user_id]
            
            if start_time:
                filtered = [e for e in filtered if e.timestamp >= start_time]
            
            if end_time:
                filtered = [e for e in filtered if e.timestamp  str:
            """Analyze events for patterns and anomalies."""
            if events is None:
                # Analyze recent events
                cutoff = datetime.now() - timedelta(hours=time_window_hours)
                events = self.query_events(start_time=cutoff)
            
            if not events:
                return "No events to analyze"
            
            # Format events for analysis
            events_text = "\n".join([
                f"[{e.timestamp.isoformat()}] {e.event_type.value} - {e.action} "
                f"(level: {e.level.value}, user: {e.user_id or 'system'})"
                for e in events[-50:]  # Last 50 events
            ])
            
            try:
                analysis = self.analysis_chain.invoke({"events": events_text})
                return analysis
            except Exception as e:
                return f"Error analyzing events: {e}"
        
        def get_user_activity(
            self,
            user_id: str,
            hours: int = 24
        ) -> Dict[str, Any]:
            """Get activity summary for a user."""
            cutoff = datetime.now() - timedelta(hours=hours)
            events = self.query_events(user_id=user_id, start_time=cutoff)
            
            return {
                "user_id": user_id,
                "period_hours": hours,
                "total_events": len(events),
                "event_types": {
                    etype.value: len([e for e in events if e.event_type == etype])
                    for etype in AuditEventType
                },
                "actions": list(set(e.action for e in events)),
                "latest_activity": events[-1].timestamp if events else None
            }
        
        def get_security_summary(
            self,
            hours: int = 24
        ) -> Dict[str, Any]:
            """Get security events summary."""
            cutoff = datetime.now() - timedelta(hours=hours)
            security_events = self.query_events(
                event_type=AuditEventType.SECURITY,
                start_time=cutoff
            )
            
            return {
                "period_hours": hours,
                "total_security_events": len(security_events),
                "by_threat_level": {
                    level.value: len([
                        e for e in security_events 
                        if e.level == level
                    ])
                    for level in AuditLevel
                },
                "critical_events": len([
                    e for e in security_events 
                    if e.level == AuditLevel.CRITICAL
                ])
            }
        
        def verify_integrity(self) -> Dict[str, Any]:
            """Verify integrity of audit log."""
            total = len(self.events)
            verified = 0
            tampered = []
            
            for event in self.events:
                expected_checksum = event.compute_checksum()
                if event.checksum == expected_checksum:
                    verified += 1
                else:
                    tampered.append(event.event_id)
            
            return {
                "total_events": total,
                "verified": verified,
                "tampered": len(tampered),
                "tampered_event_ids": tampered,
                "integrity_score": verified / total if total > 0 else 1.0
            }
        
        def export_events(
            self,
            format: str = "json",
            events: Optional[List[AuditEvent]] = None
        ) -> str:
            """Export events to specified format."""
            events_to_export = events if events else self.events
            
            if format == "json":
                return json.dumps(
                    [e.to_dict() for e in events_to_export],
                    indent=2
                )
            elif format == "csv":
                # Simple CSV export
                lines = ["timestamp,event_type,level,agent_id,user_id,action,result"]
                for e in events_to_export:
                    lines.append(
                        f"{e.timestamp.isoformat()},{e.event_type.value},"
                        f"{e.level.value},{e.agent_id},{e.user_id or ''},"
                        f"{e.action},{e.result or ''}"
                    )
                return "\n".join(lines)
            else:
                return "Unsupported format"
        
        def apply_retention_policy(self) -> int:
            """Apply retention policy and remove old events."""
            cutoff = datetime.now() - timedelta(days=self.retention_days)
            original_count = len(self.events)
            
            self.events = [e for e in self.events if e.timestamp >= cutoff]
            
            removed = original_count - len(self.events)
            return removed
    
    
    def demonstrate_audit_trail():
        """Demonstrate the audit trail and logging pattern."""
        print("=" * 80)
        print("Audit Trail & Logging Pattern Demonstration")
        print("=" * 80)
        
        agent = AuditTrailAgent(agent_id="agent_001")
        
        # Demonstration 1: Basic Event Logging
        print("\n" + "=" * 80)
        print("Demonstration 1: Basic Event Logging")
        print("=" * 80)
        
        event = agent.log_event(
            event_type=AuditEventType.ACTION,
            level=AuditLevel.INFO,
            action="Process user request",
            details={"request_type": "query", "query": "What is the weather?"},
            user_id="user_123",
            result="success",
            duration_ms=150.5
        )
        
        print(f"\nLogged Event:")
        print(f"  ID: {event.event_id}")
        print(f"  Type: {event.event_type.value}")
        print(f"  Level: {event.level.value}")
        print(f"  Action: {event.action}")
        print(f"  User: {event.user_id}")
        print(f"  Checksum: {event.checksum[:16]}...")
        
        # Demonstration 2: Data Access Logging
        print("\n" + "=" * 80)
        print("Demonstration 2: Data Access Logging")
        print("=" * 80)
        
        data_event = agent.log_data_access(
            operation=DataOperation.READ,
            data_type="customer_records",
            record_count=150,
            data_classification="sensitive",
            user_id="analyst_456",
            details={"query": "SELECT * FROM customers WHERE region='EU'"}
        )
        
        print(f"\nData Access Event:")
        print(f"  Operation: {data_event.operation.value}")
        print(f"  Data Type: {data_event.data_type}")
        print(f"  Records: {data_event.record_count}")
        print(f"  Classification: {data_event.data_classification}")
        print(f"  Level: {data_event.level.value}")
        
        # Demonstration 3: Security Event Logging
        print("\n" + "=" * 80)
        print("Demonstration 3: Security Event Logging")
        print("=" * 80)
        
        security_event = agent.log_security_event(
            action="Failed authentication attempt",
            threat_level="medium",
            details={"reason": "invalid_credentials", "attempts": 3},
            source_ip="192.168.1.100",
            attack_type="brute_force",
            user_id="unknown"
        )
        
        print(f"\nSecurity Event:")
        print(f"  Action: {security_event.action}")
        print(f"  Threat Level: {security_event.threat_level}")
        print(f"  Attack Type: {security_event.attack_type}")
        print(f"  Source IP: {security_event.source_ip}")
        print(f"  Level: {security_event.level.value}")
        
        # Log more events for testing
        agent.log_event(
            event_type=AuditEventType.DECISION,
            level=AuditLevel.MEDIUM,
            action="Route to specialist agent",
            details={"specialist": "medical", "confidence": 0.85},
            user_id="user_123"
        )
        
        agent.log_data_access(
            operation=DataOperation.WRITE,
            data_type="transaction_logs",
            record_count=1,
            data_classification="confidential",
            user_id="system"
        )
        
        # Demonstration 4: Query Events
        print("\n" + "=" * 80)
        print("Demonstration 4: Query Events")
        print("=" * 80)
        
        security_events = agent.query_events(event_type=AuditEventType.SECURITY)
        print(f"\nSecurity Events: {len(security_events)}")
        
        user_events = agent.query_events(user_id="user_123")
        print(f"Events by user_123: {len(user_events)}")
        
        data_events = agent.query_events(event_type=AuditEventType.DATA_ACCESS)
        print(f"Data Access Events: {len(data_events)}")
        
        # Demonstration 5: User Activity Summary
        print("\n" + "=" * 80)
        print("Demonstration 5: User Activity Summary")
        print("=" * 80)
        
        activity = agent.get_user_activity("user_123")
        print(f"\nUser Activity Summary:")
        print(f"  User: {activity['user_id']}")
        print(f"  Period: {activity['period_hours']} hours")
        print(f"  Total Events: {activity['total_events']}")
        print(f"  Actions: {', '.join(activity['actions'])}")
        
        # Demonstration 6: Security Summary
        print("\n" + "=" * 80)
        print("Demonstration 6: Security Summary")
        print("=" * 80)
        
        sec_summary = agent.get_security_summary()
        print(f"\nSecurity Summary:")
        print(f"  Period: {sec_summary['period_hours']} hours")
        print(f"  Total Security Events: {sec_summary['total_security_events']}")
        print(f"  Critical Events: {sec_summary['critical_events']}")
        print(f"  By Threat Level:")
        for level, count in sec_summary['by_threat_level'].items():
            if count > 0:
                print(f"    {level}: {count}")
        
        # Demonstration 7: Integrity Verification
        print("\n" + "=" * 80)
        print("Demonstration 7: Integrity Verification")
        print("=" * 80)
        
        integrity = agent.verify_integrity()
        print(f"\nIntegrity Check:")
        print(f"  Total Events: {integrity['total_events']}")
        print(f"  Verified: {integrity['verified']}")
        print(f"  Tampered: {integrity['tampered']}")
        print(f"  Integrity Score: {integrity['integrity_score']:.2%}")
        
        # Demonstration 8: Export Events
        print("\n" + "=" * 80)
        print("Demonstration 8: Export Events")
        print("=" * 80)
        
        # Export as JSON (first 2 events)
        json_export = agent.export_events(format="json", events=agent.events[:2])
        print(f"\nJSON Export (sample):")
        print(json_export[:300] + "...")
        
        # Export as CSV
        csv_export = agent.export_events(format="csv", events=agent.events[:3])
        print(f"\nCSV Export (sample):")
        print("\n".join(csv_export.split("\n")[:4]))
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Audit Trail & Logging Pattern")
        print("=" * 80)
        print(f"""
    The Audit Trail & Logging pattern provides comprehensive activity tracking:
    
    Key Features Demonstrated:
    1. Event Logging - Structured audit events with rich metadata
    2. Data Access Tracking - Specialized logging for data operations
    3. Security Events - Track security incidents and threats
    4. Query Capabilities - Filter and search audit logs
    5. User Activity - Track and analyze user behavior
    6. Security Monitoring - Monitor security events
    7. Integrity Verification - Tamper-evident logging with checksums
    8. Export Functionality - Export logs in multiple formats
    
    Current State:
    - Total Events: {len(agent.events)}
    - Event Types: {len(set(e.event_type for e in agent.events))}
    - Users: {len(set(e.user_id for e in agent.events if e.user_id))}
    - Integrity Score: {integrity['integrity_score']:.2%}
    
    Benefits:
    - Complete accountability and traceability
    - Enables incident investigation
    - Supports compliance requirements
    - Facilitates debugging
    - Detects security threats
    - Provides audit trail for legal purposes
    
    Best Practices:
    - Use structured logging (JSON)
    - Include rich metadata in events
    - Implement tamper detection
    - Set appropriate retention periods
    - Encrypt sensitive log data
    - Index logs for searching
    - Monitor log storage
    - Provide query capabilities
    - Support compliance reporting
    - Handle PII data carefully
    
    This pattern is essential for production AI systems requiring compliance,
    security monitoring, and forensic investigation capabilities.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_audit_trail()
    
        

    Agentic Design Pattern: Audit Trail

    Pattern 138: Audit Trail – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Audit Trail pattern implementation.


    Pattern Description

    Pattern 138: Audit Trail & Logging

    Description: The Audit Trail & Logging pattern provides comprehensive tracking and recording of all agent activities, decisions, and interactions. This pattern creates a complete, tamper-evident record of agent behavior that can be used for compliance, debugging, security analysis, and forensic investigation. It captures not just what happened, but why it happened, who was involved, and what data was accessed.

    Audit trails are critical for production AI systems, especially in regulated
    industries like healthcare, finance, and government. They provide accountability,
    enable incident investigation, support compliance audits, and help identify
    security threats. The pattern supports different log levels, structured logging,
    searchable records, and retention policies.
    
    This implementation provides a comprehensive audit system with activity logging,
    decision tracking, data access monitoring, security event recording, and forensic
    analysis capabilities. It supports multiple log formats, encryption, retention
    policies, and compliance reporting.
    

    Components: – Activity Logging: Records all agent actions – Decision Tracking: Captures reasoning and decisions – Data Access Monitoring: Tracks data read/write operations – Security Event Recording: Logs security-relevant events – Forensic Analysis: Tools for investigating incidents – Retention Management: Handles log retention and archival

    Use Cases: – Compliance auditing (SOX, HIPAA, GDPR) – Security incident investigation – Performance analysis and debugging – User behavior tracking – Regulatory reporting – Fraud detection – System monitoring – Forensic analysis

    LangChain Implementation: This implementation uses: – Custom audit logging system with structured records – Tamper-evident logging with checksums – LLM for log analysis and pattern detection – Structured audit events with rich metadata – Query capabilities for forensic analysis – Retention policies and archival

    Benefits: – Provides complete accountability – Enables incident investigation – Supports compliance requirements – Facilitates debugging and troubleshooting – Detects security threats – Creates audit trail for legal purposes – Enables behavior analysis

    Trade-offs: – Storage overhead for comprehensive logs – Performance impact from logging operations – Privacy concerns with detailed logging – Complexity in log management – Cost of log storage and retention – Potential information disclosure risks

    Production Considerations: – Use structured logging formats (JSON) – Implement log rotation and archival – Encrypt sensitive log data – Index logs for fast searching – Set appropriate retention periods – Monitor log storage usage – Implement log integrity verification – Provide audit reporting tools – Support log export and analysis – Handle PII data carefully – Implement access controls for logs – Consider regulatory requirements


    File Statistics

    • Total Lines: 686
    • Classes: 7
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 138: Audit Trail & Logging
    
    Description:
        The Audit Trail & Logging pattern provides comprehensive tracking and recording
        of all agent activities, decisions, and interactions. This pattern creates a
        complete, tamper-evident record of agent behavior that can be used for compliance,
        debugging, security analysis, and forensic investigation. It captures not just
        what happened, but why it happened, who was involved, and what data was accessed.
    
        Audit trails are critical for production AI systems, especially in regulated
        industries like healthcare, finance, and government. They provide accountability,
        enable incident investigation, support compliance audits, and help identify
        security threats. The pattern supports different log levels, structured logging,
        searchable records, and retention policies.
    
        This implementation provides a comprehensive audit system with activity logging,
        decision tracking, data access monitoring, security event recording, and forensic
        analysis capabilities. It supports multiple log formats, encryption, retention
        policies, and compliance reporting.
    
    Components:
        - Activity Logging: Records all agent actions
        - Decision Tracking: Captures reasoning and decisions
        - Data Access Monitoring: Tracks data read/write operations
        - Security Event Recording: Logs security-relevant events
        - Forensic Analysis: Tools for investigating incidents
        - Retention Management: Handles log retention and archival
    
    Use Cases:
        - Compliance auditing (SOX, HIPAA, GDPR)
        - Security incident investigation
        - Performance analysis and debugging
        - User behavior tracking
        - Regulatory reporting
        - Fraud detection
        - System monitoring
        - Forensic analysis
    
    LangChain Implementation:
        This implementation uses:
        - Custom audit logging system with structured records
        - Tamper-evident logging with checksums
        - LLM for log analysis and pattern detection
        - Structured audit events with rich metadata
        - Query capabilities for forensic analysis
        - Retention policies and archival
    
    Benefits:
        - Provides complete accountability
        - Enables incident investigation
        - Supports compliance requirements
        - Facilitates debugging and troubleshooting
        - Detects security threats
        - Creates audit trail for legal purposes
        - Enables behavior analysis
    
    Trade-offs:
        - Storage overhead for comprehensive logs
        - Performance impact from logging operations
        - Privacy concerns with detailed logging
        - Complexity in log management
        - Cost of log storage and retention
        - Potential information disclosure risks
    
    Production Considerations:
        - Use structured logging formats (JSON)
        - Implement log rotation and archival
        - Encrypt sensitive log data
        - Index logs for fast searching
        - Set appropriate retention periods
        - Monitor log storage usage
        - Implement log integrity verification
        - Provide audit reporting tools
        - Support log export and analysis
        - Handle PII data carefully
        - Implement access controls for logs
        - Consider regulatory requirements
    """
    

    Purpose: This module implements the Audit Trail pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.asdict
    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import datetime.timedelta
    import enum.Enum
    import json
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    

    Purpose: Core Python functionality: – dataclasses.asdict: Simplified class definitions with @dataclass – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – datetime.timedelta: Date and time handling – enum.Enum: Enumeration support – json: JSON serialization/deserialization – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    from hashlib
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: AuditEventType

    Lines 97-106

    Inherits from: Enum

    Purpose:

    Types of audit events.
    

    Class: AuditLevel

    Lines 109-115

    Inherits from: Enum

    Purpose:

    Audit log levels.
    

    Class: DataOperation

    Lines 118-124

    Inherits from: Enum

    Purpose:

    Types of data operations.
    

    Class: AuditEvent

    Lines 128-163

    Purpose:

    Audit event record.
    

    Methods:

    compute_checksum(self)

    Lines 143-155

    Purpose:

    Compute checksum for tamper detection.
    

    Parameters:

    Returns: str

    to_dict(self)

    Lines 157-163

    Purpose:

    Convert to dictionary.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D856750>


    Class: DataAccessEvent

    Lines 167-172

    Inherits from: AuditEvent

    Purpose:

    Specialized event for data access.
    

    Class: SecurityEvent

    Lines 176-180

    Inherits from: AuditEvent

    Purpose:

    Specialized event for security incidents.
    

    Class: AuditTrailAgent

    Lines 183-475

    Purpose:

    Agent that provides comprehensive audit trail and logging.
    
    This agent logs all activities, decisions, and events with rich
    metadata for compliance, security, and forensic analysis.
    

    Methods:

    __init__(self, agent_id, temperature)

    Lines 191-211

    Purpose:

    Initialize the audit trail agent.
    

    Parameters: – agent_id: Agent id – temperature: Sampling temperature (0-1)

    log_event(self, event_type, level, action, details, user_id, result, duration_ms, metadata)

    Lines 213-243

    Purpose:

    Log an audit event.
    

    Parameters: – event_type: Event type – level: Level – action: Action – details: Details – user_id: User id – result: Result – duration_ms: Duration ms – metadata: Metadata

    Returns: AuditEvent

    log_data_access(self, operation, data_type, record_count, data_classification, user_id, details)

    Lines 245-274

    Purpose:

    Log a data access event.
    

    Parameters: – operation: Operation – data_type: Data type – record_count: Record count – data_classification: Data classification – user_id: User id – details: Details

    Returns: DataAccessEvent

    log_security_event(self, action, threat_level, details, source_ip, attack_type, user_id)

    Lines 276-311

    Purpose:

    Log a security event.
    

    Parameters: – action: Action – threat_level: Threat level – details: Details – source_ip: Source ip – attack_type: Attack type – user_id: User id

    Returns: SecurityEvent

    query_events(self, event_type, level, user_id, start_time, end_time, action_pattern)

    Lines 313-343

    Purpose:

    Query audit events with filters.
    

    Parameters: – event_type: Event type – level: Level – user_id: User id – start_time: Start time – end_time: End time – action_pattern: Action pattern

    Returns: <ast.Subscript object at 0x000001A06D629ED0>

    analyze_events(self, events, time_window_hours)

    Lines 345-370

    Purpose:

    Analyze events for patterns and anomalies.
    

    Parameters: – events: Events – time_window_hours: Time window hours

    Returns: str

    get_user_activity(self, user_id, hours)

    Lines 372-391

    Purpose:

    Get activity summary for a user.
    

    Parameters: – user_id: User id – hours: Hours

    Returns: <ast.Subscript object at 0x000001A06D7E5C90>

    get_security_summary(self, hours)

    Lines 393-418

    Purpose:

    Get security events summary.
    

    Parameters: – hours: Hours

    Returns: <ast.Subscript object at 0x000001A06D824AD0>

    verify_integrity(self)

    Lines 420-439

    Purpose:

    Verify integrity of audit log.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D659BD0>

    export_events(self, format, events)

    Lines 441-465

    Purpose:

    Export events to specified format.
    

    Parameters: – format: Format – events: Events

    Returns: str

    apply_retention_policy(self)

    Lines 467-475

    Purpose:

    Apply retention policy and remove old events.
    

    Parameters:

    Returns: int


    Functions

    demonstrate_audit_trail()

    Lines 478-681

    Purpose:

    Demonstrate the audit trail and logging pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Audit Trail pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 137
    • Pattern 139

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Audit Trail pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 138_audit_trail.py
    Total Lines: 686
    Pattern Category: AI Agent Design Pattern #138


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Policy Based Control

    
        """
    Pattern 137: Policy-Based Control
    
    Description:
        The Policy-Based Control pattern implements governance mechanisms that enforce
        organizational policies, regulatory requirements, and operational constraints on
        agent behavior. This pattern provides a framework for defining, validating, and
        enforcing policies that control what actions agents can take, what data they can
        access, and how they should behave in various contexts.
    
        Policy-based control is essential for production deployments where agents must
        operate within well-defined boundaries. It enables organizations to maintain
        compliance with regulations, enforce security policies, implement business rules,
        and ensure ethical AI behavior. The pattern supports both hard constraints (must
        be followed) and soft constraints (should be followed with exceptions).
    
        This implementation provides a comprehensive policy management system with rule
        definition, policy validation, violation detection, enforcement mechanisms, and
        policy composition. It supports multiple policy types including security policies,
        data policies, behavioral policies, and resource policies, with the ability to
        combine policies and handle conflicts.
    
    Components:
        - Policy Definition: Structured rules with conditions and actions
        - Policy Validation: Checks if actions comply with policies
        - Violation Detection: Identifies policy breaches
        - Enforcement Mechanisms: Blocks, modifies, or logs violations
        - Policy Composition: Combines multiple policies
        - Conflict Resolution: Handles policy conflicts
    
    Use Cases:
        - Regulatory compliance (GDPR, HIPAA, SOC2)
        - Security policy enforcement
        - Business rule implementation
        - Ethical AI constraints
        - Resource usage governance
        - Data access control
        - Content moderation
        - Operational safety
    
    LangChain Implementation:
        This implementation uses:
        - ChatOpenAI for policy validation and interpretation
        - Custom policy engine for rule enforcement
        - Structured policy definitions with conditions and actions
        - LLM-based policy reasoning for complex scenarios
        - Policy composition and inheritance mechanisms
        - Violation tracking and audit logging
    
    Benefits:
        - Ensures compliance with regulations and standards
        - Enforces organizational policies consistently
        - Provides audit trail for policy enforcement
        - Reduces risk of policy violations
        - Enables fine-grained control over agent behavior
        - Supports policy versioning and evolution
        - Facilitates policy management at scale
    
    Trade-offs:
        - May restrict agent flexibility and autonomy
        - Can increase latency due to policy checks
        - Requires careful policy design to avoid conflicts
        - May need ongoing policy maintenance
        - Can create complexity in policy management
        - Requires balance between control and capability
    
    Production Considerations:
        - Design clear, unambiguous policies
        - Implement efficient policy evaluation
        - Cache policy decisions when possible
        - Log all policy violations with context
        - Support policy testing and simulation
        - Implement policy versioning
        - Provide policy override mechanisms
        - Monitor policy effectiveness
        - Support policy composition
        - Handle policy conflicts gracefully
        - Implement emergency policy updates
        - Provide policy documentation
    """
    
    import os
    from typing import List, Dict, Any, Optional, Set, Tuple
    from datetime import datetime
    from enum import Enum
    from dataclasses import dataclass, field
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class PolicyType(Enum):
        """Types of policies."""
        SECURITY = "security"
        DATA = "data"
        BEHAVIORAL = "behavioral"
        RESOURCE = "resource"
        COMPLIANCE = "compliance"
        ETHICAL = "ethical"
        OPERATIONAL = "operational"
        CONTENT = "content"
    
    
    class PolicySeverity(Enum):
        """Severity levels for policy violations."""
        CRITICAL = "critical"
        HIGH = "high"
        MEDIUM = "medium"
        LOW = "low"
        INFO = "info"
    
    
    class EnforcementAction(Enum):
        """Actions to take when policy is violated."""
        BLOCK = "block"
        MODIFY = "modify"
        LOG = "log"
        WARN = "warn"
        ESCALATE = "escalate"
        ALLOW = "allow"
    
    
    @dataclass
    class PolicyCondition:
        """Condition for policy rule."""
        field: str
        operator: str  # eq, ne, gt, lt, contains, matches, etc.
        value: Any
        
        def evaluate(self, context: Dict[str, Any]) -> bool:
            """Evaluate condition against context."""
            if self.field not in context:
                return False
            
            field_value = context[self.field]
            
            if self.operator == "eq":
                return field_value == self.value
            elif self.operator == "ne":
                return field_value != self.value
            elif self.operator == "gt":
                return field_value > self.value
            elif self.operator == "lt":
                return field_value = self.value
            elif self.operator == "lte":
                return field_value  None:
            """Add a policy to the system."""
            self.policies[policy.policy_id] = policy
            print(f"Added policy: {policy.name} (ID: {policy.policy_id})")
        
        def remove_policy(self, policy_id: str) -> None:
            """Remove a policy from the system."""
            if policy_id in self.policies:
                policy = self.policies.pop(policy_id)
                print(f"Removed policy: {policy.name}")
            else:
                print(f"Policy not found: {policy_id}")
        
        def enable_policy(self, policy_id: str) -> None:
            """Enable a policy."""
            if policy_id in self.policies:
                self.policies[policy_id].enabled = True
                print(f"Enabled policy: {self.policies[policy_id].name}")
        
        def disable_policy(self, policy_id: str) -> None:
            """Disable a policy."""
            if policy_id in self.policies:
                self.policies[policy_id].enabled = False
                print(f"Disabled policy: {self.policies[policy_id].name}")
        
        def validate_action(
            self,
            action: str,
            context: Dict[str, Any]
        ) -> Tuple[bool, List[PolicyViolation]]:
            """
            Validate an action against all applicable policies.
            
            Returns:
                Tuple of (is_valid, list of violations)
            """
            violations = []
            
            # Get applicable policies (enabled, sorted by priority)
            applicable_policies = sorted(
                [p for p in self.policies.values() if p.enabled],
                key=lambda p: p.priority,
                reverse=True
            )
            
            for policy in applicable_policies:
                # Check if policy conditions match
                if self._conditions_match(policy.conditions, context):
                    # Use LLM for detailed validation
                    violation = self._check_policy_violation(policy, action, context)
                    if violation:
                        violations.append(violation)
                        self.violations.append(violation)
            
            is_valid = len(violations) == 0
            return is_valid, violations
        
        def _conditions_match(
            self,
            conditions: List[PolicyCondition],
            context: Dict[str, Any]
        ) -> bool:
            """Check if all conditions match the context."""
            if not conditions:
                return True  # No conditions means always applicable
            
            return all(condition.evaluate(context) for condition in conditions)
        
        def _check_policy_violation(
            self,
            policy: Policy,
            action: str,
            context: Dict[str, Any]
        ) -> Optional[PolicyViolation]:
            """Check if action violates policy using LLM."""
            try:
                result = self.validation_chain.invoke({
                    "policy_name": policy.name,
                    "policy_description": policy.description,
                    "policy_type": policy.policy_type.value,
                    "severity": policy.severity.value,
                    "action": action,
                    "context": str(context)
                })
                
                # Parse result
                lines = result.strip().split('\n')
                violates = False
                reason = ""
                risk_level = "medium"
                
                for line in lines:
                    if line.startswith("VIOLATES:"):
                        violates = "yes" in line.lower()
                    elif line.startswith("REASON:"):
                        reason = line.replace("REASON:", "").strip()
                    elif line.startswith("RISK_LEVEL:"):
                        risk_level = line.replace("RISK_LEVEL:", "").strip()
                
                if violates:
                    violation = PolicyViolation(
                        violation_id=f"violation_{datetime.now().timestamp()}",
                        policy_id=policy.policy_id,
                        policy_name=policy.name,
                        severity=policy.severity,
                        action=action,
                        context=context,
                        enforcement_action=policy.enforcement_action
                    )
                    return violation
                
                return None
                
            except Exception as e:
                print(f"Error checking policy violation: {e}")
                return None
        
        def enforce_policies(
            self,
            action: str,
            context: Dict[str, Any]
        ) -> Dict[str, Any]:
            """
            Enforce policies on an action.
            
            Returns:
                Dictionary with enforcement result
            """
            is_valid, violations = self.validate_action(action, context)
            
            if is_valid:
                return {
                    "allowed": True,
                    "action": action,
                    "violations": [],
                    "message": "Action complies with all policies"
                }
            
            # Determine enforcement action based on violations
            critical_violations = [
                v for v in violations 
                if v.severity in [PolicySeverity.CRITICAL, PolicySeverity.HIGH]
            ]
            
            if critical_violations:
                # Block critical violations
                return {
                    "allowed": False,
                    "action": None,
                    "violations": violations,
                    "message": f"Action blocked due to {len(critical_violations)} critical policy violations"
                }
            else:
                # Log and warn for lower severity
                return {
                    "allowed": True,
                    "action": action,
                    "violations": violations,
                    "message": f"Action allowed with {len(violations)} policy warnings"
                }
        
        def compose_policies(
            self,
            policy_ids: List[str],
            composition_strategy: str = "all"
        ) -> Policy:
            """
            Compose multiple policies into a single policy.
            
            Args:
                policy_ids: List of policy IDs to compose
                composition_strategy: "all" (AND), "any" (OR)
            """
            policies = [self.policies[pid] for pid in policy_ids if pid in self.policies]
            
            if not policies:
                raise ValueError("No valid policies to compose")
            
            # Create composite policy
            composite = Policy(
                policy_id=f"composite_{datetime.now().timestamp()}",
                name=f"Composite: {', '.join(p.name for p in policies)}",
                description=f"Composite policy combining: {', '.join(p.name for p in policies)}",
                policy_type=PolicyType.COMPLIANCE,
                severity=max(policies, key=lambda p: p.severity.value).severity,
                conditions=[],  # Conditions evaluated separately
                enforcement_action=EnforcementAction.BLOCK,
                priority=max(p.priority for p in policies)
            )
            
            return composite
        
        def get_violations_by_severity(
            self,
            severity: PolicySeverity
        ) -> List[PolicyViolation]:
            """Get violations by severity level."""
            return [v for v in self.violations if v.severity == severity]
        
        def get_policy_summary(self) -> Dict[str, Any]:
            """Get summary of policy system."""
            return {
                "total_policies": len(self.policies),
                "enabled_policies": len([p for p in self.policies.values() if p.enabled]),
                "disabled_policies": len([p for p in self.policies.values() if not p.enabled]),
                "total_violations": len(self.violations),
                "critical_violations": len(self.get_violations_by_severity(PolicySeverity.CRITICAL)),
                "high_violations": len(self.get_violations_by_severity(PolicySeverity.HIGH)),
                "policy_types": {
                    ptype.value: len([p for p in self.policies.values() if p.policy_type == ptype])
                    for ptype in PolicyType
                }
            }
    
    
    def demonstrate_policy_based_control():
        """Demonstrate the policy-based control pattern."""
        print("=" * 80)
        print("Policy-Based Control Pattern Demonstration")
        print("=" * 80)
        
        agent = PolicyBasedControlAgent()
        
        # Demonstration 1: Security Policy
        print("\n" + "=" * 80)
        print("Demonstration 1: Security Policy Enforcement")
        print("=" * 80)
        
        security_policy = Policy(
            policy_id="sec_001",
            name="Data Access Policy",
            description="Restricts access to sensitive data based on user role",
            policy_type=PolicyType.SECURITY,
            severity=PolicySeverity.CRITICAL,
            conditions=[
                PolicyCondition(field="data_type", operator="eq", value="sensitive")
            ],
            enforcement_action=EnforcementAction.BLOCK,
            priority=100
        )
        
        agent.add_policy(security_policy)
        
        # Test action that violates policy
        action = "Access customer payment information"
        context = {
            "data_type": "sensitive",
            "user_role": "analyst",
            "required_role": "admin"
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Violations: {len(result['violations'])}")
        print(f"Message: {result['message']}")
        
        # Demonstration 2: Resource Policy
        print("\n" + "=" * 80)
        print("Demonstration 2: Resource Usage Policy")
        print("=" * 80)
        
        resource_policy = Policy(
            policy_id="res_001",
            name="API Rate Limit Policy",
            description="Limits API calls to prevent abuse",
            policy_type=PolicyType.RESOURCE,
            severity=PolicySeverity.HIGH,
            conditions=[
                PolicyCondition(field="api_calls", operator="gt", value=100)
            ],
            enforcement_action=EnforcementAction.BLOCK,
            priority=80
        )
        
        agent.add_policy(resource_policy)
        
        action = "Make API call to external service"
        context = {
            "api_calls": 150,
            "time_window": "1 hour"
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Message: {result['message']}")
        
        # Demonstration 3: Content Policy
        print("\n" + "=" * 80)
        print("Demonstration 3: Content Moderation Policy")
        print("=" * 80)
        
        content_policy = Policy(
            policy_id="cont_001",
            name="Content Safety Policy",
            description="Prevents generation of harmful content",
            policy_type=PolicyType.CONTENT,
            severity=PolicySeverity.CRITICAL,
            conditions=[
                PolicyCondition(field="content_category", operator="in", 
                              value=["harmful", "illegal", "offensive"])
            ],
            enforcement_action=EnforcementAction.BLOCK,
            priority=100
        )
        
        agent.add_policy(content_policy)
        
        action = "Generate content about violence"
        context = {
            "content_category": "harmful",
            "user_age": 15
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Message: {result['message']}")
        
        # Demonstration 4: Compliance Policy
        print("\n" + "=" * 80)
        print("Demonstration 4: Regulatory Compliance Policy")
        print("=" * 80)
        
        compliance_policy = Policy(
            policy_id="comp_001",
            name="GDPR Compliance Policy",
            description="Ensures GDPR compliance for EU users",
            policy_type=PolicyType.COMPLIANCE,
            severity=PolicySeverity.CRITICAL,
            conditions=[
                PolicyCondition(field="user_location", operator="eq", value="EU"),
                PolicyCondition(field="data_processing", operator="eq", value="personal")
            ],
            enforcement_action=EnforcementAction.BLOCK,
            priority=100
        )
        
        agent.add_policy(compliance_policy)
        
        action = "Process user data for marketing"
        context = {
            "user_location": "EU",
            "data_processing": "personal",
            "consent_given": False
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Message: {result['message']}")
        
        # Demonstration 5: Policy Enable/Disable
        print("\n" + "=" * 80)
        print("Demonstration 5: Dynamic Policy Management")
        print("=" * 80)
        
        print("\nDisabling resource policy...")
        agent.disable_policy("res_001")
        
        action = "Make API call to external service"
        context = {
            "api_calls": 150,
            "time_window": "1 hour"
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Message: {result['message']}")
        
        print("\nRe-enabling resource policy...")
        agent.enable_policy("res_001")
        
        # Demonstration 6: Multiple Policy Violations
        print("\n" + "=" * 80)
        print("Demonstration 6: Multiple Policy Violations")
        print("=" * 80)
        
        action = "Access and export customer data"
        context = {
            "data_type": "sensitive",
            "user_role": "analyst",
            "user_location": "EU",
            "data_processing": "personal",
            "consent_given": False
        }
        
        is_valid, violations = agent.validate_action(action, context)
        print(f"\nAction: {action}")
        print(f"Valid: {is_valid}")
        print(f"Violations: {len(violations)}")
        for i, violation in enumerate(violations, 1):
            print(f"\n  Violation {i}:")
            print(f"    Policy: {violation.policy_name}")
            print(f"    Severity: {violation.severity.value}")
            print(f"    Action: {violation.enforcement_action.value}")
        
        # Demonstration 7: Policy Priority
        print("\n" + "=" * 80)
        print("Demonstration 7: Policy Priority Handling")
        print("=" * 80)
        
        low_priority_policy = Policy(
            policy_id="low_001",
            name="Low Priority Warning",
            description="Low priority warning policy",
            policy_type=PolicyType.OPERATIONAL,
            severity=PolicySeverity.LOW,
            conditions=[
                PolicyCondition(field="action_type", operator="eq", value="read")
            ],
            enforcement_action=EnforcementAction.LOG,
            priority=10
        )
        
        agent.add_policy(low_priority_policy)
        
        action = "Read configuration file"
        context = {
            "action_type": "read",
            "file_type": "config"
        }
        
        result = agent.enforce_policies(action, context)
        print(f"\nAction: {action}")
        print(f"Allowed: {result['allowed']}")
        print(f"Violations: {len(result['violations'])}")
        print(f"Message: {result['message']}")
        
        # Demonstration 8: Policy Summary
        print("\n" + "=" * 80)
        print("Demonstration 8: Policy System Summary")
        print("=" * 80)
        
        summary = agent.get_policy_summary()
        print("\nPolicy System Summary:")
        print(f"  Total Policies: {summary['total_policies']}")
        print(f"  Enabled: {summary['enabled_policies']}")
        print(f"  Disabled: {summary['disabled_policies']}")
        print(f"  Total Violations: {summary['total_violations']}")
        print(f"  Critical Violations: {summary['critical_violations']}")
        print(f"  High Violations: {summary['high_violations']}")
        print(f"\n  Policies by Type:")
        for ptype, count in summary['policy_types'].items():
            if count > 0:
                print(f"    {ptype}: {count}")
        
        # Summary
        print("\n" + "=" * 80)
        print("Summary: Policy-Based Control Pattern")
        print("=" * 80)
        print("""
    The Policy-Based Control pattern provides comprehensive governance for agent behavior:
    
    Key Features Demonstrated:
    1. Policy Definition - Structured rules with conditions and enforcement actions
    2. Security Policies - Data access control and protection
    3. Resource Policies - API rate limiting and resource governance
    4. Content Policies - Content moderation and safety
    5. Compliance Policies - Regulatory compliance (GDPR, etc.)
    6. Dynamic Management - Enable/disable policies at runtime
    7. Multiple Violations - Handle multiple concurrent policy violations
    8. Priority System - Policy evaluation based on priority
    
    Benefits:
    - Ensures regulatory compliance
    - Enforces organizational policies
    - Provides audit trail
    - Enables fine-grained control
    - Supports policy composition
    - Reduces compliance risk
    
    Best Practices:
    - Design clear, unambiguous policies
    - Use appropriate severity levels
    - Implement efficient policy evaluation
    - Cache policy decisions when possible
    - Log all violations with context
    - Support policy testing
    - Provide policy override mechanisms
    - Monitor policy effectiveness
    - Handle policy conflicts gracefully
    - Document all policies thoroughly
    
    This pattern is essential for production AI systems requiring governance,
    compliance, and controlled agent behavior.
    """)
    
    
    if __name__ == "__main__":
        demonstrate_policy_based_control()
    
        

    Agentic Design Pattern: Policy Based Control

    Pattern 137: Policy Based Control – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Policy Based Control pattern implementation.


    Pattern Description

    Pattern 137: Policy-Based Control

    Description: The Policy-Based Control pattern implements governance mechanisms that enforce organizational policies, regulatory requirements, and operational constraints on agent behavior. This pattern provides a framework for defining, validating, and enforcing policies that control what actions agents can take, what data they can access, and how they should behave in various contexts.

    Policy-based control is essential for production deployments where agents must
    operate within well-defined boundaries. It enables organizations to maintain
    compliance with regulations, enforce security policies, implement business rules,
    and ensure ethical AI behavior. The pattern supports both hard constraints (must
    be followed) and soft constraints (should be followed with exceptions).
    
    This implementation provides a comprehensive policy management system with rule
    definition, policy validation, violation detection, enforcement mechanisms, and
    policy composition. It supports multiple policy types including security policies,
    data policies, behavioral policies, and resource policies, with the ability to
    combine policies and handle conflicts.
    

    Components: – Policy Definition: Structured rules with conditions and actions – Policy Validation: Checks if actions comply with policies – Violation Detection: Identifies policy breaches – Enforcement Mechanisms: Blocks, modifies, or logs violations – Policy Composition: Combines multiple policies – Conflict Resolution: Handles policy conflicts

    Use Cases: – Regulatory compliance (GDPR, HIPAA, SOC2) – Security policy enforcement – Business rule implementation – Ethical AI constraints – Resource usage governance – Data access control – Content moderation – Operational safety

    LangChain Implementation: This implementation uses: – ChatOpenAI for policy validation and interpretation – Custom policy engine for rule enforcement – Structured policy definitions with conditions and actions – LLM-based policy reasoning for complex scenarios – Policy composition and inheritance mechanisms – Violation tracking and audit logging

    Benefits: – Ensures compliance with regulations and standards – Enforces organizational policies consistently – Provides audit trail for policy enforcement – Reduces risk of policy violations – Enables fine-grained control over agent behavior – Supports policy versioning and evolution – Facilitates policy management at scale

    Trade-offs: – May restrict agent flexibility and autonomy – Can increase latency due to policy checks – Requires careful policy design to avoid conflicts – May need ongoing policy maintenance – Can create complexity in policy management – Requires balance between control and capability

    Production Considerations: – Design clear, unambiguous policies – Implement efficient policy evaluation – Cache policy decisions when possible – Log all policy violations with context – Support policy testing and simulation – Implement policy versioning – Provide policy override mechanisms – Monitor policy effectiveness – Support policy composition – Handle policy conflicts gracefully – Implement emergency policy updates – Provide policy documentation


    File Statistics

    • Total Lines: 727
    • Classes: 7
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 137: Policy-Based Control
    
    Description:
        The Policy-Based Control pattern implements governance mechanisms that enforce
        organizational policies, regulatory requirements, and operational constraints on
        agent behavior. This pattern provides a framework for defining, validating, and
        enforcing policies that control what actions agents can take, what data they can
        access, and how they should behave in various contexts.
    
        Policy-based control is essential for production deployments where agents must
        operate within well-defined boundaries. It enables organizations to maintain
        compliance with regulations, enforce security policies, implement business rules,
        and ensure ethical AI behavior. The pattern supports both hard constraints (must
        be followed) and soft constraints (should be followed with exceptions).
    
        This implementation provides a comprehensive policy management system with rule
        definition, policy validation, violation detection, enforcement mechanisms, and
        policy composition. It supports multiple policy types including security policies,
        data policies, behavioral policies, and resource policies, with the ability to
        combine policies and handle conflicts.
    
    Components:
        - Policy Definition: Structured rules with conditions and actions
        - Policy Validation: Checks if actions comply with policies
        - Violation Detection: Identifies policy breaches
        - Enforcement Mechanisms: Blocks, modifies, or logs violations
        - Policy Composition: Combines multiple policies
        - Conflict Resolution: Handles policy conflicts
    
    Use Cases:
        - Regulatory compliance (GDPR, HIPAA, SOC2)
        - Security policy enforcement
        - Business rule implementation
        - Ethical AI constraints
        - Resource usage governance
        - Data access control
        - Content moderation
        - Operational safety
    
    LangChain Implementation:
        This implementation uses:
        - ChatOpenAI for policy validation and interpretation
        - Custom policy engine for rule enforcement
        - Structured policy definitions with conditions and actions
        - LLM-based policy reasoning for complex scenarios
        - Policy composition and inheritance mechanisms
        - Violation tracking and audit logging
    
    Benefits:
        - Ensures compliance with regulations and standards
        - Enforces organizational policies consistently
        - Provides audit trail for policy enforcement
        - Reduces risk of policy violations
        - Enables fine-grained control over agent behavior
        - Supports policy versioning and evolution
        - Facilitates policy management at scale
    
    Trade-offs:
        - May restrict agent flexibility and autonomy
        - Can increase latency due to policy checks
        - Requires careful policy design to avoid conflicts
        - May need ongoing policy maintenance
        - Can create complexity in policy management
        - Requires balance between control and capability
    
    Production Considerations:
        - Design clear, unambiguous policies
        - Implement efficient policy evaluation
        - Cache policy decisions when possible
        - Log all policy violations with context
        - Support policy testing and simulation
        - Implement policy versioning
        - Provide policy override mechanisms
        - Monitor policy effectiveness
        - Support policy composition
        - Handle policy conflicts gracefully
        - Implement emergency policy updates
        - Provide policy documentation
    """
    

    Purpose: This module implements the Policy Based Control pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Set
    import typing.Tuple
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Set: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Tuple: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: PolicyType

    Lines 96-105

    Inherits from: Enum

    Purpose:

    Types of policies.
    

    Class: PolicySeverity

    Lines 108-114

    Inherits from: Enum

    Purpose:

    Severity levels for policy violations.
    

    Class: EnforcementAction

    Lines 117-124

    Inherits from: Enum

    Purpose:

    Actions to take when policy is violated.
    

    Class: PolicyCondition

    Lines 128-162

    Purpose:

    Condition for policy rule.
    

    Methods:

    evaluate(self, context)

    Lines 134-162

    Purpose:

    Evaluate condition against context.
    

    Parameters: – context: Context

    Returns: bool


    Class: Policy

    Lines 166-178

    Purpose:

    Policy definition.
    

    Class: PolicyViolation

    Lines 182-192

    Purpose:

    Record of policy violation.
    

    Class: PolicyBasedControlAgent

    Lines 195-446

    Purpose:

    Agent that enforces policy-based control.
    
    This agent manages policies, validates actions against policies,
    detects violations, and enforces policy constraints.
    

    Methods:

    __init__(self, temperature)

    Lines 203-232

    Purpose:

    Initialize the policy control agent.
    

    Parameters: – temperature: Sampling temperature (0-1)

    add_policy(self, policy)

    Lines 234-237

    Purpose:

    Add a policy to the system.
    

    Parameters: – policy: Policy

    Returns: None

    remove_policy(self, policy_id)

    Lines 239-245

    Purpose:

    Remove a policy from the system.
    

    Parameters: – policy_id: Policy id

    Returns: None

    enable_policy(self, policy_id)

    Lines 247-251

    Purpose:

    Enable a policy.
    

    Parameters: – policy_id: Policy id

    Returns: None

    disable_policy(self, policy_id)

    Lines 253-257

    Purpose:

    Disable a policy.
    

    Parameters: – policy_id: Policy id

    Returns: None

    validate_action(self, action, context)

    Lines 259-289

    Purpose:

    Validate an action against all applicable policies.
    
    Returns:
        Tuple of (is_valid, list of violations)
    

    Parameters: – action: Action – context: Context

    Returns: <ast.Subscript object at 0x000001A06D7EFE50>

    _conditions_match(self, conditions, context)

    Lines 291-300

    Purpose:

    Check if all conditions match the context.
    

    Parameters: – conditions: Conditions – context: Context

    Returns: bool

    _check_policy_violation(self, policy, action, context)

    Lines 302-349

    Purpose:

    Check if action violates policy using LLM.
    

    Parameters: – policy: Policy – action: Action – context: Context

    Returns: <ast.Subscript object at 0x000001A06D7BA890>

    enforce_policies(self, action, context)

    Lines 351-393

    Purpose:

    Enforce policies on an action.
    
    Returns:
        Dictionary with enforcement result
    

    Parameters: – action: Action – context: Context

    Returns: <ast.Subscript object at 0x000001A06D63C2D0>

    compose_policies(self, policy_ids, composition_strategy)

    Lines 395-424

    Purpose:

    Compose multiple policies into a single policy.
    
    Args:
        policy_ids: List of policy IDs to compose
        composition_strategy: "all" (AND), "any" (OR)
    

    Parameters: – policy_ids: Policy ids – composition_strategy: Composition strategy

    Returns: Policy

    get_violations_by_severity(self, severity)

    Lines 426-431

    Purpose:

    Get violations by severity level.
    

    Parameters: – severity: Severity

    Returns: <ast.Subscript object at 0x000001A06D7509D0>

    get_policy_summary(self)

    Lines 433-446

    Purpose:

    Get summary of policy system.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D156110>


    Functions

    demonstrate_policy_based_control()

    Lines 449-722

    Purpose:

    Demonstrate the policy-based control pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Policy Based Control pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 136
    • Pattern 138

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Policy Based Control pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 137_policy_based_control.py
    Total Lines: 727
    Pattern Category: AI Agent Design Pattern #137


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Accessibility Focused

    
        """
    Pattern 136: Accessibility-Focused Agent
    
    Description:
        The Accessibility-Focused Agent pattern creates agents designed to be
        inclusive and usable by people with diverse abilities and needs. These
        agents prioritize accessibility features such as clear communication,
        support for assistive technologies, content adaptation, and inclusive
        design principles to ensure everyone can benefit from AI assistance.
        
        Accessibility-focused agents go beyond basic usability to accommodate
        visual, auditory, cognitive, motor, and other impairments. They provide
        alternative formats, clear language, patience with interaction, and
        features that make AI technology available to all users regardless of
        their abilities or circumstances.
        
        This pattern embodies inclusive design principles, ensuring AI agents
        are not just functional but truly accessible to diverse populations
        including people with disabilities, elderly users, non-native speakers,
        and users with temporary limitations.
    
    Key Components:
        1. Clear Communication: Simple, unambiguous language
        2. Alternative Formats: Multiple content representations
        3. Assistive Tech Support: Screen readers, voice control
        4. Flexible Interaction: Multiple input/output methods
        5. Content Adaptation: Adjust to user needs
        6. Error Tolerance: Patient, forgiving interactions
        7. Inclusive Design: Universal accessibility
    
    Accessibility Features:
        1. Visual: Screen reader support, high contrast, text sizing
        2. Auditory: Text alternatives, captions, visual alerts
        3. Cognitive: Clear language, simple structure, consistency
        4. Motor: Keyboard navigation, voice control, large targets
        5. Language: Plain language, translation, glossaries
        6. Context: Help text, examples, clear feedback
        7. Time: No time pressure, save progress, pause
    
    User Accommodations:
        1. Vision impaired: Audio descriptions, clear structure
        2. Hearing impaired: Text alternatives, visual cues
        3. Cognitive differences: Simple language, clear layout
        4. Motor limitations: Alternative inputs, no precision requirements
        5. Language barriers: Clear, simple language, visual aids
        6. Elderly users: Larger text, slower pace, clear instructions
        7. Temporary limitations: Flexible interaction methods
    
    Use Cases:
        - Public service AI assistants
        - Educational platforms
        - Healthcare information systems
        - Government services
        - E-learning applications
        - Banking and finance
        - Emergency services
        - General consumer applications
    
    Advantages:
        - Inclusive user base
        - Legal compliance (ADA, WCAG)
        - Better UX for everyone
        - Broader market reach
        - Social responsibility
        - Reduced support burden
        - Positive brand image
    
    Challenges:
        - Balancing features and complexity
        - Multiple accommodation needs
        - Testing with diverse users
        - Performance overhead
        - Maintaining accessibility
        - Cost of implementation
        - Technical limitations
    
    LangChain Implementation:
        This implementation uses LangChain for:
        - Clear, simple language generation
        - Content format adaptation
        - Flexible interaction patterns
        - Accessible error messages
        
    Production Considerations:
        - Follow WCAG guidelines
        - Test with assistive technologies
        - Provide multiple interaction modes
        - Include accessibility settings
        - Document accessibility features
        - Collect user feedback
        - Regular accessibility audits
        - Train on inclusive practices
    """
    
    import os
    from typing import List, Dict, Any, Optional
    from dataclasses import dataclass, field
    from datetime import datetime
    from enum import Enum
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class AccessibilityNeed(Enum):
        """Types of accessibility needs."""
        VISUAL_IMPAIRMENT = "visual_impairment"
        HEARING_IMPAIRMENT = "hearing_impairment"
        COGNITIVE_DIFFERENCE = "cognitive_difference"
        MOTOR_LIMITATION = "motor_limitation"
        LANGUAGE_BARRIER = "language_barrier"
        ELDERLY = "elderly"
        NONE = "none"
    
    
    class ContentFormat(Enum):
        """Content output formats."""
        TEXT = "text"
        AUDIO_DESCRIPTION = "audio_description"
        SIMPLIFIED_TEXT = "simplified_text"
        STRUCTURED_DATA = "structured_data"
        BULLET_POINTS = "bullet_points"
        STEP_BY_STEP = "step_by_step"
    
    
    class ReadingLevel(Enum):
        """Reading comprehension levels."""
        ELEMENTARY = "elementary"
        MIDDLE_SCHOOL = "middle_school"
        HIGH_SCHOOL = "high_school"
        COLLEGE = "college"
        ADVANCED = "advanced"
    
    
    @dataclass
    class AccessibilityProfile:
        """
        User accessibility profile.
        
        Attributes:
            user_id: User identifier
            accessibility_needs: List of accessibility requirements
            preferred_format: Preferred content format
            reading_level: Reading comprehension level
            font_size: Text size preference (multiplier)
            high_contrast: High contrast mode
            audio_enabled: Audio output enabled
            reduced_motion: Reduce animations
            keyboard_only: Keyboard-only navigation
            screen_reader: Screen reader in use
        """
        user_id: str
        accessibility_needs: List[AccessibilityNeed] = field(default_factory=list)
        preferred_format: ContentFormat = ContentFormat.TEXT
        reading_level: ReadingLevel = ReadingLevel.HIGH_SCHOOL
        font_size: float = 1.0
        high_contrast: bool = False
        audio_enabled: bool = False
        reduced_motion: bool = False
        keyboard_only: bool = False
        screen_reader: bool = False
    
    
    @dataclass
    class AccessibleResponse:
        """
        Accessible response with multiple formats.
        
        Attributes:
            response_id: Unique identifier
            primary_content: Main response
            alternative_formats: Alternative representations
            metadata: Accessibility metadata
            reading_time: Estimated reading time (seconds)
            complexity_score: Content complexity (0-1)
            timestamp: When generated
        """
        response_id: str
        primary_content: str
        alternative_formats: Dict[ContentFormat, str] = field(default_factory=dict)
        metadata: Dict[str, Any] = field(default_factory=dict)
        reading_time: float = 0.0
        complexity_score: float = 0.5
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class AccessibilityAgent:
        """
        Agent focused on accessibility and inclusive design.
        
        This agent adapts content and interaction to accommodate
        diverse user needs and abilities.
        """
        
        def __init__(self, temperature: float = 0.5):
            """
            Initialize accessibility-focused agent.
            
            Args:
                temperature: LLM temperature
            """
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.user_profiles: Dict[str, AccessibilityProfile] = {}
            self.response_history: List[AccessibleResponse] = []
            self.response_counter = 0
        
        def set_user_profile(self, profile: AccessibilityProfile):
            """Set accessibility profile for a user."""
            self.user_profiles[profile.user_id] = profile
        
        def simplify_text(self, text: str, reading_level: ReadingLevel) -> str:
            """
            Simplify text to appropriate reading level.
            
            Args:
                text: Original text
                reading_level: Target reading level
                
            Returns:
                Simplified text
            """
            level_descriptions = {
                ReadingLevel.ELEMENTARY: "a 3rd grader",
                ReadingLevel.MIDDLE_SCHOOL: "a 7th grader",
                ReadingLevel.HIGH_SCHOOL: "a high school student",
                ReadingLevel.COLLEGE: "a college student",
                ReadingLevel.ADVANCED: "an expert"
            }
            
            prompt = ChatPromptTemplate.from_template(
                "Rewrite this text so {target_audience} can understand it easily:\n\n"
                "{text}\n\n"
                "Use:\n"
                "- Simple, common words\n"
                "- Short sentences\n"
                "- Clear structure\n"
                "- Active voice\n"
                "- Concrete examples\n\n"
                "Simplified version:"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            simplified = chain.invoke({
                "text": text,
                "target_audience": level_descriptions[reading_level]
            })
            
            return simplified.strip()
        
        def create_audio_description(self, content: str) -> str:
            """
            Create audio-friendly description.
            
            Args:
                content: Visual content to describe
                
            Returns:
                Audio description
            """
            prompt = ChatPromptTemplate.from_template(
                "Create an audio description for screen readers:\n\n"
                "{content}\n\n"
                "Provide a clear, verbal description that:\n"
                "- Describes visual elements\n"
                "- Explains layout and structure\n"
                "- Indicates interactive elements\n"
                "- Uses clear navigation cues\n\n"
                "Audio description:"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            description = chain.invoke({"content": content})
            return description.strip()
        
        def format_as_steps(self, content: str) -> List[str]:
            """
            Format content as clear steps.
            
            Args:
                content: Content to format
                
            Returns:
                List of steps
            """
            prompt = ChatPromptTemplate.from_template(
                "Break this into clear, numbered steps:\n\n"
                "{content}\n\n"
                "Each step should:\n"
                "- Be one clear action\n"
                "- Use simple language\n"
                "- Start with an action verb\n"
                "- Be easy to follow\n\n"
                "List each step on a new line starting with the number."
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            result = chain.invoke({"content": content})
            
            # Parse steps
            steps = []
            for line in result.strip().split('\n'):
                line = line.strip()
                if line and (line[0].isdigit() or line.startswith('-') or line.startswith('•')):
                    # Remove numbering/bullets
                    step = line.lstrip('0123456789.-• ')
                    if step:
                        steps.append(step)
            
            return steps
        
        def respond(
            self,
            user_input: str,
            user_id: Optional[str] = None
        ) -> AccessibleResponse:
            """
            Generate accessible response.
            
            Args:
                user_input: User's question/request
                user_id: User identifier for profile
                
            Returns:
                Accessible response with multiple formats
            """
            self.response_counter += 1
            response_id = f"resp_{self.response_counter}"
            
            # Get user profile
            profile = self.user_profiles.get(user_id) if user_id else None
            reading_level = profile.reading_level if profile else ReadingLevel.HIGH_SCHOOL
            
            # Generate primary response
            prompt = ChatPromptTemplate.from_template(
                "Provide a clear, helpful response to this question:\n\n"
                "{user_input}\n\n"
                "Use:\n"
                "- Clear, simple language\n"
                "- Short paragraphs\n"
                "- Specific examples\n"
                "- Organized structure\n\n"
                "Response:"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            primary_content = chain.invoke({"user_input": user_input})
            
            # Create alternative formats
            alternative_formats = {}
            
            # Simplified version
            alternative_formats[ContentFormat.SIMPLIFIED_TEXT] = self.simplify_text(
                primary_content,
                ReadingLevel.MIDDLE_SCHOOL
            )
            
            # Step-by-step if applicable
            if any(word in user_input.lower() for word in ['how', 'steps', 'guide', 'do']):
                steps = self.format_as_steps(primary_content)
                alternative_formats[ContentFormat.STEP_BY_STEP] = '\n'.join(
                    f"{i+1}. {step}" for i, step in enumerate(steps)
                )
            
            # Audio description for screen readers
            if profile and profile.screen_reader:
                alternative_formats[ContentFormat.AUDIO_DESCRIPTION] = \
                    self.create_audio_description(primary_content)
            
            # Estimate reading time (average 200 words/min)
            word_count = len(primary_content.split())
            reading_time = (word_count / 200) * 60  # seconds
            
            # Create response
            response = AccessibleResponse(
                response_id=response_id,
                primary_content=primary_content,
                alternative_formats=alternative_formats,
                reading_time=reading_time,
                metadata={
                    "user_id": user_id,
                    "reading_level": reading_level.value if reading_level else None,
                    "word_count": word_count
                }
            )
            
            self.response_history.append(response)
            return response
        
        def provide_help(self, context: str = "general") -> str:
            """
            Provide accessible help information.
            
            Args:
                context: Help context
                
            Returns:
                Help text
            """
            prompt = ChatPromptTemplate.from_template(
                "Provide clear, beginner-friendly help for: {context}\n\n"
                "Include:\n"
                "- What it does (simple explanation)\n"
                "- How to use it (step-by-step)\n"
                "- Common issues and solutions\n"
                "- Where to get more help\n\n"
                "Use very simple language. Be encouraging and supportive."
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            help_text = chain.invoke({"context": context})
            return help_text
        
        def check_accessibility(self, content: str) -> Dict[str, Any]:
            """
            Check content for accessibility issues.
            
            Args:
                content: Content to check
                
            Returns:
                Accessibility assessment
            """
            prompt = ChatPromptTemplate.from_template(
                "Review this content for accessibility:\n\n"
                "{content}\n\n"
                "Check for:\n"
                "1. Reading level (is it clear and simple?)\n"
                "2. Structure (is it well organized?)\n"
                "3. Language (avoids jargon and complex terms?)\n"
                "4. Length (appropriate, not overwhelming?)\n"
                "5. Clarity (easy to understand?)\n\n"
                "Provide:\n"
                "SCORE: 0-10\n"
                "ISSUES: [list any problems]\n"
                "SUGGESTIONS: [how to improve]"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            assessment = chain.invoke({"content": content})
            
            # Parse assessment
            score = 7.0  # default
            lines = assessment.split('\n')
            for line in lines:
                if line.startswith("SCORE:"):
                    try:
                        score = float(line.split(":")[1].strip().split()[0])
                    except:
                        pass
            
            return {
                "score": score,
                "assessment": assessment,
                "accessible": score >= 7.0
            }
        
        def get_accessibility_report(self) -> Dict[str, Any]:
            """Get accessibility usage report."""
            if not self.response_history:
                return {
                    "total_responses": 0,
                    "avg_reading_time": 0.0,
                    "formats_provided": []
                }
            
            total_reading_time = sum(r.reading_time for r in self.response_history)
            avg_reading_time = total_reading_time / len(self.response_history)
            
            # Count formats
            format_counts = {}
            for response in self.response_history:
                for format_type in response.alternative_formats.keys():
                    format_counts[format_type] = format_counts.get(format_type, 0) + 1
            
            return {
                "total_responses": len(self.response_history),
                "avg_reading_time": avg_reading_time,
                "formats_provided": list(format_counts.keys()),
                "format_usage": {fmt.value: count for fmt, count in format_counts.items()},
                "users_served": len(self.user_profiles)
            }
    
    
    def demonstrate_accessibility():
        """Demonstrate accessibility-focused agent pattern."""
        
        print("=" * 80)
        print("ACCESSIBILITY-FOCUSED AGENT PATTERN DEMONSTRATION")
        print("=" * 80)
        
        agent = AccessibilityAgent()
        
        # Example 1: User profiles
        print("\n" + "=" * 80)
        print("Example 1: Accessibility Profiles")
        print("=" * 80)
        
        profiles = [
            AccessibilityProfile(
                user_id="user1",
                accessibility_needs=[AccessibilityNeed.VISUAL_IMPAIRMENT],
                screen_reader=True,
                audio_enabled=True,
                reading_level=ReadingLevel.MIDDLE_SCHOOL
            ),
            AccessibilityProfile(
                user_id="user2",
                accessibility_needs=[AccessibilityNeed.COGNITIVE_DIFFERENCE],
                preferred_format=ContentFormat.STEP_BY_STEP,
                reading_level=ReadingLevel.ELEMENTARY,
                reduced_motion=True
            ),
            AccessibilityProfile(
                user_id="user3",
                accessibility_needs=[AccessibilityNeed.ELDERLY],
                font_size=1.5,
                high_contrast=True,
                reading_level=ReadingLevel.HIGH_SCHOOL
            )
        ]
        
        for profile in profiles:
            agent.set_user_profile(profile)
            print(f"\nProfile: {profile.user_id}")
            print(f"  Needs: {[need.value for need in profile.accessibility_needs]}")
            print(f"  Reading Level: {profile.reading_level.value}")
            print(f"  Screen Reader: {profile.screen_reader}")
        
        # Example 2: Simplified text
        print("\n" + "=" * 80)
        print("Example 2: Text Simplification")
        print("=" * 80)
        
        complex_text = """
        The implementation leverages sophisticated algorithms to optimize 
        performance characteristics through parallelization and caching mechanisms,
        thereby enhancing computational efficiency and reducing latency.
        """
        
        print("\nOriginal (Complex):")
        print(complex_text)
        
        simplified = agent.simplify_text(complex_text, ReadingLevel.MIDDLE_SCHOOL)
        print("\nSimplified (Middle School Level):")
        print(simplified)
        
        # Example 3: Step-by-step format
        print("\n" + "=" * 80)
        print("Example 3: Step-by-Step Instructions")
        print("=" * 80)
        
        content = "To reset your password, access the settings, find security options, and select password reset."
        
        print("\nOriginal:")
        print(content)
        
        steps = agent.format_as_steps(content)
        print("\nAs Steps:")
        for i, step in enumerate(steps, 1):
            print(f"{i}. {step}")
        
        # Example 4: Accessible responses
        print("\n" + "=" * 80)
        print("Example 4: Accessible Responses with Multiple Formats")
        print("=" * 80)
        
        question = "How do I change my account settings?"
        
        print(f"\nQuestion: {question}")
        print(f"User: user2 (needs: cognitive support, step-by-step)")
        
        response = agent.respond(question, user_id="user2")
        
        print(f"\nPrimary Response:")
        print(response.primary_content[:200] + "...")
        
        print(f"\nAlternative Formats Available:")
        for format_type in response.alternative_formats.keys():
            print(f"  - {format_type.value}")
        
        if ContentFormat.STEP_BY_STEP in response.alternative_formats:
            print(f"\nStep-by-Step Version:")
            print(response.alternative_formats[ContentFormat.STEP_BY_STEP])
        
        print(f"\nReading Time: {response.reading_time:.0f} seconds")
        
        # Example 5: Screen reader support
        print("\n" + "=" * 80)
        print("Example 5: Screen Reader Audio Descriptions")
        print("=" * 80)
        
        visual_content = "A login form with email field, password field, and login button"
        
        print(f"\nVisual Content: {visual_content}")
        
        audio_desc = agent.create_audio_description(visual_content)
        print(f"\nAudio Description for Screen Readers:")
        print(audio_desc)
        
        # Example 6: Help system
        print("\n" + "=" * 80)
        print("Example 6: Accessible Help System")
        print("=" * 80)
        
        print("\nRequesting help for: 'uploading files'")
        
        help_text = agent.provide_help("uploading files")
        print(f"\nHelp Content:")
        print(help_text[:400] + "...")
        
        # Example 7: Accessibility checking
        print("\n" + "=" * 80)
        print("Example 7: Content Accessibility Assessment")
        print("=" * 80)
        
        test_content = "This is a simple, clear explanation that anyone can understand."
        
        print(f"\nChecking content: \"{test_content}\"")
        
        assessment = agent.check_accessibility(test_content)
        print(f"\nAccessibility Score: {assessment['score']}/10")
        print(f"Accessible: {assessment['accessible']}")
        
        # Example 8: Usage report
        print("\n" + "=" * 80)
        print("Example 8: Accessibility Usage Report")
        print("=" * 80)
        
        # Generate some responses
        test_questions = [
            "What is AI?",
            "How do I sign up?",
            "Explain machine learning"
        ]
        
        for q in test_questions:
            agent.respond(q, user_id="user1")
        
        report = agent.get_accessibility_report()
        
        print("\nACCESSIBILITY REPORT:")
        print("=" * 60)
        print(f"Total Responses: {report['total_responses']}")
        print(f"Average Reading Time: {report['avg_reading_time']:.0f} seconds")
        print(f"Users Served: {report['users_served']}")
        print(f"\nFormats Provided:")
        for format_name, count in report.get('format_usage', {}).items():
            print(f"  {format_name}: {count}")
        
        # Summary
        print("\n" + "=" * 80)
        print("SUMMARY: Accessibility-Focused Agent Pattern")
        print("=" * 80)
        
        summary = """
        The Accessibility-Focused Agent pattern demonstrated:
        
        1. USER PROFILES (Example 1):
           - Accessibility needs tracking
           - Individual preferences
           - Assistive technology support
           - Reading level customization
           - Visual preferences
        
        2. TEXT SIMPLIFICATION (Example 2):
           - Reading level adaptation
           - Complex to simple conversion
           - Clear language
           - Appropriate vocabulary
           - Comprehension support
        
        3. STRUCTURED FORMATS (Example 3):
           - Step-by-step instructions
           - Clear organization
           - Easy-to-follow format
           - Actionable guidance
           - Sequential clarity
        
        4. MULTIPLE FORMATS (Example 4):
           - Primary content
           - Alternative representations
           - Format flexibility
           - User choice
           - Comprehensive options
        
        5. SCREEN READER SUPPORT (Example 5):
           - Audio descriptions
           - Verbal navigation
           - Structure explanation
           - Accessible interaction
           - Non-visual access
        
        6. HELP SYSTEM (Example 6):
           - Clear, simple help
           - Beginner-friendly
           - Encouraging tone
           - Practical guidance
           - Support resources
        
        7. QUALITY ASSURANCE (Example 7):
           - Accessibility scoring
           - Content assessment
           - Issue identification
           - Improvement suggestions
           - Standards compliance
        
        8. MONITORING (Example 8):
           - Usage tracking
           - Format analytics
           - Performance metrics
           - User statistics
           - Continuous improvement
        
        KEY BENEFITS:
        ✓ Inclusive user base
        ✓ Legal compliance (ADA, WCAG)
        ✓ Better UX for everyone
        ✓ Broader market reach
        ✓ Social responsibility
        ✓ Reduced support burden
        ✓ Positive brand image
        ✓ Universal usability
        
        USE CASES:
        • Public service AI assistants
        • Educational platforms
        • Healthcare information
        • Government services
        • E-learning applications
        • Banking and finance
        • Emergency services
        • Consumer applications
        
        ACCESSIBILITY FEATURES:
        → Visual: Screen readers, high contrast, text sizing
        → Auditory: Text alternatives, captions, visual alerts
        → Cognitive: Clear language, simple structure
        → Motor: Keyboard navigation, voice control
        → Language: Plain language, translation
        → Context: Help text, examples, clear feedback
        → Time: No pressure, save progress, pause
        
        USER ACCOMMODATIONS:
        • Vision impaired: Audio descriptions
        • Hearing impaired: Text alternatives
        • Cognitive differences: Simple language
        • Motor limitations: Alternative inputs
        • Language barriers: Clear communication
        • Elderly users: Larger text, slower pace
        • Temporary limitations: Flexible methods
        
        BEST PRACTICES:
        1. Follow WCAG guidelines
        2. Test with assistive technologies
        3. Provide multiple interaction modes
        4. Include accessibility settings
        5. Document accessibility features
        6. Collect user feedback
        7. Regular accessibility audits
        8. Train on inclusive practices
        
        TRADE-OFFS:
        • Features vs. simplicity
        • Customization vs. consistency
        • Performance vs. alternatives
        • Automation vs. user control
        
        PRODUCTION CONSIDERATIONS:
        → Comply with ADA and WCAG standards
        → Test with real assistive technologies
        → Provide clear documentation
        → Support keyboard-only navigation
        → Ensure color contrast ratios
        → Include skip navigation links
        → Provide text alternatives
        → Enable font size adjustment
        → Support screen magnification
        → Test with diverse users
        
        This pattern ensures AI agents are truly inclusive, making technology
        accessible to all users regardless of their abilities, creating a more
        equitable and usable experience for everyone.
        """
        
        print(summary)
    
    
    if __name__ == "__main__":
        demonstrate_accessibility()
    
        

    Agentic Design Pattern: Accessibility Focused

    Pattern 136: Accessibility Focused – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Accessibility Focused pattern implementation.


    Pattern Description

    Pattern 136: Accessibility-Focused Agent

    Description: The Accessibility-Focused Agent pattern creates agents designed to be inclusive and usable by people with diverse abilities and needs. These agents prioritize accessibility features such as clear communication, support for assistive technologies, content adaptation, and inclusive design principles to ensure everyone can benefit from AI assistance.

    Accessibility-focused agents go beyond basic usability to accommodate
    visual, auditory, cognitive, motor, and other impairments. They provide
    alternative formats, clear language, patience with interaction, and
    features that make AI technology available to all users regardless of
    their abilities or circumstances.
    
    This pattern embodies inclusive design principles, ensuring AI agents
    are not just functional but truly accessible to diverse populations
    including people with disabilities, elderly users, non-native speakers,
    and users with temporary limitations.
    

    Key Components: 1. Clear Communication: Simple, unambiguous language 2. Alternative Formats: Multiple content representations 3. Assistive Tech Support: Screen readers, voice control 4. Flexible Interaction: Multiple input/output methods 5. Content Adaptation: Adjust to user needs 6. Error Tolerance: Patient, forgiving interactions 7. Inclusive Design: Universal accessibility

    Accessibility Features: 1. Visual: Screen reader support, high contrast, text sizing 2. Auditory: Text alternatives, captions, visual alerts 3. Cognitive: Clear language, simple structure, consistency 4. Motor: Keyboard navigation, voice control, large targets 5. Language: Plain language, translation, glossaries 6. Context: Help text, examples, clear feedback 7. Time: No time pressure, save progress, pause

    User Accommodations: 1. Vision impaired: Audio descriptions, clear structure 2. Hearing impaired: Text alternatives, visual cues 3. Cognitive differences: Simple language, clear layout 4. Motor limitations: Alternative inputs, no precision requirements 5. Language barriers: Clear, simple language, visual aids 6. Elderly users: Larger text, slower pace, clear instructions 7. Temporary limitations: Flexible interaction methods

    Use Cases: – Public service AI assistants – Educational platforms – Healthcare information systems – Government services – E-learning applications – Banking and finance – Emergency services – General consumer applications

    Advantages: – Inclusive user base – Legal compliance (ADA, WCAG) – Better UX for everyone – Broader market reach – Social responsibility – Reduced support burden – Positive brand image

    Challenges: – Balancing features and complexity – Multiple accommodation needs – Testing with diverse users – Performance overhead – Maintaining accessibility – Cost of implementation – Technical limitations

    LangChain Implementation: This implementation uses LangChain for: – Clear, simple language generation – Content format adaptation – Flexible interaction patterns – Accessible error messages

    Production Considerations: – Follow WCAG guidelines – Test with assistive technologies – Provide multiple interaction modes – Include accessibility settings – Document accessibility features – Collect user feedback – Regular accessibility audits – Train on inclusive practices


    File Statistics

    • Total Lines: 799
    • Classes: 6
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 136: Accessibility-Focused Agent
    
    Description:
        The Accessibility-Focused Agent pattern creates agents designed to be
        inclusive and usable by people with diverse abilities and needs. These
        agents prioritize accessibility features such as clear communication,
        support for assistive technologies, content adaptation, and inclusive
        design principles to ensure everyone can benefit from AI assistance.
    
        Accessibility-focused agents go beyond basic usability to accommodate
        visual, auditory, cognitive, motor, and other impairments. They provide
        alternative formats, clear language, patience with interaction, and
        features that make AI technology available to all users regardless of
        their abilities or circumstances.
    
        This pattern embodies inclusive design principles, ensuring AI agents
        are not just functional but truly accessible to diverse populations
        including people with disabilities, elderly users, non-native speakers,
        and users with temporary limitations.
    
    Key Components:
        1. Clear Communication: Simple, unambiguous language
        2. Alternative Formats: Multiple content representations
        3. Assistive Tech Support: Screen readers, voice control
        4. Flexible Interaction: Multiple input/output methods
        5. Content Adaptation: Adjust to user needs
        6. Error Tolerance: Patient, forgiving interactions
        7. Inclusive Design: Universal accessibility
    
    Accessibility Features:
        1. Visual: Screen reader support, high contrast, text sizing
        2. Auditory: Text alternatives, captions, visual alerts
        3. Cognitive: Clear language, simple structure, consistency
        4. Motor: Keyboard navigation, voice control, large targets
        5. Language: Plain language, translation, glossaries
        6. Context: Help text, examples, clear feedback
        7. Time: No time pressure, save progress, pause
    
    User Accommodations:
        1. Vision impaired: Audio descriptions, clear structure
        2. Hearing impaired: Text alternatives, visual cues
        3. Cognitive differences: Simple language, clear layout
        4. Motor limitations: Alternative inputs, no precision requirements
        5. Language barriers: Clear, simple language, visual aids
        6. Elderly users: Larger text, slower pace, clear instructions
        7. Temporary limitations: Flexible interaction methods
    
    Use Cases:
        - Public service AI assistants
        - Educational platforms
        - Healthcare information systems
        - Government services
        - E-learning applications
        - Banking and finance
        - Emergency services
        - General consumer applications
    
    Advantages:
        - Inclusive user base
        - Legal compliance (ADA, WCAG)
        - Better UX for everyone
        - Broader market reach
        - Social responsibility
        - Reduced support burden
        - Positive brand image
    
    Challenges:
        - Balancing features and complexity
        - Multiple accommodation needs
        - Testing with diverse users
        - Performance overhead
        - Maintaining accessibility
        - Cost of implementation
        - Technical limitations
    
    LangChain Implementation:
        This implementation uses LangChain for:
        - Clear, simple language generation
        - Content format adaptation
        - Flexible interaction patterns
        - Accessible error messages
    
    Production Considerations:
        - Follow WCAG guidelines
        - Test with assistive technologies
        - Provide multiple interaction modes
        - Include accessibility settings
        - Document accessibility features
        - Collect user feedback
        - Regular accessibility audits
        - Train on inclusive practices
    """
    

    Purpose: This module implements the Accessibility Focused pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: AccessibilityNeed

    Lines 109-117

    Inherits from: Enum

    Purpose:

    Types of accessibility needs.
    

    Class: ContentFormat

    Lines 120-127

    Inherits from: Enum

    Purpose:

    Content output formats.
    

    Class: ReadingLevel

    Lines 130-136

    Inherits from: Enum

    Purpose:

    Reading comprehension levels.
    

    Class: AccessibilityProfile

    Lines 140-165

    Purpose:

    User accessibility profile.
    
    Attributes:
        user_id: User identifier
        accessibility_needs: List of accessibility requirements
        preferred_format: Preferred content format
        reading_level: Reading comprehension level
        font_size: Text size preference (multiplier)
        high_contrast: High contrast mode
        audio_enabled: Audio output enabled
        reduced_motion: Reduce animations
        keyboard_only: Keyboard-only navigation
        screen_reader: Screen reader in use
    

    Class: AccessibleResponse

    Lines 169-188

    Purpose:

    Accessible response with multiple formats.
    
    Attributes:
        response_id: Unique identifier
        primary_content: Main response
        alternative_formats: Alternative representations
        metadata: Accessibility metadata
        reading_time: Estimated reading time (seconds)
        complexity_score: Content complexity (0-1)
        timestamp: When generated
    

    Class: AccessibilityAgent

    Lines 191-490

    Purpose:

    Agent focused on accessibility and inclusive design.
    
    This agent adapts content and interaction to accommodate
    diverse user needs and abilities.
    

    Methods:

    __init__(self, temperature)

    Lines 199-209

    Purpose:

    Initialize accessibility-focused agent.
    
    Args:
        temperature: LLM temperature
    

    Parameters: – temperature: Sampling temperature (0-1)

    set_user_profile(self, profile)

    Lines 211-213

    Purpose:

    Set accessibility profile for a user.
    

    Parameters: – profile: Profile

    simplify_text(self, text, reading_level)

    Lines 215-253

    Purpose:

    Simplify text to appropriate reading level.
    
    Args:
        text: Original text
        reading_level: Target reading level
    
    Returns:
        Simplified text
    

    Parameters: – text: Text – reading_level: Reading level

    Returns: str

    create_audio_description(self, content)

    Lines 255-279

    Purpose:

    Create audio-friendly description.
    
    Args:
        content: Visual content to describe
    
    Returns:
        Audio description
    

    Parameters: – content: Content

    Returns: str

    format_as_steps(self, content)

    Lines 281-316

    Purpose:

    Format content as clear steps.
    
    Args:
        content: Content to format
    
    Returns:
        List of steps
    

    Parameters: – content: Content

    Returns: <ast.Subscript object at 0x000001A06D74B450>

    respond(self, user_input, user_id)

    Lines 318-394

    Purpose:

    Generate accessible response.
    
    Args:
        user_input: User's question/request
        user_id: User identifier for profile
    
    Returns:
        Accessible response with multiple formats
    

    Parameters: – user_input: Input data – user_id: User id

    Returns: AccessibleResponse

    provide_help(self, context)

    Lines 396-419

    Purpose:

    Provide accessible help information.
    
    Args:
        context: Help context
    
    Returns:
        Help text
    

    Parameters: – context: Context

    Returns: str

    check_accessibility(self, content)

    Lines 421-464

    Purpose:

    Check content for accessibility issues.
    
    Args:
        content: Content to check
    
    Returns:
        Accessibility assessment
    

    Parameters: – content: Content

    Returns: <ast.Subscript object at 0x000001A06D825FD0>

    get_accessibility_report(self)

    Lines 466-490

    Purpose:

    Get accessibility usage report.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D60E9D0>


    Functions

    demonstrate_accessibility()

    Lines 493-794

    Purpose:

    Demonstrate accessibility-focused agent pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Accessibility Focused pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 135
    • Pattern 137

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Accessibility Focused pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 136_accessibility_focused.py
    Total Lines: 799
    Pattern Category: AI Agent Design Pattern #136


    Generated from actual implementation code
    For educational and reference purposes

  • Agentic Design Pattern: Polyglot Agent

    
        """
    Pattern 135: Polyglot Agent
    
    Description:
        The Polyglot Agent pattern creates agents capable of understanding and
        generating content in multiple languages. Unlike single-language agents,
        polyglot agents can seamlessly switch between languages, translate content,
        detect language automatically, and maintain context across language boundaries.
        They understand cultural nuances, idioms, and context-specific language usage.
        
        Polyglot agents are essential for global applications where users speak different
        languages, content needs translation, or multilingual support is required. They
        go beyond simple translation to understand context, maintain consistency across
        languages, and adapt tone appropriately for different cultures.
        
        This pattern enables truly global AI applications that can serve users in their
        native languages while maintaining quality and coherence across all interactions.
    
    Key Components:
        1. Language Detection: Automatic language identification
        2. Translation Engine: High-quality translation
        3. Context Preservation: Maintain meaning across languages
        4. Cultural Adaptation: Culturally appropriate responses
        5. Language Memory: Track language preferences
        6. Multilingual Knowledge: Language-specific information
        7. Quality Validation: Translation accuracy checks
    
    Language Capabilities:
        1. Detection: Identify input language
        2. Translation: Convert between languages
        3. Generation: Create content in any language
        4. Code-Switching: Handle mixed-language input
        5. Localization: Adapt to regional variants
        6. Transcreation: Culturally adapt content
        7. Validation: Check translation quality
    
    Supported Features:
        1. Multiple Languages: Support for many languages
        2. Automatic Detection: Identify language without prompting
        3. Context Awareness: Understand conversation flow
        4. Cultural Sensitivity: Respect cultural differences
        5. Consistent Terminology: Maintain vocabulary across languages
        6. Bidirectional: Handle any language pair
        7. Real-time: Fast language processing
    
    Use Cases:
        - Global customer support
        - Multilingual content generation
        - International e-commerce
        - Language learning applications
        - Global documentation
        - Multilingual chatbots
        - International business communication
        - Content localization
    
    Advantages:
        - Global reach
        - Better user experience
        - Market expansion
        - Cultural inclusivity
        - Reduced translation costs
        - Real-time multilingual support
        - Consistent brand voice
    
    Challenges:
        - Translation accuracy
        - Cultural nuances
        - Idiom handling
        - Context preservation
        - Language model quality
        - Regional variations
        - Technical terminology
    
    LangChain Implementation:
        This implementation uses LangChain for:
        - Language detection
        - Translation chains
        - Multilingual generation
        - Context tracking
        
    Production Considerations:
        - Support major languages first
        - Provide human translation fallback
        - Track translation quality
        - Handle rare languages gracefully
        - Cache common translations
        - Monitor language coverage
        - Respect regional preferences
        - Enable language selection override
    """
    
    import os
    from typing import List, Dict, Any, Optional, Tuple
    from dataclasses import dataclass, field
    from datetime import datetime
    from enum import Enum
    from dotenv import load_dotenv
    
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain_openai import ChatOpenAI
    
    load_dotenv()
    
    
    class Language(Enum):
        """Supported languages."""
        ENGLISH = "en"
        SPANISH = "es"
        FRENCH = "fr"
        GERMAN = "de"
        ITALIAN = "it"
        PORTUGUESE = "pt"
        RUSSIAN = "ru"
        JAPANESE = "ja"
        KOREAN = "ko"
        CHINESE = "zh"
        ARABIC = "ar"
        HINDI = "hi"
        UNKNOWN = "unknown"
    
    
    @dataclass
    class LanguagePreference:
        """
        User language preferences.
        
        Attributes:
            primary_language: Main language
            secondary_languages: Alternative languages
            regional_variant: Regional dialect (e.g., en-US, en-GB)
            formality_level: Formal or informal
        """
        primary_language: Language
        secondary_languages: List[Language] = field(default_factory=list)
        regional_variant: Optional[str] = None
        formality_level: str = "neutral"
    
    
    @dataclass
    class MultilingualMessage:
        """
        Message with language information.
        
        Attributes:
            message_id: Unique identifier
            content: Message content
            detected_language: Detected language
            target_language: Desired output language
            translation: Translated content if applicable
            confidence: Language detection confidence
            timestamp: When created
        """
        message_id: str
        content: str
        detected_language: Language
        target_language: Optional[Language] = None
        translation: Optional[str] = None
        confidence: float = 0.0
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class PolyglotAgent:
        """
        Agent with multilingual capabilities.
        
        This agent can understand and generate content in multiple
        languages with context preservation and cultural awareness.
        """
        
        def __init__(self, temperature: float = 0.5):
            """
            Initialize polyglot agent.
            
            Args:
                temperature: LLM temperature
            """
            self.llm = ChatOpenAI(temperature=temperature, model="gpt-3.5-turbo")
            self.conversation_history: List[MultilingualMessage] = []
            self.message_counter = 0
            self.user_preferences: Dict[str, LanguagePreference] = {}
            
            # Common phrases in different languages for detection
            self.language_patterns = {
                Language.ENGLISH: ["hello", "thank you", "please", "the", "is", "are"],
                Language.SPANISH: ["hola", "gracias", "por favor", "el", "la", "es"],
                Language.FRENCH: ["bonjour", "merci", "s'il vous plaît", "le", "la", "est"],
                Language.GERMAN: ["hallo", "danke", "bitte", "der", "die", "ist"],
                Language.ITALIAN: ["ciao", "grazie", "per favore", "il", "la", "è"],
                Language.PORTUGUESE: ["olá", "obrigado", "por favor", "o", "a", "é"],
            }
        
        def detect_language(self, text: str) -> Tuple[Language, float]:
            """
            Detect language of text.
            
            Args:
                text: Input text
                
            Returns:
                Detected language and confidence
            """
            text_lower = text.lower()
            
            # Simple pattern matching for demo
            language_scores = {}
            for language, patterns in self.language_patterns.items():
                score = sum(1 for pattern in patterns if pattern in text_lower)
                if score > 0:
                    language_scores[language] = score
            
            if language_scores:
                detected = max(language_scores, key=language_scores.get)
                max_score = language_scores[detected]
                confidence = min(0.9, max_score / len(self.language_patterns[detected]))
                return detected, confidence
            
            # Fallback to LLM detection
            prompt = ChatPromptTemplate.from_template(
                "Detect the language of this text: \"{text}\"\n\n"
                "Respond with just the language code (en, es, fr, de, it, pt, ru, ja, ko, zh, ar, hi)"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            result = chain.invoke({"text": text})
            
            # Map result to Language enum
            code = result.strip().lower()[:2]
            for lang in Language:
                if lang.value == code:
                    return lang, 0.7
            
            return Language.UNKNOWN, 0.3
        
        def translate(
            self,
            text: str,
            source_language: Language,
            target_language: Language,
            context: Optional[str] = None
        ) -> str:
            """
            Translate text between languages.
            
            Args:
                text: Text to translate
                source_language: Source language
                target_language: Target language
                context: Optional context
                
            Returns:
                Translated text
            """
            if source_language == target_language:
                return text
            
            prompt_template = (
                "Translate the following text from {source} to {target}.\n"
            )
            
            if context:
                prompt_template += "Context: {context}\n"
            
            prompt_template += (
                "Maintain the tone, meaning, and cultural appropriateness.\n\n"
                "Text: {text}\n\n"
                "Translation:"
            )
            
            prompt = ChatPromptTemplate.from_template(prompt_template)
            chain = prompt | self.llm | StrOutputParser()
            
            input_data = {
                "text": text,
                "source": source_language.name,
                "target": target_language.name
            }
            
            if context:
                input_data["context"] = context
            
            translation = chain.invoke(input_data)
            return translation.strip()
        
        def respond(
            self,
            user_input: str,
            target_language: Optional[Language] = None,
            user_id: Optional[str] = None
        ) -> str:
            """
            Respond to user in appropriate language.
            
            Args:
                user_input: User's message
                target_language: Desired response language (auto-detect if None)
                user_id: User identifier for preferences
                
            Returns:
                Response in target language
            """
            # Detect input language
            detected_lang, confidence = self.detect_language(user_input)
            
            # Determine target language
            if target_language is None:
                # Check user preferences
                if user_id and user_id in self.user_preferences:
                    target_language = self.user_preferences[user_id].primary_language
                else:
                    # Default to input language
                    target_language = detected_lang
            
            # Store message
            self.message_counter += 1
            message = MultilingualMessage(
                message_id=f"msg_{self.message_counter}",
                content=user_input,
                detected_language=detected_lang,
                target_language=target_language,
                confidence=confidence
            )
            self.conversation_history.append(message)
            
            # Generate response
            prompt = ChatPromptTemplate.from_template(
                "Respond to this message in {target_language}:\n\n"
                "User message: {user_input}\n"
                "(Detected language: {detected_language})\n\n"
                "Provide a helpful response in {target_language}, maintaining "
                "appropriate cultural context and tone."
            )
            
            chain = prompt | self.llm | StrOutputParser()
            
            response = chain.invoke({
                "user_input": user_input,
                "target_language": target_language.name,
                "detected_language": detected_lang.name
            })
            
            return response
        
        def set_user_preference(self, user_id: str, preference: LanguagePreference):
            """Set language preferences for a user."""
            self.user_preferences[user_id] = preference
        
        def handle_mixed_language(self, text: str) -> Dict[str, Any]:
            """
            Handle text with multiple languages (code-switching).
            
            Args:
                text: Mixed-language text
                
            Returns:
                Analysis of language segments
            """
            prompt = ChatPromptTemplate.from_template(
                "This text contains multiple languages:\n\n"
                "\"{text}\"\n\n"
                "Identify the languages used and provide:\n"
                "1. List of languages detected\n"
                "2. Dominant language\n"
                "3. Translation to English if needed"
            )
            
            chain = prompt | self.llm | StrOutputParser()
            analysis = chain.invoke({"text": text})
            
            return {
                "original_text": text,
                "analysis": analysis
            }
        
        def localize_content(
            self,
            content: str,
            target_language: Language,
            region: Optional[str] = None
        ) -> str:
            """
            Localize content for specific language and region.
            
            Args:
                content: Content to localize
                target_language: Target language
                region: Regional variant (e.g., US, UK, BR)
                
            Returns:
                Localized content
            """
            prompt_template = (
                "Localize this content for {target_language}"
            )
            
            if region:
                prompt_template += " ({region} region)"
            
            prompt_template += (
                ".\n\n"
                "Original content:\n"
                "{content}\n\n"
                "Adapt idioms, cultural references, units, dates, and formats "
                "to be appropriate for the target audience.\n\n"
                "Localized content:"
            )
            
            prompt = ChatPromptTemplate.from_template(prompt_template)
            chain = prompt | self.llm | StrOutputParser()
            
            input_data = {
                "content": content,
                "target_language": target_language.name
            }
            
            if region:
                input_data["region"] = region
            
            localized = chain.invoke(input_data)
            return localized.strip()
        
        def get_language_statistics(self) -> Dict[str, Any]:
            """Get statistics about language usage."""
            if not self.conversation_history:
                return {
                    "total_messages": 0,
                    "languages_detected": [],
                    "most_common": None
                }
            
            language_counts = {}
            for msg in self.conversation_history:
                lang = msg.detected_language
                language_counts[lang] = language_counts.get(lang, 0) + 1
            
            most_common = max(language_counts, key=language_counts.get) if language_counts else None
            
            return {
                "total_messages": len(self.conversation_history),
                "languages_detected": list(language_counts.keys()),
                "language_distribution": {lang.name: count for lang, count in language_counts.items()},
                "most_common": most_common.name if most_common else None
            }
    
    
    def demonstrate_polyglot():
        """Demonstrate polyglot agent pattern."""
        
        print("=" * 80)
        print("POLYGLOT AGENT PATTERN DEMONSTRATION")
        print("=" * 80)
        
        agent = PolyglotAgent()
        
        # Example 1: Language detection
        print("\n" + "=" * 80)
        print("Example 1: Automatic Language Detection")
        print("=" * 80)
        
        test_phrases = [
            "Hello, how are you?",
            "Hola, ¿cómo estás?",
            "Bonjour, comment allez-vous?",
            "Hallo, wie geht es dir?",
            "Ciao, come stai?"
        ]
        
        print("\nDetecting languages:")
        for phrase in test_phrases:
            detected, confidence = agent.detect_language(phrase)
            print(f'\n"{phrase}"')
            print(f"  → Language: {detected.name} (confidence: {confidence:.2f})")
        
        # Example 2: Translation
        print("\n" + "=" * 80)
        print("Example 2: Translation Between Languages")
        print("=" * 80)
        
        original = "I love learning new languages!"
        source = Language.ENGLISH
        targets = [Language.SPANISH, Language.FRENCH, Language.GERMAN]
        
        print(f"\nOriginal ({source.name}): {original}")
        print("\nTranslations:")
        
        for target in targets:
            translation = agent.translate(original, source, target)
            print(f"  {target.name}: {translation}")
        
        # Example 3: Multilingual conversation
        print("\n" + "=" * 80)
        print("Example 3: Multilingual Conversation")
        print("=" * 80)
        
        print("\nConversation with automatic language switching:")
        
        messages = [
            ("Hello! Can you help me?", None),
            ("Sí, ¿en qué puedo ayudarte?", Language.SPANISH),
            ("Thanks! Do you speak French too?", Language.ENGLISH)
        ]
        
        for msg, target_lang in messages:
            detected, conf = agent.detect_language(msg)
            print(f'\nUser ({detected.name}): {msg}')
            response = agent.respond(msg, target_language=target_lang)
            print(f'Agent: {response}')
        
        # Example 4: User language preferences
        print("\n" + "=" * 80)
        print("Example 4: User Language Preferences")
        print("=" * 80)
        
        # Set preference for user
        user_pref = LanguagePreference(
            primary_language=Language.SPANISH,
            secondary_languages=[Language.ENGLISH],
            regional_variant="es-MX",
            formality_level="formal"
        )
        
        agent.set_user_preference("user123", user_pref)
        
        print("\nUser Preference Set:")
        print(f"  Primary: {user_pref.primary_language.name}")
        print(f"  Variant: {user_pref.regional_variant}")
        print(f"  Formality: {user_pref.formality_level}")
        
        print("\nUser sends message in English:")
        user_msg = "What's the weather like?"
        print(f'User: {user_msg}')
        
        response = agent.respond(user_msg, user_id="user123")
        print(f'Agent (responds in Spanish): {response}')
        
        # Example 5: Code-switching handling
        print("\n" + "=" * 80)
        print("Example 5: Handling Code-Switching")
        print("=" * 80)
        
        mixed_text = "I went to the mercado to buy some légumes and Brot"
        
        print(f'\nMixed-language text: "{mixed_text}"')
        print("(English + Spanish + French + German)")
        
        analysis = agent.handle_mixed_language(mixed_text)
        print("\nAnalysis:")
        print(analysis["analysis"])
        
        # Example 6: Content localization
        print("\n" + "=" * 80)
        print("Example 6: Content Localization")
        print("=" * 80)
        
        us_content = "The conference starts at 9 AM on 12/25. Temperature will be 72°F."
        
        print(f"\nOriginal (US English):")
        print(f"  {us_content}")
        
        print("\nLocalized versions:")
        
        # UK English
        uk_version = agent.localize_content(us_content, Language.ENGLISH, region="UK")
        print(f"\nUK English:")
        print(f"  {uk_version}")
        
        # Spanish (Mexico)
        mx_version = agent.localize_content(us_content, Language.SPANISH, region="MX")
        print(f"\nSpanish (Mexico):")
        print(f"  {mx_version}")
        
        # Example 7: Multilingual customer support
        print("\n" + "=" * 80)
        print("Example 7: Multilingual Customer Support")
        print("=" * 80)
        
        support_queries = [
            ("My order hasn't arrived yet", Language.ENGLISH),
            ("¿Dónde está mi pedido?", Language.SPANISH),
            ("Je n'ai pas reçu ma commande", Language.FRENCH)
        ]
        
        print("\nHandling support queries in multiple languages:")
        
        for query, response_lang in support_queries:
            detected, _ = agent.detect_language(query)
            print(f'\n[{detected.name}] Customer: {query}')
            response = agent.respond(query, target_language=response_lang)
            print(f'[{response_lang.name}] Agent: {response}')
        
        # Example 8: Language statistics
        print("\n" + "=" * 80)
        print("Example 8: Language Usage Statistics")
        print("=" * 80)
        
        stats = agent.get_language_statistics()
        
        print("\nLANGUAGE STATISTICS:")
        print("=" * 60)
        print(f"Total Messages: {stats['total_messages']}")
        print(f"\nLanguages Detected: {', '.join([lang.name for lang in stats['languages_detected']])}")
        
        if stats.get('language_distribution'):
            print("\nDistribution:")
            for lang, count in stats['language_distribution'].items():
                percentage = (count / stats['total_messages']) * 100
                print(f"  {lang}: {count} ({percentage:.1f}%)")
        
        print(f"\nMost Common: {stats['most_common']}")
        
        # Summary
        print("\n" + "=" * 80)
        print("SUMMARY: Polyglot Agent Pattern")
        print("=" * 80)
        
        summary = """
        The Polyglot Agent pattern demonstrated:
        
        1. LANGUAGE DETECTION (Example 1):
           - Automatic identification
           - Confidence scoring
           - Multiple language support
           - Pattern recognition
           - Fallback mechanisms
        
        2. TRANSLATION (Example 2):
           - High-quality conversion
           - Context preservation
           - Multiple target languages
           - Tone maintenance
           - Cultural awareness
        
        3. MULTILINGUAL CONVERSATION (Example 3):
           - Seamless language switching
           - Context preservation
           - Natural flow
           - Automatic adaptation
           - Real-time processing
        
        4. USER PREFERENCES (Example 4):
           - Language preference storage
           - Regional variants
           - Formality levels
           - Automatic application
           - Personalization
        
        5. CODE-SWITCHING (Example 5):
           - Mixed-language handling
           - Language segment identification
           - Dominant language detection
           - Comprehensive translation
           - Context understanding
        
        6. LOCALIZATION (Example 6):
           - Regional adaptation
           - Cultural appropriateness
           - Unit conversion
           - Date format adjustment
           - Idiomatic expressions
        
        7. CUSTOMER SUPPORT (Example 7):
           - Multilingual queries
           - Appropriate responses
           - Language matching
           - Service quality
           - Global reach
        
        8. USAGE ANALYTICS (Example 8):
           - Language distribution
           - Usage patterns
           - Statistics tracking
           - Popular languages
           - Coverage analysis
        
        KEY BENEFITS:
        ✓ Global reach
        ✓ Better user experience
        ✓ Market expansion
        ✓ Cultural inclusivity
        ✓ Reduced translation costs
        ✓ Real-time support
        ✓ Consistent quality
        ✓ Wider accessibility
        
        USE CASES:
        • Global customer support
        • Multilingual content generation
        • International e-commerce
        • Language learning apps
        • Global documentation
        • Multilingual chatbots
        • Business communication
        • Content localization
        
        LANGUAGE CAPABILITIES:
        → Detection: Identify input language
        → Translation: Convert between languages
        → Generation: Create multilingual content
        → Code-Switching: Handle mixed languages
        → Localization: Regional adaptation
        → Transcreation: Cultural adaptation
        → Validation: Quality checking
        
        SUPPORTED FEATURES:
        • Multiple languages (10+)
        • Automatic detection
        • Context awareness
        • Cultural sensitivity
        • Consistent terminology
        • Bidirectional translation
        • Real-time processing
        
        BEST PRACTICES:
        1. Support major languages first
        2. Provide human translation fallback
        3. Track translation quality
        4. Handle rare languages gracefully
        5. Cache common translations
        6. Monitor language coverage
        7. Respect regional preferences
        8. Enable language override
        
        TRADE-OFFS:
        • Coverage vs. quality
        • Speed vs. accuracy
        • Automation vs. human review
        • Generalization vs. specialization
        
        PRODUCTION CONSIDERATIONS:
        → Prioritize high-traffic languages
        → Implement quality monitoring
        → Cache frequent translations
        → Provide confidence scores
        → Enable human escalation
        → Track accuracy metrics
        → Support regional variants
        → Handle cultural sensitivities
        → Maintain terminology consistency
        → Update language models regularly
        
        This pattern enables truly global AI applications that can serve users
        in their native languages while maintaining quality and cultural
        appropriateness across all interactions.
        """
        
        print(summary)
    
    
    if __name__ == "__main__":
        demonstrate_polyglot()
    
        

    Agentic Design Pattern: Polyglot Agent

    Pattern 135: Polyglot Agent – Line-by-Line Explanation

    Overview

    This document provides a detailed line-by-line explanation of the Polyglot Agent pattern implementation.


    Pattern Description

    Pattern 135: Polyglot Agent

    Description: The Polyglot Agent pattern creates agents capable of understanding and generating content in multiple languages. Unlike single-language agents, polyglot agents can seamlessly switch between languages, translate content, detect language automatically, and maintain context across language boundaries. They understand cultural nuances, idioms, and context-specific language usage.

    Polyglot agents are essential for global applications where users speak different
    languages, content needs translation, or multilingual support is required. They
    go beyond simple translation to understand context, maintain consistency across
    languages, and adapt tone appropriately for different cultures.
    
    This pattern enables truly global AI applications that can serve users in their
    native languages while maintaining quality and coherence across all interactions.
    

    Key Components: 1. Language Detection: Automatic language identification 2. Translation Engine: High-quality translation 3. Context Preservation: Maintain meaning across languages 4. Cultural Adaptation: Culturally appropriate responses 5. Language Memory: Track language preferences 6. Multilingual Knowledge: Language-specific information 7. Quality Validation: Translation accuracy checks

    Language Capabilities: 1. Detection: Identify input language 2. Translation: Convert between languages 3. Generation: Create content in any language 4. Code-Switching: Handle mixed-language input 5. Localization: Adapt to regional variants 6. Transcreation: Culturally adapt content 7. Validation: Check translation quality

    Supported Features: 1. Multiple Languages: Support for many languages 2. Automatic Detection: Identify language without prompting 3. Context Awareness: Understand conversation flow 4. Cultural Sensitivity: Respect cultural differences 5. Consistent Terminology: Maintain vocabulary across languages 6. Bidirectional: Handle any language pair 7. Real-time: Fast language processing

    Use Cases: – Global customer support – Multilingual content generation – International e-commerce – Language learning applications – Global documentation – Multilingual chatbots – International business communication – Content localization

    Advantages: – Global reach – Better user experience – Market expansion – Cultural inclusivity – Reduced translation costs – Real-time multilingual support – Consistent brand voice

    Challenges: – Translation accuracy – Cultural nuances – Idiom handling – Context preservation – Language model quality – Regional variations – Technical terminology

    LangChain Implementation: This implementation uses LangChain for: – Language detection – Translation chains – Multilingual generation – Context tracking

    Production Considerations: – Support major languages first – Provide human translation fallback – Track translation quality – Handle rare languages gracefully – Cache common translations – Monitor language coverage – Respect regional preferences – Enable language selection override


    File Statistics

    • Total Lines: 751
    • Classes: 4
    • Functions: 1
    • Constants: 0

    Module Documentation

    """
    Pattern 135: Polyglot Agent
    
    Description:
        The Polyglot Agent pattern creates agents capable of understanding and
        generating content in multiple languages. Unlike single-language agents,
        polyglot agents can seamlessly switch between languages, translate content,
        detect language automatically, and maintain context across language boundaries.
        They understand cultural nuances, idioms, and context-specific language usage.
    
        Polyglot agents are essential for global applications where users speak different
        languages, content needs translation, or multilingual support is required. They
        go beyond simple translation to understand context, maintain consistency across
        languages, and adapt tone appropriately for different cultures.
    
        This pattern enables truly global AI applications that can serve users in their
        native languages while maintaining quality and coherence across all interactions.
    
    Key Components:
        1. Language Detection: Automatic language identification
        2. Translation Engine: High-quality translation
        3. Context Preservation: Maintain meaning across languages
        4. Cultural Adaptation: Culturally appropriate responses
        5. Language Memory: Track language preferences
        6. Multilingual Knowledge: Language-specific information
        7. Quality Validation: Translation accuracy checks
    
    Language Capabilities:
        1. Detection: Identify input language
        2. Translation: Convert between languages
        3. Generation: Create content in any language
        4. Code-Switching: Handle mixed-language input
        5. Localization: Adapt to regional variants
        6. Transcreation: Culturally adapt content
        7. Validation: Check translation quality
    
    Supported Features:
        1. Multiple Languages: Support for many languages
        2. Automatic Detection: Identify language without prompting
        3. Context Awareness: Understand conversation flow
        4. Cultural Sensitivity: Respect cultural differences
        5. Consistent Terminology: Maintain vocabulary across languages
        6. Bidirectional: Handle any language pair
        7. Real-time: Fast language processing
    
    Use Cases:
        - Global customer support
        - Multilingual content generation
        - International e-commerce
        - Language learning applications
        - Global documentation
        - Multilingual chatbots
        - International business communication
        - Content localization
    
    Advantages:
        - Global reach
        - Better user experience
        - Market expansion
        - Cultural inclusivity
        - Reduced translation costs
        - Real-time multilingual support
        - Consistent brand voice
    
    Challenges:
        - Translation accuracy
        - Cultural nuances
        - Idiom handling
        - Context preservation
        - Language model quality
        - Regional variations
        - Technical terminology
    
    LangChain Implementation:
        This implementation uses LangChain for:
        - Language detection
        - Translation chains
        - Multilingual generation
        - Context tracking
    
    Production Considerations:
        - Support major languages first
        - Provide human translation fallback
        - Track translation quality
        - Handle rare languages gracefully
        - Cache common translations
        - Monitor language coverage
        - Respect regional preferences
        - Enable language selection override
    """
    

    Purpose: This module implements the Polyglot Agent pattern, providing a structured approach to agent design and implementation.


    Import Statements

    Standard Library Imports

    import dataclasses.dataclass
    import dataclasses.field
    import datetime.datetime
    import enum.Enum
    import langchain_core.output_parsers.StrOutputParser
    import langchain_core.prompts.ChatPromptTemplate
    import os
    import typing.Any
    import typing.Dict
    import typing.List
    import typing.Optional
    import typing.Tuple
    

    Purpose: Core Python functionality: – dataclasses.dataclass: Simplified class definitions with @dataclass – dataclasses.field: Simplified class definitions with @dataclass – datetime.datetime: Date and time handling – enum.Enum: Enumeration support – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Tuple: Type hints for better code documentation (List, Dict, Any, Optional)

    LangChain Imports

    from langchain_openai.ChatOpenAI
    

    Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

    Other Imports

    from dotenv.load_dotenv
    

    Purpose: Additional dependencies for specialized functionality.


    Classes

    Class: Language

    Lines 106-120

    Inherits from: Enum

    Purpose:

    Supported languages.
    

    Class: LanguagePreference

    Lines 124-137

    Purpose:

    User language preferences.
    
    Attributes:
        primary_language: Main language
        secondary_languages: Alternative languages
        regional_variant: Regional dialect (e.g., en-US, en-GB)
        formality_level: Formal or informal
    

    Class: MultilingualMessage

    Lines 141-160

    Purpose:

    Message with language information.
    
    Attributes:
        message_id: Unique identifier
        content: Message content
        detected_language: Detected language
        target_language: Desired output language
        translation: Translated content if applicable
        confidence: Language detection confidence
        timestamp: When created
    

    Class: PolyglotAgent

    Lines 163-443

    Purpose:

    Agent with multilingual capabilities.
    
    This agent can understand and generate content in multiple
    languages with context preservation and cultural awareness.
    

    Methods:

    __init__(self, temperature)

    Lines 171-191

    Purpose:

    Initialize polyglot agent.
    
    Args:
        temperature: LLM temperature
    

    Parameters: – temperature: Sampling temperature (0-1)

    detect_language(self, text)

    Lines 193-233

    Purpose:

    Detect language of text.
    
    Args:
        text: Input text
    
    Returns:
        Detected language and confidence
    

    Parameters: – text: Text

    Returns: <ast.Subscript object at 0x000001A06D165BD0>

    translate(self, text, source_language, target_language, context)

    Lines 235-283

    Purpose:

    Translate text between languages.
    
    Args:
        text: Text to translate
        source_language: Source language
        target_language: Target language
        context: Optional context
    
    Returns:
        Translated text
    

    Parameters: – text: Text – source_language: Source language – target_language: Target language – context: Context

    Returns: str

    respond(self, user_input, target_language, user_id)

    Lines 285-342

    Purpose:

    Respond to user in appropriate language.
    
    Args:
        user_input: User's message
        target_language: Desired response language (auto-detect if None)
        user_id: User identifier for preferences
    
    Returns:
        Response in target language
    

    Parameters: – user_input: Input data – target_language: Target language – user_id: User id

    Returns: str

    set_user_preference(self, user_id, preference)

    Lines 344-346

    Purpose:

    Set language preferences for a user.
    

    Parameters: – user_id: User id – preference: Preference

    handle_mixed_language(self, text)

    Lines 348-373

    Purpose:

    Handle text with multiple languages (code-switching).
    
    Args:
        text: Mixed-language text
    
    Returns:
        Analysis of language segments
    

    Parameters: – text: Text

    Returns: <ast.Subscript object at 0x000001A06D85B7D0>

    localize_content(self, content, target_language, region)

    Lines 375-420

    Purpose:

    Localize content for specific language and region.
    
    Args:
        content: Content to localize
        target_language: Target language
        region: Regional variant (e.g., US, UK, BR)
    
    Returns:
        Localized content
    

    Parameters: – content: Content – target_language: Target language – region: Region

    Returns: str

    get_language_statistics(self)

    Lines 422-443

    Purpose:

    Get statistics about language usage.
    

    Parameters:

    Returns: <ast.Subscript object at 0x000001A06D7CB110>


    Functions

    demonstrate_polyglot()

    Lines 446-746

    Purpose:

    Demonstrate polyglot agent pattern.
    

    Key Concepts Summary

    Pattern Overview

    The Polyglot Agent pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

    Core Components

    1. Agent Class: Main implementation of the pattern logic
    2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
    3. Tool Integration: Optional tools for extended functionality
    4. Memory Management: State handling across interactions
    5. Output Parsing: Structured response processing

    When to Use

    ✅ Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

    ❌ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


    Best Practices Demonstrated

    1. Environment Configuration

    • ✅ Use .env files for API keys
    • ✅ Never hardcode credentials
    • ✅ Validate environment variables on startup

    2. Error Handling

    • ✅ Graceful degradation on failures
    • ✅ Informative error messages
    • ✅ Logging for debugging

    3. Prompt Engineering

    • ✅ Clear, specific instructions
    • ✅ Structured output formats
    • ✅ Few-shot examples when beneficial

    4. Code Organization

    • ✅ Separation of concerns (class-based design)
    • ✅ Type hints for clarity
    • ✅ Docstrings for documentation

    5. Performance Optimization

    • ✅ Appropriate temperature settings
    • ✅ Token budget management
    • ✅ Caching where applicable

    LangChain Components Used

    • ChatOpenAI: OpenAI model interface
    • Chains: Sequential component composition

    Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


    Execution Flow

    1. Initialize agent with configuration
       ↓
    2. Load environment and dependencies
       ↓
    3. Prepare prompt with input data
       ↓
    4. Invoke LLM through LangChain
       ↓
    5. Parse and validate response
       ↓
    6. Return formatted result
    

    Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


    Performance Considerations

    Computational Cost

    • LLM Calls: Primary cost factor (based on tokens)
    • Latency: Depends on model and input size
    • Memory: Minimal for most patterns

    Optimization Strategies

    1. Prompt Optimization: Minimize tokens while maintaining clarity
    2. Caching: Cache repeated queries/responses
    3. Model Selection: Use smaller models when appropriate
    4. Batch Processing: Group similar requests
    5. Streaming: For long responses, stream tokens

    Cost Estimates (Approximate)

    • GPT-3.5-turbo: ~$0.002 per request
    • GPT-4: ~$0.03 per request
    • GPT-4-turbo: ~$0.01 per request

    Actual costs vary based on input/output token counts


    Common Pitfalls to Avoid

    1. Prompt Design Issues

    ❌ Vague or ambiguous instructions
    ✅ Clear, specific, testable prompts

    2. Error Handling

    ❌ Ignoring API errors or rate limits
    ✅ Implement retry logic and graceful degradation

    3. Token Management

    ❌ Exceeding model token limits
    ✅ Truncate inputs, chunk processing

    4. Security

    ❌ Exposing API keys in code
    ✅ Use environment variables

    5. Testing

    ❌ Only testing happy paths
    ✅ Test edge cases, failures, malformed inputs

    6. Cost Management

    ❌ Unmonitored API usage
    ✅ Implement budgets, alerts, and monitoring


    Further Improvements

    Production Enhancements

    1. Monitoring: Add logging, metrics, tracing
    2. Validation: Input/output schema validation
    3. Rate Limiting: Implement request throttling
    4. Caching: Redis or in-memory cache for responses
    5. Async Support: Non-blocking operations for scalability

    Feature Extensions

    1. Multi-Model Support: Switch between providers
    2. Fallback Models: Graceful degradation strategy
    3. Custom Tools: Domain-specific function calling
    4. Advanced Memory: Vector stores for long-term memory
    5. Evaluation: Automated testing and benchmarking

    Code Quality

    1. Testing: Unit tests, integration tests
    2. Documentation: API docs, usage examples
    3. Type Safety: Stricter type hints
    4. Configuration: Externalized config files

    Related Patterns

    • Pattern 134
    • Pattern 136

    Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


    Summary

    The Polyglot Agent pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

    ✅ Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

    🎯 Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

    📚 Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


    File: 135_polyglot_agent.py
    Total Lines: 751
    Pattern Category: AI Agent Design Pattern #135


    Generated from actual implementation code
    For educational and reference purposes