Research-Stack/5-Applications/tools-scripts/geoweird/swarm_orchestrator_v2.py

559 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
GeoWeird Swarm Orchestrator v2
Uses Lean self-typing pipeline for perspective selection and universe assignment.
Flow:
1. Load domain experts from EXHAUSTIVE_DOMAIN_EXPERT_LIST.md
2. Register each with SelfTypingBridge (7D → ConstraintFeatures → MultiTypedDomain)
3. Pair agents for collaboration
4. For each pair, ask Lean: "What perspective maximizes consensus?"
5. Receive collapsed universe + perspective
6. Spawn Projector agents IN THAT UNIVERSE
7. Critics evaluate using shared metric signature
8. Integrator derives invariant from collapsed manifold geometry
"""
import json
import random
from typing import List, Dict, Optional, Any, Tuple
from dataclasses import dataclass, field
from pathlib import Path
from collections import defaultdict
from geoweird.self_typing_bridge import (
SelfTypingBridge, UniverseType, Perspective, CollisionResult,
init_self_typing_bridge, get_self_typing_bridge
)
from geoweird.geo_aware_agent import (
GeoWeirdAwareAgent, GeoWeirdContext,
create_geo_weird_agent, load_domain_experts_from_markdown
)
@dataclass
class CollaborationSession:
"""A collaboration session between two agents"""
session_id: str
agent_a: str
agent_b: str
context: GeoWeirdContext
projectors: List[Dict[str, Any]] = field(default_factory=list)
critiques: List[Dict[str, Any]] = field(default_factory=list)
integrated_result: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
return {
"session_id": self.session_id,
"agents": [self.agent_a, self.agent_b],
"universe": self.context.universe_type.value,
"perspective": self.context.perspective.value,
"metric": self.context.metric_signature,
"projectors": len(self.projectors),
"critiques": len(self.critiques),
"integrated": self.integrated_result is not None
}
@dataclass
class SwarmMetrics:
"""Metrics for swarm performance"""
total_collaborations: int = 0
successful_collaborations: int = 0
universe_distribution: Dict[str, int] = field(default_factory=dict)
perspective_distribution: Dict[str, int] = field(default_factory=dict)
average_consensus: float = 0.0
convergence_rate: float = 0.0
class GeoWeirdSwarmOrchestrator:
"""
Swarm orchestrator that uses Lean self-typing for perspective selection.
This orchestrator:
1. Maintains pool of GeoWeird-aware agents
2. Pairs agents based on complementarity
3. Uses Lean to select optimal perspective for each pair
4. Spawns projectors in collapsed universe
5. Coordinates critique and integration
6. Tracks emergent typing rules
"""
def __init__(
self,
self_typing_bridge: Optional[SelfTypingBridge] = None,
consensus_threshold: float = 0.5
):
self.bridge = self_typing_bridge or get_self_typing_bridge()
if not self.bridge:
raise RuntimeError("SelfTypingBridge not initialized")
self.consensus_threshold = consensus_threshold
self.agents: Dict[str, GeoWeirdAwareAgent] = {}
self.sessions: List[CollaborationSession] = []
self.metrics = SwarmMetrics()
# Learned patterns
self.successful_pairs: List[Tuple[str, str]] = []
self.universe_preferences: Dict[str, Dict[UniverseType, float]] = defaultdict(
lambda: defaultdict(float)
)
# ========================================================================
# AGENT MANAGEMENT
# ========================================================================
def register_agent(self, agent: GeoWeirdAwareAgent) -> 'GeoWeirdSwarmOrchestrator':
"""Register a GeoWeird-aware agent"""
self.agents[agent.name] = agent
return self
def register_agents(self, agents: List[GeoWeirdAwareAgent]) -> 'GeoWeirdSwarmOrchestrator':
"""Register multiple agents"""
for agent in agents:
self.register_agent(agent)
return self
def load_from_markdown(self, md_path: Path) -> 'GeoWeirdSwarmOrchestrator':
"""Load domain experts from EXHAUSTIVE_DOMAIN_EXPERT_LIST.md"""
agents = load_domain_experts_from_markdown(md_path)
return self.register_agents(agents)
def get_agent(self, name: str) -> Optional[GeoWeirdAwareAgent]:
"""Get agent by name"""
return self.agents.get(name)
# ========================================================================
# PAIRING STRATEGIES
# ========================================================================
def pair_agents_random(self) -> List[Tuple[GeoWeirdAwareAgent, GeoWeirdAwareAgent]]:
"""Random pairing strategy"""
agent_list = list(self.agents.values())
random.shuffle(agent_list)
pairs = []
for i in range(0, len(agent_list) - 1, 2):
pairs.append((agent_list[i], agent_list[i + 1]))
return pairs
def pair_agents_complementary(self) -> List[Tuple[GeoWeirdAwareAgent, GeoWeirdAwareAgent]]:
"""
Pair agents with complementary superpositions.
Agents with different dominant universe types are more likely
to produce interesting collisions.
"""
agent_list = list(self.agents.values())
pairs = []
# Sort by dominant universe type
by_universe: Dict[UniverseType, List[GeoWeirdAwareAgent]] = defaultdict(list)
for agent in agent_list:
if agent.domain.superposition:
dominant = agent.domain.superposition[0].universe_type
by_universe[dominant].append(agent)
# Pair across universe types
universes = list(by_universe.keys())
for i, u1 in enumerate(universes):
for u2 in universes[i + 1:]:
agents1 = by_universe[u1]
agents2 = by_universe[u2]
min_len = min(len(agents1), len(agents2))
for j in range(min_len):
pairs.append((agents1[j], agents2[j]))
# Fallback to random pairing if no complementary pairs found
if not pairs:
pairs = self.pair_agents_random()
return pairs
def pair_agents_learned(self) -> List[Tuple[GeoWeirdAwareAgent, GeoWeirdAwareAgent]]:
"""
Pair agents based on learned successful collaborations.
If two agents have collaborated successfully before,
they're likely to do so again.
"""
# Start with known successful pairs
pairs = []
used = set()
for (name_a, name_b) in self.successful_pairs:
if name_a in self.agents and name_b in self.agents:
if name_a not in used and name_b not in used:
pairs.append((self.agents[name_a], self.agents[name_b]))
used.add(name_a)
used.add(name_b)
# Fill remaining with random
remaining = [a for name, a in self.agents.items() if name not in used]
random.shuffle(remaining)
for i in range(0, len(remaining) - 1, 2):
pairs.append((remaining[i], remaining[i + 1]))
return pairs
# ========================================================================
# MAIN ORCHESTRATION LOOP
# ========================================================================
def run_collaboration_round(
self,
pairing_strategy: str = "complementary",
task_template: str = "Collaborative analysis of {domain}"
) -> List[CollaborationSession]:
"""
Run one round of collaborations.
This is the main entry point that implements the flow:
1. Pair agents
2. For each pair, ask Lean for optimal perspective
3. Spawn projectors in collapsed universe
4. Coordinate critique and integration
"""
# Select pairing strategy
if pairing_strategy == "random":
pairs = self.pair_agents_random()
elif pairing_strategy == "complementary":
pairs = self.pair_agents_complementary()
elif pairing_strategy == "learned":
pairs = self.pair_agents_learned()
else:
pairs = self.pair_agents_random()
sessions = []
for agent_a, agent_b in pairs:
print(f"\n[Orchestrator] Pairing: {agent_a.name}{agent_b.name}")
# Step 1: Ask Lean "What perspective maximizes consensus?"
collision = self.bridge.select_best_perspective(agent_a.name, agent_b.name)
if not collision:
print(f" No viable consensus found")
continue
if collision.consensus_strength < self.consensus_threshold:
print(f" Consensus {collision.consensus_strength:.2f} below threshold")
continue
print(f" Selected: {collision.universe_a.value} × {collision.universe_b.value}")
print(f" Consensus: {collision.consensus_strength:.2f}")
print(f" Perspective: {collision.perspective.value}")
# Step 2: Create task
task = task_template.format(domain=f"{agent_a.name} + {agent_b.name}")
# Step 3: Both agents initiate collaboration using precomputed collision
context_a = agent_a.initiate_collaboration(agent_b, task, collision=collision)
context_b = agent_b.initiate_collaboration(agent_a, task, collision=collision)
if not context_a or not context_b:
print(f" Collaboration initiation failed")
continue
# Step 4: Create session
session = CollaborationSession(
session_id=context_a.collaboration_id,
agent_a=agent_a.name,
agent_b=agent_b.name,
context=context_a,
projectors=agent_a.spawned_projectors.copy()
)
# Step 5: Simulate projector execution
projector_outputs = self._simulate_projectors(session)
# Step 6: Critique using shared metric signature
critiques = self._coordinate_critique(agent_a, agent_b, projector_outputs)
session.critiques = critiques
# Step 7: Integrate results
integrated = agent_a.integrate_results(projector_outputs, critiques)
session.integrated_result = integrated
# Step 8: Update learned patterns
self._update_learned_patterns(agent_a, agent_b, collision)
# Step 9: Update metrics
self._update_metrics(collision, session)
sessions.append(session)
self.sessions.append(session)
print(f" Session complete: {integrated.get('final_invariant', 0.0):.3f}")
return sessions
def _simulate_projectors(
self,
session: CollaborationSession
) -> List[Dict[str, Any]]:
"""Simulate projector agent execution"""
outputs = []
for projector in session.projectors:
# Simulate execution in specific universe
output = {
"projector_id": projector["id"],
"universe": projector["universe"],
"content": {
"analysis": f"Analysis from {projector['universe']} perspective",
"confidence": random.uniform(0.6, 0.95)
}
}
outputs.append(output)
return outputs
def _coordinate_critique(
self,
agent_a: GeoWeirdAwareAgent,
agent_b: GeoWeirdAwareAgent,
projector_outputs: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
"""Coordinate critique between agents using shared metric"""
critiques = []
# Agent A critiques
for output in projector_outputs:
critique = agent_a.critique_output(
output=json.dumps(output),
criteria=["completeness", "consistency", "novelty"],
other_agent=agent_b
)
critiques.append(critique)
# Agent B critiques
for output in projector_outputs:
critique = agent_b.critique_output(
output=json.dumps(output),
criteria=["completeness", "consistency", "novelty"],
other_agent=agent_a
)
critiques.append(critique)
return critiques
def _update_learned_patterns(
self,
agent_a: GeoWeirdAwareAgent,
agent_b: GeoWeirdAwareAgent,
collision: CollisionResult
):
"""Update learned successful collaboration patterns"""
# Record successful pair
pair = tuple(sorted([agent_a.name, agent_b.name]))
if pair not in self.successful_pairs:
self.successful_pairs.append(pair)
# Update universe preferences
self.universe_preferences[agent_a.name][collision.universe_a] += collision.consensus_strength
self.universe_preferences[agent_b.name][collision.universe_b] += collision.consensus_strength
# Update agents' internal preferences
self.bridge.update_domain_after_collision(agent_a.name, collision)
self.bridge.update_domain_after_collision(agent_b.name, collision)
def _update_metrics(
self,
collision: CollisionResult,
session: CollaborationSession
):
"""Update swarm metrics"""
self.metrics.total_collaborations += 1
self.metrics.successful_collaborations += 1
# Universe distribution
u_name = collision.universe_a.value
self.metrics.universe_distribution[u_name] = \
self.metrics.universe_distribution.get(u_name, 0) + 1
# Perspective distribution
p_name = collision.perspective.value
self.metrics.perspective_distribution[p_name] = \
self.metrics.perspective_distribution.get(p_name, 0) + 1
# Average consensus (EMA)
self.metrics.average_consensus = (
0.9 * self.metrics.average_consensus +
0.1 * collision.consensus_strength
)
# ========================================================================
# CONVERGENCE DETECTION
# ========================================================================
def check_convergence(self, threshold: float = 0.8) -> Dict[str, UniverseType]:
"""Check which agents have converged on single universe type"""
converged = {}
for name, agent in self.agents.items():
u_type = self.bridge.has_converged(name, threshold)
if u_type:
converged[name] = u_type
self.metrics.convergence_rate = len(converged) / len(self.agents) if self.agents else 0.0
return converged
def run_to_convergence(
self,
max_rounds: int = 10,
convergence_threshold: float = 0.8
) -> List[CollaborationSession]:
"""Run collaboration rounds until agents converge"""
all_sessions = []
for round_num in range(max_rounds):
print(f"\n{'='*60}")
print(f"COLLABORATION ROUND {round_num + 1}/{max_rounds}")
print(f"{'='*60}")
sessions = self.run_collaboration_round()
all_sessions.extend(sessions)
# Check convergence
converged = self.check_convergence(convergence_threshold)
print(f"\nConverged agents: {len(converged)}/{len(self.agents)}")
if len(converged) == len(self.agents):
print("\n✓ All agents converged!")
break
return all_sessions
# ========================================================================
# REPORTING
# ========================================================================
def generate_report(self) -> Dict[str, Any]:
"""Generate comprehensive swarm report"""
# Learned rules from collision history
learned_rules = self.bridge.get_learned_rules()
# Converged agents
converged = self.check_convergence()
return {
"metrics": {
"total_collaborations": self.metrics.total_collaborations,
"successful_collaborations": self.metrics.successful_collaborations,
"average_consensus": self.metrics.average_consensus,
"convergence_rate": self.metrics.convergence_rate,
"universe_distribution": self.metrics.universe_distribution,
"perspective_distribution": self.metrics.perspective_distribution
},
"learned_rules": learned_rules,
"converged_agents": {
name: u_type.value for name, u_type in converged.items()
},
"sessions": [s.to_dict() for s in self.sessions],
"successful_pairs": list(self.successful_pairs)
}
def print_report(self):
"""Print formatted report"""
report = self.generate_report()
print("\n" + "="*60)
print("GEOWEIRD SWARM ORCHESTRATOR REPORT")
print("="*60)
print("\n📊 Metrics:")
print(f" Total collaborations: {report['metrics']['total_collaborations']}")
print(f" Successful: {report['metrics']['successful_collaborations']}")
print(f" Average consensus: {report['metrics']['average_consensus']:.3f}")
print(f" Convergence rate: {report['metrics']['convergence_rate']:.1%}")
print("\n🌌 Universe Distribution:")
for u_name, count in report['metrics']['universe_distribution'].items():
print(f" {u_name}: {count}")
print("\n👁 Perspective Distribution:")
for p_name, count in report['metrics']['perspective_distribution'].items():
print(f" {p_name}: {count}")
print("\n📜 Learned Rules:")
for rule in report['learned_rules']:
print(f" {rule['universe_type']}: conf={rule['confidence']:.2f}, "
f"evidence={rule['evidence_count']}")
print("\n✓ Converged Agents:")
for name, u_type in report['converged_agents'].items():
print(f" {name}: {u_type}")
print("\n" + "="*60)
# ================================================================================
# MAIN ENTRY POINT
# ================================================================================
def run_geo_weird_swarm(
md_path: Optional[Path] = None,
max_rounds: int = 5,
consensus_threshold: float = 0.5
) -> Dict[str, Any]:
"""
Run complete GeoWeird swarm with self-typing.
Usage:
result = run_geo_weird_swarm(
md_path=Path("EXHAUSTIVE_DOMAIN_EXPERT_LIST.md"),
max_rounds=10
)
"""
# Initialize self-typing bridge
bridge = init_self_typing_bridge()
# Create orchestrator
orchestrator = GeoWeirdSwarmOrchestrator(
self_typing_bridge=bridge,
consensus_threshold=consensus_threshold
)
# Load agents
if md_path and md_path.exists():
orchestrator.load_from_markdown(md_path)
else:
# Create demo agents
demo_agents = [
create_geo_weird_agent("Lighthouse Keeper", constraints_7d={
"T": 0.8, "S": 0.7, "C": 0.6, "F": 0.5, "R": 0.9, "P": 0.4, "W": 0.3
}),
create_geo_weird_agent("Maritime Chart", constraints_7d={
"T": 0.3, "S": 0.9, "C": 0.2, "F": 0.4, "R": 0.1, "P": 0.6, "W": 0.2
}),
create_geo_weird_agent("Fog Signal", constraints_7d={
"T": 0.9, "S": 0.3, "C": 0.7, "F": 0.6, "R": 0.2, "P": 0.5, "W": 0.8
}),
create_geo_weird_agent("Lens Prism", constraints_7d={
"T": 0.4, "S": 0.6, "C": 0.3, "F": 0.8, "R": 0.95, "P": 0.7, "W": 0.4
}),
]
orchestrator.register_agents(demo_agents)
print(f"Loaded {len(orchestrator.agents)} agents")
# Run to convergence
sessions = orchestrator.run_to_convergence(max_rounds=max_rounds)
# Generate report
orchestrator.print_report()
return orchestrator.generate_report()
if __name__ == "__main__":
# Demo run
result = run_geo_weird_swarm()
print("\n" + json.dumps(result, indent=2))