Agentic Architecture Pattern: Supervised Learning

Written by

in


    """
Pattern 52: Supervised Learning Agent

Agent learns from labeled training data to make predictions on new inputs.
Implements training, validation, evaluation, and prediction using LLM-based learning.
"""

from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import random
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI


class LearningStrategy(Enum):
    """Strategies for learning from examples."""
    FEW_SHOT = "few_shot"  # Use examples in prompt
    FINE_TUNING = "fine_tuning"  # Simulate fine-tuning
    PATTERN_MATCHING = "pattern_matching"  # Match patterns


@dataclass
class TrainingExample:
    """Labeled training example."""
    input_data: str
    label: str
    weight: float = 1.0
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class ValidationResult:
    """Validation metrics."""
    accuracy: float
    precision: float
    recall: float
    f1_score: float
    confusion_matrix: Dict[str, int]


@dataclass
class Prediction:
    """Model prediction with confidence."""
    input_data: str
    predicted_label: str
    confidence: float
    reasoning: Optional[str] = None
    actual_label: Optional[str] = None
    correct: Optional[bool] = None
    timestamp: datetime = field(default_factory=datetime.now)


class SupervisedLearningAgent:
    """Agent that learns from labeled examples."""
    
    def __init__(self, name: str, task_description: str, 
                 model_name: str = "gpt-4", strategy: LearningStrategy = LearningStrategy.FEW_SHOT):
        self.name = name
        self.task_description = task_description
        self.llm = ChatOpenAI(model=model_name, temperature=0.3)
        self.strategy = strategy
        self.training_examples: List[TrainingExample] = []
        self.validation_examples: List[TrainingExample] = []
        self.predictions: List[Prediction] = []
        self.is_trained = False
    
    def train(self, examples: List[TrainingExample], validation_split: float = 0.2):
        """Train on labeled examples."""
        print(f"\n๐Ÿ“š {self.name}: Training on {len(examples)} examples...")
        
        # Split into training and validation
        shuffled = examples.copy()
        random.shuffle(shuffled)
        
        split_idx = int(len(shuffled) * (1 - validation_split))
        self.training_examples = shuffled[:split_idx]
        self.validation_examples = shuffled[split_idx:]
        
        self.is_trained = True
        print(f"   Training examples: {len(self.training_examples)}")
        print(f"   Validation examples: {len(self.validation_examples)}")
    
    def _create_few_shot_prompt(self, input_data: str, num_examples: int = 5) -> str:
        """Create few-shot prompt with training examples."""
        prompt_parts = [f"Task: {self.task_description}\n"]
        prompt_parts.append("Learn from these labeled examples:\n")
        
        # Sample random training examples
        sample_examples = random.sample(
            self.training_examples, 
            min(num_examples, len(self.training_examples))
        )
        
        for i, ex in enumerate(sample_examples, 1):
            prompt_parts.append(f"\nExample {i}:")
            prompt_parts.append(f"Input: {ex.input_data}")
            prompt_parts.append(f"Label: {ex.label}")
        
        prompt_parts.append(f"\n\nNow predict the label for this input:")
        prompt_parts.append(f"Input: {input_data}")
        prompt_parts.append("Label:")
        
        return "\n".join(prompt_parts)
    
    def predict(self, input_data: str, return_reasoning: bool = False) -> Prediction:
        """Make prediction using learned patterns."""
        if not self.is_trained:
            raise ValueError(f"{self.name} has not been trained yet")
        
        # Create prompt based on strategy
        if self.strategy == LearningStrategy.FEW_SHOT:
            prompt_text = self._create_few_shot_prompt(input_data)
        else:
            prompt_text = f"Task: {self.task_description}\n\nInput: {input_data}\n\nLabel:"
        
        # Get prediction from LLM
        prompt = ChatPromptTemplate.from_messages([
            ("system", f"You are {self.name}, a supervised learning model. "
                      f"Predict labels based on learned patterns. "
                      f"{'Provide brief reasoning.' if return_reasoning else 'Provide only the label.'}"),
            ("human", "{prompt}")
        ])
        
        chain = prompt | self.llm | StrOutputParser()
        response = chain.invoke({"prompt": prompt_text})
        
        # Parse response
        lines = response.strip().split('\n')
        predicted_label = lines[0].strip()
        reasoning = '\n'.join(lines[1:]).strip() if len(lines) > 1 and return_reasoning else None
        
        # Estimate confidence (simplified)
        confidence = 0.85 if len(self.training_examples) > 10 else 0.70
        
        prediction = Prediction(
            input_data=input_data,
            predicted_label=predicted_label,
            confidence=confidence,
            reasoning=reasoning
        )
        
        self.predictions.append(prediction)
        return prediction
    
    def evaluate(self, test_examples: List[TrainingExample]) -> ValidationResult:
        """Evaluate model on test set."""
        print(f"\n๐Ÿงช Evaluating {self.name} on {len(test_examples)} test examples...")
        
        correct = 0
        predictions_dict: Dict[str, List[Prediction]] = {}
        confusion = {}
        
        for ex in test_examples:
            pred = self.predict(ex.input_data)
            pred.actual_label = ex.label
            pred.correct = (pred.predicted_label.lower().strip() == ex.label.lower().strip())
            
            if pred.correct:
                correct += 1
            
            # Track predictions by label
            if ex.label not in predictions_dict:
                predictions_dict[ex.label] = []
            predictions_dict[ex.label].append(pred)
            
            # Confusion matrix
            key = f"{ex.label}->{pred.predicted_label}"
            confusion[key] = confusion.get(key, 0) + 1
        
        accuracy = correct / len(test_examples) if test_examples else 0
        
        # Calculate precision, recall, F1 per class (simplified)
        precision = accuracy  # Simplified
        recall = accuracy
        f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
        
        return ValidationResult(
            accuracy=accuracy,
            precision=precision,
            recall=recall,
            f1_score=f1_score,
            confusion_matrix=confusion
        )
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get learning statistics."""
        correct_predictions = sum(1 for p in self.predictions if p.correct)
        total_predictions = len([p for p in self.predictions if p.actual_label is not None])
        
        return {
            "name": self.name,
            "is_trained": self.is_trained,
            "training_examples": len(self.training_examples),
            "validation_examples": len(self.validation_examples),
            "total_predictions": len(self.predictions),
            "accuracy": correct_predictions / total_predictions if total_predictions > 0 else 0,
            "strategy": self.strategy.value
        }


class SupervisedLearningPattern:
    """
    Supervised Learning Agent Pattern - Learn from labeled data.
    
    Trains agents on labeled examples to make predictions on new inputs.
    Supports training, validation, evaluation, and prediction workflows.
    """
    
    def __init__(self, model_name: str = "gpt-4"):
        self.model_name = model_name
        self.agents: Dict[str, SupervisedLearningAgent] = {}
    
    def create_agent(self, name: str, task_description: str, 
                    strategy: LearningStrategy = LearningStrategy.FEW_SHOT) -> SupervisedLearningAgent:
        """Create a supervised learning agent."""
        agent = SupervisedLearningAgent(name, task_description, self.model_name, strategy)
        self.agents[name] = agent
        return agent
    
    def train_test_split(self, examples: List[TrainingExample], 
                        test_size: float = 0.2) -> Tuple[List[TrainingExample], List[TrainingExample]]:
        """Split examples into training and test sets."""
        shuffled = examples.copy()
        random.shuffle(shuffled)
        
        split_idx = int(len(shuffled) * (1 - test_size))
        return shuffled[:split_idx], shuffled[split_idx:]
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get pattern statistics."""
        return {
            "total_agents": len(self.agents),
            "agents": {name: agent.get_statistics() for name, agent in self.agents.items()}
        }


# Example usage
if __name__ == "__main__":
    print("=== Supervised Learning Agent Demo ===\n")
    
    # Create pattern
    print("1. Creating supervised learning pattern...")
    pattern = SupervisedLearningPattern()
    
    # Task: Sentiment Analysis
    print(f"\n{'='*70}")
    print("๐Ÿ“Š Task: Sentiment Analysis")
    print(f"{'='*70}")
    
    agent = pattern.create_agent(
        name="SentimentClassifier",
        task_description="Classify text sentiment as Positive, Negative, or Neutral"
    )
    
    # Create training data
    training_data = [
        TrainingExample("I love this product! Amazing quality!", "Positive"),
        TrainingExample("Best purchase ever! Highly recommend!", "Positive"),
        TrainingExample("Fantastic experience, will buy again", "Positive"),
        TrainingExample("Really impressed with the service", "Positive"),
        TrainingExample("Excellent quality and fast shipping", "Positive"),
        
        TrainingExample("Terrible experience, very disappointed", "Negative"),
        TrainingExample("Waste of money, doesn't work at all", "Negative"),
        TrainingExample("Poor quality, broke after one use", "Negative"),
        TrainingExample("Worst product I've ever bought", "Negative"),
        TrainingExample("Not worth it, total ripoff", "Negative"),
        
        TrainingExample("It's okay, nothing special", "Neutral"),
        TrainingExample("Average product, does the job", "Neutral"),
        TrainingExample("Not bad, not great either", "Neutral"),
        TrainingExample("Acceptable quality for the price", "Neutral"),
        TrainingExample("Standard product, meets expectations", "Neutral"),
    ]
    
    # Split data
    train_data, test_data = pattern.train_test_split(training_data, test_size=0.3)
    
    # Train agent
    print("\n2. Training agent...")
    agent.train(train_data, validation_split=0.2)
    
    # Make predictions
    print(f"\n{'='*70}")
    print("๐Ÿ”ฎ Making Predictions")
    print(f"{'='*70}\n")
    
    test_inputs = [
        "This is absolutely wonderful!",
        "Completely useless and broken",
        "It works as expected, nothing more"
    ]
    
    for test_input in test_inputs:
        pred = agent.predict(test_input, return_reasoning=False)
        print(f"Input: {pred.input_data}")
        print(f"Prediction: {pred.predicted_label}")
        print(f"Confidence: {pred.confidence:.2%}\n")
    
    # Evaluate on test set
    print(f"\n{'='*70}")
    print("๐Ÿ“ˆ Model Evaluation")
    print(f"{'='*70}\n")
    
    results = agent.evaluate(test_data)
    
    print(f"Accuracy: {results.accuracy:.2%}")
    print(f"Precision: {results.precision:.2%}")
    print(f"Recall: {results.recall:.2%}")
    print(f"F1 Score: {results.f1_score:.2%}")
    
    print(f"\nConfusion Matrix:")
    for key, count in sorted(results.confusion_matrix.items()):
        print(f"  {key}: {count}")
    
    # Statistics
    print(f"\n{'='*70}")
    print("๐Ÿ“Š Learning Statistics")
    print(f"{'='*70}")
    
    stats = pattern.get_statistics()
    
    for agent_name, agent_stats in stats['agents'].items():
        print(f"\nAgent: {agent_name}")
        print(f"  Trained: {agent_stats['is_trained']}")
        print(f"  Training examples: {agent_stats['training_examples']}")
        print(f"  Validation examples: {agent_stats['validation_examples']}")
        print(f"  Total predictions: {agent_stats['total_predictions']}")
        print(f"  Accuracy: {agent_stats['accuracy']:.2%}")
        print(f"  Strategy: {agent_stats['strategy']}")
    
    print("\nโœ“ Supervised learning enabled prediction from labeled data!")

    

Agentic Architecture Pattern: Supervised Learning

Pattern 52: 52 Supervised Learning – Line-by-Line Explanation

Overview

Pattern 52: Supervised Learning Agent

Agent learns from labeled training data to make predictions on new inputs. Implements training, validation, evaluation, and prediction using LLM-based learning.


Code Structure Analysis

File Information

  • Pattern Number: 52
  • Pattern Name: 52 Supervised Learning
  • File Path: patterns\learning_adaptation\52_supervised_learning.py
  • Total Lines: 336

Detailed Code Explanation

Module Docstring

"""
Pattern 52: Supervised Learning Agent

Agent learns from labeled training data to make predictions on new inputs.
Implements training, validation, evaluation, and prediction using LLM-based learning.
"""

Explanation: This module docstring describes the purpose and key features of the 52 Supervised Learning pattern.


Imports Section

from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import random
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Tuple: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime: Date and time utilities – from enum import Enum: Enumeration support for fixed sets of values – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_core.output_parsers import StrOutputParser: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Enumerations

LearningStrategy (Line 18)

class LearningStrategy(Enum):
    FEW_SHOT
    FINE_TUNING
    PATTERN_MATCHING

Purpose: Strategies for learning from examples.

Values: – FEW_SHOT: Represents few shot – FINE_TUNING: Represents fine tuning – PATTERN_MATCHING: Represents pattern matching


Classes

LearningStrategy (Line 18)

Description: Strategies for learning from examples.

TrainingExample (Line 26)

Description: Labeled training example.

ValidationResult (Line 35)

Description: Validation metrics.

Prediction (Line 45)

Description: Model prediction with confidence.

SupervisedLearningAgent (Line 56)

Description: Agent that learns from labeled examples.

Methods:

__init__ (Line 59)
def __init__(self, name, task_description, model_name, strategy):
    """No method docstring."""

Purpose: No method docstring.

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

train (Line 70)
def train(self, examples, validation_split):
    """Train on labeled examples."""

Purpose: Train on labeled examples.

Parameters: – self: Instance reference – examples: Parameter for examples – validation_split: Parameter for validation_split

_create_few_shot_prompt (Line 86)
def _create_few_shot_prompt(self, input_data, num_examples):
    """Create few-shot prompt with training examples."""

Purpose: Create few-shot prompt with training examples.

Parameters: – self: Instance reference – input_data: Parameter for input_data – num_examples: Parameter for num_examples

predict (Line 108)
def predict(self, input_data, return_reasoning):
    """Make prediction using learned patterns."""

Purpose: Make prediction using learned patterns.

Parameters: – self: Instance reference – input_data: Parameter for input_data – return_reasoning: Parameter for return_reasoning

evaluate (Line 148)
def evaluate(self, test_examples):
    """Evaluate model on test set."""

Purpose: Evaluate model on test set.

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

get_statistics (Line 188)
def get_statistics(self):
    """Get learning statistics."""

Purpose: Get learning statistics.

Parameters: – self: Instance reference

SupervisedLearningPattern (Line 204)

Description: Supervised Learning Agent Pattern – Learn from labeled data.

Trains agents on labeled examples to make predictions on new inputs. Supports training, validation, evaluation, and prediction workflows.

Methods:

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

Purpose: No method docstring.

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

create_agent (Line 216)
def create_agent(self, name, task_description, strategy):
    """Create a supervised learning agent."""

Purpose: Create a supervised learning agent.

Parameters: – self: Instance reference – name: Identifier or label – task_description: Parameter for task_description – strategy: Parameter for strategy

train_test_split (Line 223)
def train_test_split(self, examples, test_size):
    """Split examples into training and test sets."""

Purpose: Split examples into training and test sets.

Parameters: – self: Instance reference – examples: Parameter for examples – test_size: Parameter for test_size

get_statistics (Line 232)
def get_statistics(self):
    """Get pattern statistics."""

Purpose: Get pattern statistics.

Parameters: – self: Instance reference


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = LearningStrategy(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

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