Files
gh-francyjglisboa-agent-ski…/integrations/agentdb_bridge.py
2025-11-29 18:27:25 +08:00

753 lines
27 KiB
Python

#!/usr/bin/env python3
"""
AgentDB Bridge - Invisible Intelligence Layer
This module provides seamless AgentDB integration that is completely transparent
to the end user. All complexity is hidden behind simple interfaces.
The user never needs to know AgentDB exists - they just get smarter agents.
Principles:
- Zero configuration required
- Automatic setup and maintenance
- Graceful fallback if AgentDB unavailable
- Progressive enhancement without user awareness
"""
import json
import os
import subprocess
import logging
from pathlib import Path
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
from datetime import datetime
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class AgentDBIntelligence:
"""Container for AgentDB-enhanced decision making"""
template_choice: Optional[str] = None
success_probability: float = 0.0
learned_improvements: List[str] = None
historical_context: Dict[str, Any] = None
mathematical_proof: Optional[str] = None
def __post_init__(self):
if self.learned_improvements is None:
self.learned_improvements = []
if self.historical_context is None:
self.historical_context = {}
class AgentDBBridge:
"""
Invisible AgentDB integration layer.
Provides AgentDB capabilities without exposing complexity to users.
All AgentDB operations happen transparently behind the scenes.
"""
def __init__(self):
self.is_available = False
self.is_configured = False
self.error_count = 0
self.max_errors = 3 # Graceful fallback after 3 errors
# Initialize silently
self._initialize_silently()
def _initialize_silently(self):
"""Initialize AgentDB silently without user intervention"""
try:
# Step 1: Try detection first (current behavior)
cli_available = self._check_cli_availability()
npx_available = self._check_npx_availability()
if cli_available or npx_available:
self.is_available = True
self.use_cli = cli_available # Prefer native CLI
self._auto_configure()
logger.info("AgentDB initialized successfully (invisible mode)")
return
# Step 2: Try automatic installation if not found
logger.info("AgentDB not found - attempting automatic installation")
if self._attempt_automatic_install():
logger.info("AgentDB automatically installed and configured")
return
# Step 3: Fallback mode if installation fails
logger.info("AgentDB not available - using fallback mode")
except Exception as e:
logger.info(f"AgentDB initialization failed: {e} - using fallback mode")
def _check_cli_availability(self) -> bool:
"""Check if AgentDB native CLI is available"""
try:
result = subprocess.run(
["agentdb", "--help"],
capture_output=True,
text=True,
timeout=10
)
return result.returncode == 0
except (FileNotFoundError, subprocess.TimeoutExpired):
return False
def _check_npx_availability(self) -> bool:
"""Check if AgentDB is available via npx"""
try:
result = subprocess.run(
["npx", "@anthropic-ai/agentdb", "--help"],
capture_output=True,
text=True,
timeout=10
)
return result.returncode == 0
except (FileNotFoundError, subprocess.TimeoutExpired):
return False
def _attempt_automatic_install(self) -> bool:
"""Attempt to install AgentDB automatically"""
try:
# Check if npm is available first
if not self._check_npm_availability():
logger.info("npm not available - cannot install AgentDB automatically")
return False
# Try installation methods in order of preference
installation_methods = [
self._install_npm_global,
self._install_npx_fallback
]
for method in installation_methods:
try:
if method():
# Verify installation worked
if self._verify_installation():
self.is_available = True
self._auto_configure()
logger.info("AgentDB automatically installed and configured")
return True
except Exception as e:
logger.info(f"Installation method failed: {e}")
continue
logger.info("All automatic installation methods failed")
return False
except Exception as e:
logger.info(f"Automatic installation failed: {e}")
return False
def _check_npm_availability(self) -> bool:
"""Check if npm is available"""
try:
result = subprocess.run(
["npm", "--version"],
capture_output=True,
text=True,
timeout=10
)
return result.returncode == 0
except (FileNotFoundError, subprocess.TimeoutExpired):
return False
def _install_npm_global(self) -> bool:
"""Install AgentDB globally via npm"""
try:
logger.info("Attempting npm global installation of AgentDB...")
result = subprocess.run(
["npm", "install", "-g", "@anthropic-ai/agentdb"],
capture_output=True,
text=True,
timeout=300 # 5 minutes timeout
)
if result.returncode == 0:
logger.info("npm global installation successful")
return True
else:
logger.info(f"npm global installation failed: {result.stderr}")
return False
except Exception as e:
logger.info(f"npm global installation error: {e}")
return False
def _install_npx_fallback(self) -> bool:
"""Try to use npx approach (doesn't require global installation)"""
try:
logger.info("Testing npx approach for AgentDB...")
# Test if npx can download and run agentdb
result = subprocess.run(
["npx", "@anthropic-ai/agentdb", "--version"],
capture_output=True,
text=True,
timeout=60
)
if result.returncode == 0:
logger.info("npx approach successful - AgentDB available via npx")
return True
else:
logger.info(f"npx approach failed: {result.stderr}")
return False
except Exception as e:
logger.info(f"npx approach error: {e}")
return False
def _verify_installation(self) -> bool:
"""Verify that AgentDB was installed successfully"""
try:
# Check CLI availability first
if self._check_cli_availability():
logger.info("AgentDB CLI verified after installation")
return True
# Check npx availability as fallback
if self._check_npx_availability():
logger.info("AgentDB npx availability verified after installation")
return True
logger.info("AgentDB installation verification failed")
return False
except Exception as e:
logger.info(f"Installation verification error: {e}")
return False
def _auto_configure(self):
"""Auto-configure AgentDB for optimal performance"""
try:
# Create default configuration
config = {
"reflexion": {
"auto_save": True,
"compression": True
},
"causal": {
"auto_track": True,
"utility_model": "outcome_based"
},
"skills": {
"auto_extract": True,
"success_threshold": 0.8
},
"nightly_learner": {
"enabled": True,
"schedule": "2:00 AM"
}
}
# Write configuration silently
config_path = Path.home() / ".agentdb" / "config.json"
config_path.parent.mkdir(exist_ok=True)
with open(config_path, 'w') as f:
json.dump(config, f, indent=2)
self.is_configured = True
logger.info("AgentDB auto-configured successfully")
except Exception as e:
logger.warning(f"AgentDB auto-configuration failed: {e}")
def enhance_agent_creation(self, user_input: str, domain: str = None) -> AgentDBIntelligence:
"""
Enhance agent creation with AgentDB intelligence.
Returns intelligence data transparently.
"""
intelligence = AgentDBIntelligence()
if not self.is_available or not self.is_configured:
return intelligence # Return empty intelligence for fallback
try:
# Use real AgentDB commands if CLI is available
if hasattr(self, 'use_cli') and self.use_cli:
intelligence = self._enhance_with_real_agentdb(user_input, domain)
else:
# Fallback to legacy implementation
intelligence = self._enhance_with_legacy_agentdb(user_input, domain)
# Store this decision for learning
self._store_creation_decision(user_input, intelligence)
logger.info(f"AgentDB enhanced creation: template={intelligence.template_choice}")
except Exception as e:
logger.warning(f"AgentDB enhancement failed: {e}")
# Return empty intelligence on error
self.error_count += 1
if self.error_count >= self.max_errors:
logger.warning("AgentDB error threshold reached, switching to fallback mode")
self.is_available = False
return intelligence
def _enhance_with_real_agentdb(self, user_input: str, domain: str = None) -> AgentDBIntelligence:
"""Enhance using real AgentDB CLI commands"""
intelligence = AgentDBIntelligence()
try:
# 1. Search for relevant skills
skills_result = self._execute_agentdb_command([
"agentdb" if self.use_cli else "npx", "agentdb", "skill", "search", user_input, "5"
])
if skills_result:
# Parse skills from output
skills = self._parse_skills_from_output(skills_result)
if skills:
intelligence.learned_improvements = [f"Skill available: {skill.get('name', 'unknown')}" for skill in skills[:3]]
# 2. Retrieve relevant episodes
episodes_result = self._execute_agentdb_command([
"agentdb" if self.use_cli else "npx", "agentdb", "reflexion", "retrieve", user_input, "3", "0.6"
])
if episodes_result:
episodes = self._parse_episodes_from_output(episodes_result)
if episodes:
success_rate = sum(1 for e in episodes if e.get('success', False)) / len(episodes)
intelligence.success_probability = success_rate
# 3. Query causal effects
if domain:
causal_result = self._execute_agentdb_command([
"agentdb" if self.use_cli else "npx", "agentdb", "causal", "query",
f"use_{domain}_template", "", "0.7", "0.1", "5"
])
if causal_result:
# Parse best causal effect
effects = self._parse_causal_effects_from_output(causal_result)
if effects:
best_effect = max(effects, key=lambda x: x.get('uplift', 0))
intelligence.template_choice = f"{domain}-analysis"
intelligence.mathematical_proof = f"Causal uplift: {best_effect.get('uplift', 0):.2%}"
logger.info(f"Real AgentDB enhancement completed for {domain}")
except Exception as e:
logger.error(f"Real AgentDB enhancement failed: {e}")
return intelligence
def _enhance_with_legacy_agentdb(self, user_input: str, domain: str = None) -> AgentDBIntelligence:
"""Enhance using legacy AgentDB implementation"""
intelligence = AgentDBIntelligence()
try:
# Legacy implementation using npx
template_result = self._execute_agentdb_command([
"npx", "agentdb", "causal", "recall",
f"best_template_for_domain:{domain or 'unknown'}",
"--format", "json"
])
if template_result:
intelligence.template_choice = self._parse_template_result(template_result)
intelligence.success_probability = self._calculate_success_probability(
intelligence.template_choice, domain
)
# Get learned improvements
improvements_result = self._execute_agentdb_command([
"npx", "agentdb", "skills", "list",
f"domain:{domain or 'unknown'}",
"--success-rate", "0.8"
])
if improvements_result:
intelligence.learned_improvements = self._parse_improvements(improvements_result)
logger.info(f"Legacy AgentDB enhancement completed for {domain}")
except Exception as e:
logger.error(f"Legacy AgentDB enhancement failed: {e}")
return intelligence
def _parse_skills_from_output(self, output: str) -> List[Dict[str, Any]]:
"""Parse skills from AgentDB CLI output"""
skills = []
lines = output.split('\n')
current_skill = {}
for line in lines:
line = line.strip()
if line.startswith("#") and "Found" not in line:
if current_skill:
skills.append(current_skill)
skill_name = line.replace("#1:", "").strip()
current_skill = {"name": skill_name}
elif ":" in line and current_skill:
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
if key == "Description":
current_skill["description"] = value
elif key == "Success Rate":
try:
current_skill["success_rate"] = float(value.replace("%", "")) / 100
except ValueError:
pass
if current_skill:
skills.append(current_skill)
return skills
def _parse_episodes_from_output(self, output: str) -> List[Dict[str, Any]]:
"""Parse episodes from AgentDB CLI output"""
episodes = []
lines = output.split('\n')
current_episode = {}
for line in lines:
line = line.strip()
if line.startswith("#") and "Episode" in line:
if current_episode:
episodes.append(current_episode)
current_episode = {"episode_id": line.split()[1].replace(":", "")}
elif ":" in line and current_episode:
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
if key == "Task":
current_episode["task"] = value
elif key == "Success":
current_episode["success"] = "Yes" in value
elif key == "Reward":
try:
current_episode["reward"] = float(value)
except ValueError:
pass
if current_episode:
episodes.append(current_episode)
return episodes
def _parse_causal_effects_from_output(self, output: str) -> List[Dict[str, Any]]:
"""Parse causal effects from AgentDB CLI output"""
effects = []
lines = output.split('\n')
for line in lines:
if "" in line and "uplift" in line.lower():
parts = line.split("")
if len(parts) >= 2:
cause = parts[0].strip()
effect_rest = parts[1]
effect = effect_rest.split("(")[0].strip()
uplift = 0.0
if "uplift:" in effect_rest:
uplift_part = effect_rest.split("uplift:")[1].split(",")[0].strip()
try:
uplift = float(uplift_part)
except ValueError:
pass
effects.append({
"cause": cause,
"effect": effect,
"uplift": uplift
})
return effects
def _execute_agentdb_command(self, command: List[str]) -> Optional[str]:
"""Execute AgentDB command and return output"""
try:
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=30,
cwd=str(Path.cwd())
)
if result.returncode == 0:
return result.stdout.strip()
else:
logger.debug(f"AgentDB command failed: {result.stderr}")
return None
except Exception as e:
logger.debug(f"AgentDB command execution failed: {e}")
return None
def _parse_template_result(self, result: str) -> Optional[str]:
"""Parse template selection result"""
try:
if result.strip().startswith('{'):
data = json.loads(result)
return data.get('template', 'default')
else:
return result.strip()
except:
return None
def _parse_improvements(self, result: str) -> List[str]:
"""Parse learned improvements result"""
try:
if result.strip().startswith('{'):
data = json.loads(result)
return data.get('improvements', [])
else:
return [line.strip() for line in result.split('\n') if line.strip()]
except:
return []
def _calculate_success_probability(self, template: str, domain: str) -> float:
"""Calculate success probability based on historical data"""
# Simplified calculation - in real implementation this would query AgentDB
base_prob = 0.8 # Base success rate
# Increase probability for templates with good history
if template and "financial" in template.lower():
base_prob += 0.1
if template and "analysis" in template.lower():
base_prob += 0.05
return min(base_prob, 0.95) # Cap at 95%
def _store_creation_decision(self, user_input: str, intelligence: AgentDBIntelligence):
"""Store creation decision for learning"""
if not self.is_available:
return
try:
# Create session ID
session_id = f"creation-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
# Store reflexion data
self._execute_agentdb_command([
"npx", "agentdb", "reflexion", "store",
session_id,
"agent_creation_decision",
str(intelligence.success_probability * 100)
])
# Store causal relationship
if intelligence.template_choice:
self._execute_agentdb_command([
"npx", "agentdb", "causal", "store",
f"user_input:{user_input[:50]}...",
f"template_selected:{intelligence.template_choice}",
"created_successfully"
])
logger.info(f"Stored creation decision: {session_id}")
except Exception as e:
logger.debug(f"Failed to store creation decision: {e}")
def enhance_template(self, template_name: str, domain: str) -> Dict[str, Any]:
"""
Enhance template with learned improvements
"""
enhancements = {
"agentdb_integration": {
"enabled": self.is_available,
"success_rate": 0.0,
"learned_improvements": [],
"historical_usage": 0
}
}
if not self.is_available:
return enhancements
try:
# Get historical success rate
success_result = self._execute_agentdb_command([
"npx", "agentdb", "causal", "recall",
f"template_success_rate:{template_name}"
])
if success_result:
try:
success_data = json.loads(success_result)
enhancements["agentdb_integration"]["success_rate"] = success_data.get("success_rate", 0.8)
enhancements["agentdb_integration"]["historical_usage"] = success_data.get("usage_count", 0)
except:
enhancements["agentdb_integration"]["success_rate"] = 0.8
# Get learned improvements
improvements_result = self._execute_agentdb_command([
"npx", "agentdb", "skills", "list",
f"template:{template_name}"
])
if improvements_result:
enhancements["agentdb_integration"]["learned_improvements"] = self._parse_improvements(improvements_result)
logger.info(f"Template {template_name} enhanced with AgentDB intelligence")
except Exception as e:
logger.debug(f"Failed to enhance template {template_name}: {e}")
return enhancements
def store_agent_experience(self, agent_name: str, experience: Dict[str, Any]):
"""
Store agent experience for learning
"""
if not self.is_available:
return
try:
session_id = f"agent-{agent_name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
# Store reflexion
success_rate = experience.get('success_rate', 0.5)
self._execute_agentdb_command([
"npx", "agentdb", "reflexion", "store",
session_id,
"agent_execution",
str(int(success_rate * 100))
])
# Store causal relationships
for cause, effect in experience.get('causal_observations', {}).items():
self._execute_agentdb_command([
"npx", "agentdb", "causal", "store",
str(cause),
str(effect),
"agent_observation"
])
# Extract skills if successful
if success_rate > 0.8:
for skill_data in experience.get('successful_skills', []):
self._execute_agentdb_command([
"npx", "agentdb", "skills", "store",
skill_data.get('name', 'unnamed_skill'),
json.dumps(skill_data)
])
logger.info(f"Stored experience for agent: {agent_name}")
except Exception as e:
logger.debug(f"Failed to store agent experience: {e}")
def get_learning_summary(self, agent_name: str) -> Dict[str, Any]:
"""
Get learning summary for an agent (for internal use)
"""
summary = {
"total_sessions": 0,
"success_rate": 0.0,
"learned_skills": [],
"causal_patterns": []
}
if not self.is_available:
return summary
try:
# Get reflexion history
reflexion_result = self._execute_agentdb_command([
"npx", "agentdb", "reflexion", "recall",
f"agent:{agent_name}",
"--format", "json"
])
if reflexion_result:
try:
data = json.loads(reflexion_result)
summary["total_sessions"] = len(data.get('sessions', []))
if data.get('sessions'):
rewards = [s.get('reward', 0) for s in data['sessions']]
summary["success_rate"] = sum(rewards) / len(rewards) / 100
except:
pass
# Get learned skills
skills_result = self._execute_agentdb_command([
"npx", "agentdb", "skills", "list",
f"agent:{agent_name}"
])
if skills_result:
summary["learned_skills"] = self._parse_improvements(skills_result)
# Get causal patterns
causal_result = self._execute_agentdb_command([
"npx", "agentdb", "causal", "recall",
f"agent:{agent_name}",
"--format", "json"
])
if causal_result:
try:
data = json.loads(causal_result)
summary["causal_patterns"] = data.get('patterns', [])
except:
pass
except Exception as e:
logger.debug(f"Failed to get learning summary for {agent_name}: {e}")
return summary
# Global instance - invisible to users
_agentdb_bridge = None
def get_agentdb_bridge() -> AgentDBBridge:
"""Get the global AgentDB bridge instance"""
global _agentdb_bridge
if _agentdb_bridge is None:
_agentdb_bridge = AgentDBBridge()
return _agentdb_bridge
def enhance_agent_creation(user_input: str, domain: str = None) -> AgentDBIntelligence:
"""
Public interface for enhancing agent creation with AgentDB intelligence.
This is what the Agent-Creator calls internally.
The user never calls this directly - it's all hidden behind the scenes.
"""
bridge = get_agentdb_bridge()
return bridge.enhance_agent_creation(user_input, domain)
def enhance_template(template_name: str, domain: str) -> Dict[str, Any]:
"""
Enhance a template with AgentDB learned improvements.
Called internally during template selection.
"""
bridge = get_agentdb_bridge()
return bridge.enhance_template(template_name, domain)
def store_agent_experience(agent_name: str, experience: Dict[str, Any]):
"""
Store agent execution experience for learning.
Called internally after agent execution.
"""
bridge = get_agentdb_bridge()
bridge.store_agent_experience(agent_name, experience)
def get_agent_learning_summary(agent_name: str) -> Dict[str, Any]:
"""
Get learning summary for an agent.
Used internally for progress tracking.
"""
bridge = get_agentdb_bridge()
return bridge.get_learning_summary(agent_name)
# Auto-initialize when module is imported
get_agentdb_bridge()