"""
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
Leave a Reply