Agentic Architecture Pattern: Extension

Written by

in


    """
Pattern 96: Extension Pattern

Extend agent behavior by adding new capabilities without modifying existing code.
Extensions inherit from base agent and add specific features.
"""

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from abc import ABC, abstractmethod
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


class BaseAgent(ABC):
    """Base agent that can be extended."""
    
    def __init__(self, name: str, model_name: str = "gpt-4"):
        self.name = name
        self.llm = ChatOpenAI(model=model_name, temperature=0.7)
    
    @abstractmethod
    def core_capability(self, input_data: str) -> str:
        """Core capability of the agent."""
        pass


@dataclass
class Extension:
    """Extension that adds capabilities."""
    name: str
    capability: Callable
    description: str
    priority: int = 0
    enabled: bool = True
    call_count: int = 0


class ExtensibleAgent(BaseAgent):
    """Agent that supports extensions."""
    
    def __init__(self, name: str, model_name: str = "gpt-4"):
        super().__init__(name, model_name)
        self.extensions: Dict[str, Extension] = {}
    
    def core_capability(self, input_data: str) -> str:
        """Core capability - basic text processing."""
        prompt = ChatPromptTemplate.from_messages([
            ("system", f"You are {self.name}, a base AI agent."),
            ("human", "{input}")
        ])
        chain = prompt | self.llm
        response = chain.invoke({"input": input_data})
        return str(response.content)
    
    def add_extension(self, name: str, capability: Callable, 
                     description: str, priority: int = 0):
        """Add an extension."""
        extension = Extension(
            name=name,
            capability=capability,
            description=description,
            priority=priority
        )
        self.extensions[name] = extension
        print(f"Extension added: {name} (priority: {priority})")
    
    def remove_extension(self, name: str):
        """Remove an extension."""
        if name in self.extensions:
            del self.extensions[name]
            print(f"Extension removed: {name}")
    
    def enable_extension(self, name: str):
        """Enable an extension."""
        if name in self.extensions:
            self.extensions[name].enabled = True
    
    def disable_extension(self, name: str):
        """Disable an extension."""
        if name in self.extensions:
            self.extensions[name].enabled = False
    
    def execute_with_extensions(self, input_data: str) -> Dict[str, Any]:
        """Execute with all enabled extensions."""
        results = {
            "core_result": self.core_capability(input_data),
            "extension_results": {}
        }
        
        # Sort extensions by priority (higher priority first)
        sorted_extensions = sorted(
            self.extensions.values(),
            key=lambda e: e.priority,
            reverse=True
        )
        
        # Execute enabled extensions
        for ext in sorted_extensions:
            if ext.enabled:
                try:
                    result = ext.capability(input_data)
                    results["extension_results"][ext.name] = result
                    ext.call_count += 1
                except Exception as e:
                    results["extension_results"][ext.name] = f"Error: {str(e)}"
        
        return results
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get extension statistics."""
        return {
            "total_extensions": len(self.extensions),
            "enabled_extensions": sum(1 for e in self.extensions.values() if e.enabled),
            "extension_calls": {
                name: ext.call_count
                for name, ext in self.extensions.items()
            }
        }


class ExtensionPattern:
    """
    Extension Pattern - Manage extensible agents.
    """
    
    def __init__(self, model_name: str = "gpt-4"):
        self.model_name = model_name
        self.agents: Dict[str, ExtensibleAgent] = {}
    
    def create_agent(self, name: str) -> ExtensibleAgent:
        """Create a new extensible agent."""
        agent = ExtensibleAgent(name, self.model_name)
        self.agents[name] = agent
        return agent
    
    def run(self, agent_name: str, input_data: str) -> Dict[str, Any]:
        """Run agent with extensions."""
        if agent_name not in self.agents:
            return {"error": f"Agent {agent_name} not found"}
        
        agent = self.agents[agent_name]
        result = agent.execute_with_extensions(input_data)
        
        return {
            "agent": agent_name,
            "input": input_data,
            "results": result,
            "statistics": agent.get_statistics()
        }


# Example usage
if __name__ == "__main__":
    # Create pattern
    pattern = ExtensionPattern()
    
    # Create agent
    agent = pattern.create_agent("SmartAssistant")
    
    # Add extensions
    agent.add_extension(
        name="sentiment_analyzer",
        capability=lambda text: "positive" if "good" in text.lower() or "great" in text.lower() else "neutral",
        description="Analyzes sentiment of text",
        priority=10
    )
    
    agent.add_extension(
        name="word_counter",
        capability=lambda text: f"{len(text.split())} words",
        description="Counts words in text",
        priority=5
    )
    
    agent.add_extension(
        name="text_summarizer",
        capability=lambda text: text[:50] + "..." if len(text) > 50 else text,
        description="Summarizes long text",
        priority=3
    )
    
    # Test
    print("=== Extension Pattern Demo ===\n")
    
    # Run with extensions
    result = pattern.run("SmartAssistant", "This is a great day to learn about AI extensions!")
    
    print(f"Agent: {result['agent']}")
    print(f"\nInput: {result['input']}")
    print(f"\nCore Result: {result['results']['core_result'][:100]}...")
    print(f"\nExtension Results:")
    for ext_name, ext_result in result['results']['extension_results'].items():
        print(f"  - {ext_name}: {ext_result}")
    
    print(f"\nStatistics:")
    stats = result['statistics']
    print(f"  Total extensions: {stats['total_extensions']}")
    print(f"  Enabled extensions: {stats['enabled_extensions']}")
    print(f"  Extension calls: {stats['extension_calls']}")
    
    # Test with disabled extension
    print("\n\n=== Disabling sentiment_analyzer ===")
    agent.disable_extension("sentiment_analyzer")
    
    result2 = pattern.run("SmartAssistant", "Another test message")
    print(f"\nExtension Results:")
    for ext_name, ext_result in result2['results']['extension_results'].items():
        print(f"  - {ext_name}: {ext_result}")

    

Agentic Architecture Pattern: Extension

Pattern 96: 96 Extension – Line-by-Line Explanation

Overview

Pattern 96: Extension Pattern

Extend agent behavior by adding new capabilities without modifying existing code. Extensions inherit from base agent and add specific features.


Code Structure Analysis

File Information

  • Pattern Number: 96
  • Pattern Name: 96 Extension
  • File Path: patterns\tool_resource\96_extension.py
  • Total Lines: 211

Detailed Code Explanation

Module Docstring

"""
Pattern 96: Extension Pattern

Extend agent behavior by adding new capabilities without modifying existing code.
Extensions inherit from base agent and add specific features.
"""

Explanation: This module docstring describes the purpose and key features of the 96 Extension pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from abc import ABC, abstractmethod
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Callable: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime: Date and time utilities – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Classes

BaseAgent (Line 16)

Description: Base agent that can be extended.

Methods:

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

Purpose: No method docstring.

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

core_capability (Line 24)
def core_capability(self, input_data):
    """Core capability of the agent."""

Purpose: Core capability of the agent.

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

Extension (Line 30)

Description: Extension that adds capabilities.

ExtensibleAgent (Line 40)

Description: Agent that supports extensions.

Methods:

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

Purpose: No method docstring.

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

core_capability (Line 47)
def core_capability(self, input_data):
    """Core capability - basic text processing."""

Purpose: Core capability – basic text processing.

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

add_extension (Line 57)
def add_extension(self, name, capability, description, priority):
    """Add an extension."""

Purpose: Add an extension.

Parameters: – self: Instance reference – name: Identifier or label – capability: Parameter for capability – description: Parameter for description – priority: Parameter for priority

remove_extension (Line 69)
def remove_extension(self, name):
    """Remove an extension."""

Purpose: Remove an extension.

Parameters: – self: Instance reference – name: Identifier or label

enable_extension (Line 75)
def enable_extension(self, name):
    """Enable an extension."""

Purpose: Enable an extension.

Parameters: – self: Instance reference – name: Identifier or label

disable_extension (Line 80)
def disable_extension(self, name):
    """Disable an extension."""

Purpose: Disable an extension.

Parameters: – self: Instance reference – name: Identifier or label

execute_with_extensions (Line 85)
def execute_with_extensions(self, input_data):
    """Execute with all enabled extensions."""

Purpose: Execute with all enabled extensions.

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

get_statistics (Line 111)
def get_statistics(self):
    """Get extension statistics."""

Purpose: Get extension statistics.

Parameters: – self: Instance reference

ExtensionPattern (Line 123)

Description: Extension Pattern – Manage extensible agents.

Methods:

__init__ (Line 128)
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 132)
def create_agent(self, name):
    """Create a new extensible agent."""

Purpose: Create a new extensible agent.

Parameters: – self: Instance reference – name: Identifier or label

run (Line 138)
def run(self, agent_name, input_data):
    """Run agent with extensions."""

Purpose: Run agent with extensions.

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


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 = BaseAgent(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 *