Tag: artificial-intelligence

  • Agentic Architecture Pattern: Long Term Memory

    
        """
    Pattern 64: Long-Term Memory Pattern
    
    Agents maintain persistent memory across sessions with consolidation and retrieval.
    Combines episodic, semantic, and procedural memory for long-term storage.
    """
    
    from typing import Dict, Any, List, Optional, Set
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from enum import Enum
    import json
    from pathlib import Path
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class MemoryType(Enum):
        """Type of long-term memory."""
        EPISODIC = "episodic"  # Personal experiences
        SEMANTIC = "semantic"  # Facts and concepts
        PROCEDURAL = "procedural"  # Skills and procedures
        AUTOBIOGRAPHICAL = "autobiographical"  # Personal history
    
    
    class MemoryStrength(Enum):
        """Memory consolidation strength."""
        WEAK = 0.3
        MODERATE = 0.6
        STRONG = 0.9
        PERMANENT = 1.0
    
    
    @dataclass
    class LongTermMemoryEntry:
        """Entry in long-term memory."""
        memory_id: str
        memory_type: MemoryType
        content: Any
        context: Dict[str, Any]
        strength: float = 0.5  # Consolidation strength
        created_at: datetime = field(default_factory=datetime.now)
        last_accessed: datetime = field(default_factory=datetime.now)
        access_count: int = 0
        tags: Set[str] = field(default_factory=set)
        associations: Set[str] = field(default_factory=set)  # IDs of related memories
    
    
    @dataclass
    class MemoryConsolidation:
        """Record of memory consolidation process."""
        consolidation_id: str
        memories_consolidated: List[str]
        original_strength: float
        new_strength: float
        timestamp: datetime = field(default_factory=datetime.now)
        reason: str = ""
    
    
    class LongTermMemoryStore:
        """Persistent long-term memory storage."""
        
        def __init__(self, storage_path: Optional[Path] = None):
            self.storage_path = storage_path
            self.memories: Dict[str, LongTermMemoryEntry] = {}
            self.consolidation_history: List[MemoryConsolidation] = []
            self.indexes: Dict[str, Dict[Any, Set[str]]] = {
                "type": {},
                "tags": {},
                "date": {}
            }
        
        def store(self, memory: LongTermMemoryEntry):
            """Store memory in long-term storage."""
            self.memories[memory.memory_id] = memory
            
            # Update indexes
            mem_type = memory.memory_type.value
            if mem_type not in self.indexes["type"]:
                self.indexes["type"][mem_type] = set()
            self.indexes["type"][mem_type].add(memory.memory_id)
            
            # Tag index
            for tag in memory.tags:
                if tag not in self.indexes["tags"]:
                    self.indexes["tags"][tag] = set()
                self.indexes["tags"][tag].add(memory.memory_id)
            
            # Date index (by day)
            date_key = memory.created_at.date()
            if date_key not in self.indexes["date"]:
                self.indexes["date"][date_key] = set()
            self.indexes["date"][date_key].add(memory.memory_id)
        
        def retrieve(self, memory_id: str) -> Optional[LongTermMemoryEntry]:
            """Retrieve memory and update access metadata."""
            if memory_id not in self.memories:
                return None
            
            memory = self.memories[memory_id]
            memory.last_accessed = datetime.now()
            memory.access_count += 1
            
            # Strengthen memory through retrieval
            self._strengthen_memory(memory_id, 0.05)
            
            return memory
        
        def search_by_type(self, memory_type: MemoryType) -> List[LongTermMemoryEntry]:
            """Search memories by type."""
            memory_ids = self.indexes["type"].get(memory_type.value, set())
            return [self.memories[mid] for mid in memory_ids if mid in self.memories]
        
        def search_by_tags(self, tags: Set[str]) -> List[LongTermMemoryEntry]:
            """Search memories by tags."""
            result_ids = None
            for tag in tags:
                tag_ids = self.indexes["tags"].get(tag, set())
                if result_ids is None:
                    result_ids = tag_ids
                else:
                    result_ids = result_ids & tag_ids
            
            if result_ids is None:
                return []
            
            return [self.memories[mid] for mid in result_ids if mid in self.memories]
        
        def search_by_date_range(self, start: datetime, end: datetime) -> List[LongTermMemoryEntry]:
            """Search memories by date range."""
            results = []
            current_date = start.date()
            end_date = end.date()
            
            while current_date  7:  # Only decay after a week
                    decay = decay_rate * (days_since_access / 7)
                    memory.strength = max(0.1, memory.strength - decay)
    
    
    class LongTermMemoryAgent:
        """Agent with long-term persistent memory."""
        
        def __init__(self, name: str, storage_path: Optional[Path] = None, model_name: str = "gpt-4"):
            self.name = name
            self.llm = ChatOpenAI(model=model_name, temperature=0.3)
            self.memory_store = LongTermMemoryStore(storage_path)
        
        def remember(self, memory_id: str, content: Any, memory_type: MemoryType,
                     context: Optional[Dict[str, Any]] = None, tags: Optional[Set[str]] = None):
            """Store in long-term memory."""
            memory = LongTermMemoryEntry(
                memory_id=memory_id,
                memory_type=memory_type,
                content=content,
                context=context or {},
                tags=tags or set(),
                strength=0.5
            )
            
            self.memory_store.store(memory)
            print(f"   💾 {self.name} stored long-term memory: {memory_id} ({memory_type.value})")
        
        def recall(self, memory_id: str) -> Optional[Any]:
            """Recall from long-term memory."""
            memory = self.memory_store.retrieve(memory_id)
            if memory:
                print(f"   ✓ {self.name} recalled: {memory_id} (strength: {memory.strength:.2f}, accessed {memory.access_count}x)")
                return memory.content
            else:
                print(f"   ✗ {self.name} couldn't recall: {memory_id}")
                return None
        
        def reflect_on_memories(self, memory_type: MemoryType, limit: int = 5) -> str:
            """Reflect on memories using LLM."""
            memories = self.memory_store.search_by_type(memory_type)[:limit]
            
            if not memories:
                return f"No {memory_type.value} memories to reflect on."
            
            memories_text = "\n".join([
                f"- [{m.memory_id}] {m.content} (strength: {m.strength:.2f}, accessed: {m.access_count}x)"
                for m in sorted(memories, key=lambda x: x.strength, reverse=True)
            ])
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name} reflecting on your long-term memories."),
                ("human", f"Your {memory_type.value} memories:\n{memories_text}\n\n"
                         f"Reflect on these memories. What patterns or insights do you notice? "
                         f"(2-3 sentences)")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            return str(response.content)[:300]
    
    
    class LongTermMemoryPattern:
        """
        Long-Term Memory Pattern - Persistent cross-session memory.
        
        Agents maintain memories across sessions with consolidation,
        strengthening through retrieval, and decay of unused memories.
        """
        
        def __init__(self):
            self.agents: Dict[str, LongTermMemoryAgent] = {}
        
        def register_agent(self, agent: LongTermMemoryAgent):
            """Register agent with long-term memory."""
            self.agents[agent.name] = agent
        
        def run(self, scenario: str) -> Dict[str, Any]:
            """Execute long-term memory pattern."""
            total_memories = sum(len(agent.memory_store.memories) for agent in self.agents.values())
            total_consolidations = sum(len(agent.memory_store.consolidation_history) for agent in self.agents.values())
            
            avg_strength = 0
            if total_memories > 0:
                all_strengths = [m.strength for agent in self.agents.values() 
                               for m in agent.memory_store.memories.values()]
                avg_strength = sum(all_strengths) / len(all_strengths)
            
            return {
                "pattern": "LongTermMemory",
                "scenario": scenario,
                "total_agents": len(self.agents),
                "total_memories": total_memories,
                "average_strength": avg_strength,
                "total_consolidations": total_consolidations
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Long-Term Memory Pattern Demo ===\n")
        
        # Create pattern
        pattern = LongTermMemoryPattern()
        
        # Create agent with long-term memory
        print("1. Creating agent with long-term memory...")
        agent = LongTermMemoryAgent("Sage")
        pattern.register_agent(agent)
        
        # Store various types of memories
        print("\n2. Storing long-term memories...")
        
        # Episodic memories
        agent.remember("first_project", "Led AI chatbot project in 2023", MemoryType.EPISODIC,
                       context={"year": 2023, "role": "lead"}, tags={"work", "project", "ai"})
        
        agent.remember("conference_talk", "Presented at AI Conference 2024", MemoryType.EPISODIC,
                       context={"year": 2024, "type": "presentation"}, tags={"conference", "public_speaking"})
        
        # Semantic memories
        agent.remember("python_knowledge", "Python is a high-level programming language", MemoryType.SEMANTIC,
                       tags={"programming", "knowledge"})
        
        agent.remember("ml_concept", "Machine learning enables computers to learn from data", MemoryType.SEMANTIC,
                       tags={"ai", "knowledge", "ml"})
        
        # Procedural memories
        agent.remember("debug_process", "Check logs, isolate issue, fix, test", MemoryType.PROCEDURAL,
                       tags={"programming", "skill"})
        
        # Recall memories
        print("\n3. Recalling memories...")
        agent.recall("first_project")
        agent.recall("python_knowledge")
        agent.recall("debug_process")
        agent.recall("first_project")  # Second recall strengthens
        
        # Search by type
        print("\n4. Searching memories by type...")
        episodic = agent.memory_store.search_by_type(MemoryType.EPISODIC)
        print(f"   Episodic memories: {len(episodic)}")
        for mem in episodic:
            print(f"   - {mem.memory_id}: {mem.content} (strength: {mem.strength:.2f})")
        
        # Search by tags
        print("\n5. Searching memories by tags...")
        ai_memories = agent.memory_store.search_by_tags({"ai"})
        print(f"   Memories tagged 'ai': {len(ai_memories)}")
        for mem in ai_memories:
            print(f"   - {mem.memory_id}: {mem.content}")
        
        # Consolidate important memories
        print("\n6. Consolidating memories...")
        important_ids = ["first_project", "ml_concept"]
        agent.memory_store.consolidate_memories(important_ids, reason="frequently accessed")
        print(f"   Consolidated {len(important_ids)} memories")
        
        # Check strength after consolidation
        for mid in important_ids:
            mem = agent.memory_store.memories.get(mid)
            if mem:
                print(f"   - {mid}: strength now {mem.strength:.2f}")
        
        # Reflection
        print("\n7. Reflecting on memories...")
        reflection = agent.reflect_on_memories(MemoryType.EPISODIC)
        print(f"   Reflection on episodic memories:")
        print(f"   {reflection}")
        
        # Statistics
        print("\n8. Memory Statistics:")
        summary = pattern.run("Personal knowledge and experience")
        print(f"   Total Memories: {summary['total_memories']}")
        print(f"   Average Strength: {summary['average_strength']:.2f}")
        print(f"   Consolidations: {summary['total_consolidations']}")
        
        # Memory distribution
        print("\n9. Memory Distribution:")
        for mem_type in MemoryType:
            count = len(agent.memory_store.search_by_type(mem_type))
            print(f"   {mem_type.value}: {count}")
        
        print("\n✓ Long-term memory pattern enabled persistent knowledge storage!")
    
        

    Agentic Architecture Pattern: Long Term Memory

    Pattern 64: 64 Long Term Memory – Line-by-Line Explanation

    Overview

    Pattern 64: Long-Term Memory Pattern

    Agents maintain persistent memory across sessions with consolidation and retrieval. Combines episodic, semantic, and procedural memory for long-term storage.


    Code Structure Analysis

    File Information

    • Pattern Number: 64
    • Pattern Name: 64 Long Term Memory
    • File Path: patterns\memory_knowledge\64_long_term_memory.py
    • Total Lines: 366

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 64: Long-Term Memory Pattern
    
    Agents maintain persistent memory across sessions with consolidation and retrieval.
    Combines episodic, semantic, and procedural memory for long-term storage.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 64 Long Term Memory pattern.


    Imports Section

    from typing import Dict, Any, List, Optional, Set
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from enum import Enum
    import json
    from pathlib import Path
    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, Set: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime, timedelta: Date and time utilities – from enum import Enum: Enumeration support for fixed sets of values – import json: JSON parsing and serialization – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    MemoryType (Line 18)

    class MemoryType(Enum):
        EPISODIC
        SEMANTIC
        PROCEDURAL
        AUTOBIOGRAPHICAL
    

    Purpose: Type of long-term memory.

    Values: – EPISODIC: Represents episodic – SEMANTIC: Represents semantic – PROCEDURAL: Represents procedural – AUTOBIOGRAPHICAL: Represents autobiographical

    MemoryStrength (Line 26)

    class MemoryStrength(Enum):
        WEAK
        MODERATE
        STRONG
        PERMANENT
    

    Purpose: Memory consolidation strength.

    Values: – WEAK: Represents weak – MODERATE: Represents moderate – STRONG: Represents strong – PERMANENT: Represents permanent


    Classes

    MemoryType (Line 18)

    Description: Type of long-term memory.

    MemoryStrength (Line 26)

    Description: Memory consolidation strength.

    LongTermMemoryEntry (Line 35)

    Description: Entry in long-term memory.

    MemoryConsolidation (Line 50)

    Description: Record of memory consolidation process.

    LongTermMemoryStore (Line 60)

    Description: Persistent long-term memory storage.

    Methods:

    __init__ (Line 63)
    def __init__(self, storage_path):
        """No method docstring."""
    

    Purpose: No method docstring.

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

    store (Line 73)
    def store(self, memory):
        """Store memory in long-term storage."""
    

    Purpose: Store memory in long-term storage.

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

    retrieve (Line 95)
    def retrieve(self, memory_id):
        """Retrieve memory and update access metadata."""
    

    Purpose: Retrieve memory and update access metadata.

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

    search_by_type (Line 109)
    def search_by_type(self, memory_type):
        """Search memories by type."""
    

    Purpose: Search memories by type.

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

    search_by_tags (Line 114)
    def search_by_tags(self, tags):
        """Search memories by tags."""
    

    Purpose: Search memories by tags.

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

    search_by_date_range (Line 129)
    def search_by_date_range(self, start, end):
        """Search memories by date range."""
    

    Purpose: Search memories by date range.

    Parameters: – self: Instance reference – start: Parameter for start – end: Parameter for end

    consolidate_memories (Line 144)
    def consolidate_memories(self, memory_ids, reason):
        """Consolidate memories to strengthen retention."""
    

    Purpose: Consolidate memories to strengthen retention.

    Parameters: – self: Instance reference – memory_ids: Parameter for memory_ids – reason: Parameter for reason

    _strengthen_memory (Line 168)
    def _strengthen_memory(self, memory_id, amount):
        """Strengthen memory consolidation."""
    

    Purpose: Strengthen memory consolidation.

    Parameters: – self: Instance reference – memory_id: Parameter for memory_id – amount: Parameter for amount

    decay_memories (Line 174)
    def decay_memories(self, decay_rate):
        """Apply memory decay to unused memories."""
    

    Purpose: Apply memory decay to unused memories.

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

    LongTermMemoryAgent (Line 186)

    Description: Agent with long-term persistent memory.

    Methods:

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

    Purpose: No method docstring.

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

    remember (Line 194)
    def remember(self, memory_id, content, memory_type, context, tags):
        """Store in long-term memory."""
    

    Purpose: Store in long-term memory.

    Parameters: – self: Instance reference – memory_id: Parameter for memory_id – content: Parameter for content – memory_type: Parameter for memory_type – context: Parameter for context – tags: Parameter for tags

    recall (Line 209)
    def recall(self, memory_id):
        """Recall from long-term memory."""
    

    Purpose: Recall from long-term memory.

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

    reflect_on_memories (Line 219)
    def reflect_on_memories(self, memory_type, limit):
        """Reflect on memories using LLM."""
    

    Purpose: Reflect on memories using LLM.

    Parameters: – self: Instance reference – memory_type: Parameter for memory_type – limit: Parameter for limit

    LongTermMemoryPattern (Line 244)

    Description: Long-Term Memory Pattern – Persistent cross-session memory.

    Agents maintain memories across sessions with consolidation, strengthening through retrieval, and decay of unused memories.

    Methods:

    __init__ (Line 252)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 255)
    def register_agent(self, agent):
        """Register agent with long-term memory."""
    

    Purpose: Register agent with long-term memory.

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

    run (Line 259)
    def run(self, scenario):
        """Execute long-term memory pattern."""
    

    Purpose: Execute long-term memory pattern.

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


    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 = MemoryType(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

  • Agentic Architecture Pattern: Working Memory

    
        """
    Pattern 63: Working Memory Pattern
    
    Agents maintain temporary, limited-capacity memory for current task execution.
    Similar to human working memory: holds active information needed right now.
    """
    
    from typing import Dict, Any, List, Optional, Deque
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from collections import deque
    from enum import Enum
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class ItemPriority(Enum):
        """Priority level for working memory items."""
        CRITICAL = 3
        HIGH = 2
        NORMAL = 1
        LOW = 0
    
    
    @dataclass
    class WorkingMemoryItem:
        """Item in working memory."""
        item_id: str
        content: Any
        priority: ItemPriority
        created_at: datetime = field(default_factory=datetime.now)
        last_accessed: datetime = field(default_factory=datetime.now)
        access_count: int = 0
        ttl: Optional[timedelta] = None  # Time to live
        context: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class MemoryOperation:
        """Record of memory operation."""
        operation_type: str  # "store", "retrieve", "update", "evict"
        item_id: str
        timestamp: datetime = field(default_factory=datetime.now)
        success: bool = True
    
    
    class WorkingMemory:
        """
        Limited-capacity working memory with eviction policies.
        """
        
        def __init__(self, capacity: int = 7):  # Miller's law: 7±2 items
            self.capacity = capacity
            self.items: Dict[str, WorkingMemoryItem] = {}
            self.access_queue: Deque[str] = deque()  # For LRU eviction
            self.operations: List[MemoryOperation] = []
        
        def store(self, item: WorkingMemoryItem) -> bool:
            """Store item in working memory."""
            # Check if we need to evict
            if len(self.items) >= self.capacity and item.item_id not in self.items:
                self._evict()
            
            # Store item
            self.items[item.item_id] = item
            
            # Update access queue
            if item.item_id in self.access_queue:
                self.access_queue.remove(item.item_id)
            self.access_queue.append(item.item_id)
            
            # Record operation
            self.operations.append(MemoryOperation("store", item.item_id))
            
            return True
        
        def retrieve(self, item_id: str) -> Optional[WorkingMemoryItem]:
            """Retrieve item from working memory."""
            if item_id not in self.items:
                self.operations.append(MemoryOperation("retrieve", item_id, success=False))
                return None
            
            item = self.items[item_id]
            
            # Check TTL
            if item.ttl and datetime.now() - item.created_at > item.ttl:
                self._remove(item_id)
                return None
            
            # Update access metadata
            item.last_accessed = datetime.now()
            item.access_count += 1
            
            # Update LRU queue
            if item_id in self.access_queue:
                self.access_queue.remove(item_id)
            self.access_queue.append(item_id)
            
            # Record operation
            self.operations.append(MemoryOperation("retrieve", item_id))
            
            return item
        
        def update(self, item_id: str, new_content: Any) -> bool:
            """Update item in working memory."""
            if item_id not in self.items:
                return False
            
            self.items[item_id].content = new_content
            self.items[item_id].last_accessed = datetime.now()
            
            self.operations.append(MemoryOperation("update", item_id))
            
            return True
        
        def _evict(self):
            """Evict item based on priority and recency."""
            if not self.items:
                return
            
            # Find lowest priority, least recently used item
            candidates = []
            for item_id in list(self.access_queue):
                if item_id in self.items:
                    item = self.items[item_id]
                    # Score: lower is better for eviction
                    score = item.priority.value * 100 + item.access_count
                    candidates.append((score, item_id))
            
            if candidates:
                candidates.sort()
                item_id_to_evict = candidates[0][1]
                self._remove(item_id_to_evict)
        
        def _remove(self, item_id: str):
            """Remove item from working memory."""
            if item_id in self.items:
                del self.items[item_id]
            if item_id in self.access_queue:
                self.access_queue.remove(item_id)
            
            self.operations.append(MemoryOperation("evict", item_id))
        
        def get_all(self) -> List[WorkingMemoryItem]:
            """Get all items in working memory."""
            return list(self.items.values())
        
        def clear(self):
            """Clear all working memory."""
            self.items.clear()
            self.access_queue.clear()
        
        def is_full(self) -> bool:
            """Check if working memory is at capacity."""
            return len(self.items) >= self.capacity
    
    
    class WorkingMemoryAgent:
        """Agent with working memory for current task execution."""
        
        def __init__(self, name: str, memory_capacity: int = 7, model_name: str = "gpt-4"):
            self.name = name
            self.llm = ChatOpenAI(model=model_name, temperature=0.7)
            self.working_memory = WorkingMemory(capacity=memory_capacity)
            self.task_context: Dict[str, Any] = {}
        
        def start_task(self, task_name: str, task_data: Dict[str, Any]):
            """Start new task, initializing working memory."""
            self.task_context = {"task": task_name, "data": task_data}
            
            # Store task info in working memory
            task_item = WorkingMemoryItem(
                "current_task",
                task_name,
                ItemPriority.CRITICAL,
                context=task_data
            )
            self.working_memory.store(task_item)
            
            print(f"   🎯 {self.name} started task: {task_name}")
        
        def remember(self, key: str, value: Any, priority: ItemPriority = ItemPriority.NORMAL,
                     ttl: Optional[timedelta] = None):
            """Store information in working memory."""
            item = WorkingMemoryItem(key, value, priority, ttl=ttl)
            self.working_memory.store(item)
            print(f"   💭 {self.name} remembered: {key} (priority: {priority.name})")
        
        def recall(self, key: str) -> Optional[Any]:
            """Recall information from working memory."""
            item = self.working_memory.retrieve(key)
            if item:
                print(f"   ✓ {self.name} recalled: {key}")
                return item.content
            else:
                print(f"   ✗ {self.name} couldn't recall: {key}")
                return None
        
        def process_with_context(self, query: str) -> str:
            """Process query using working memory context."""
            # Get all working memory items
            items = self.working_memory.get_all()
            
            context_text = "\n".join([
                f"- {item.item_id}: {item.content} (priority: {item.priority.name}, accessed: {item.access_count})"
                for item in sorted(items, key=lambda x: x.priority.value, reverse=True)
            ])
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name} working on a task. "
                          f"Use your working memory to inform your response."),
                ("human", f"Current working memory:\n{context_text}\n\n"
                         f"Current task: {self.task_context.get('task', 'None')}\n\n"
                         f"Query: {query}\n\n"
                         f"Respond based on your working memory context. Be brief (2-3 sentences).")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            return str(response.content)[:300]
        
        def get_memory_status(self) -> Dict[str, Any]:
            """Get current working memory status."""
            items = self.working_memory.get_all()
            
            return {
                "capacity": self.working_memory.capacity,
                "used": len(items),
                "available": self.working_memory.capacity - len(items),
                "is_full": self.working_memory.is_full(),
                "items": [
                    {
                        "id": item.item_id,
                        "priority": item.priority.name,
                        "access_count": item.access_count
                    }
                    for item in sorted(items, key=lambda x: x.priority.value, reverse=True)
                ]
            }
    
    
    class WorkingMemoryPattern:
        """
        Working Memory Pattern - Temporary task-focused memory.
        
        Agents maintain limited-capacity memory for current task execution,
        with automatic eviction based on priority and recency.
        """
        
        def __init__(self):
            self.agents: Dict[str, WorkingMemoryAgent] = {}
        
        def register_agent(self, agent: WorkingMemoryAgent):
            """Register agent with working memory."""
            self.agents[agent.name] = agent
        
        def run(self, scenario: str) -> Dict[str, Any]:
            """Execute working memory pattern."""
            total_items = sum(len(agent.working_memory.items) for agent in self.agents.values())
            total_operations = sum(len(agent.working_memory.operations) for agent in self.agents.values())
            
            return {
                "pattern": "WorkingMemory",
                "scenario": scenario,
                "total_agents": len(self.agents),
                "total_items_in_memory": total_items,
                "total_operations": total_operations
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Working Memory Pattern Demo ===\n")
        
        # Create pattern
        pattern = WorkingMemoryPattern()
        
        # Create agent with limited working memory
        print("1. Creating agent with working memory (capacity: 5)...")
        agent = WorkingMemoryAgent("Worker", memory_capacity=5)
        pattern.register_agent(agent)
        
        # Start a task
        print("\n2. Starting task...")
        agent.start_task("Data Analysis", {"dataset": "sales_2024", "goal": "find trends"})
        
        # Store items in working memory
        print("\n3. Storing information in working memory...")
        agent.remember("total_records", 10000, ItemPriority.HIGH)
        agent.remember("date_range", "2024-01-01 to 2024-12-31", ItemPriority.NORMAL)
        agent.remember("primary_metric", "revenue", ItemPriority.HIGH)
        agent.remember("secondary_metric", "customer_count", ItemPriority.NORMAL)
        
        # Check memory status
        print("\n4. Working memory status:")
        status = agent.get_memory_status()
        print(f"   Capacity: {status['used']}/{status['capacity']}")
        print(f"   Items:")
        for item in status['items']:
            print(f"   - {item['id']} (priority: {item['priority']}, accessed: {item['access_count']}x)")
        
        # Store more items (will trigger eviction)
        print("\n5. Adding more items (triggers eviction)...")
        agent.remember("temp_calculation", 42, ItemPriority.LOW)
        agent.remember("current_filter", "region=North", ItemPriority.NORMAL)
        
        status = agent.get_memory_status()
        print(f"   Memory now: {status['used']}/{status['capacity']}")
        
        # Recall items
        print("\n6. Recalling items from working memory...")
        agent.recall("primary_metric")
        agent.recall("total_records")
        agent.recall("temp_calculation")  # May have been evicted
        
        # Process with context
        print("\n7. Processing queries with working memory context...")
        queries = [
            "What is the main goal of my current task?",
            "What metrics am I tracking?",
            "Summarize what I'm working on"
        ]
        
        for query in queries:
            print(f"\n   Q: {query}")
            response = agent.process_with_context(query)
            print(f"   A: {response}")
        
        # Memory operations statistics
        print("\n8. Memory operation statistics:")
        ops = agent.working_memory.operations
        op_counts = {}
        for op in ops:
            op_counts[op.operation_type] = op_counts.get(op.operation_type, 0) + 1
        
        print(f"   Total operations: {len(ops)}")
        for op_type, count in op_counts.items():
            print(f"   - {op_type}: {count}")
        
        # Summary
        print("\n9. Pattern Summary:")
        summary = pattern.run("Task-focused data analysis")
        print(f"   Total items in memory: {summary['total_items_in_memory']}")
        print(f"   Total operations: {summary['total_operations']}")
        
        print("\n✓ Working memory pattern enabled limited-capacity task execution!")
    
        

    Agentic Architecture Pattern: Working Memory

    Pattern 63: 63 Working Memory – Line-by-Line Explanation

    Overview

    Pattern 63: Working Memory Pattern

    Agents maintain temporary, limited-capacity memory for current task execution. Similar to human working memory: holds active information needed right now.


    Code Structure Analysis

    File Information

    • Pattern Number: 63
    • Pattern Name: 63 Working Memory
    • File Path: patterns\memory_knowledge\63_working_memory.py
    • Total Lines: 348

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 63: Working Memory Pattern
    
    Agents maintain temporary, limited-capacity memory for current task execution.
    Similar to human working memory: holds active information needed right now.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 63 Working Memory pattern.


    Imports Section

    from typing import Dict, Any, List, Optional, Deque
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from collections import deque
    from enum import Enum
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI  # First 20 imports shown
    

    Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Deque: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime, timedelta: 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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    ItemPriority (Line 17)

    class ItemPriority(Enum):
        CRITICAL
        HIGH
        NORMAL
        LOW
    

    Purpose: Priority level for working memory items.

    Values: – CRITICAL: Represents critical – HIGH: Represents high – NORMAL: Represents normal – LOW: Represents low


    Classes

    ItemPriority (Line 17)

    Description: Priority level for working memory items.

    WorkingMemoryItem (Line 26)

    Description: Item in working memory.

    MemoryOperation (Line 39)

    Description: Record of memory operation.

    WorkingMemory (Line 47)

    Description: Limited-capacity working memory with eviction policies.

    Methods:

    __init__ (Line 52)
    def __init__(self, capacity):
        """No method docstring."""
    

    Purpose: No method docstring.

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

    store (Line 58)
    def store(self, item):
        """Store item in working memory."""
    

    Purpose: Store item in working memory.

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

    retrieve (Line 77)
    def retrieve(self, item_id):
        """Retrieve item from working memory."""
    

    Purpose: Retrieve item from working memory.

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

    update (Line 104)
    def update(self, item_id, new_content):
        """Update item in working memory."""
    

    Purpose: Update item in working memory.

    Parameters: – self: Instance reference – item_id: Parameter for item_id – new_content: Parameter for new_content

    _evict (Line 116)
    def _evict(self):
        """Evict item based on priority and recency."""
    

    Purpose: Evict item based on priority and recency.

    Parameters: – self: Instance reference

    _remove (Line 135)
    def _remove(self, item_id):
        """Remove item from working memory."""
    

    Purpose: Remove item from working memory.

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

    get_all (Line 144)
    def get_all(self):
        """Get all items in working memory."""
    

    Purpose: Get all items in working memory.

    Parameters: – self: Instance reference

    clear (Line 148)
    def clear(self):
        """Clear all working memory."""
    

    Purpose: Clear all working memory.

    Parameters: – self: Instance reference

    is_full (Line 153)
    def is_full(self):
        """Check if working memory is at capacity."""
    

    Purpose: Check if working memory is at capacity.

    Parameters: – self: Instance reference

    WorkingMemoryAgent (Line 158)

    Description: Agent with working memory for current task execution.

    Methods:

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

    Purpose: No method docstring.

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

    start_task (Line 167)
    def start_task(self, task_name, task_data):
        """Start new task, initializing working memory."""
    

    Purpose: Start new task, initializing working memory.

    Parameters: – self: Instance reference – task_name: Parameter for task_name – task_data: Parameter for task_data

    remember (Line 182)
    def remember(self, key, value, priority, ttl):
        """Store information in working memory."""
    

    Purpose: Store information in working memory.

    Parameters: – self: Instance reference – key: Parameter for key – value: Parameter for value – priority: Parameter for priority – ttl: Parameter for ttl

    recall (Line 189)
    def recall(self, key):
        """Recall information from working memory."""
    

    Purpose: Recall information from working memory.

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

    process_with_context (Line 199)
    def process_with_context(self, query):
        """Process query using working memory context."""
    

    Purpose: Process query using working memory context.

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

    get_memory_status (Line 223)
    def get_memory_status(self):
        """Get current working memory status."""
    

    Purpose: Get current working memory status.

    Parameters: – self: Instance reference

    WorkingMemoryPattern (Line 243)

    Description: Working Memory Pattern – Temporary task-focused memory.

    Agents maintain limited-capacity memory for current task execution, with automatic eviction based on priority and recency.

    Methods:

    __init__ (Line 251)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 254)
    def register_agent(self, agent):
        """Register agent with working memory."""
    

    Purpose: Register agent with working memory.

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

    run (Line 258)
    def run(self, scenario):
        """Execute working memory pattern."""
    

    Purpose: Execute working memory pattern.

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


    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 = ItemPriority(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

  • Agentic Architecture Pattern: Semantic Memory

    
        """
    Pattern 62: Semantic Memory Pattern
    
    Agents store general knowledge, concepts, and meanings independent of context.
    Unlike episodic memory (specific events), semantic memory contains abstract facts.
    """
    
    from typing import Dict, Any, List, Optional, Set
    from dataclasses import dataclass, field
    from datetime import datetime
    from enum import Enum
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class ConceptType(Enum):
        """Type of semantic concept."""
        CATEGORY = "category"
        PROPERTY = "property"
        RELATIONSHIP = "relationship"
        PROCEDURE = "procedure"
        DEFINITION = "definition"
    
    
    @dataclass
    class Concept:
        """Semantic concept with properties."""
        concept_id: str
        name: str
        concept_type: ConceptType
        definition: str
        properties: Dict[str, Any] = field(default_factory=dict)
        related_concepts: Set[str] = field(default_factory=set)
        examples: List[str] = field(default_factory=list)
        confidence: float = 1.0
        learned_date: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class Association:
        """Association between concepts."""
        from_concept: str
        to_concept: str
        relationship: str
        strength: float = 1.0  # 0 to 1
    
    
    @dataclass
    class ConceptQuery:
        """Query about semantic knowledge."""
        query: str
        matched_concepts: List[Concept]
        associations: List[Association]
        answer: str
    
    
    class SemanticMemory:
        """Semantic memory storage and retrieval."""
        
        def __init__(self):
            self.concepts: Dict[str, Concept] = {}
            self.associations: List[Association] = []
            self.categories: Dict[str, Set[str]] = {}
        
        def add_concept(self, concept: Concept):
            """Add concept to semantic memory."""
            self.concepts[concept.concept_id] = concept
            
            # Add to category index
            category_name = concept.concept_type.value
            if category_name not in self.categories:
                self.categories[category_name] = set()
            self.categories[category_name].add(concept.concept_id)
        
        def add_association(self, from_concept: str, to_concept: str, 
                           relationship: str, strength: float = 1.0):
            """Add association between concepts."""
            assoc = Association(from_concept, to_concept, relationship, strength)
            self.associations.append(assoc)
            
            # Update related concepts
            if from_concept in self.concepts:
                self.concepts[from_concept].related_concepts.add(to_concept)
            if to_concept in self.concepts:
                self.concepts[to_concept].related_concepts.add(from_concept)
        
        def find_concept(self, name: str) -> Optional[Concept]:
            """Find concept by name."""
            for concept in self.concepts.values():
                if concept.name.lower() == name.lower():
                    return concept
            return None
        
        def get_related_concepts(self, concept_id: str, max_depth: int = 2) -> List[Concept]:
            """Get concepts related to given concept."""
            if concept_id not in self.concepts:
                return []
            
            related_ids = set()
            to_explore = [(concept_id, 0)]
            explored = set()
            
            while to_explore:
                current_id, depth = to_explore.pop(0)
                if current_id in explored or depth > max_depth:
                    continue
                
                explored.add(current_id)
                if current_id != concept_id:
                    related_ids.add(current_id)
                
                # Add related concepts to explore
                if current_id in self.concepts:
                    for related_id in self.concepts[current_id].related_concepts:
                        if related_id not in explored:
                            to_explore.append((related_id, depth + 1))
            
            return [self.concepts[cid] for cid in related_ids if cid in self.concepts]
        
        def get_by_category(self, category: ConceptType) -> List[Concept]:
            """Get all concepts in a category."""
            concept_ids = self.categories.get(category.value, set())
            return [self.concepts[cid] for cid in concept_ids if cid in self.concepts]
    
    
    class SemanticMemoryAgent:
        """Agent with semantic memory capabilities."""
        
        def __init__(self, name: str, model_name: str = "gpt-4"):
            self.name = name
            self.llm = ChatOpenAI(model=model_name, temperature=0.3)
            self.memory = SemanticMemory()
            self.queries: List[ConceptQuery] = []
        
        def learn_concept(self, concept: Concept):
            """Learn new concept."""
            self.memory.add_concept(concept)
            print(f"   🧠 {self.name} learned concept: {concept.name} ({concept.concept_type.value})")
        
        def associate_concepts(self, concept1: str, concept2: str, relationship: str, strength: float = 1.0):
            """Create association between concepts."""
            self.memory.add_association(concept1, concept2, relationship, strength)
            print(f"   🔗 {self.name} associated: {concept1} --{relationship}-> {concept2}")
        
        def explain_concept(self, concept_name: str) -> str:
            """Explain concept using LLM."""
            concept = self.memory.find_concept(concept_name)
            if not concept:
                return f"I don't have semantic knowledge about '{concept_name}'."
            
            # Get related concepts
            related = self.memory.get_related_concepts(concept.concept_id)
            related_names = [self.memory.concepts[rc.concept_id].name for rc in related if rc.concept_id in self.memory.concepts]
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name}, explaining concepts from your semantic memory."),
                ("human", f"Explain this concept:\n"
                         f"Name: {concept.name}\n"
                         f"Type: {concept.concept_type.value}\n"
                         f"Definition: {concept.definition}\n"
                         f"Properties: {concept.properties}\n"
                         f"Related concepts: {', '.join(related_names[:5])}\n\n"
                         f"Provide a clear, educational explanation (2-3 sentences).")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            return str(response.content)[:300]
        
        def query_knowledge(self, query: str) -> ConceptQuery:
            """Query semantic knowledge."""
            # Get all concepts as context
            concepts_text = "\n".join([
                f"- {c.name} ({c.concept_type.value}): {c.definition}"
                for c in self.memory.concepts.values()
            ])
            
            associations_text = "\n".join([
                f"- {a.from_concept} {a.relationship} {a.to_concept}"
                for a in self.memory.associations[:20]  # Limit for context
            ])
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name} with semantic memory. "
                          f"Answer questions using your general knowledge."),
                ("human", f"My semantic knowledge:\n{concepts_text}\n\n"
                         f"Associations:\n{associations_text}\n\n"
                         f"Question: {query}\n\n"
                         f"Answer based on the semantic knowledge above.")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            result = ConceptQuery(
                query=query,
                matched_concepts=list(self.memory.concepts.values())[:5],
                associations=self.memory.associations[:5],
                answer=str(response.content)
            )
            
            self.queries.append(result)
            return result
    
    
    class SemanticMemoryPattern:
        """
        Semantic Memory Pattern - Abstract knowledge storage.
        
        Agents maintain general facts, concepts, and meanings, enabling
        them to reason about abstract ideas and relationships.
        """
        
        def __init__(self):
            self.agents: Dict[str, SemanticMemoryAgent] = {}
        
        def register_agent(self, agent: SemanticMemoryAgent):
            """Register agent with semantic memory."""
            self.agents[agent.name] = agent
        
        def share_concept(self, source_agent: str, target_agent: str, concept_name: str):
            """Share concept between agents."""
            if source_agent in self.agents and target_agent in self.agents:
                source = self.agents[source_agent]
                target = self.agents[target_agent]
                
                concept = source.memory.find_concept(concept_name)
                if concept:
                    target.learn_concept(concept)
        
        def run(self, domain: str) -> Dict[str, Any]:
            """Execute semantic memory pattern."""
            total_concepts = sum(len(agent.memory.concepts) for agent in self.agents.values())
            total_associations = sum(len(agent.memory.associations) for agent in self.agents.values())
            total_queries = sum(len(agent.queries) for agent in self.agents.values())
            
            return {
                "pattern": "SemanticMemory",
                "domain": domain,
                "total_agents": len(self.agents),
                "total_concepts": total_concepts,
                "total_associations": total_associations,
                "total_queries": total_queries
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Semantic Memory Pattern Demo ===\n")
        
        # Create pattern
        pattern = SemanticMemoryPattern()
        
        # Create agent with semantic memory
        print("1. Creating agent with semantic memory...")
        agent = SemanticMemoryAgent("Sage")
        pattern.register_agent(agent)
        
        # Learn concepts
        print("\n2. Learning concepts...")
        
        concepts = [
            Concept("ml", "Machine Learning", ConceptType.CATEGORY,
                    "Field of AI focused on learning from data",
                    properties={"paradigm": "supervised/unsupervised", "goal": "prediction"},
                    examples=["classification", "regression", "clustering"]),
            
            Concept("nn", "Neural Network", ConceptType.CATEGORY,
                    "Computing system inspired by biological neural networks",
                    properties={"layers": "input, hidden, output", "learning": "backpropagation"},
                    examples=["CNN", "RNN", "Transformer"]),
            
            Concept("supervised", "Supervised Learning", ConceptType.PROCEDURE,
                    "Learning from labeled training data",
                    properties={"requires": "labeled data", "output": "prediction model"},
                    examples=["classification", "regression"]),
            
            Concept("unsupervised", "Unsupervised Learning", ConceptType.PROCEDURE,
                    "Learning patterns from unlabeled data",
                    properties={"requires": "unlabeled data", "output": "patterns, clusters"},
                    examples=["clustering", "dimensionality reduction"]),
            
            Concept("backprop", "Backpropagation", ConceptType.PROCEDURE,
                    "Algorithm for training neural networks using gradient descent",
                    properties={"technique": "gradient descent", "direction": "backward"},
                    examples=["SGD", "Adam optimizer"]),
        ]
        
        for concept in concepts:
            agent.learn_concept(concept)
        
        # Create associations
        print("\n3. Creating concept associations...")
        agent.associate_concepts("ml", "supervised", "includes", 0.9)
        agent.associate_concepts("ml", "unsupervised", "includes", 0.9)
        agent.associate_concepts("nn", "ml", "is_part_of", 0.95)
        agent.associate_concepts("nn", "backprop", "uses", 1.0)
        agent.associate_concepts("supervised", "nn", "can_use", 0.8)
        
        # Explain concepts
        print("\n4. Explaining concepts...")
        for concept_name in ["Machine Learning", "Neural Network", "Backpropagation"]:
            explanation = agent.explain_concept(concept_name)
            print(f"\n   {concept_name}:")
            print(f"   {explanation}")
        
        # Query semantic knowledge
        print("\n5. Querying semantic knowledge...")
        queries = [
            "What is the relationship between neural networks and machine learning?",
            "Explain the difference between supervised and unsupervised learning",
            "How does backpropagation work?"
        ]
        
        for query in queries:
            print(f"\n   Q: {query}")
            result = agent.query_knowledge(query)
            print(f"   A: {result.answer[:200]}...")
        
        # Explore concept network
        print("\n6. Exploring concept relationships...")
        ml_concept = agent.memory.find_concept("Machine Learning")
        if ml_concept:
            related = agent.memory.get_related_concepts(ml_concept.concept_id)
            print(f"   Concepts related to 'Machine Learning': {len(related)}")
            for rc in related:
                print(f"   - {rc.name} ({rc.concept_type.value})")
        
        # Statistics
        print("\n7. Semantic Memory Statistics:")
        summary = pattern.run("AI and Machine Learning")
        print(f"   Total Concepts: {summary['total_concepts']}")
        print(f"   Total Associations: {summary['total_associations']}")
        print(f"   Total Queries: {summary['total_queries']}")
        
        categories = agent.memory.categories
        print(f"\n   Concepts by category:")
        for category, concept_ids in categories.items():
            print(f"   - {category}: {len(concept_ids)}")
        
        print("\n✓ Semantic memory pattern enabled abstract knowledge reasoning!")
    
        

    Agentic Architecture Pattern: Semantic Memory

    Pattern 62: 62 Semantic Memory – Line-by-Line Explanation

    Overview

    Pattern 62: Semantic Memory Pattern

    Agents store general knowledge, concepts, and meanings independent of context. Unlike episodic memory (specific events), semantic memory contains abstract facts.


    Code Structure Analysis

    File Information

    • Pattern Number: 62
    • Pattern Name: 62 Semantic Memory
    • File Path: patterns\memory_knowledge\62_semantic_memory.py
    • Total Lines: 343

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 62: Semantic Memory Pattern
    
    Agents store general knowledge, concepts, and meanings independent of context.
    Unlike episodic memory (specific events), semantic memory contains abstract facts.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 62 Semantic Memory pattern.


    Imports Section

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

    Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Set: 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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    ConceptType (Line 16)

    class ConceptType(Enum):
        CATEGORY
        PROPERTY
        RELATIONSHIP
        PROCEDURE
        DEFINITION
    

    Purpose: Type of semantic concept.

    Values: – CATEGORY: Represents category – PROPERTY: Represents property – RELATIONSHIP: Represents relationship – PROCEDURE: Represents procedure – DEFINITION: Represents definition


    Classes

    ConceptType (Line 16)

    Description: Type of semantic concept.

    Concept (Line 26)

    Description: Semantic concept with properties.

    Association (Line 40)

    Description: Association between concepts.

    ConceptQuery (Line 49)

    Description: Query about semantic knowledge.

    SemanticMemory (Line 57)

    Description: Semantic memory storage and retrieval.

    Methods:

    __init__ (Line 60)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    add_concept (Line 65)
    def add_concept(self, concept):
        """Add concept to semantic memory."""
    

    Purpose: Add concept to semantic memory.

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

    add_association (Line 75)
    def add_association(self, from_concept, to_concept, relationship, strength):
        """Add association between concepts."""
    

    Purpose: Add association between concepts.

    Parameters: – self: Instance reference – from_concept: Parameter for from_concept – to_concept: Parameter for to_concept – relationship: Parameter for relationship – strength: Parameter for strength

    find_concept (Line 87)
    def find_concept(self, name):
        """Find concept by name."""
    

    Purpose: Find concept by name.

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

    get_related_concepts (Line 94)
    def get_related_concepts(self, concept_id, max_depth):
        """Get concepts related to given concept."""
    

    Purpose: Get concepts related to given concept.

    Parameters: – self: Instance reference – concept_id: Parameter for concept_id – max_depth: Parameter for max_depth

    get_by_category (Line 120)
    def get_by_category(self, category):
        """Get all concepts in a category."""
    

    Purpose: Get all concepts in a category.

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

    SemanticMemoryAgent (Line 126)

    Description: Agent with semantic memory capabilities.

    Methods:

    __init__ (Line 129)
    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”)

    learn_concept (Line 135)
    def learn_concept(self, concept):
        """Learn new concept."""
    

    Purpose: Learn new concept.

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

    associate_concepts (Line 140)
    def associate_concepts(self, concept1, concept2, relationship, strength):
        """Create association between concepts."""
    

    Purpose: Create association between concepts.

    Parameters: – self: Instance reference – concept1: Parameter for concept1 – concept2: Parameter for concept2 – relationship: Parameter for relationship – strength: Parameter for strength

    explain_concept (Line 145)
    def explain_concept(self, concept_name):
        """Explain concept using LLM."""
    

    Purpose: Explain concept using LLM.

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

    query_knowledge (Line 171)
    def query_knowledge(self, query):
        """Query semantic knowledge."""
    

    Purpose: Query semantic knowledge.

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

    SemanticMemoryPattern (Line 207)

    Description: Semantic Memory Pattern – Abstract knowledge storage.

    Agents maintain general facts, concepts, and meanings, enabling them to reason about abstract ideas and relationships.

    Methods:

    __init__ (Line 215)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 218)
    def register_agent(self, agent):
        """Register agent with semantic memory."""
    

    Purpose: Register agent with semantic memory.

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

    share_concept (Line 222)
    def share_concept(self, source_agent, target_agent, concept_name):
        """Share concept between agents."""
    

    Purpose: Share concept between agents.

    Parameters: – self: Instance reference – source_agent: Parameter for source_agent – target_agent: Parameter for target_agent – concept_name: Parameter for concept_name

    run (Line 232)
    def run(self, domain):
        """Execute semantic memory pattern."""
    

    Purpose: Execute semantic memory pattern.

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


    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 = ConceptType(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

  • Agentic Architecture Pattern: Episodic Memory

    
        """
    Pattern 61: Episodic Memory Pattern
    
    Agents store and recall specific experiences/episodes with temporal context.
    Like human episodic memory: "I remember when X happened at time Y."
    """
    
    from typing import Dict, Any, List, Optional
    from dataclasses import dataclass, field
    from datetime import datetime
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    from langchain_core.messages import HumanMessage, AIMessage
    
    
    @dataclass
    class Episode:
        """Single episode in memory."""
        episode_id: str
        description: str
        context: Dict[str, Any]
        participants: List[str]
        location: Optional[str] = None
        outcome: Optional[str] = None
        emotional_valence: float = 0.0  # -1 (negative) to +1 (positive)
        importance: float = 0.5  # 0 to 1
        timestamp: datetime = field(default_factory=datetime.now)
        tags: List[str] = field(default_factory=list)
    
    
    @dataclass
    class MemoryRecall:
        """Result of memory recall."""
        query: str
        recalled_episodes: List[Episode]
        relevance_scores: Dict[str, float]
        recall_time: datetime = field(default_factory=datetime.now)
    
    
    class EpisodicMemoryAgent:
        """Agent with episodic memory capabilities."""
        
        def __init__(self, name: str, model_name: str = "gpt-4"):
            self.name = name
            self.llm = ChatOpenAI(model=model_name, temperature=0.7)
            self.episodes: List[Episode] = []
            self.recall_history: List[MemoryRecall] = []
        
        def store_episode(self, episode: Episode):
            """Store new episode in memory."""
            self.episodes.append(episode)
            print(f"   📝 {self.name} stored: {episode.description}")
        
        def recall_by_time(self, start_time: datetime, end_time: datetime) -> List[Episode]:
            """Recall episodes within time range."""
            return [ep for ep in self.episodes 
                    if start_time <= ep.timestamp  List[Episode]:
            """Recall episodes with specific context."""
            return [ep for ep in self.episodes 
                    if ep.context.get(context_key) == context_value]
        
        def recall_by_participant(self, participant: str) -> List[Episode]:
            """Recall episodes involving specific participant."""
            return [ep for ep in self.episodes 
                    if participant in ep.participants]
        
        def recall_similar(self, query: str, top_k: int = 5) -> MemoryRecall:
            """Recall episodes similar to query using LLM."""
            if not self.episodes:
                return MemoryRecall(query, [], {})
            
            # Create episode descriptions
            episodes_text = "\n".join([
                f"{i+1}. [{ep.timestamp.strftime('%Y-%m-%d %H:%M')}] {ep.description} "
                f"(Participants: {', '.join(ep.participants)})"
                for i, ep in enumerate(self.episodes)
            ])
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name} recalling past experiences. "
                          f"Review your episodic memories and identify which ones are most relevant to the query."),
                ("human", f"Your episodic memories:\n{episodes_text}\n\n"
                         f"Query: {query}\n\n"
                         f"List the episode numbers (1-{len(self.episodes)}) most relevant to this query, "
                         f"in order of relevance. Format: just the numbers separated by commas.")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            # Parse episode numbers
            content = str(response.content)
            try:
                import re
                numbers = re.findall(r'\d+', content)
                indices = [int(n) - 1 for n in numbers if 0 < int(n) <= len(self.episodes)][:top_k]
                
                recalled = [self.episodes[i] for i in indices if i  str:
            """Reflect on stored episode using LLM."""
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are {self.name} reflecting on a past experience."),
                ("human", f"Experience: {episode.description}\n"
                         f"When: {episode.timestamp.strftime('%Y-%m-%d %H:%M')}\n"
                         f"Context: {episode.context}\n"
                         f"Outcome: {episode.outcome}\n\n"
                         f"What did you learn from this experience? (2-3 sentences)")
            ])
            
            chain = prompt | self.llm
            response = chain.invoke({})
            
            return str(response.content)[:200]
    
    
    class EpisodicMemoryPattern:
        """
        Episodic Memory Pattern - Experience-based memory system.
        
        Agents store specific experiences with temporal and contextual information,
        enabling recall of "what happened when" and learning from past experiences.
        """
        
        def __init__(self):
            self.agents: Dict[str, EpisodicMemoryAgent] = {}
        
        def register_agent(self, agent: EpisodicMemoryAgent):
            """Register agent with episodic memory."""
            self.agents[agent.name] = agent
        
        def create_shared_episode(self, episode: Episode):
            """Create episode shared by all agents."""
            for agent in self.agents.values():
                agent.store_episode(episode)
        
        def run(self, scenario: str) -> Dict[str, Any]:
            """Execute episodic memory pattern."""
            total_episodes = sum(len(agent.episodes) for agent in self.agents.values())
            total_recalls = sum(len(agent.recall_history) for agent in self.agents.values())
            
            return {
                "pattern": "EpisodicMemory",
                "total_agents": len(self.agents),
                "total_episodes": total_episodes,
                "total_recalls": total_recalls,
                "scenario": scenario
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Episodic Memory Pattern Demo ===\n")
        
        # Create pattern
        pattern = EpisodicMemoryPattern()
        
        # Create agent with episodic memory
        print("1. Creating agent with episodic memory...")
        agent = EpisodicMemoryAgent("Alice")
        pattern.register_agent(agent)
        
        # Store episodes
        print("\n2. Storing episodic memories...")
        
        episodes = [
            Episode(
                "ep1",
                "First meeting with project team",
                context={"project": "AI Platform", "phase": "kickoff"},
                participants=["Alice", "Bob", "Carol"],
                location="Conference Room A",
                outcome="Agreed on project timeline",
                emotional_valence=0.8,
                importance=0.9,
                tags=["meeting", "project", "team"]
            ),
            Episode(
                "ep2",
                "Debugged critical production issue",
                context={"project": "AI Platform", "phase": "deployment"},
                participants=["Alice", "Dave"],
                location="Remote",
                outcome="Fixed database connection bug",
                emotional_valence=-0.3,
                importance=0.95,
                tags=["debugging", "production", "urgent"]
            ),
            Episode(
                "ep3",
                "Celebrated successful product launch",
                context={"project": "AI Platform", "phase": "launch"},
                participants=["Alice", "Bob", "Carol", "Dave", "Eve"],
                location="Office",
                outcome="Product launched successfully",
                emotional_valence=1.0,
                importance=1.0,
                tags=["celebration", "launch", "success"]
            ),
            Episode(
                "ep4",
                "Code review session with junior developer",
                context={"project": "AI Platform", "phase": "development"},
                participants=["Alice", "Frank"],
                location="Video call",
                outcome="Improved code quality and mentored junior dev",
                emotional_valence=0.6,
                importance=0.7,
                tags=["code_review", "mentoring", "learning"]
            ),
        ]
        
        for episode in episodes:
            agent.store_episode(episode)
        
        # Recall by different criteria
        print("\n3. Recalling memories by participant...")
        bob_episodes = agent.recall_by_participant("Bob")
        print(f"   Episodes with Bob: {len(bob_episodes)}")
        for ep in bob_episodes:
            print(f"   - {ep.description}")
        
        print("\n4. Recalling memories by context...")
        launch_episodes = agent.recall_by_context("phase", "launch")
        print(f"   Episodes in launch phase: {len(launch_episodes)}")
        for ep in launch_episodes:
            print(f"   - {ep.description}")
        
        print("\n5. Recalling similar memories (semantic search)...")
        queries = [
            "Tell me about times when we had problems",
            "What were our team celebrations?",
            "Experiences with code and development"
        ]
        
        for query in queries:
            print(f"\n   Query: {query}")
            recall = agent.recall_similar(query, top_k=2)
            for ep in recall.recalled_episodes:
                print(f"   - {ep.description} (relevance: {recall.relevance_scores.get(ep.episode_id, 0):.1f})")
        
        # Reflection
        print("\n6. Reflecting on experience...")
        important_ep = max(agent.episodes, key=lambda e: e.importance)
        reflection = agent.reflect_on_experience(important_ep)
        print(f"   Reflecting on: {important_ep.description}")
        print(f"   Reflection: {reflection}")
        
        # Summary
        print("\n7. Memory Statistics:")
        summary = pattern.run("Personal experience tracking")
        print(f"   Total Episodes: {summary['total_episodes']}")
        print(f"   Total Recalls: {summary['total_recalls']}")
        print(f"   Average importance: {sum(ep.importance for ep in agent.episodes) / len(agent.episodes):.2f}")
        print(f"   Average emotional valence: {sum(ep.emotional_valence for ep in agent.episodes) / len(agent.episodes):.2f}")
        
        print("\n✓ Episodic memory pattern enabled experience-based recall!")
    
        

    Agentic Architecture Pattern: Episodic Memory

    Pattern 61: 61 Episodic Memory – Line-by-Line Explanation

    Overview

    Pattern 61: Episodic Memory Pattern

    Agents store and recall specific experiences/episodes with temporal context. Like human episodic memory: “I remember when X happened at time Y.”


    Code Structure Analysis

    File Information

    • Pattern Number: 61
    • Pattern Name: 61 Episodic Memory
    • File Path: patterns\memory_knowledge\61_episodic_memory.py
    • Total Lines: 268

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 61: Episodic Memory Pattern
    
    Agents store and recall specific experiences/episodes with temporal context.
    Like human episodic memory: "I remember when X happened at time Y."
    """
    

    Explanation: This module docstring describes the purpose and key features of the 61 Episodic Memory pattern.


    Imports Section

    from typing import Dict, Any, List, Optional
    from dataclasses import dataclass, field
    from datetime import datetime
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    from langchain_core.messages import HumanMessage, AIMessage
                import re  # First 20 imports shown
    

    Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional: 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 – from langchain_core.messages import HumanMessage, AIMessage: LangChain framework integration


    Classes

    Episode (Line 17)

    Description: Single episode in memory.

    MemoryRecall (Line 32)

    Description: Result of memory recall.

    EpisodicMemoryAgent (Line 40)

    Description: Agent with episodic memory capabilities.

    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”)

    store_episode (Line 49)
    def store_episode(self, episode):
        """Store new episode in memory."""
    

    Purpose: Store new episode in memory.

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

    recall_by_time (Line 54)
    def recall_by_time(self, start_time, end_time):
        """Recall episodes within time range."""
    

    Purpose: Recall episodes within time range.

    Parameters: – self: Instance reference – start_time: Parameter for start_time – end_time: Parameter for end_time

    recall_by_context (Line 59)
    def recall_by_context(self, context_key, context_value):
        """Recall episodes with specific context."""
    

    Purpose: Recall episodes with specific context.

    Parameters: – self: Instance reference – context_key: Parameter for context_key – context_value: Parameter for context_value

    recall_by_participant (Line 64)
    def recall_by_participant(self, participant):
        """Recall episodes involving specific participant."""
    

    Purpose: Recall episodes involving specific participant.

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

    recall_similar (Line 69)
    def recall_similar(self, query, top_k):
        """Recall episodes similar to query using LLM."""
    

    Purpose: Recall episodes similar to query using LLM.

    Parameters: – self: Instance reference – query: Parameter for query – top_k: Parameter for top_k

    reflect_on_experience (Line 110)
    def reflect_on_experience(self, episode):
        """Reflect on stored episode using LLM."""
    

    Purpose: Reflect on stored episode using LLM.

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

    EpisodicMemoryPattern (Line 127)

    Description: Episodic Memory Pattern – Experience-based memory system.

    Agents store specific experiences with temporal and contextual information, enabling recall of “what happened when” and learning from past experiences.

    Methods:

    __init__ (Line 135)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 138)
    def register_agent(self, agent):
        """Register agent with episodic memory."""
    

    Purpose: Register agent with episodic memory.

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

    create_shared_episode (Line 142)
    def create_shared_episode(self, episode):
        """Create episode shared by all agents."""
    

    Purpose: Create episode shared by all agents.

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

    run (Line 147)
    def run(self, scenario):
        """Execute episodic memory pattern."""
    

    Purpose: Execute episodic memory pattern.

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


    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 = Episode(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

  • Agentic Architecture Pattern: Self Organizing

    
        """
    Pattern 60: Self-Organizing Agent
    
    Purpose: Agents spontaneously organize into emergent structures without central control.
    Bottom-up organization through local interactions and simple rules leading to complex behaviors.
    """
    
    from typing import Dict, Any, List, Optional, Set
    from dataclasses import dataclass, field
    from datetime import datetime
    from enum import Enum
    import random
    import math
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class OrganizationRule(Enum):
        """Rules for self-organization."""
        PROXIMITY = "proximity"  # Organize by nearness
        SIMILARITY = "similarity"  # Organize by similarity
        COMPLEMENTARITY = "complementarity"  # Organize by complementary skills
        WORKLOAD_BALANCE = "workload_balance"  # Balance workload
    
    
    class AgentRole(Enum):
        """Emergent roles agents can take."""
        COORDINATOR = "coordinator"  # Coordinates group activities
        SPECIALIST = "specialist"  # Specialized in specific tasks
        GENERALIST = "generalist"  # Handles diverse tasks
        CONNECTOR = "connector"  # Bridges between groups
    
    
    @dataclass
    class AgentProfile:
        """Profile of an autonomous agent."""
        id: str
        name: str
        skills: Set[str]
        capacity: float  # 0.0 to 1.0
        workload: float = 0.0
        connections: Set[str] = field(default_factory=set)
        position: tuple = (0.0, 0.0)  # 2D position for proximity-based organization
        role: Optional[AgentRole] = None
    
    
    @dataclass
    class Task:
        """Task to be organized."""
        id: str
        name: str
        required_skills: Set[str]
        complexity: float  # 0.0 to 1.0
        assigned_to: Optional[str] = None
    
    
    @dataclass
    class OrganizationCluster:
        """Emergent cluster of agents."""
        id: str
        agents: Set[str]
        centroid: tuple = (0.0, 0.0)
        shared_skills: Set[str] = field(default_factory=set)
        total_capacity: float = 0.0
    
    
    class SelfOrganizingAgent:
        """
        Self-Organizing Agent - Spontaneous organization through local interactions.
        
        Agents organize themselves into emergent structures without central control,
        following simple rules that lead to complex adaptive behaviors.
        """
        
        def __init__(self,
                     organization_rule: OrganizationRule = OrganizationRule.SIMILARITY,
                     proximity_threshold: float = 5.0,
                     similarity_threshold: float = 0.3,
                     reorg_interval: int = 10):
            self.organization_rule = organization_rule
            self.proximity_threshold = proximity_threshold
            self.similarity_threshold = similarity_threshold
            self.reorg_interval = reorg_interval
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Agent population
            self.agents: Dict[str, AgentProfile] = {}
            self.tasks: Dict[str, Task] = {}
            
            # Emergent structures
            self.clusters: Dict[str, OrganizationCluster] = {}
            self.interaction_count = 0
            self.reorganization_count = 0
            
            # History
            self.organization_history: List[Dict[str, Any]] = []
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "total_agents": 0,
                "total_tasks": 0,
                "total_clusters": 0,
                "reorganizations": 0,
                "avg_cluster_size": 0.0
            }
        
        def add_agent(self, agent: AgentProfile):
            """Add autonomous agent to population."""
            self.agents[agent.id] = agent
            self.statistics["total_agents"] = len(self.agents)
            print(f"   Added agent: {agent.name} with skills {agent.skills}")
        
        def add_task(self, task: Task):
            """Add task to be organized."""
            self.tasks[task.id] = task
            self.statistics["total_tasks"] = len(self.tasks)
        
        def organize(self):
            """Trigger self-organization based on current rule."""
            print(f"\n   Self-organizing using {self.organization_rule.value} rule...")
            
            if self.organization_rule == OrganizationRule.PROXIMITY:
                self._organize_by_proximity()
            elif self.organization_rule == OrganizationRule.SIMILARITY:
                self._organize_by_similarity()
            elif self.organization_rule == OrganizationRule.COMPLEMENTARITY:
                self._organize_by_complementarity()
            else:  # WORKLOAD_BALANCE
                self._organize_by_workload()
            
            self._assign_roles()
            self._assign_tasks()
            self.reorganization_count += 1
            self.statistics["reorganizations"] = self.reorganization_count
            
            # Record organization state
            self.organization_history.append({
                "reorganization": self.reorganization_count,
                "clusters": len(self.clusters),
                "avg_cluster_size": self._calculate_avg_cluster_size(),
                "timestamp": datetime.now()
            })
        
        def _organize_by_proximity(self):
            """Organize agents by spatial proximity."""
            self.clusters = {}
            unclustered = set(self.agents.keys())
            cluster_id = 0
            
            while unclustered:
                # Start new cluster with random agent
                seed_id = random.choice(list(unclustered))
                seed_agent = self.agents[seed_id]
                
                cluster_agents = {seed_id}
                unclustered.remove(seed_id)
                
                # Add nearby agents
                for agent_id in list(unclustered):
                    agent = self.agents[agent_id]
                    distance = self._calculate_distance(seed_agent.position, agent.position)
                    
                    if distance = self.similarity_threshold:
                        cluster_agents.add(agent_id)
                        unclustered.remove(agent_id)
                
                # Calculate shared skills
                shared_skills = set.intersection(*[self.agents[aid].skills 
                                                  for aid in cluster_agents])
                
                cluster = OrganizationCluster(
                    id=f"cluster_{cluster_id}",
                    agents=cluster_agents,
                    shared_skills=shared_skills
                )
                self.clusters[cluster.id] = cluster
                cluster_id += 1
            
            print(f"   Formed {len(self.clusters)} similarity-based clusters")
        
        def _organize_by_complementarity(self):
            """Organize agents with complementary skills."""
            self.clusters = {}
            unclustered = set(self.agents.keys())
            cluster_id = 0
            
            # Collect all skills
            all_skills = set()
            for agent in self.agents.values():
                all_skills.update(agent.skills)
            
            while unclustered and len(unclustered) >= 2:
                # Start with random agent
                seed_id = random.choice(list(unclustered))
                cluster_agents = {seed_id}
                unclustered.remove(seed_id)
                
                cluster_skills = set(self.agents[seed_id].skills)
                
                # Add agents with complementary skills
                for agent_id in list(unclustered):
                    agent = self.agents[agent_id]
                    # Complementary if they have different skills
                    overlap = len(cluster_skills & agent.skills)
                    if overlap = 4:  # Limit cluster size
                        break
                
                cluster = OrganizationCluster(
                    id=f"cluster_{cluster_id}",
                    agents=cluster_agents,
                    shared_skills=cluster_skills
                )
                self.clusters[cluster.id] = cluster
                cluster_id += 1
            
            # Add remaining agents to existing clusters
            for agent_id in unclustered:
                if self.clusters:
                    # Add to smallest cluster
                    smallest_cluster = min(self.clusters.values(), key=lambda c: len(c.agents))
                    smallest_cluster.agents.add(agent_id)
            
            print(f"   Formed {len(self.clusters)} complementarity-based clusters")
        
        def _organize_by_workload(self):
            """Organize to balance workload."""
            # Target cluster count
            target_clusters = max(1, len(self.agents) // 4)
            
            self.clusters = {}
            for i in range(target_clusters):
                self.clusters[f"cluster_{i}"] = OrganizationCluster(
                    id=f"cluster_{i}",
                    agents=set()
                )
            
            # Sort agents by workload (descending)
            sorted_agents = sorted(self.agents.items(), 
                                  key=lambda x: x[1].workload, reverse=True)
            
            # Distribute agents to balance load
            cluster_loads = {cid: 0.0 for cid in self.clusters.keys()}
            
            for agent_id, agent in sorted_agents:
                # Assign to least loaded cluster
                min_cluster = min(cluster_loads.items(), key=lambda x: x[1])
                cluster_id = min_cluster[0]
                
                self.clusters[cluster_id].agents.add(agent_id)
                cluster_loads[cluster_id] += agent.workload
            
            print(f"   Formed {len(self.clusters)} workload-balanced clusters")
        
        def _assign_roles(self):
            """Assign emergent roles to agents based on their position in structure."""
            for cluster in self.clusters.values():
                if len(cluster.agents) == 0:
                    continue
                
                # Coordinator: agent with most connections
                max_connections = 0
                coordinator = None
                for agent_id in cluster.agents:
                    agent = self.agents[agent_id]
                    conn_count = len(agent.connections & cluster.agents)
                    if conn_count > max_connections:
                        max_connections = conn_count
                        coordinator = agent_id
                
                if coordinator:
                    self.agents[coordinator].role = AgentRole.COORDINATOR
                
                # Specialists: agents with unique skills
                for agent_id in cluster.agents:
                    if agent_id == coordinator:
                        continue
                    agent = self.agents[agent_id]
                    # Check if has unique skills
                    others_skills = set()
                    for other_id in cluster.agents:
                        if other_id != agent_id:
                            others_skills.update(self.agents[other_id].skills)
                    
                    unique_skills = agent.skills - others_skills
                    if unique_skills:
                        agent.role = AgentRole.SPECIALIST
                    else:
                        agent.role = AgentRole.GENERALIST
        
        def _assign_tasks(self):
            """Assign tasks to agents based on organization."""
            for task in self.tasks.values():
                if task.assigned_to:
                    continue  # Already assigned
                
                # Find best cluster for task
                best_cluster = None
                best_score = 0.0
                
                for cluster in self.clusters.values():
                    # Calculate cluster's match with task
                    cluster_skills = set()
                    total_capacity = 0.0
                    for agent_id in cluster.agents:
                        agent = self.agents[agent_id]
                        cluster_skills.update(agent.skills)
                        total_capacity += (agent.capacity - agent.workload)
                    
                    skill_match = len(task.required_skills & cluster_skills) / len(task.required_skills) \
                                 if task.required_skills else 1.0
                    capacity_score = min(1.0, total_capacity / task.complexity)
                    score = skill_match * 0.7 + capacity_score * 0.3
                    
                    if score > best_score:
                        best_score = score
                        best_cluster = cluster
                
                # Assign to best agent in cluster
                if best_cluster and best_cluster.agents:
                    best_agent = None
                    best_agent_score = 0.0
                    
                    for agent_id in best_cluster.agents:
                        agent = self.agents[agent_id]
                        skill_match = len(task.required_skills & agent.skills) / len(task.required_skills) \
                                     if task.required_skills else 1.0
                        capacity_available = agent.capacity - agent.workload
                        agent_score = skill_match * 0.8 + min(1.0, capacity_available / task.complexity) * 0.2
                        
                        if agent_score > best_agent_score and capacity_available > 0:
                            best_agent_score = agent_score
                            best_agent = agent_id
                    
                    if best_agent:
                        task.assigned_to = best_agent
                        self.agents[best_agent].workload += task.complexity
        
        def _calculate_distance(self, pos1: tuple, pos2: tuple) -> float:
            """Calculate Euclidean distance."""
            return math.sqrt((pos1[0] - pos2[0])**2 + (pos1[1] - pos2[1])**2)
        
        def _calculate_centroid(self, agent_ids: Set[str]) -> tuple:
            """Calculate centroid of agent positions."""
            if not agent_ids:
                return (0.0, 0.0)
            
            x_sum = sum(self.agents[aid].position[0] for aid in agent_ids)
            y_sum = sum(self.agents[aid].position[1] for aid in agent_ids)
            count = len(agent_ids)
            
            return (x_sum / count, y_sum / count)
        
        def _calculate_skill_similarity(self, skills1: Set[str], skills2: Set[str]) -> float:
            """Calculate Jaccard similarity between skill sets."""
            if not skills1 or not skills2:
                return 0.0
            
            intersection = len(skills1 & skills2)
            union = len(skills1 | skills2)
            
            return intersection / union if union > 0 else 0.0
        
        def _calculate_avg_cluster_size(self) -> float:
            """Calculate average cluster size."""
            if not self.clusters:
                return 0.0
            
            total_agents = sum(len(c.agents) for c in self.clusters.values())
            return total_agents / len(self.clusters)
        
        def get_organization_summary(self) -> Dict[str, Any]:
            """Get summary of current organization."""
            cluster_info = []
            for cluster in self.clusters.values():
                cluster_info.append({
                    "id": cluster.id,
                    "size": len(cluster.agents),
                    "skills": list(cluster.shared_skills),
                    "agents": [self.agents[aid].name for aid in cluster.agents]
                })
            
            role_distribution = {}
            for agent in self.agents.values():
                role = agent.role.value if agent.role else "unassigned"
                role_distribution[role] = role_distribution.get(role, 0) + 1
            
            return {
                "total_clusters": len(self.clusters),
                "avg_cluster_size": self._calculate_avg_cluster_size(),
                "clusters": cluster_info,
                "role_distribution": role_distribution,
                "tasks_assigned": sum(1 for t in self.tasks.values() if t.assigned_to)
            }
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive statistics."""
            self.statistics["total_clusters"] = len(self.clusters)
            self.statistics["avg_cluster_size"] = self._calculate_avg_cluster_size()
            
            return self.statistics
    
    
    class SelfOrganizingPattern:
        """
        Self-Organizing Pattern - Coordinate emergent organization.
        
        Manages self-organizing systems where complex structures emerge from simple rules.
        """
        
        def __init__(self):
            self.systems: Dict[str, SelfOrganizingAgent] = {}
        
        def register_system(self, system: SelfOrganizingAgent, name: str):
            """Register self-organizing system."""
            self.systems[name] = system
            print(f"   Registered system: {name}")
        
        def run(self) -> Dict[str, Any]:
            """Run self-organization."""
            print("=== Self-Organizing Agent Pattern Demo ===\n")
            
            print("1. Organizing systems...")
            for name, system in self.systems.items():
                print(f"\n   System: {name}")
                system.organize()
            
            print("\n2. Getting organization summaries...")
            summaries = {}
            for name, system in self.systems.items():
                summaries[name] = system.get_organization_summary()
            
            return {
                "pattern": "Self-Organizing",
                "systems": len(self.systems),
                "summaries": summaries
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Self-Organizing Agent Pattern Demo ===\n")
        
        # Create pattern
        pattern = SelfOrganizingPattern()
        
        # Create self-organizing system with similarity rule
        print("1. Creating self-organizing system...")
        system = SelfOrganizingAgent(
            organization_rule=OrganizationRule.SIMILARITY,
            similarity_threshold=0.25
        )
        
        # Add agents with different skills
        print("\n2. Adding autonomous agents...")
        agents_data = [
            ("A1", "Alice", {"python", "ml", "data_analysis"}),
            ("A2", "Bob", {"python", "web", "backend"}),
            ("A3", "Carol", {"ml", "data_analysis", "statistics"}),
            ("A4", "Dave", {"web", "frontend", "design"}),
            ("A5", "Eve", {"python", "ml", "nlp"}),
            ("A6", "Frank", {"backend", "database", "devops"}),
            ("A7", "Grace", {"data_analysis", "statistics", "visualization"}),
            ("A8", "Hank", {"frontend", "design", "ux"}),
        ]
        
        for agent_id, name, skills in agents_data:
            agent = AgentProfile(
                id=agent_id,
                name=name,
                skills=skills,
                capacity=random.uniform(0.7, 1.0),
                position=(random.uniform(0, 10), random.uniform(0, 10))
            )
            system.add_agent(agent)
        
        # Add tasks
        print("\n3. Adding tasks...")
        tasks_data = [
            ("T1", "ML Pipeline", {"python", "ml", "data_analysis"}),
            ("T2", "Web API", {"python", "web", "backend"}),
            ("T3", "Data Dashboard", {"data_analysis", "visualization", "frontend"}),
            ("T4", "NLP Model", {"python", "ml", "nlp"}),
        ]
        
        for task_id, name, skills in tasks_data:
            task = Task(
                id=task_id,
                name=name,
                required_skills=skills,
                complexity=random.uniform(0.3, 0.8)
            )
            system.add_task(task)
        
        # Register and run
        pattern.register_system(system, "SkillBasedOrganization")
        
        print("\n4. Running self-organization...")
        summary = pattern.run()
        
        print(f"\n5. Organization Results:")
        print(f"   Pattern: {summary['pattern']}")
        print(f"   Systems: {summary['systems']}")
        
        for system_name, system_summary in summary['summaries'].items():
            print(f"\n   System: {system_name}")
            print(f"      Total clusters: {system_summary['total_clusters']}")
            print(f"      Avg cluster size: {system_summary['avg_cluster_size']:.1f}")
            print(f"      Tasks assigned: {system_summary['tasks_assigned']}/{len(tasks_data)}")
            
            print(f"\n      Clusters:")
            for cluster in system_summary['clusters']:
                print(f"         {cluster['id']}: {cluster['size']} agents")
                print(f"            Skills: {cluster['skills']}")
                print(f"            Agents: {cluster['agents']}")
            
            print(f"\n      Role distribution:")
            for role, count in system_summary['role_distribution'].items():
                print(f"         {role}: {count}")
        
        print(f"\n6. Statistics:")
        stats = system.get_statistics()
        for key, value in stats.items():
            if isinstance(value, float):
                print(f"   {key}: {value:.2f}")
            else:
                print(f"   {key}: {value}")
        
        print("\n✓ Agents self-organized into emergent structures!")
    
        

    Agentic Architecture Pattern: Self Organizing

    Pattern 60: 60 Self Organizing – Line-by-Line Explanation

    Overview

    Pattern 60: Self-Organizing Agent

    Purpose: Agents spontaneously organize into emergent structures without central control. Bottom-up organization through local interactions and simple rules leading to complex behaviors.


    Code Structure Analysis

    File Information

    • Pattern Number: 60
    • Pattern Name: 60 Self Organizing
    • File Path: patterns\learning_adaptation\60_self_organizing.py
    • Total Lines: 568

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 60: Self-Organizing Agent
    
    Purpose: Agents spontaneously organize into emergent structures without central control.
    Bottom-up organization through local interactions and simple rules leading to complex behaviors.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 60 Self Organizing pattern.


    Imports Section

    from typing import Dict, Any, List, Optional, Set
    from dataclasses import dataclass, field
    from datetime import datetime
    from enum import Enum
    import random
    import math
    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, Set: 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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    OrganizationRule (Line 18)

    class OrganizationRule(Enum):
        PROXIMITY
        SIMILARITY
        COMPLEMENTARITY
        WORKLOAD_BALANCE
    

    Purpose: Rules for self-organization.

    Values: – PROXIMITY: Represents proximity – SIMILARITY: Represents similarity – COMPLEMENTARITY: Represents complementarity – WORKLOAD_BALANCE: Represents workload balance

    AgentRole (Line 26)

    class AgentRole(Enum):
        COORDINATOR
        SPECIALIST
        GENERALIST
        CONNECTOR
    

    Purpose: Emergent roles agents can take.

    Values: – COORDINATOR: Represents coordinator – SPECIALIST: Represents specialist – GENERALIST: Represents generalist – CONNECTOR: Represents connector


    Classes

    OrganizationRule (Line 18)

    Description: Rules for self-organization.

    AgentRole (Line 26)

    Description: Emergent roles agents can take.

    AgentProfile (Line 35)

    Description: Profile of an autonomous agent.

    Task (Line 48)

    Description: Task to be organized.

    OrganizationCluster (Line 58)

    Description: Emergent cluster of agents.

    SelfOrganizingAgent (Line 67)

    Description: Self-Organizing Agent – Spontaneous organization through local interactions.

    Agents organize themselves into emergent structures without central control, following simple rules that lead to complex adaptive behaviors.

    Methods:

    __init__ (Line 75)
    def __init__(self, organization_rule, proximity_threshold, similarity_threshold, reorg_interval):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference – organization_rule: Parameter for organization_rule – proximity_threshold: Parameter for proximity_threshold – similarity_threshold: Parameter for similarity_threshold – reorg_interval: Parameter for reorg_interval

    add_agent (Line 107)
    def add_agent(self, agent):
        """Add autonomous agent to population."""
    

    Purpose: Add autonomous agent to population.

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

    add_task (Line 113)
    def add_task(self, task):
        """Add task to be organized."""
    

    Purpose: Add task to be organized.

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

    organize (Line 118)
    def organize(self):
        """Trigger self-organization based on current rule."""
    

    Purpose: Trigger self-organization based on current rule.

    Parameters: – self: Instance reference

    _organize_by_proximity (Line 144)
    def _organize_by_proximity(self):
        """Organize agents by spatial proximity."""
    

    Purpose: Organize agents by spatial proximity.

    Parameters: – self: Instance reference

    _organize_by_similarity (Line 178)
    def _organize_by_similarity(self):
        """Organize agents by skill similarity."""
    

    Purpose: Organize agents by skill similarity.

    Parameters: – self: Instance reference

    _organize_by_complementarity (Line 215)
    def _organize_by_complementarity(self):
        """Organize agents with complementary skills."""
    

    Purpose: Organize agents with complementary skills.

    Parameters: – self: Instance reference

    _organize_by_workload (Line 264)
    def _organize_by_workload(self):
        """Organize to balance workload."""
    

    Purpose: Organize to balance workload.

    Parameters: – self: Instance reference

    _assign_roles (Line 293)
    def _assign_roles(self):
        """Assign emergent roles to agents based on their position in structure."""
    

    Purpose: Assign emergent roles to agents based on their position in structure.

    Parameters: – self: Instance reference

    _assign_tasks (Line 329)
    def _assign_tasks(self):
        """Assign tasks to agents based on organization."""
    

    Purpose: Assign tasks to agents based on organization.

    Parameters: – self: Instance reference

    _calculate_distance (Line 377)
    def _calculate_distance(self, pos1, pos2):
        """Calculate Euclidean distance."""
    

    Purpose: Calculate Euclidean distance.

    Parameters: – self: Instance reference – pos1: Parameter for pos1 – pos2: Parameter for pos2

    _calculate_centroid (Line 381)
    def _calculate_centroid(self, agent_ids):
        """Calculate centroid of agent positions."""
    

    Purpose: Calculate centroid of agent positions.

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

    _calculate_skill_similarity (Line 392)
    def _calculate_skill_similarity(self, skills1, skills2):
        """Calculate Jaccard similarity between skill sets."""
    

    Purpose: Calculate Jaccard similarity between skill sets.

    Parameters: – self: Instance reference – skills1: Parameter for skills1 – skills2: Parameter for skills2

    _calculate_avg_cluster_size (Line 402)
    def _calculate_avg_cluster_size(self):
        """Calculate average cluster size."""
    

    Purpose: Calculate average cluster size.

    Parameters: – self: Instance reference

    get_organization_summary (Line 410)
    def get_organization_summary(self):
        """Get summary of current organization."""
    

    Purpose: Get summary of current organization.

    Parameters: – self: Instance reference

    …and 1 more methods

    SelfOrganizingPattern (Line 442)

    Description: Self-Organizing Pattern – Coordinate emergent organization.

    Manages self-organizing systems where complex structures emerge from simple rules.

    Methods:

    __init__ (Line 449)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_system (Line 452)
    def register_system(self, system, name):
        """Register self-organizing system."""
    

    Purpose: Register self-organizing system.

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

    run (Line 457)
    def run(self):
        """Run self-organization."""
    

    Purpose: Run self-organization.

    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 = OrganizationRule(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

  • Agentic Architecture Pattern: Adaptive Agent

    
        """
    Pattern 59: Adaptive Agent
    
    Purpose: Dynamically adapt strategies and behaviors based on environmental feedback.
    Monitor performance, detect changes, and adjust strategies in real-time.
    """
    
    from typing import Dict, Any, List, Optional
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from enum import Enum
    from collections import deque
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class AdaptationTrigger(Enum):
        """Triggers for adaptation."""
        PERFORMANCE_DROP = "performance_drop"  # Performance below threshold
        ENVIRONMENT_CHANGE = "environment_change"  # Detected environment shift
        TIME_BASED = "time_based"  # Periodic adaptation
        USER_FEEDBACK = "user_feedback"  # Based on user input
    
    
    class StrategyType(Enum):
        """Types of strategies available."""
        CONSERVATIVE = "conservative"  # Low risk, stable
        AGGRESSIVE = "aggressive"  # High risk, high reward
        BALANCED = "balanced"  # Middle ground
        EXPLORATORY = "exploratory"  # Exploration focused
        EXPLOITATIVE = "exploitative"  # Exploitation focused
    
    
    @dataclass
    class EnvironmentState:
        """Current state of the environment."""
        difficulty: float  # 0.0 to 1.0
        volatility: float  # 0.0 to 1.0 (how much it changes)
        resource_availability: float  # 0.0 to 1.0
        competition_level: float  # 0.0 to 1.0
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class PerformanceMetrics:
        """Performance metrics for adaptation decisions."""
        success_rate: float
        avg_response_time: float
        error_rate: float
        efficiency: float
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class AdaptationEvent:
        """Record of an adaptation event."""
        trigger: AdaptationTrigger
        old_strategy: StrategyType
        new_strategy: StrategyType
        reason: str
        performance_before: float
        performance_after: Optional[float] = None
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class AdaptiveAgent:
        """
        Adaptive Agent - Dynamically adapt to changing environments.
        
        Monitors performance, detects environmental changes, and adjusts
        strategies in real-time to maintain optimal performance.
        """
        
        def __init__(self,
                     name: str,
                     performance_threshold: float = 0.6,
                     adaptation_cooldown: int = 5,
                     window_size: int = 20):
            self.name = name
            self.performance_threshold = performance_threshold
            self.adaptation_cooldown = adaptation_cooldown  # Min actions between adaptations
            self.window_size = window_size
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Current state
            self.current_strategy = StrategyType.BALANCED
            self.environment = EnvironmentState(
                difficulty=0.5,
                volatility=0.3,
                resource_availability=0.8,
                competition_level=0.5
            )
            
            # Performance tracking
            self.performance_history: deque = deque(maxlen=window_size)
            self.recent_actions: deque = deque(maxlen=window_size)
            self.actions_since_adaptation = 0
            
            # Adaptation history
            self.adaptation_events: List[AdaptationEvent] = []
            self.strategy_performance: Dict[StrategyType, List[float]] = {
                strategy: [] for strategy in StrategyType
            }
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "total_actions": 0,
                "total_adaptations": 0,
                "avg_performance": 0.0,
                "best_strategy": None
            }
        
        def act(self, task: str, environment: Optional[EnvironmentState] = None) -> Dict[str, Any]:
            """Perform action with current strategy."""
            if environment:
                self.environment = environment
            
            # Strategy-specific behavior
            strategy_params = self._get_strategy_params()
            
            # Simulate action performance (in practice, execute real task)
            performance = self._simulate_action(task, strategy_params)
            
            # Record action
            self.recent_actions.append({
                "task": task,
                "strategy": self.current_strategy,
                "performance": performance,
                "timestamp": datetime.now()
            })
            self.performance_history.append(performance)
            self.strategy_performance[self.current_strategy].append(performance)
            
            self.actions_since_adaptation += 1
            self.statistics["total_actions"] += 1
            
            # Check if adaptation needed
            adaptation_needed, trigger, reason = self._check_adaptation_needed()
            
            if adaptation_needed:
                self._adapt(trigger, reason)
            
            return {
                "task": task,
                "strategy": self.current_strategy.value,
                "performance": performance,
                "adapted": adaptation_needed
            }
        
        def _get_strategy_params(self) -> Dict[str, float]:
            """Get parameters for current strategy."""
            if self.current_strategy == StrategyType.CONSERVATIVE:
                return {"risk": 0.2, "exploration": 0.1, "speed": 0.5}
            elif self.current_strategy == StrategyType.AGGRESSIVE:
                return {"risk": 0.9, "exploration": 0.3, "speed": 0.9}
            elif self.current_strategy == StrategyType.EXPLORATORY:
                return {"risk": 0.5, "exploration": 0.9, "speed": 0.6}
            elif self.current_strategy == StrategyType.EXPLOITATIVE:
                return {"risk": 0.3, "exploration": 0.1, "speed": 0.8}
            else:  # BALANCED
                return {"risk": 0.5, "exploration": 0.5, "speed": 0.7}
        
        def _simulate_action(self, task: str, params: Dict[str, float]) -> float:
            """Simulate action performance."""
            # Base performance from strategy params
            base_perf = (params["risk"] * 0.3 + 
                        params["exploration"] * 0.3 + 
                        params["speed"] * 0.4)
            
            # Adjust for environment
            env_factor = (1.0 - self.environment.difficulty * 0.5 +
                         self.environment.resource_availability * 0.3)
            
            # Add some noise
            import random
            noise = random.uniform(-0.1, 0.1)
            
            performance = base_perf * env_factor + noise
            return max(0.0, min(1.0, performance))
        
        def _check_adaptation_needed(self) -> Tuple[bool, AdaptationTrigger, str]:
            """Check if adaptation is needed."""
            # Must wait for cooldown
            if self.actions_since_adaptation < self.adaptation_cooldown:
                return False, AdaptationTrigger.PERFORMANCE_DROP, ""
            
            # Need minimum history
            if len(self.performance_history) < 5:
                return False, AdaptationTrigger.PERFORMANCE_DROP, ""
            
            # Check performance drop
            recent_perf = sum(list(self.performance_history)[-5:]) / 5
            if recent_perf = 10:
                older_perf = sum(list(self.performance_history)[-10:-5]) / 5
                if abs(recent_perf - older_perf) > 0.15:
                    return True, AdaptationTrigger.ENVIRONMENT_CHANGE, \
                           f"Performance shift detected: {older_perf:.3f} -> {recent_perf:.3f}"
            
            # Time-based adaptation
            if self.actions_since_adaptation >= self.window_size:
                return True, AdaptationTrigger.TIME_BASED, \
                       f"Periodic adaptation after {self.actions_since_adaptation} actions"
            
            return False, AdaptationTrigger.PERFORMANCE_DROP, ""
        
        def _adapt(self, trigger: AdaptationTrigger, reason: str):
            """Adapt strategy based on trigger."""
            old_strategy = self.current_strategy
            old_performance = sum(self.performance_history) / len(self.performance_history)
            
            # Select new strategy
            new_strategy = self._select_new_strategy(trigger)
            
            # Apply adaptation
            self.current_strategy = new_strategy
            self.actions_since_adaptation = 0
            
            # Record event
            event = AdaptationEvent(
                trigger=trigger,
                old_strategy=old_strategy,
                new_strategy=new_strategy,
                reason=reason,
                performance_before=old_performance
            )
            self.adaptation_events.append(event)
            
            self.statistics["total_adaptations"] += 1
            
            print(f"   {self.name}: Adapted {old_strategy.value} -> {new_strategy.value}")
            print(f"      Trigger: {trigger.value}")
            print(f"      Reason: {reason}")
        
        def _select_new_strategy(self, trigger: AdaptationTrigger) -> StrategyType:
            """Select new strategy based on situation."""
            # Get performance stats for each strategy
            strategy_scores = {}
            for strategy, perfs in self.strategy_performance.items():
                if perfs:
                    strategy_scores[strategy] = sum(perfs) / len(perfs)
                else:
                    strategy_scores[strategy] = 0.5  # Default score
            
            # Remove current strategy
            available_strategies = [s for s in StrategyType if s != self.current_strategy]
            
            if trigger == AdaptationTrigger.PERFORMANCE_DROP:
                # Switch to best performing alternative
                best_strategy = max(available_strategies, 
                                  key=lambda s: strategy_scores.get(s, 0.5))
                return best_strategy
            
            elif trigger == AdaptationTrigger.ENVIRONMENT_CHANGE:
                # Try exploratory to learn about new environment
                if self.current_strategy != StrategyType.EXPLORATORY:
                    return StrategyType.EXPLORATORY
                else:
                    return StrategyType.BALANCED
            
            elif trigger == AdaptationTrigger.TIME_BASED:
                # Rotate to untested or underperforming strategies
                least_tested = min(available_strategies, 
                                 key=lambda s: len(self.strategy_performance[s]))
                return least_tested
            
            else:  # USER_FEEDBACK
                return StrategyType.BALANCED
        
        def update_environment(self, environment: EnvironmentState):
            """Update environment state."""
            self.environment = environment
            print(f"   {self.name}: Environment updated")
            print(f"      Difficulty: {environment.difficulty:.2f}")
            print(f"      Volatility: {environment.volatility:.2f}")
        
        def get_adaptation_summary(self) -> Dict[str, Any]:
            """Get summary of adaptations."""
            if not self.adaptation_events:
                return {"total_adaptations": 0}
            
            trigger_counts = {}
            for event in self.adaptation_events:
                trigger_counts[event.trigger.value] = \
                    trigger_counts.get(event.trigger.value, 0) + 1
            
            strategy_usage = {}
            for action in self.recent_actions:
                strategy = action["strategy"].value
                strategy_usage[strategy] = strategy_usage.get(strategy, 0) + 1
            
            return {
                "total_adaptations": len(self.adaptation_events),
                "trigger_counts": trigger_counts,
                "current_strategy": self.current_strategy.value,
                "strategy_usage": strategy_usage,
                "recent_performance": sum(self.performance_history) / len(self.performance_history) \
                                     if self.performance_history else 0.0
            }
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive statistics."""
            # Calculate best strategy
            best_strategy = None
            best_avg = 0.0
            for strategy, perfs in self.strategy_performance.items():
                if perfs:
                    avg = sum(perfs) / len(perfs)
                    if avg > best_avg:
                        best_avg = avg
                        best_strategy = strategy
            
            self.statistics["best_strategy"] = best_strategy.value if best_strategy else None
            self.statistics["avg_performance"] = \
                sum(self.performance_history) / len(self.performance_history) \
                if self.performance_history else 0.0
            
            return self.statistics
    
    
    class AdaptivePattern:
        """
        Adaptive Pattern - Coordinate multiple adaptive agents.
        
        Manages agents that dynamically adjust to changing conditions.
        """
        
        def __init__(self):
            self.agents: Dict[str, AdaptiveAgent] = {}
        
        def register_agent(self, agent: AdaptiveAgent):
            """Register adaptive agent."""
            self.agents[agent.name] = agent
            print(f"   Registered agent: {agent.name}")
        
        def run(self, tasks: List[str], environment_changes: List[EnvironmentState]) -> Dict[str, Any]:
            """Run adaptive agents through tasks."""
            print("=== Adaptive Agent Pattern Demo ===\n")
            
            print("1. Running agents through tasks...")
            results = []
            
            for i, task in enumerate(tasks):
                # Update environment if applicable
                if i < len(environment_changes):
                    for agent in self.agents.values():
                        agent.update_environment(environment_changes[i])
                
                # Execute task with each agent
                for agent in self.agents.values():
                    result = agent.act(task)
                    results.append(result)
            
            # Get summaries
            print("\n2. Adaptation summaries:")
            summaries = {}
            for name, agent in self.agents.items():
                summary = agent.get_adaptation_summary()
                summaries[name] = summary
                print(f"\n   {name}:")
                print(f"      Total adaptations: {summary['total_adaptations']}")
                print(f"      Current strategy: {summary['current_strategy']}")
                print(f"      Recent performance: {summary['recent_performance']:.3f}")
            
            return {
                "pattern": "Adaptive",
                "agents": len(self.agents),
                "tasks": len(tasks),
                "total_actions": sum(agent.statistics["total_actions"] 
                                   for agent in self.agents.values()),
                "summaries": summaries
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Adaptive Agent Pattern Demo ===\n")
        
        # Create pattern
        pattern = AdaptivePattern()
        
        # Create agents
        print("1. Creating adaptive agents...")
        agent1 = AdaptiveAgent(name="Agent1", performance_threshold=0.55, adaptation_cooldown=3)
        agent2 = AdaptiveAgent(name="Agent2", performance_threshold=0.60, adaptation_cooldown=5)
        
        pattern.register_agent(agent1)
        pattern.register_agent(agent2)
        
        # Define tasks
        tasks = [
            "Process easy task",
            "Handle medium task",
            "Solve complex task",
            "Quick response needed",
            "Detailed analysis",
            "Creative solution",
            "Optimize performance",
            "Handle uncertainty",
            "Resource management",
            "Competitive scenario",
            "Adapt to change",
            "Maintain stability",
            "Explore options",
            "Exploit knowledge",
            "Balance tradeoffs"
        ]
        
        # Define environment changes
        environments = [
            EnvironmentState(difficulty=0.3, volatility=0.2, resource_availability=0.9, competition_level=0.3),
            EnvironmentState(difficulty=0.5, volatility=0.4, resource_availability=0.7, competition_level=0.5),
            EnvironmentState(difficulty=0.7, volatility=0.6, resource_availability=0.5, competition_level=0.7),
            EnvironmentState(difficulty=0.4, volatility=0.3, resource_availability=0.8, competition_level=0.4),
        ]
        
        # Run pattern
        print("\n2. Running tasks with environmental changes...")
        summary = pattern.run(tasks, environments)
        
        print(f"\n3. Overall Results:")
        print(f"   Pattern: {summary['pattern']}")
        print(f"   Agents: {summary['agents']}")
        print(f"   Tasks: {summary['tasks']}")
        print(f"   Total actions: {summary['total_actions']}")
        
        print(f"\n4. Agent Statistics:")
        for name, agent in pattern.agents.items():
            stats = agent.get_statistics()
            print(f"\n   {name}:")
            for key, value in stats.items():
                if isinstance(value, float):
                    print(f"     {key}: {value:.4f}")
                else:
                    print(f"     {key}: {value}")
        
        print("\n✓ Agents successfully adapted to changing environments!")
    
        

    Agentic Architecture Pattern: Adaptive Agent

    Pattern 59: 59 Adaptive Agent – Line-by-Line Explanation

    Overview

    Pattern 59: Adaptive Agent

    Purpose: Dynamically adapt strategies and behaviors based on environmental feedback. Monitor performance, detect changes, and adjust strategies in real-time.


    Code Structure Analysis

    File Information

    • Pattern Number: 59
    • Pattern Name: 59 Adaptive Agent
    • File Path: patterns\learning_adaptation\59_adaptive_agent.py
    • Total Lines: 442

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 59: Adaptive Agent
    
    Purpose: Dynamically adapt strategies and behaviors based on environmental feedback.
    Monitor performance, detect changes, and adjust strategies in real-time.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 59 Adaptive Agent pattern.


    Imports Section

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

    Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime, timedelta: 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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    AdaptationTrigger (Line 17)

    class AdaptationTrigger(Enum):
        PERFORMANCE_DROP
        ENVIRONMENT_CHANGE
        TIME_BASED
        USER_FEEDBACK
    

    Purpose: Triggers for adaptation.

    Values: – PERFORMANCE_DROP: Represents performance drop – ENVIRONMENT_CHANGE: Represents environment change – TIME_BASED: Represents time based – USER_FEEDBACK: Represents user feedback

    StrategyType (Line 25)

    class StrategyType(Enum):
        CONSERVATIVE
        AGGRESSIVE
        BALANCED
        EXPLORATORY
        EXPLOITATIVE
    

    Purpose: Types of strategies available.

    Values: – CONSERVATIVE: Represents conservative – AGGRESSIVE: Represents aggressive – BALANCED: Represents balanced – EXPLORATORY: Represents exploratory – EXPLOITATIVE: Represents exploitative


    Classes

    AdaptationTrigger (Line 17)

    Description: Triggers for adaptation.

    StrategyType (Line 25)

    Description: Types of strategies available.

    EnvironmentState (Line 35)

    Description: Current state of the environment.

    PerformanceMetrics (Line 45)

    Description: Performance metrics for adaptation decisions.

    AdaptationEvent (Line 55)

    Description: Record of an adaptation event.

    AdaptiveAgent (Line 66)

    Description: Adaptive Agent – Dynamically adapt to changing environments.

    Monitors performance, detects environmental changes, and adjusts strategies in real-time to maintain optimal performance.

    Methods:

    __init__ (Line 74)
    def __init__(self, name, performance_threshold, adaptation_cooldown, window_size):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference – name: Identifier or label – performance_threshold: Parameter for performance_threshold – adaptation_cooldown: Parameter for adaptation_cooldown – window_size: Parameter for window_size

    act (Line 113)
    def act(self, task, environment):
        """Perform action with current strategy."""
    

    Purpose: Perform action with current strategy.

    Parameters: – self: Instance reference – task: Parameter for task – environment: Parameter for environment

    _get_strategy_params (Line 150)
    def _get_strategy_params(self):
        """Get parameters for current strategy."""
    

    Purpose: Get parameters for current strategy.

    Parameters: – self: Instance reference

    _simulate_action (Line 163)
    def _simulate_action(self, task, params):
        """Simulate action performance."""
    

    Purpose: Simulate action performance.

    Parameters: – self: Instance reference – task: Parameter for task – params: Parameter for params

    _check_adaptation_needed (Line 181)
    def _check_adaptation_needed(self):
        """Check if adaptation is needed."""
    

    Purpose: Check if adaptation is needed.

    Parameters: – self: Instance reference

    _adapt (Line 211)
    def _adapt(self, trigger, reason):
        """Adapt strategy based on trigger."""
    

    Purpose: Adapt strategy based on trigger.

    Parameters: – self: Instance reference – trigger: Parameter for trigger – reason: Parameter for reason

    _select_new_strategy (Line 239)
    def _select_new_strategy(self, trigger):
        """Select new strategy based on situation."""
    

    Purpose: Select new strategy based on situation.

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

    update_environment (Line 274)
    def update_environment(self, environment):
        """Update environment state."""
    

    Purpose: Update environment state.

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

    get_adaptation_summary (Line 281)
    def get_adaptation_summary(self):
        """Get summary of adaptations."""
    

    Purpose: Get summary of adaptations.

    Parameters: – self: Instance reference

    get_statistics (Line 305)
    def get_statistics(self):
        """Get comprehensive statistics."""
    

    Purpose: Get comprehensive statistics.

    Parameters: – self: Instance reference

    AdaptivePattern (Line 325)

    Description: Adaptive Pattern – Coordinate multiple adaptive agents.

    Manages agents that dynamically adjust to changing conditions.

    Methods:

    __init__ (Line 332)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 335)
    def register_agent(self, agent):
        """Register adaptive agent."""
    

    Purpose: Register adaptive agent.

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

    run (Line 340)
    def run(self, tasks, environment_changes):
        """Run adaptive agents through tasks."""
    

    Purpose: Run adaptive agents through tasks.

    Parameters: – self: Instance reference – tasks: Parameter for tasks – environment_changes: Parameter for environment_changes


    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 = AdaptationTrigger(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

  • Agentic Architecture Pattern: Evolutionary Agent

    
        """
    Pattern 58: Evolutionary Agent
    
    Purpose: Use genetic algorithms to evolve agent strategies and behaviors.
    Selection, crossover, and mutation to optimize agent parameters over generations.
    """
    
    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_openai import ChatOpenAI
    
    
    class SelectionStrategy(Enum):
        """Selection strategies for evolution."""
        TOURNAMENT = "tournament"  # Tournament selection
        ROULETTE = "roulette"  # Roulette wheel (fitness-proportionate)
        RANK = "rank"  # Rank-based selection
        ELITISM = "elitism"  # Keep best individuals
    
    
    @dataclass
    class Gene:
        """A gene representing part of an agent's strategy."""
        name: str
        value: float
        min_value: float = 0.0
        max_value: float = 1.0
        mutation_rate: float = 0.1
    
    
    @dataclass
    class Genome:
        """A genome representing an agent's complete strategy."""
        id: str
        genes: Dict[str, Gene]
        fitness: float = 0.0
        generation: int = 0
        parent_ids: List[str] = field(default_factory=list)
    
    
    @dataclass
    class GenerationStats:
        """Statistics for a generation."""
        generation: int
        population_size: int
        best_fitness: float
        avg_fitness: float
        worst_fitness: float
        diversity: float
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class EvolutionaryAgent:
        """
        Evolutionary Agent - Evolve strategies using genetic algorithms.
        
        Uses selection, crossover, and mutation to optimize agent behaviors
        over multiple generations. LLM assists in evaluating fitness.
        """
        
        def __init__(self,
                     name: str,
                     gene_names: List[str],
                     population_size: int = 20,
                     selection_strategy: SelectionStrategy = SelectionStrategy.TOURNAMENT,
                     mutation_rate: float = 0.1,
                     crossover_rate: float = 0.7,
                     elitism_count: int = 2):
            self.name = name
            self.gene_names = gene_names
            self.population_size = population_size
            self.selection_strategy = selection_strategy
            self.mutation_rate = mutation_rate
            self.crossover_rate = crossover_rate
            self.elitism_count = elitism_count
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Population
            self.population: List[Genome] = []
            self.current_generation: int = 0
            self.best_genome: Optional[Genome] = None
            
            # History
            self.generation_stats: List[GenerationStats] = []
            self.evolution_history: List[Genome] = []
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "total_generations": 0,
                "total_evaluations": 0,
                "best_fitness_ever": 0.0,
                "avg_fitness_improvement": 0.0
            }
        
        def initialize_population(self):
            """Initialize random population."""
            print(f"   {self.name}: Initializing population of {self.population_size}...")
            
            self.population = []
            for i in range(self.population_size):
                genes = {}
                for gene_name in self.gene_names:
                    genes[gene_name] = Gene(
                        name=gene_name,
                        value=random.uniform(0.0, 1.0),
                        mutation_rate=self.mutation_rate
                    )
                
                genome = Genome(
                    id=f"gen0_ind{i}",
                    genes=genes,
                    generation=0
                )
                self.population.append(genome)
            
            print(f"   Population initialized with {len(self.population)} individuals")
        
        def evaluate_fitness(self, genome: Genome, task: str) -> float:
            """Evaluate fitness of a genome for a task."""
            # Simulate fitness based on gene values
            # In practice, this would run the agent with these parameters
            
            # Simple fitness function: weighted sum of genes
            fitness = 0.0
            weights = {"learning_rate": 0.3, "exploration": 0.2, "memory_size": 0.2,
                      "attention": 0.15, "creativity": 0.15}
            
            for gene_name, gene in genome.genes.items():
                weight = weights.get(gene_name, 1.0 / len(genome.genes))
                fitness += gene.value * weight
            
            # Add some randomness to simulate noisy evaluation
            fitness *= random.uniform(0.8, 1.2)
            fitness = max(0.0, min(1.0, fitness))
            
            self.statistics["total_evaluations"] += 1
            
            return fitness
        
        def evaluate_population(self, task: str):
            """Evaluate fitness for entire population."""
            for genome in self.population:
                genome.fitness = self.evaluate_fitness(genome, task)
            
            # Sort by fitness
            self.population.sort(key=lambda g: g.fitness, reverse=True)
            
            # Track best genome
            if not self.best_genome or self.population[0].fitness > self.best_genome.fitness:
                self.best_genome = self.population[0]
                self.statistics["best_fitness_ever"] = self.best_genome.fitness
        
        def select_parents(self) -> Tuple[Genome, Genome]:
            """Select two parents for reproduction."""
            if self.selection_strategy == SelectionStrategy.TOURNAMENT:
                return self._tournament_selection(), self._tournament_selection()
            elif self.selection_strategy == SelectionStrategy.ROULETTE:
                return self._roulette_selection(), self._roulette_selection()
            elif self.selection_strategy == SelectionStrategy.RANK:
                return self._rank_selection(), self._rank_selection()
            else:  # ELITISM
                # Select from top half
                top_half = self.population[:len(self.population)//2]
                return random.choice(top_half), random.choice(top_half)
        
        def _tournament_selection(self, tournament_size: int = 3) -> Genome:
            """Tournament selection."""
            tournament = random.sample(self.population, min(tournament_size, len(self.population)))
            return max(tournament, key=lambda g: g.fitness)
        
        def _roulette_selection(self) -> Genome:
            """Roulette wheel selection."""
            total_fitness = sum(g.fitness for g in self.population)
            if total_fitness == 0:
                return random.choice(self.population)
            
            pick = random.uniform(0, total_fitness)
            current = 0
            for genome in self.population:
                current += genome.fitness
                if current > pick:
                    return genome
            return self.population[-1]
        
        def _rank_selection(self) -> Genome:
            """Rank-based selection."""
            # Already sorted by fitness
            ranks = list(range(len(self.population), 0, -1))
            total_rank = sum(ranks)
            pick = random.uniform(0, total_rank)
            
            current = 0
            for i, genome in enumerate(self.population):
                current += ranks[i]
                if current > pick:
                    return genome
            return self.population[-1]
        
        def crossover(self, parent1: Genome, parent2: Genome) -> Tuple[Genome, Genome]:
            """Perform crossover between two parents."""
            child1_genes = {}
            child2_genes = {}
            
            for gene_name in self.gene_names:
                if random.random() < self.crossover_rate:
                    # Crossover: swap genes
                    child1_genes[gene_name] = Gene(
                        name=gene_name,
                        value=parent2.genes[gene_name].value,
                        mutation_rate=self.mutation_rate
                    )
                    child2_genes[gene_name] = Gene(
                        name=gene_name,
                        value=parent1.genes[gene_name].value,
                        mutation_rate=self.mutation_rate
                    )
                else:
                    # No crossover: copy parent genes
                    child1_genes[gene_name] = Gene(
                        name=gene_name,
                        value=parent1.genes[gene_name].value,
                        mutation_rate=self.mutation_rate
                    )
                    child2_genes[gene_name] = Gene(
                        name=gene_name,
                        value=parent2.genes[gene_name].value,
                        mutation_rate=self.mutation_rate
                    )
            
            child1 = Genome(
                id=f"gen{self.current_generation + 1}_child",
                genes=child1_genes,
                generation=self.current_generation + 1,
                parent_ids=[parent1.id, parent2.id]
            )
            
            child2 = Genome(
                id=f"gen{self.current_generation + 1}_child",
                genes=child2_genes,
                generation=self.current_generation + 1,
                parent_ids=[parent1.id, parent2.id]
            )
            
            return child1, child2
        
        def mutate(self, genome: Genome):
            """Mutate a genome."""
            for gene in genome.genes.values():
                if random.random() < gene.mutation_rate:
                    # Gaussian mutation
                    mutation = random.gauss(0, 0.1)
                    gene.value = max(gene.min_value, min(gene.max_value, gene.value + mutation))
        
        def evolve_generation(self, task: str):
            """Evolve one generation."""
            self.current_generation += 1
            
            # Evaluate current population
            self.evaluate_population(task)
            
            # Record statistics
            fitnesses = [g.fitness for g in self.population]
            diversity = self._calculate_diversity()
            
            stats = GenerationStats(
                generation=self.current_generation,
                population_size=len(self.population),
                best_fitness=max(fitnesses),
                avg_fitness=sum(fitnesses) / len(fitnesses),
                worst_fitness=min(fitnesses),
                diversity=diversity
            )
            self.generation_stats.append(stats)
            
            # Create new population
            new_population = []
            
            # Elitism: keep best individuals
            new_population.extend(self.population[:self.elitism_count])
            
            # Generate offspring
            while len(new_population) < self.population_size:
                parent1, parent2 = self.select_parents()
                child1, child2 = self.crossover(parent1, parent2)
                
                self.mutate(child1)
                self.mutate(child2)
                
                new_population.append(child1)
                if len(new_population)  1:
                improvement = (self.generation_stats[-1].avg_fitness - 
                              self.generation_stats[-2].avg_fitness)
                self.statistics["avg_fitness_improvement"] = improvement
        
        def _calculate_diversity(self) -> float:
            """Calculate population diversity."""
            if len(self.population)  0 else 0.0
        
        def run_evolution(self, task: str, generations: int):
            """Run evolution for multiple generations."""
            print(f"\n   {self.name}: Running evolution for {generations} generations...")
            
            if not self.population:
                self.initialize_population()
            
            for gen in range(generations):
                self.evolve_generation(task)
                
                if (gen + 1) % max(1, generations // 5) == 0:
                    stats = self.generation_stats[-1]
                    print(f"   Generation {self.current_generation}: "
                          f"Best={stats.best_fitness:.3f}, "
                          f"Avg={stats.avg_fitness:.3f}, "
                          f"Diversity={stats.diversity:.3f}")
            
            print(f"   Evolution complete! Best fitness: {self.best_genome.fitness:.3f}")
        
        def get_best_strategy(self) -> Dict[str, float]:
            """Get the best evolved strategy."""
            if not self.best_genome:
                return {}
            
            return {gene_name: gene.value 
                    for gene_name, gene in self.best_genome.genes.items()}
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive statistics."""
            return {
                "name": self.name,
                "selection_strategy": self.selection_strategy.value,
                "population_size": self.population_size,
                "current_generation": self.current_generation,
                "best_fitness": self.best_genome.fitness if self.best_genome else 0.0,
                **self.statistics
            }
    
    
    class EvolutionaryPattern:
        """
        Evolutionary Pattern - Evolve agent strategies via genetic algorithms.
        
        Coordinates multiple evolutionary agents optimizing different strategies.
        """
        
        def __init__(self):
            self.agents: Dict[str, EvolutionaryAgent] = {}
            self.tasks: List[str] = []
        
        def register_agent(self, agent: EvolutionaryAgent):
            """Register evolutionary agent."""
            self.agents[agent.name] = agent
            print(f"   Registered agent: {agent.name} ({agent.selection_strategy.value})")
        
        def add_task(self, task: str):
            """Add optimization task."""
            self.tasks.append(task)
        
        def evolve_all(self, generations: int):
            """Evolve all agents."""
            print("\n   Evolving all agents...")
            
            for agent in self.agents.values():
                for task in self.tasks:
                    agent.run_evolution(task, generations)
        
        def run(self, tasks: List[str], generations: int = 20) -> Dict[str, Any]:
            """Run evolutionary optimization."""
            print("=== Evolutionary Agent Pattern Demo ===\n")
            
            # Add tasks
            print("1. Adding tasks...")
            for task in tasks:
                self.add_task(task)
            
            # Evolve all agents
            print("\n2. Running evolution...")
            self.evolve_all(generations)
            
            # Get best strategies
            print("\n3. Extracting best strategies...")
            best_strategies = {name: agent.get_best_strategy() 
                              for name, agent in self.agents.items()}
            
            # Get statistics
            stats = {name: agent.get_statistics() 
                    for name, agent in self.agents.items()}
            
            return {
                "pattern": "Evolutionary",
                "agents": len(self.agents),
                "tasks": len(tasks),
                "generations": generations,
                "best_strategies": best_strategies,
                "statistics": stats
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Evolutionary Agent Pattern Demo ===\n")
        
        # Create pattern
        pattern = EvolutionaryPattern()
        
        # Define gene names (strategy parameters to evolve)
        gene_names = ["learning_rate", "exploration", "memory_size", "attention", "creativity"]
        
        # Create agents with different selection strategies
        print("1. Creating evolutionary agents...")
        tournament_agent = EvolutionaryAgent(
            name="TournamentAgent",
            gene_names=gene_names,
            population_size=30,
            selection_strategy=SelectionStrategy.TOURNAMENT,
            mutation_rate=0.1
        )
        pattern.register_agent(tournament_agent)
        
        roulette_agent = EvolutionaryAgent(
            name="RouletteAgent",
            gene_names=gene_names,
            population_size=30,
            selection_strategy=SelectionStrategy.ROULETTE,
            mutation_rate=0.15
        )
        pattern.register_agent(roulette_agent)
        
        # Define tasks
        tasks = ["optimize_learning_strategy", "maximize_performance"]
        
        # Run evolution
        print("\n2. Running evolutionary optimization...")
        summary = pattern.run(tasks, generations=30)
        
        print(f"\n3. Results:")
        print(f"   Agents: {summary['agents']}")
        print(f"   Tasks: {summary['tasks']}")
        print(f"   Generations: {summary['generations']}")
        
        print(f"\n4. Best Evolved Strategies:")
        for agent_name, strategy in summary['best_strategies'].items():
            print(f"\n   {agent_name}:")
            for param, value in strategy.items():
                print(f"     {param}: {value:.3f}")
        
        print(f"\n5. Evolution Statistics:")
        for agent_name, stats in summary['statistics'].items():
            print(f"\n   {agent_name}:")
            for key, value in stats.items():
                if key != "name":
                    if isinstance(value, float):
                        print(f"     {key}: {value:.4f}")
                    else:
                        print(f"     {key}: {value}")
        
        print("\n✓ Evolutionary algorithms optimized agent strategies!")
    
        

    Agentic Architecture Pattern: Evolutionary Agent

    Pattern 58: 58 Evolutionary Agent – Line-by-Line Explanation

    Overview

    Pattern 58: Evolutionary Agent

    Purpose: Use genetic algorithms to evolve agent strategies and behaviors. Selection, crossover, and mutation to optimize agent parameters over generations.


    Code Structure Analysis

    File Information

    • Pattern Number: 58
    • Pattern Name: 58 Evolutionary Agent
    • File Path: patterns\learning_adaptation\58_evolutionary_agent.py
    • Total Lines: 486

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 58: Evolutionary Agent
    
    Purpose: Use genetic algorithms to evolve agent strategies and behaviors.
    Selection, crossover, and mutation to optimize agent parameters over generations.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 58 Evolutionary Agent 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_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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    SelectionStrategy (Line 17)

    class SelectionStrategy(Enum):
        TOURNAMENT
        ROULETTE
        RANK
        ELITISM
    

    Purpose: Selection strategies for evolution.

    Values: – TOURNAMENT: Represents tournament – ROULETTE: Represents roulette – RANK: Represents rank – ELITISM: Represents elitism


    Classes

    SelectionStrategy (Line 17)

    Description: Selection strategies for evolution.

    Gene (Line 26)

    Description: A gene representing part of an agent’s strategy.

    Genome (Line 36)

    Description: A genome representing an agent’s complete strategy.

    GenerationStats (Line 46)

    Description: Statistics for a generation.

    EvolutionaryAgent (Line 57)

    Description: Evolutionary Agent – Evolve strategies using genetic algorithms.

    Uses selection, crossover, and mutation to optimize agent behaviors over multiple generations. LLM assists in evaluating fitness.

    Methods:

    __init__ (Line 65)
    def __init__(self, name, gene_names, population_size, selection_strategy, mutation_rate, crossover_rate, elitism_count):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference – name: Identifier or label – gene_names: Parameter for gene_names – population_size: Parameter for population_size – selection_strategy: Parameter for selection_strategy – mutation_rate: Parameter for mutation_rate – crossover_rate: Parameter for crossover_rate – elitism_count: Parameter for elitism_count

    initialize_population (Line 99)
    def initialize_population(self):
        """Initialize random population."""
    

    Purpose: Initialize random population.

    Parameters: – self: Instance reference

    evaluate_fitness (Line 122)
    def evaluate_fitness(self, genome, task):
        """Evaluate fitness of a genome for a task."""
    

    Purpose: Evaluate fitness of a genome for a task.

    Parameters: – self: Instance reference – genome: Parameter for genome – task: Parameter for task

    evaluate_population (Line 144)
    def evaluate_population(self, task):
        """Evaluate fitness for entire population."""
    

    Purpose: Evaluate fitness for entire population.

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

    select_parents (Line 157)
    def select_parents(self):
        """Select two parents for reproduction."""
    

    Purpose: Select two parents for reproduction.

    Parameters: – self: Instance reference

    _tournament_selection (Line 170)
    def _tournament_selection(self, tournament_size):
        """Tournament selection."""
    

    Purpose: Tournament selection.

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

    _roulette_selection (Line 175)
    def _roulette_selection(self):
        """Roulette wheel selection."""
    

    Purpose: Roulette wheel selection.

    Parameters: – self: Instance reference

    _rank_selection (Line 189)
    def _rank_selection(self):
        """Rank-based selection."""
    

    Purpose: Rank-based selection.

    Parameters: – self: Instance reference

    crossover (Line 203)
    def crossover(self, parent1, parent2):
        """Perform crossover between two parents."""
    

    Purpose: Perform crossover between two parents.

    Parameters: – self: Instance reference – parent1: Parameter for parent1 – parent2: Parameter for parent2

    mutate (Line 250)
    def mutate(self, genome):
        """Mutate a genome."""
    

    Purpose: Mutate a genome.

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

    evolve_generation (Line 258)
    def evolve_generation(self, task):
        """Evolve one generation."""
    

    Purpose: Evolve one generation.

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

    _calculate_diversity (Line 306)
    def _calculate_diversity(self):
        """Calculate population diversity."""
    

    Purpose: Calculate population diversity.

    Parameters: – self: Instance reference

    run_evolution (Line 328)
    def run_evolution(self, task, generations):
        """Run evolution for multiple generations."""
    

    Purpose: Run evolution for multiple generations.

    Parameters: – self: Instance reference – task: Parameter for task – generations: Parameter for generations

    get_best_strategy (Line 347)
    def get_best_strategy(self):
        """Get the best evolved strategy."""
    

    Purpose: Get the best evolved strategy.

    Parameters: – self: Instance reference

    get_statistics (Line 355)
    def get_statistics(self):
        """Get comprehensive statistics."""
    

    Purpose: Get comprehensive statistics.

    Parameters: – self: Instance reference

    EvolutionaryPattern (Line 367)

    Description: Evolutionary Pattern – Evolve agent strategies via genetic algorithms.

    Coordinates multiple evolutionary agents optimizing different strategies.

    Methods:

    __init__ (Line 374)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 378)
    def register_agent(self, agent):
        """Register evolutionary agent."""
    

    Purpose: Register evolutionary agent.

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

    add_task (Line 383)
    def add_task(self, task):
        """Add optimization task."""
    

    Purpose: Add optimization task.

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

    evolve_all (Line 387)
    def evolve_all(self, generations):
        """Evolve all agents."""
    

    Purpose: Evolve all agents.

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

    run (Line 395)
    def run(self, tasks, generations):
        """Run evolutionary optimization."""
    

    Purpose: Run evolutionary optimization.

    Parameters: – self: Instance reference – tasks: Parameter for tasks – generations: Parameter for generations


    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 = SelectionStrategy(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

  • Agentic Architecture Pattern: Meta Learning

    
        """
    Pattern 57: Meta-Learning
    
    Purpose: Learn how to learn - optimize learning algorithms and strategies themselves.
    "Learning to learn" by improving the learning process across multiple tasks.
    """
    
    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_openai import ChatOpenAI
    
    
    class MetaStrategy(Enum):
        """Meta-learning strategies."""
        MAML = "maml"  # Model-Agnostic Meta-Learning
        REPTILE = "reptile"  # Simplified MAML
        LEARNING_TO_COMPARE = "learning_to_compare"  # Siamese networks
        OPTIMIZATION_AS_MODEL = "optimization_as_model"  # Learn optimizer
    
    
    @dataclass
    class Task:
        """A learning task."""
        id: str
        name: str
        domain: str
        examples: List[Dict[str, Any]] = field(default_factory=list)
        difficulty: float = 0.5
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class LearningExperience:
        """Experience from learning a task."""
        task_id: str
        strategy_used: str
        initial_performance: float
        final_performance: float
        learning_rate: float
        steps_taken: int
        insights: List[str] = field(default_factory=list)
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class MetaKnowledge:
        """Meta-level knowledge about learning."""
        strategy: str
        effectiveness: float
        best_domains: List[str]
        optimal_learning_rate: float
        optimal_batch_size: int
        learned_from_tasks: int
        confidence: float
    
    
    class MetaLearningAgent:
        """
        Meta-Learning Agent - Learn how to learn.
        
        Optimizes the learning process itself by learning from experience
        across multiple tasks. Discovers optimal learning strategies,
        hyperparameters, and approaches.
        """
        
        def __init__(self, 
                     name: str,
                     strategy: MetaStrategy = MetaStrategy.MAML):
            self.name = name
            self.strategy = strategy
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Meta-knowledge base
            self.learning_experiences: List[LearningExperience] = []
            self.meta_knowledge: Dict[str, MetaKnowledge] = {}
            
            # Learned meta-parameters
            self.optimal_learning_rate: float = 0.01
            self.optimal_batch_size: int = 32
            self.adaptation_steps: int = 5
            
            # Performance tracking
            self.tasks_learned: int = 0
            self.meta_updates: int = 0
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "avg_initial_performance": 0.0,
                "avg_final_performance": 0.0,
                "avg_improvement": 0.0,
                "best_strategy": None
            }
        
        def meta_train(self, tasks: List[Task]):
            """Meta-train on multiple tasks to learn how to learn."""
            print(f"\n   {self.name}: Meta-training on {len(tasks)} tasks...")
            
            for task in tasks:
                # Learn the task
                experience = self._learn_task(task)
                self.learning_experiences.append(experience)
                self.tasks_learned += 1
                
                # Update meta-knowledge
                self._update_meta_knowledge(experience)
                self.meta_updates += 1
                
                if self.tasks_learned % 5 == 0:
                    print(f"   Meta-trained on {self.tasks_learned} tasks...")
            
            # Extract meta-level insights
            self._extract_meta_insights()
            
            print(f"   Meta-training complete! Learned from {self.tasks_learned} tasks")
        
        def _learn_task(self, task: Task) -> LearningExperience:
            """Learn a single task and record the experience."""
            # Simulate initial performance
            initial_perf = random.uniform(0.3, 0.5)
            
            # Apply current meta-knowledge
            learning_rate = self.optimal_learning_rate
            batch_size = self.optimal_batch_size
            
            # Simulate learning with current strategy
            final_perf = initial_perf
            steps = 0
            
            for step in range(self.adaptation_steps):
                # Simulate improvement based on strategy effectiveness
                if self.strategy == MetaStrategy.MAML:
                    improvement = 0.08 * (1.0 - final_perf)  # Fast adaptation
                elif self.strategy == MetaStrategy.REPTILE:
                    improvement = 0.06 * (1.0 - final_perf)
                elif self.strategy == MetaStrategy.LEARNING_TO_COMPARE:
                    improvement = 0.07 * (1.0 - final_perf)
                else:  # OPTIMIZATION_AS_MODEL
                    improvement = 0.09 * (1.0 - final_perf)
                
                # Add some randomness
                improvement *= random.uniform(0.8, 1.2)
                final_perf = min(0.95, final_perf + improvement)
                steps += 1
                
                # Early stopping if good enough
                if final_perf > 0.85:
                    break
            
            # Generate insights using LLM
            insights = self._generate_learning_insights(task, initial_perf, final_perf, steps)
            
            return LearningExperience(
                task_id=task.id,
                strategy_used=self.strategy.value,
                initial_performance=initial_perf,
                final_performance=final_perf,
                learning_rate=learning_rate,
                steps_taken=steps,
                insights=insights
            )
        
        def _generate_learning_insights(self, 
                                        task: Task,
                                        initial_perf: float,
                                        final_perf: float,
                                        steps: int) -> List[str]:
            """Generate insights about the learning process."""
            prompt = ChatPromptTemplate.from_messages([
                ("system", "You are a meta-learning expert analyzing learning experiences. "
                          "Provide 2-3 brief insights about what made this learning process effective or ineffective."),
                ("human", f"Task: {task.name} (domain: {task.domain})\n"
                         f"Strategy: {self.strategy.value}\n"
                         f"Initial Performance: {initial_perf:.2%}\n"
                         f"Final Performance: {final_perf:.2%}\n"
                         f"Steps: {steps}\n"
                         f"Improvement: {final_perf - initial_perf:.2%}\n\n"
                         f"Provide 2-3 brief insights (one per line):")
            ])
            
            chain = prompt | self.llm
            insights = []
            
            try:
                response = chain.invoke({})
                content = response.content if isinstance(response.content, str) else str(response.content)
                lines = content.strip().split('\n')
                insights = [line.strip('- ').strip() for line in lines if line.strip()][:3]
            except:
                insights = [
                    f"Improved from {initial_perf:.2%} to {final_perf:.2%}",
                    f"Required {steps} adaptation steps",
                    f"Strategy {self.strategy.value} was effective"
                ]
            
            return insights
        
        def _update_meta_knowledge(self, experience: LearningExperience):
            """Update meta-knowledge based on learning experience."""
            strategy_key = experience.strategy_used
            
            if strategy_key not in self.meta_knowledge:
                self.meta_knowledge[strategy_key] = MetaKnowledge(
                    strategy=strategy_key,
                    effectiveness=0.0,
                    best_domains=[],
                    optimal_learning_rate=self.optimal_learning_rate,
                    optimal_batch_size=self.optimal_batch_size,
                    learned_from_tasks=0,
                    confidence=0.0
                )
            
            meta = self.meta_knowledge[strategy_key]
            
            # Update effectiveness (moving average)
            improvement = experience.final_performance - experience.initial_performance
            meta.effectiveness = (
                0.9 * meta.effectiveness + 0.1 * improvement
            ) if meta.learned_from_tasks > 0 else improvement
            
            meta.learned_from_tasks += 1
            meta.confidence = min(1.0, meta.learned_from_tasks / 20.0)
            
            # Optimize learning rate based on experience
            if improvement > 0.3:
                # Good improvement, slightly increase LR
                self.optimal_learning_rate = min(0.1, self.optimal_learning_rate * 1.05)
            elif improvement  Dict[str, Any]:
            """Rapidly adapt to a new task using meta-learned knowledge."""
            if not self.meta_knowledge:
                print(f"   {self.name}: No meta-knowledge yet. Learning from scratch...")
                experience = self._learn_task(task)
                return {
                    "task": task.name,
                    "initial_performance": experience.initial_performance,
                    "final_performance": experience.final_performance,
                    "steps": experience.steps_taken,
                    "used_meta_knowledge": False
                }
            
            print(f"\n   {self.name}: Adapting to new task '{task.name}' using meta-knowledge...")
            
            # Use meta-knowledge for faster adaptation
            best_meta = max(self.meta_knowledge.values(), key=lambda m: m.effectiveness)
            
            # Apply optimal hyperparameters
            old_lr = self.optimal_learning_rate
            old_steps = self.adaptation_steps
            
            self.optimal_learning_rate = best_meta.optimal_learning_rate
            self.adaptation_steps = max(2, self.adaptation_steps // 2)  # Faster adaptation
            
            # Learn with meta-knowledge
            experience = self._learn_task(task)
            
            # Restore
            self.optimal_learning_rate = old_lr
            self.adaptation_steps = old_steps
            
            print(f"   Adapted in {experience.steps_taken} steps "
                  f"(improvement: {experience.final_performance - experience.initial_performance:.2%})")
            
            return {
                "task": task.name,
                "initial_performance": experience.initial_performance,
                "final_performance": experience.final_performance,
                "steps": experience.steps_taken,
                "used_meta_knowledge": True,
                "strategy": best_meta.strategy,
                "insights": experience.insights
            }
        
        def get_meta_insights(self) -> str:
            """Get human-readable meta-learning insights."""
            if not self.learning_experiences:
                return "No learning experiences yet."
            
            insights = []
            insights.append(f"Meta-learned from {self.tasks_learned} tasks using {self.strategy.value}")
            insights.append(f"Average improvement: {self.statistics['avg_improvement']:.2%}")
            insights.append(f"Optimal learning rate: {self.optimal_learning_rate:.4f}")
            insights.append(f"Optimal adaptation steps: {self.adaptation_steps}")
            
            if self.meta_knowledge:
                insights.append(f"\nMeta-knowledge acquired:")
                for strategy, meta in self.meta_knowledge.items():
                    insights.append(f"  - {strategy}: effectiveness {meta.effectiveness:.2%} "
                                  f"(confidence: {meta.confidence:.2%})")
            
            return "\n".join(insights)
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive statistics."""
            return {
                "name": self.name,
                "strategy": self.strategy.value,
                "tasks_learned": self.tasks_learned,
                "meta_updates": self.meta_updates,
                "optimal_learning_rate": self.optimal_learning_rate,
                "optimal_batch_size": self.optimal_batch_size,
                "adaptation_steps": self.adaptation_steps,
                **self.statistics
            }
    
    
    class MetaLearningPattern:
        """
        Meta-Learning Pattern - Learn how to learn.
        
        Coordinates meta-learning agents to optimize learning strategies
        across multiple tasks.
        """
        
        def __init__(self):
            self.agents: Dict[str, MetaLearningAgent] = {}
            self.training_tasks: List[Task] = []
            self.test_tasks: List[Task] = []
        
        def register_agent(self, agent: MetaLearningAgent):
            """Register meta-learning agent."""
            self.agents[agent.name] = agent
            print(f"   Registered agent: {agent.name} ({agent.strategy.value})")
        
        def add_training_tasks(self, tasks: List[Task]):
            """Add tasks for meta-training."""
            self.training_tasks.extend(tasks)
            print(f"   Added {len(tasks)} training tasks (total: {len(self.training_tasks)})")
        
        def add_test_tasks(self, tasks: List[Task]):
            """Add tasks for testing adaptation."""
            self.test_tasks.extend(tasks)
            print(f"   Added {len(tasks)} test tasks")
        
        def meta_train_all(self):
            """Meta-train all agents."""
            print("\n   Meta-training all agents...")
            for agent in self.agents.values():
                agent.meta_train(self.training_tasks)
        
        def test_adaptation(self) -> Dict[str, List[Dict[str, Any]]]:
            """Test rapid adaptation on new tasks."""
            print("\n   Testing adaptation on new tasks...")
            
            results = {}
            for agent_name, agent in self.agents.items():
                agent_results = []
                for task in self.test_tasks:
                    result = agent.adapt_to_new_task(task)
                    agent_results.append(result)
                results[agent_name] = agent_results
            
            return results
        
        def run(self, 
                training_tasks: List[Task],
                test_tasks: List[Task]) -> Dict[str, Any]:
            """Run meta-learning pipeline."""
            print("=== Meta-Learning Pattern Demo ===\n")
            
            # Add tasks
            print("1. Adding tasks...")
            self.add_training_tasks(training_tasks)
            self.add_test_tasks(test_tasks)
            
            # Meta-train
            print("\n2. Meta-training...")
            self.meta_train_all()
            
            # Test adaptation
            print("\n3. Testing rapid adaptation...")
            adaptation_results = self.test_adaptation()
            
            # Get insights
            print("\n4. Extracting meta-insights...")
            insights = {name: agent.get_meta_insights() 
                       for name, agent in self.agents.items()}
            
            # Get statistics
            stats = {name: agent.get_statistics() 
                    for name, agent in self.agents.items()}
            
            return {
                "pattern": "MetaLearning",
                "agents": len(self.agents),
                "training_tasks": len(training_tasks),
                "test_tasks": len(test_tasks),
                "adaptation_results": adaptation_results,
                "insights": insights,
                "statistics": stats
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Meta-Learning Pattern Demo ===\n")
        
        # Create pattern
        pattern = MetaLearningPattern()
        
        # Create agents with different strategies
        print("1. Creating meta-learning agents...")
        maml_agent = MetaLearningAgent(
            name="MAML_Agent",
            strategy=MetaStrategy.MAML
        )
        pattern.register_agent(maml_agent)
        
        reptile_agent = MetaLearningAgent(
            name="Reptile_Agent",
            strategy=MetaStrategy.REPTILE
        )
        pattern.register_agent(reptile_agent)
        
        # Create training tasks (diverse domains)
        print("\n2. Creating training tasks...")
        training_tasks = [
            Task(id="train_1", name="Sentiment Classification", domain="text", 
                 examples=[{"text": "good", "label": "pos"}]),
            Task(id="train_2", name="Spam Detection", domain="text",
                 examples=[{"text": "buy now", "label": "spam"}]),
            Task(id="train_3", name="Image Classification", domain="vision",
                 examples=[{"image": "cat.jpg", "label": "cat"}]),
            Task(id="train_4", name="Language Translation", domain="text",
                 examples=[{"en": "hello", "fr": "bonjour"}]),
            Task(id="train_5", name="Recommendation", domain="recommendation",
                 examples=[{"user": "1", "item": "A", "rating": 5}]),
            Task(id="train_6", name="Named Entity Recognition", domain="text",
                 examples=[{"text": "John lives in NYC", "entities": ["John", "NYC"]}]),
            Task(id="train_7", name="Question Answering", domain="text",
                 examples=[{"q": "What is AI?", "a": "Artificial Intelligence"}]),
            Task(id="train_8", name="Regression", domain="numeric",
                 examples=[{"x": 1, "y": 2}]),
        ]
        
        # Create test tasks (new but similar domains)
        print("\n3. Creating test tasks...")
        test_tasks = [
            Task(id="test_1", name="Review Classification", domain="text",
                 examples=[{"text": "amazing product", "label": "positive"}]),
            Task(id="test_2", name="Intent Detection", domain="text",
                 examples=[{"text": "book a flight", "label": "booking"}]),
        ]
        
        # Run meta-learning
        print("\n4. Running meta-learning pipeline...")
        summary = pattern.run(training_tasks, test_tasks)
        
        print(f"\n5. Results:")
        print(f"   Agents: {summary['agents']}")
        print(f"   Training Tasks: {summary['training_tasks']}")
        print(f"   Test Tasks: {summary['test_tasks']}")
        
        print(f"\n6. Adaptation Results:")
        for agent_name, results in summary['adaptation_results'].items():
            print(f"\n   {agent_name}:")
            for result in results:
                print(f"     {result['task']}: {result['steps']} steps, "
                      f"improvement: {result['final_performance'] - result['initial_performance']:.2%}")
        
        print(f"\n7. Meta-Insights:")
        for agent_name, insight in summary['insights'].items():
            print(f"\n   {agent_name}:")
            for line in insight.split('\n'):
                print(f"   {line}")
        
        print(f"\n8. Statistics:")
        for agent_name, stats in summary['statistics'].items():
            print(f"\n   {agent_name}:")
            for key, value in stats.items():
                if key != "name":
                    if isinstance(value, float):
                        print(f"     {key}: {value:.4f}")
                    else:
                        print(f"     {key}: {value}")
        
        print("\n✓ Meta-learning enabled rapid adaptation to new tasks!")
    
        

    Agentic Architecture Pattern: Meta Learning

    Pattern 57: 57 Meta Learning – Line-by-Line Explanation

    Overview

    Pattern 57: Meta-Learning

    Purpose: Learn how to learn – optimize learning algorithms and strategies themselves. “Learning to learn” by improving the learning process across multiple tasks.


    Code Structure Analysis

    File Information

    • Pattern Number: 57
    • Pattern Name: 57 Meta Learning
    • File Path: patterns\learning_adaptation\57_meta_learning.py
    • Total Lines: 504

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 57: Meta-Learning
    
    Purpose: Learn how to learn - optimize learning algorithms and strategies themselves.
    "Learning to learn" by improving the learning process across multiple tasks.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 57 Meta 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_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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    MetaStrategy (Line 17)

    class MetaStrategy(Enum):
        MAML
        REPTILE
        LEARNING_TO_COMPARE
        OPTIMIZATION_AS_MODEL
    

    Purpose: Meta-learning strategies.

    Values: – MAML: Represents maml – REPTILE: Represents reptile – LEARNING_TO_COMPARE: Represents learning to compare – OPTIMIZATION_AS_MODEL: Represents optimization as model


    Classes

    MetaStrategy (Line 17)

    Description: Meta-learning strategies.

    Task (Line 26)

    Description: A learning task.

    LearningExperience (Line 37)

    Description: Experience from learning a task.

    MetaKnowledge (Line 50)

    Description: Meta-level knowledge about learning.

    MetaLearningAgent (Line 61)

    Description: Meta-Learning Agent – Learn how to learn.

    Optimizes the learning process itself by learning from experience across multiple tasks. Discovers optimal learning strategies, hyperparameters, and approaches.

    Methods:

    __init__ (Line 70)
    def __init__(self, name, strategy):
        """No method docstring."""
    

    Purpose: No method docstring.

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

    meta_train (Line 98)
    def meta_train(self, tasks):
        """Meta-train on multiple tasks to learn how to learn."""
    

    Purpose: Meta-train on multiple tasks to learn how to learn.

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

    _learn_task (Line 120)
    def _learn_task(self, task):
        """Learn a single task and record the experience."""
    

    Purpose: Learn a single task and record the experience.

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

    _generate_learning_insights (Line 166)
    def _generate_learning_insights(self, task, initial_perf, final_perf, steps):
        """Generate insights about the learning process."""
    

    Purpose: Generate insights about the learning process.

    Parameters: – self: Instance reference – task: Parameter for task – initial_perf: Parameter for initial_perf – final_perf: Parameter for final_perf – steps: Parameter for steps

    _update_meta_knowledge (Line 201)
    def _update_meta_knowledge(self, experience):
        """Update meta-knowledge based on learning experience."""
    

    Purpose: Update meta-knowledge based on learning experience.

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

    _extract_meta_insights (Line 235)
    def _extract_meta_insights(self):
        """Extract high-level meta-learning insights."""
    

    Purpose: Extract high-level meta-learning insights.

    Parameters: – self: Instance reference

    adapt_to_new_task (Line 255)
    def adapt_to_new_task(self, task):
        """Rapidly adapt to a new task using meta-learned knowledge."""
    

    Purpose: Rapidly adapt to a new task using meta-learned knowledge.

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

    get_meta_insights (Line 300)
    def get_meta_insights(self):
        """Get human-readable meta-learning insights."""
    

    Purpose: Get human-readable meta-learning insights.

    Parameters: – self: Instance reference

    get_statistics (Line 319)
    def get_statistics(self):
        """Get comprehensive statistics."""
    

    Purpose: Get comprehensive statistics.

    Parameters: – self: Instance reference

    MetaLearningPattern (Line 333)

    Description: Meta-Learning Pattern – Learn how to learn.

    Coordinates meta-learning agents to optimize learning strategies across multiple tasks.

    Methods:

    __init__ (Line 341)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 346)
    def register_agent(self, agent):
        """Register meta-learning agent."""
    

    Purpose: Register meta-learning agent.

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

    add_training_tasks (Line 351)
    def add_training_tasks(self, tasks):
        """Add tasks for meta-training."""
    

    Purpose: Add tasks for meta-training.

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

    add_test_tasks (Line 356)
    def add_test_tasks(self, tasks):
        """Add tasks for testing adaptation."""
    

    Purpose: Add tasks for testing adaptation.

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

    meta_train_all (Line 361)
    def meta_train_all(self):
        """Meta-train all agents."""
    

    Purpose: Meta-train all agents.

    Parameters: – self: Instance reference

    test_adaptation (Line 367)
    def test_adaptation(self):
        """Test rapid adaptation on new tasks."""
    

    Purpose: Test rapid adaptation on new tasks.

    Parameters: – self: Instance reference

    run (Line 381)
    def run(self, training_tasks, test_tasks):
        """Run meta-learning pipeline."""
    

    Purpose: Run meta-learning pipeline.

    Parameters: – self: Instance reference – training_tasks: Parameter for training_tasks – test_tasks: Parameter for test_tasks


    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 = MetaStrategy(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

  • Agentic Architecture Pattern: Offline Learning

    
        """
    Pattern 56: Offline Learning
    
    Purpose: Learn from complete datasets in batch mode before deployment.
    Trains on static datasets and evaluates comprehensively before production use.
    """
    
    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_openai import ChatOpenAI
    
    
    class BatchStrategy(Enum):
        """Strategies for batch learning."""
        FULL_BATCH = "full_batch"  # Train on entire dataset at once
        MINI_BATCH = "mini_batch"  # Train on small batches with multiple epochs
        STOCHASTIC = "stochastic"  # Train on random samples
        STRATIFIED = "stratified"  # Ensure balanced class distribution
    
    
    @dataclass
    class TrainingExample:
        """A training example."""
        id: str
        features: Dict[str, Any]
        label: str
        weight: float = 1.0
        fold: Optional[int] = None  # For cross-validation
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class EpochMetrics:
        """Metrics for a training epoch."""
        epoch: int
        train_loss: float
        train_accuracy: float
        val_loss: float
        val_accuracy: float
        learning_rate: float
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    @dataclass
    class ModelCheckpoint:
        """Saved model checkpoint."""
        epoch: int
        accuracy: float
        loss: float
        model_state: Dict[str, Any]
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class OfflineLearningAgent:
        """
        Offline Learning Agent - Learn from complete datasets in batch mode.
        
        Trains on static datasets with multiple epochs, proper train/val/test
        splits, and comprehensive evaluation before deployment.
        """
        
        def __init__(self, 
                     name: str,
                     task_description: str,
                     strategy: BatchStrategy = BatchStrategy.MINI_BATCH,
                     batch_size: int = 32,
                     epochs: int = 10,
                     learning_rate: float = 0.01):
            self.name = name
            self.task_description = task_description
            self.strategy = strategy
            self.batch_size = batch_size
            self.epochs = epochs
            self.learning_rate = learning_rate
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Datasets
            self.train_set: List[TrainingExample] = []
            self.val_set: List[TrainingExample] = []
            self.test_set: List[TrainingExample] = []
            
            # Training state
            self.model_patterns: Dict[str, Any] = {}
            self.trained: bool = False
            self.current_epoch: int = 0
            
            # Training history
            self.epoch_history: List[EpochMetrics] = []
            self.checkpoints: List[ModelCheckpoint] = []
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "total_examples": 0,
                "training_time": 0.0,
                "best_val_accuracy": 0.0,
                "final_test_accuracy": 0.0
            }
        
        def prepare_data(self, 
                        examples: List[TrainingExample],
                        train_ratio: float = 0.7,
                        val_ratio: float = 0.15):
            """Split data into train/val/test sets."""
            # Shuffle data
            shuffled = examples.copy()
            random.shuffle(shuffled)
            
            # Calculate split points
            n = len(shuffled)
            train_end = int(n * train_ratio)
            val_end = int(n * (train_ratio + val_ratio))
            
            # Split data
            self.train_set = shuffled[:train_end]
            self.val_set = shuffled[train_end:val_end]
            self.test_set = shuffled[val_end:]
            
            self.statistics["total_examples"] = n
            
            print(f"   {self.name}: Data prepared - "
                  f"Train: {len(self.train_set)}, Val: {len(self.val_set)}, Test: {len(self.test_set)}")
        
        def train(self):
            """Train model on complete dataset."""
            if not self.train_set:
                print(f"   {self.name}: No training data!")
                return
            
            print(f"\n   {self.name}: Training with {self.strategy.value} strategy...")
            start_time = datetime.now()
            
            for epoch in range(self.epochs):
                self.current_epoch = epoch + 1
                
                # Train epoch
                train_loss, train_acc = self._train_epoch()
                
                # Validate
                val_loss, val_acc = self._validate()
                
                # Record metrics
                metrics = EpochMetrics(
                    epoch=self.current_epoch,
                    train_loss=train_loss,
                    train_accuracy=train_acc,
                    val_loss=val_loss,
                    val_accuracy=val_acc,
                    learning_rate=self.learning_rate
                )
                self.epoch_history.append(metrics)
                
                # Save checkpoint if best so far
                if val_acc > self.statistics["best_val_accuracy"]:
                    self.statistics["best_val_accuracy"] = val_acc
                    self._save_checkpoint(val_acc, val_loss)
                
                # Progress update
                if (epoch + 1) % max(1, self.epochs // 5) == 0:
                    print(f"   Epoch {self.current_epoch}/{self.epochs}: "
                          f"Train Acc: {train_acc:.2%}, Val Acc: {val_acc:.2%}")
            
            self.trained = True
            self.statistics["training_time"] = (datetime.now() - start_time).total_seconds()
            
            print(f"   Training complete! Best val accuracy: {self.statistics['best_val_accuracy']:.2%}")
        
        def _train_epoch(self) -> Tuple[float, float]:
            """Train for one epoch."""
            # Prepare batches based on strategy
            if self.strategy == BatchStrategy.FULL_BATCH:
                batches = [self.train_set]
            elif self.strategy == BatchStrategy.MINI_BATCH:
                batches = self._create_mini_batches(self.train_set, self.batch_size)
            elif self.strategy == BatchStrategy.STOCHASTIC:
                batches = [[ex] for ex in self.train_set]  # One example per batch
            else:  # STRATIFIED
                batches = self._create_stratified_batches(self.train_set, self.batch_size)
            
            # Train on batches
            total_loss = 0.0
            correct = 0
            total = 0
            
            for batch in batches:
                # Extract patterns from batch using LLM
                patterns = self._extract_batch_patterns(batch)
                
                # Update model patterns
                for pattern_id, pattern_data in patterns.items():
                    if pattern_id in self.model_patterns:
                        # Average with existing pattern
                        self.model_patterns[pattern_id]["confidence"] = (
                            0.9 * self.model_patterns[pattern_id]["confidence"] + 
                            0.1 * pattern_data["confidence"]
                        )
                    else:
                        self.model_patterns[pattern_id] = pattern_data
                
                # Simulate training metrics
                batch_loss = random.uniform(0.1, 0.5) * (1.0 - self.current_epoch / self.epochs)
                total_loss += batch_loss * len(batch)
                
                # Predict on batch
                for ex in batch:
                    pred = self._predict_internal(ex)
                    if pred == ex.label:
                        correct += 1
                    total += 1
            
            avg_loss = total_loss / total if total > 0 else 0.0
            accuracy = correct / total if total > 0 else 0.0
            
            return avg_loss, accuracy
        
        def _validate(self) -> Tuple[float, float]:
            """Validate on validation set."""
            if not self.val_set:
                return 0.0, 0.0
            
            correct = 0
            total_loss = 0.0
            
            for ex in self.val_set:
                pred = self._predict_internal(ex)
                if pred == ex.label:
                    correct += 1
                
                # Simulate loss
                total_loss += random.uniform(0.1, 0.4)
            
            accuracy = correct / len(self.val_set)
            avg_loss = total_loss / len(self.val_set)
            
            return avg_loss, accuracy
        
        def test(self) -> Dict[str, float]:
            """Test on test set."""
            if not self.test_set:
                print(f"   {self.name}: No test data!")
                return {"accuracy": 0.0}
            
            if not self.trained:
                print(f"   {self.name}: Model not trained yet!")
                return {"accuracy": 0.0}
            
            print(f"\n   {self.name}: Testing on {len(self.test_set)} examples...")
            
            correct = 0
            predictions = []
            
            for ex in self.test_set:
                pred = self.predict(ex)
                predictions.append(pred)
                if pred["predicted_label"] == ex.label:
                    correct += 1
            
            accuracy = correct / len(self.test_set)
            self.statistics["final_test_accuracy"] = accuracy
            
            print(f"   Test Accuracy: {accuracy:.2%}")
            
            return {
                "accuracy": accuracy,
                "correct": correct,
                "total": len(self.test_set),
                "predictions": predictions
            }
        
        def _predict_internal(self, example: TrainingExample) -> str:
            """Internal prediction without full response."""
            if not self.model_patterns:
                return "unknown"
            
            # Simple prediction based on learned patterns
            # Use most confident pattern
            if self.model_patterns:
                best_pattern = max(self.model_patterns.values(), 
                                 key=lambda p: p.get("confidence", 0))
                # Extract label from pattern description if possible
                if "label" in best_pattern:
                    return best_pattern["label"]
            
            return "unknown"
        
        def predict(self, example: TrainingExample) -> Dict[str, Any]:
            """Make prediction on example."""
            if not self.trained:
                return {
                    "example_id": example.id,
                    "predicted_label": "untrained",
                    "confidence": 0.0
                }
            
            # Create prompt with learned patterns
            patterns_text = ""
            for pattern_id, pattern_data in list(self.model_patterns.items())[:5]:
                patterns_text += f"  - {pattern_data.get('description', pattern_id)}\n"
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are performing {self.task_description}. "
                          f"You have been trained on a dataset and learned these patterns:\n{patterns_text}"),
                ("human", f"Example: {example.features}\n\n"
                         f"Predict the label and confidence (0.0-1.0).\n"
                         f"Format: LABEL|CONFIDENCE")
            ])
            
            chain = prompt | self.llm
            
            try:
                response = chain.invoke({})
                content = response.content if isinstance(response.content, str) else str(response.content)
                parts = content.strip().split('|')
                
                predicted_label = parts[0].strip()
                confidence = float(parts[1].strip()) if len(parts) > 1 else 0.7
            except:
                predicted_label = self._predict_internal(example)
                confidence = 0.6
            
            return {
                "example_id": example.id,
                "predicted_label": predicted_label,
                "actual_label": example.label,
                "confidence": confidence,
                "correct": predicted_label == example.label
            }
        
        def _extract_batch_patterns(self, batch: List[TrainingExample]) -> Dict[str, Any]:
            """Extract patterns from a batch of examples."""
            if not batch:
                return {}
            
            # Group by label
            label_groups = {}
            for ex in batch:
                if ex.label not in label_groups:
                    label_groups[ex.label] = []
                label_groups[ex.label].append(ex)
            
            patterns = {}
            for label, examples in label_groups.items():
                pattern_id = f"label_{label}"
                patterns[pattern_id] = {
                    "description": f"Pattern for label {label}",
                    "label": label,
                    "confidence": len(examples) / len(batch),
                    "support": len(examples)
                }
            
            return patterns
        
        def _create_mini_batches(self, 
                                examples: List[TrainingExample], 
                                batch_size: int) -> List[List[TrainingExample]]:
            """Create mini-batches."""
            batches = []
            for i in range(0, len(examples), batch_size):
                batches.append(examples[i:i + batch_size])
            return batches
        
        def _create_stratified_batches(self, 
                                      examples: List[TrainingExample],
                                      batch_size: int) -> List[List[TrainingExample]]:
            """Create stratified batches with balanced class distribution."""
            # Group by label
            label_groups = {}
            for ex in examples:
                if ex.label not in label_groups:
                    label_groups[ex.label] = []
                label_groups[ex.label].append(ex)
            
            # Create balanced batches
            batches = []
            max_batches = len(examples) // batch_size
            
            for batch_idx in range(max_batches):
                batch = []
                for label, examples in label_groups.items():
                    # Add examples from each class
                    start_idx = (batch_idx * batch_size) // len(label_groups)
                    end_idx = start_idx + batch_size // len(label_groups)
                    batch.extend(examples[start_idx:end_idx])
                
                if batch:
                    batches.append(batch[:batch_size])
            
            return batches
        
        def _save_checkpoint(self, accuracy: float, loss: float):
            """Save model checkpoint."""
            checkpoint = ModelCheckpoint(
                epoch=self.current_epoch,
                accuracy=accuracy,
                loss=loss,
                model_state={
                    "patterns": self.model_patterns.copy(),
                    "epoch": self.current_epoch
                }
            )
            self.checkpoints.append(checkpoint)
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive training statistics."""
            return {
                "name": self.name,
                "strategy": self.strategy.value,
                "trained": self.trained,
                "epochs_completed": self.current_epoch,
                "train_size": len(self.train_set),
                "val_size": len(self.val_set),
                "test_size": len(self.test_set),
                "patterns_learned": len(self.model_patterns),
                "checkpoints_saved": len(self.checkpoints),
                **self.statistics
            }
    
    
    class OfflineLearningPattern:
        """
        Offline Learning Pattern - Batch learning from complete datasets.
        
        Coordinates multiple agents training on static datasets with
        different batch strategies.
        """
        
        def __init__(self):
            self.agents: Dict[str, OfflineLearningAgent] = {}
            self.dataset: List[TrainingExample] = []
        
        def register_agent(self, agent: OfflineLearningAgent):
            """Register offline learning agent."""
            self.agents[agent.name] = agent
            print(f"   Registered agent: {agent.name} ({agent.strategy.value})")
        
        def load_dataset(self, examples: List[TrainingExample]):
            """Load dataset for all agents."""
            self.dataset = examples
            print(f"   Loaded dataset: {len(examples)} examples")
            
            # Prepare data for each agent
            for agent in self.agents.values():
                agent.prepare_data(examples)
        
        def train_all(self):
            """Train all agents."""
            print("\n   Training all agents...")
            for agent in self.agents.values():
                agent.train()
        
        def test_all(self) -> Dict[str, Dict[str, float]]:
            """Test all agents."""
            print("\n   Testing all agents...")
            results = {}
            for name, agent in self.agents.items():
                results[name] = agent.test()
            return results
        
        def run(self, examples: List[TrainingExample]) -> Dict[str, Any]:
            """Run offline learning pipeline."""
            print("=== Offline Learning Pattern Demo ===\n")
            
            # Load dataset
            print("1. Loading dataset...")
            self.load_dataset(examples)
            
            # Train all agents
            print("\n2. Training agents...")
            self.train_all()
            
            # Test all agents
            print("\n3. Testing agents...")
            test_results = self.test_all()
            
            # Get statistics
            stats = {name: agent.get_statistics() 
                    for name, agent in self.agents.items()}
            
            return {
                "pattern": "OfflineLearning",
                "agents": len(self.agents),
                "dataset_size": len(examples),
                "test_results": test_results,
                "statistics": stats
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Offline Learning Pattern Demo ===\n")
        
        # Create pattern
        pattern = OfflineLearningPattern()
        
        # Create agents with different strategies
        print("1. Creating agents...")
        full_batch_agent = OfflineLearningAgent(
            name="FullBatchAgent",
            task_description="classify sentiment as positive or negative",
            strategy=BatchStrategy.FULL_BATCH,
            epochs=5
        )
        pattern.register_agent(full_batch_agent)
        
        mini_batch_agent = OfflineLearningAgent(
            name="MiniBatchAgent",
            task_description="classify sentiment as positive or negative",
            strategy=BatchStrategy.MINI_BATCH,
            batch_size=16,
            epochs=10
        )
        pattern.register_agent(mini_batch_agent)
        
        # Generate dataset
        print("\n2. Generating dataset...")
        dataset = []
        
        # Positive examples
        for i in range(80):
            dataset.append(TrainingExample(
                id=f"pos_{i}",
                features={"text": f"positive_example_{i}", "score": random.uniform(0.6, 1.0)},
                label="positive"
            ))
        
        # Negative examples
        for i in range(80):
            dataset.append(TrainingExample(
                id=f"neg_{i}",
                features={"text": f"negative_example_{i}", "score": random.uniform(0.0, 0.4)},
                label="negative"
            ))
        
        # Run offline learning
        print("\n3. Running offline learning...")
        summary = pattern.run(dataset)
        
        print(f"\n4. Results:")
        print(f"   Agents: {summary['agents']}")
        print(f"   Dataset Size: {summary['dataset_size']}")
        
        print(f"\n5. Test Results:")
        for agent_name, results in summary['test_results'].items():
            print(f"\n   {agent_name}:")
            print(f"     Accuracy: {results['accuracy']:.2%}")
            print(f"     Correct: {results['correct']}/{results['total']}")
        
        print(f"\n6. Training Statistics:")
        for agent_name, stats in summary['statistics'].items():
            print(f"\n   {agent_name}:")
            for key, value in stats.items():
                if key != "name":
                    if isinstance(value, float):
                        print(f"     {key}: {value:.4f}")
                    else:
                        print(f"     {key}: {value}")
        
        print("\n✓ Offline learning completed comprehensive training and evaluation!")
    
        

    Agentic Architecture Pattern: Offline Learning

    Pattern 56: 56 Offline Learning – Line-by-Line Explanation

    Overview

    Pattern 56: Offline Learning

    Purpose: Learn from complete datasets in batch mode before deployment. Trains on static datasets and evaluates comprehensively before production use.


    Code Structure Analysis

    File Information

    • Pattern Number: 56
    • Pattern Name: 56 Offline Learning
    • File Path: patterns\learning_adaptation\56_offline_learning.py
    • Total Lines: 562

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 56: Offline Learning
    
    Purpose: Learn from complete datasets in batch mode before deployment.
    Trains on static datasets and evaluates comprehensively before production use.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 56 Offline 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_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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    BatchStrategy (Line 17)

    class BatchStrategy(Enum):
        FULL_BATCH
        MINI_BATCH
        STOCHASTIC
        STRATIFIED
    

    Purpose: Strategies for batch learning.

    Values: – FULL_BATCH: Represents full batch – MINI_BATCH: Represents mini batch – STOCHASTIC: Represents stochastic – STRATIFIED: Represents stratified


    Classes

    BatchStrategy (Line 17)

    Description: Strategies for batch learning.

    TrainingExample (Line 26)

    Description: A training example.

    EpochMetrics (Line 37)

    Description: Metrics for a training epoch.

    ModelCheckpoint (Line 49)

    Description: Saved model checkpoint.

    OfflineLearningAgent (Line 58)

    Description: Offline Learning Agent – Learn from complete datasets in batch mode.

    Trains on static datasets with multiple epochs, proper train/val/test splits, and comprehensive evaluation before deployment.

    Methods:

    __init__ (Line 66)
    def __init__(self, name, task_description, strategy, batch_size, epochs, learning_rate):
        """No method docstring."""
    

    Purpose: No method docstring.

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

    prepare_data (Line 103)
    def prepare_data(self, examples, train_ratio, val_ratio):
        """Split data into train/val/test sets."""
    

    Purpose: Split data into train/val/test sets.

    Parameters: – self: Instance reference – examples: Parameter for examples – train_ratio: Parameter for train_ratio – val_ratio: Parameter for val_ratio

    train (Line 127)
    def train(self):
        """Train model on complete dataset."""
    

    Purpose: Train model on complete dataset.

    Parameters: – self: Instance reference

    _train_epoch (Line 171)
    def _train_epoch(self):
        """Train for one epoch."""
    

    Purpose: Train for one epoch.

    Parameters: – self: Instance reference

    _validate (Line 219)
    def _validate(self):
        """Validate on validation set."""
    

    Purpose: Validate on validation set.

    Parameters: – self: Instance reference

    test (Line 240)
    def test(self):
        """Test on test set."""
    

    Purpose: Test on test set.

    Parameters: – self: Instance reference

    _predict_internal (Line 273)
    def _predict_internal(self, example):
        """Internal prediction without full response."""
    

    Purpose: Internal prediction without full response.

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

    predict (Line 289)
    def predict(self, example):
        """Make prediction on example."""
    

    Purpose: Make prediction on example.

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

    _extract_batch_patterns (Line 332)
    def _extract_batch_patterns(self, batch):
        """Extract patterns from a batch of examples."""
    

    Purpose: Extract patterns from a batch of examples.

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

    _create_mini_batches (Line 356)
    def _create_mini_batches(self, examples, batch_size):
        """Create mini-batches."""
    

    Purpose: Create mini-batches.

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

    _create_stratified_batches (Line 365)
    def _create_stratified_batches(self, examples, batch_size):
        """Create stratified batches with balanced class distribution."""
    

    Purpose: Create stratified batches with balanced class distribution.

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

    _save_checkpoint (Line 393)
    def _save_checkpoint(self, accuracy, loss):
        """Save model checkpoint."""
    

    Purpose: Save model checkpoint.

    Parameters: – self: Instance reference – accuracy: Parameter for accuracy – loss: Parameter for loss

    get_statistics (Line 406)
    def get_statistics(self):
        """Get comprehensive training statistics."""
    

    Purpose: Get comprehensive training statistics.

    Parameters: – self: Instance reference

    OfflineLearningPattern (Line 422)

    Description: Offline Learning Pattern – Batch learning from complete datasets.

    Coordinates multiple agents training on static datasets with different batch strategies.

    Methods:

    __init__ (Line 430)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 434)
    def register_agent(self, agent):
        """Register offline learning agent."""
    

    Purpose: Register offline learning agent.

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

    load_dataset (Line 439)
    def load_dataset(self, examples):
        """Load dataset for all agents."""
    

    Purpose: Load dataset for all agents.

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

    train_all (Line 448)
    def train_all(self):
        """Train all agents."""
    

    Purpose: Train all agents.

    Parameters: – self: Instance reference

    test_all (Line 454)
    def test_all(self):
        """Test all agents."""
    

    Purpose: Test all agents.

    Parameters: – self: Instance reference

    run (Line 462)
    def run(self, examples):
        """Run offline learning pipeline."""
    

    Purpose: Run offline learning pipeline.

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


    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 = BatchStrategy(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

  • Agentic Architecture Pattern: Online Learning

    
        """
    Pattern 55: Online Learning
    
    Purpose: Learn continuously from streaming data in real-time.
    Updates model incrementally as new data arrives, adapting to changing patterns.
    """
    
    from typing import Dict, Any, List, Optional, Deque
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from enum import Enum
    from collections import deque
    import random
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    
    class UpdateStrategy(Enum):
        """Strategies for online model updates."""
        IMMEDIATE = "immediate"  # Update after each example
        MINI_BATCH = "mini_batch"  # Update after small batches
        SLIDING_WINDOW = "sliding_window"  # Maintain recent examples only
        ADAPTIVE = "adaptive"  # Adjust based on performance
    
    
    @dataclass
    class StreamingExample:
        """A single streaming data example."""
        id: str
        features: Dict[str, Any]
        label: Optional[str] = None
        timestamp: datetime = field(default_factory=datetime.now)
        weight: float = 1.0
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class ModelSnapshot:
        """Snapshot of model state at a point in time."""
        timestamp: datetime
        examples_seen: int
        accuracy: float
        parameters: Dict[str, Any]
        metadata: Dict[str, Any] = field(default_factory=dict)
    
    
    @dataclass
    class PerformanceMetrics:
        """Real-time performance metrics."""
        accuracy: float
        recent_accuracy: float  # Last N examples
        examples_processed: int
        updates_performed: int
        avg_confidence: float
        timestamp: datetime = field(default_factory=datetime.now)
    
    
    class OnlineLearningAgent:
        """
        Online Learning Agent - Learn continuously from streaming data.
        
        Adapts in real-time as new examples arrive, maintaining performance
        on non-stationary data streams. Uses LLM for pattern recognition.
        """
        
        def __init__(self, 
                     name: str,
                     task_description: str,
                     strategy: UpdateStrategy = UpdateStrategy.IMMEDIATE,
                     window_size: int = 100,
                     batch_size: int = 10):
            self.name = name
            self.task_description = task_description
            self.strategy = strategy
            self.window_size = window_size
            self.batch_size = batch_size
            self.llm = ChatOpenAI(model="gpt-4", temperature=0.3)
            
            # Streaming data buffer
            self.data_buffer: Deque[StreamingExample] = deque(maxlen=window_size)
            self.batch_buffer: List[StreamingExample] = []
            
            # Model state
            self.learned_patterns: Dict[str, Any] = {}
            self.examples_seen: int = 0
            self.updates_performed: int = 0
            
            # Performance tracking
            self.predictions: List[Dict[str, Any]] = []
            self.snapshots: List[ModelSnapshot] = []
            
            # Statistics
            self.statistics: Dict[str, Any] = {
                "correct_predictions": 0,
                "total_predictions": 0,
                "recent_correct": 0,
                "recent_total": 0
            }
        
        def process_stream(self, example: StreamingExample):
            """Process a single streaming example."""
            self.examples_seen += 1
            
            # Add to buffer
            if self.strategy == UpdateStrategy.SLIDING_WINDOW:
                self.data_buffer.append(example)
            else:
                self.data_buffer.append(example)
                self.batch_buffer.append(example)
            
            # Decide when to update
            should_update = False
            
            if self.strategy == UpdateStrategy.IMMEDIATE:
                should_update = True
            elif self.strategy == UpdateStrategy.MINI_BATCH:
                should_update = len(self.batch_buffer) >= self.batch_size
            elif self.strategy == UpdateStrategy.ADAPTIVE:
                # Update when performance drops
                recent_accuracy = self._calculate_recent_accuracy()
                should_update = recent_accuracy = self.batch_size
            
            # Perform update
            if should_update:
                self._update_model()
                if self.strategy != UpdateStrategy.SLIDING_WINDOW:
                    self.batch_buffer.clear()
            
            print(f"   {self.name}: Processed example {example.id} (total: {self.examples_seen})")
        
        def _update_model(self):
            """Update model with current buffer."""
            if not self.data_buffer:
                return
            
            self.updates_performed += 1
            
            # Get recent examples for context
            recent_examples = list(self.data_buffer)[-min(20, len(self.data_buffer)):]
            
            # Extract patterns using LLM
            patterns = self._extract_patterns(recent_examples)
            
            # Update learned patterns
            for pattern_id, pattern_data in patterns.items():
                if pattern_id in self.learned_patterns:
                    # Merge with existing pattern
                    self.learned_patterns[pattern_id]["confidence"] = (
                        0.7 * self.learned_patterns[pattern_id]["confidence"] + 
                        0.3 * pattern_data["confidence"]
                    )
                    self.learned_patterns[pattern_id]["occurrences"] += 1
                else:
                    self.learned_patterns[pattern_id] = pattern_data
                    self.learned_patterns[pattern_id]["occurrences"] = 1
            
            # Take snapshot periodically
            if self.updates_performed % 10 == 0:
                self._take_snapshot()
            
            print(f"   {self.name}: Model updated (update #{self.updates_performed}, "
                  f"{len(self.learned_patterns)} patterns)")
        
        def _extract_patterns(self, examples: List[StreamingExample]) -> Dict[str, Any]:
            """Extract patterns from examples using LLM."""
            if not examples:
                return {}
            
            # Prepare examples summary
            examples_text = ""
            for ex in examples[:5]:
                label_text = f"→ {ex.label}" if ex.label else "→ ?"
                examples_text += f"  - {ex.features} {label_text}\n"
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", "You are analyzing streaming data patterns. "
                          "Identify key patterns that could help with prediction."),
                ("human", f"Task: {self.task_description}\n\n"
                         f"Recent examples ({len(examples)} total):\n{examples_text}\n"
                         f"Identify 2-3 key patterns. Format each as: PATTERN_ID|DESCRIPTION|CONFIDENCE\n"
                         f"Example: pattern_1|High feature X correlates with label A|0.8")
            ])
            
            chain = prompt | self.llm
            patterns = {}
            
            try:
                response = chain.invoke({})
                content = response.content if isinstance(response.content, str) else str(response.content)
                lines = content.strip().split('\n')
                
                for line in lines[:3]:
                    try:
                        parts = line.split('|')
                        if len(parts) >= 3:
                            pattern_id = parts[0].strip()
                            description = parts[1].strip()
                            confidence = float(parts[2].strip())
                            
                            patterns[pattern_id] = {
                                "description": description,
                                "confidence": confidence
                            }
                    except:
                        continue
            except:
                pass
            
            # Add simple statistical patterns
            if examples and examples[0].label:
                # Label distribution
                label_counts = {}
                for ex in examples:
                    if ex.label:
                        label_counts[ex.label] = label_counts.get(ex.label, 0) + 1
                
                if label_counts:
                    most_common = max(label_counts.items(), key=lambda x: x[1])
                    patterns["label_distribution"] = {
                        "description": f"Most common label: {most_common[0]}",
                        "confidence": most_common[1] / len(examples)
                    }
            
            return patterns
        
        def predict(self, example: StreamingExample) -> Dict[str, Any]:
            """Make prediction on streaming example."""
            # Create prompt with learned patterns
            patterns_text = ""
            for pattern_id, pattern_data in list(self.learned_patterns.items())[:5]:
                patterns_text += f"  - {pattern_data['description']} (confidence: {pattern_data['confidence']:.2f})\n"
            
            if not patterns_text:
                patterns_text = "  - No patterns learned yet\n"
            
            prompt = ChatPromptTemplate.from_messages([
                ("system", f"You are performing {self.task_description}. "
                          f"Use the learned patterns to make predictions."),
                ("human", f"Learned patterns:\n{patterns_text}\n"
                         f"New example: {example.features}\n\n"
                         f"Predict the label and confidence (0.0-1.0).\n"
                         f"Format: LABEL|CONFIDENCE")
            ])
            
            chain = prompt | self.llm
            
            try:
                response = chain.invoke({})
                content = response.content if isinstance(response.content, str) else str(response.content)
                parts = content.strip().split('|')
                
                predicted_label = parts[0].strip()
                confidence = float(parts[1].strip()) if len(parts) > 1 else 0.5
            except:
                # Fallback: use most common pattern
                if self.learned_patterns and "label_distribution" in self.learned_patterns:
                    predicted_label = self.learned_patterns["label_distribution"]["description"].split(": ")[1]
                    confidence = 0.5
                else:
                    predicted_label = "unknown"
                    confidence = 0.3
            
            # Track prediction
            is_correct = example.label and predicted_label == example.label
            
            prediction = {
                "example_id": example.id,
                "predicted_label": predicted_label,
                "actual_label": example.label,
                "confidence": confidence,
                "correct": is_correct,
                "timestamp": datetime.now()
            }
            
            self.predictions.append(prediction)
            
            # Update statistics
            self.statistics["total_predictions"] += 1
            self.statistics["recent_total"] += 1
            
            if is_correct:
                self.statistics["correct_predictions"] += 1
                self.statistics["recent_correct"] += 1
            
            # Keep only recent stats (last 50)
            if self.statistics["recent_total"] > 50:
                self.statistics["recent_total"] = 50
                self.statistics["recent_correct"] = sum(
                    1 for p in self.predictions[-50:] if p["correct"]
                )
            
            return prediction
        
        def _calculate_recent_accuracy(self) -> float:
            """Calculate accuracy on recent predictions."""
            if self.statistics["recent_total"] == 0:
                return 0.0
            return self.statistics["recent_correct"] / self.statistics["recent_total"]
        
        def _take_snapshot(self):
            """Take snapshot of current model state."""
            accuracy = (self.statistics["correct_predictions"] / 
                       self.statistics["total_predictions"] 
                       if self.statistics["total_predictions"] > 0 else 0.0)
            
            snapshot = ModelSnapshot(
                timestamp=datetime.now(),
                examples_seen=self.examples_seen,
                accuracy=accuracy,
                parameters={
                    "num_patterns": len(self.learned_patterns),
                    "updates": self.updates_performed
                }
            )
            self.snapshots.append(snapshot)
        
        def get_performance_metrics(self) -> PerformanceMetrics:
            """Get current performance metrics."""
            accuracy = (self.statistics["correct_predictions"] / 
                       self.statistics["total_predictions"] 
                       if self.statistics["total_predictions"] > 0 else 0.0)
            
            recent_accuracy = self._calculate_recent_accuracy()
            
            avg_confidence = (sum(p["confidence"] for p in self.predictions[-50:]) / 
                             min(50, len(self.predictions))
                             if self.predictions else 0.0)
            
            return PerformanceMetrics(
                accuracy=accuracy,
                recent_accuracy=recent_accuracy,
                examples_processed=self.examples_seen,
                updates_performed=self.updates_performed,
                avg_confidence=avg_confidence
            )
        
        def get_statistics(self) -> Dict[str, Any]:
            """Get comprehensive statistics."""
            metrics = self.get_performance_metrics()
            
            return {
                "name": self.name,
                "strategy": self.strategy.value,
                "examples_seen": self.examples_seen,
                "updates_performed": self.updates_performed,
                "accuracy": metrics.accuracy,
                "recent_accuracy": metrics.recent_accuracy,
                "patterns_learned": len(self.learned_patterns),
                "snapshots": len(self.snapshots)
            }
    
    
    class OnlineLearningPattern:
        """
        Online Learning Pattern - Continuous learning from data streams.
        
        Coordinates multiple agents learning from streaming data with
        different update strategies.
        """
        
        def __init__(self):
            self.agents: Dict[str, OnlineLearningAgent] = {}
            self.stream_history: List[StreamingExample] = []
        
        def register_agent(self, agent: OnlineLearningAgent):
            """Register online learning agent."""
            self.agents[agent.name] = agent
            print(f"   Registered agent: {agent.name} ({agent.strategy.value})")
        
        def simulate_stream(self, 
                           examples: List[StreamingExample],
                           test_interval: int = 10):
            """Simulate streaming data with online learning."""
            print("\n   Simulating data stream...")
            
            for i, example in enumerate(examples):
                self.stream_history.append(example)
                
                # Each agent processes the stream
                for agent in self.agents.values():
                    agent.process_stream(example)
                    
                    # Test prediction every N examples
                    if (i + 1) % test_interval == 0:
                        prediction = agent.predict(example)
                
                # Progress update
                if (i + 1) % 20 == 0:
                    print(f"   Processed {i + 1}/{len(examples)} examples...")
        
        def run(self, examples: List[StreamingExample]) -> Dict[str, Any]:
            """Run online learning simulation."""
            print("=== Online Learning Pattern Demo ===\n")
            
            print("1. Simulating data stream...")
            self.simulate_stream(examples)
            
            print("\n2. Collecting performance metrics...")
            metrics = {name: agent.get_performance_metrics() 
                      for name, agent in self.agents.items()}
            
            stats = {name: agent.get_statistics() 
                    for name, agent in self.agents.items()}
            
            return {
                "pattern": "OnlineLearning",
                "agents": len(self.agents),
                "stream_length": len(examples),
                "metrics": metrics,
                "statistics": stats
            }
    
    
    # Example usage
    if __name__ == "__main__":
        print("=== Online Learning Pattern Demo ===\n")
        
        # Create pattern
        pattern = OnlineLearningPattern()
        
        # Create agents with different strategies
        print("1. Creating agents...")
        immediate_agent = OnlineLearningAgent(
            name="ImmediateAgent",
            task_description="classify sentiment as positive or negative",
            strategy=UpdateStrategy.IMMEDIATE
        )
        pattern.register_agent(immediate_agent)
        
        batch_agent = OnlineLearningAgent(
            name="BatchAgent",
            task_description="classify sentiment as positive or negative",
            strategy=UpdateStrategy.MINI_BATCH,
            batch_size=10
        )
        pattern.register_agent(batch_agent)
        
        adaptive_agent = OnlineLearningAgent(
            name="AdaptiveAgent",
            task_description="classify sentiment as positive or negative",
            strategy=UpdateStrategy.ADAPTIVE
        )
        pattern.register_agent(adaptive_agent)
        
        # Generate streaming data
        print("\n2. Generating streaming data...")
        stream = []
        
        # Initial positive examples
        for i in range(30):
            stream.append(StreamingExample(
                id=f"ex_{i}",
                features={"text": f"positive_text_{i}", "score": random.uniform(0.6, 1.0)},
                label="positive"
            ))
        
        # Shift to negative examples (concept drift)
        for i in range(30, 60):
            stream.append(StreamingExample(
                id=f"ex_{i}",
                features={"text": f"negative_text_{i}", "score": random.uniform(0.0, 0.4)},
                label="negative"
            ))
        
        # Mixed examples
        for i in range(60, 100):
            label = random.choice(["positive", "negative"])
            score = random.uniform(0.6, 1.0) if label == "positive" else random.uniform(0.0, 0.4)
            stream.append(StreamingExample(
                id=f"ex_{i}",
                features={"text": f"{label}_text_{i}", "score": score},
                label=label
            ))
        
        # Run online learning
        print("\n3. Running online learning...")
        summary = pattern.run(stream)
        
        print(f"\n4. Results:")
        print(f"   Agents: {summary['agents']}")
        print(f"   Stream Length: {summary['stream_length']}")
        
        print(f"\n5. Performance Metrics:")
        for agent_name, metrics in summary['metrics'].items():
            print(f"\n   {agent_name}:")
            print(f"     Accuracy: {metrics.accuracy:.2%}")
            print(f"     Recent Accuracy: {metrics.recent_accuracy:.2%}")
            print(f"     Examples Processed: {metrics.examples_processed}")
            print(f"     Updates Performed: {metrics.updates_performed}")
            print(f"     Avg Confidence: {metrics.avg_confidence:.2f}")
        
        print(f"\n6. Statistics:")
        for agent_name, stats in summary['statistics'].items():
            print(f"\n   {agent_name}:")
            for key, value in stats.items():
                if key != "name":
                    print(f"     {key}: {value}")
        
        print("\n✓ Online learning adapted to streaming data in real-time!")
    
        

    Agentic Architecture Pattern: Online Learning

    Pattern 55: 55 Online Learning – Line-by-Line Explanation

    Overview

    Pattern 55: Online Learning

    Purpose: Learn continuously from streaming data in real-time. Updates model incrementally as new data arrives, adapting to changing patterns.


    Code Structure Analysis

    File Information

    • Pattern Number: 55
    • Pattern Name: 55 Online Learning
    • File Path: patterns\learning_adaptation\55_online_learning.py
    • Total Lines: 500

    Detailed Code Explanation

    Module Docstring

    """
    Pattern 55: Online Learning
    
    Purpose: Learn continuously from streaming data in real-time.
    Updates model incrementally as new data arrives, adapting to changing patterns.
    """
    

    Explanation: This module docstring describes the purpose and key features of the 55 Online Learning pattern.


    Imports Section

    from typing import Dict, Any, List, Optional, Deque
    from dataclasses import dataclass, field
    from datetime import datetime, timedelta
    from enum import Enum
    from collections import deque
    import random
    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, Deque: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime, timedelta: 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_openai import ChatOpenAI: LangChain framework integration


    Enumerations

    UpdateStrategy (Line 18)

    class UpdateStrategy(Enum):
        IMMEDIATE
        MINI_BATCH
        SLIDING_WINDOW
        ADAPTIVE
    

    Purpose: Strategies for online model updates.

    Values: – IMMEDIATE: Represents immediate – MINI_BATCH: Represents mini batch – SLIDING_WINDOW: Represents sliding window – ADAPTIVE: Represents adaptive


    Classes

    UpdateStrategy (Line 18)

    Description: Strategies for online model updates.

    StreamingExample (Line 27)

    Description: A single streaming data example.

    ModelSnapshot (Line 38)

    Description: Snapshot of model state at a point in time.

    PerformanceMetrics (Line 48)

    Description: Real-time performance metrics.

    OnlineLearningAgent (Line 58)

    Description: Online Learning Agent – Learn continuously from streaming data.

    Adapts in real-time as new examples arrive, maintaining performance on non-stationary data streams. Uses LLM for pattern recognition.

    Methods:

    __init__ (Line 66)
    def __init__(self, name, task_description, strategy, window_size, batch_size):
        """No method docstring."""
    

    Purpose: No method docstring.

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

    process_stream (Line 100)
    def process_stream(self, example):
        """Process a single streaming example."""
    

    Purpose: Process a single streaming example.

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

    _update_model (Line 131)
    def _update_model(self):
        """Update model with current buffer."""
    

    Purpose: Update model with current buffer.

    Parameters: – self: Instance reference

    _extract_patterns (Line 164)
    def _extract_patterns(self, examples):
        """Extract patterns from examples using LLM."""
    

    Purpose: Extract patterns from examples using LLM.

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

    predict (Line 226)
    def predict(self, example):
        """Make prediction on streaming example."""
    

    Purpose: Make prediction on streaming example.

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

    _calculate_recent_accuracy (Line 294)
    def _calculate_recent_accuracy(self):
        """Calculate accuracy on recent predictions."""
    

    Purpose: Calculate accuracy on recent predictions.

    Parameters: – self: Instance reference

    _take_snapshot (Line 300)
    def _take_snapshot(self):
        """Take snapshot of current model state."""
    

    Purpose: Take snapshot of current model state.

    Parameters: – self: Instance reference

    get_performance_metrics (Line 317)
    def get_performance_metrics(self):
        """Get current performance metrics."""
    

    Purpose: Get current performance metrics.

    Parameters: – self: Instance reference

    get_statistics (Line 337)
    def get_statistics(self):
        """Get comprehensive statistics."""
    

    Purpose: Get comprehensive statistics.

    Parameters: – self: Instance reference

    OnlineLearningPattern (Line 353)

    Description: Online Learning Pattern – Continuous learning from data streams.

    Coordinates multiple agents learning from streaming data with different update strategies.

    Methods:

    __init__ (Line 361)
    def __init__(self):
        """No method docstring."""
    

    Purpose: No method docstring.

    Parameters: – self: Instance reference

    register_agent (Line 365)
    def register_agent(self, agent):
        """Register online learning agent."""
    

    Purpose: Register online learning agent.

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

    simulate_stream (Line 370)
    def simulate_stream(self, examples, test_interval):
        """Simulate streaming data with online learning."""
    

    Purpose: Simulate streaming data with online learning.

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

    run (Line 391)
    def run(self, examples):
        """Run online learning simulation."""
    

    Purpose: Run online learning simulation.

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


    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 = UpdateStrategy(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