157 lines
5.9 KiB
Rust
Raw Normal View History

feat: Phase 5.3 - Multi-Agent Learning Infrastructure Implement intelligent agent learning from Knowledge Graph execution history with per-task-type expertise tracking, recency bias, and learning curves. ## Phase 5.3 Implementation ### Learning Infrastructure (✅ Complete) - LearningProfileService with per-task-type expertise metrics - TaskTypeExpertise model tracking success_rate, confidence, learning curves - Recency bias weighting: recent 7 days weighted 3x higher (exponential decay) - Confidence scoring prevents overfitting: min(1.0, executions / 20) - Learning curves computed from daily execution windows ### Agent Scoring Service (✅ Complete) - Unified AgentScore combining SwarmCoordinator + learning profiles - Scoring formula: 0.3*base + 0.5*expertise + 0.2*confidence - Rank agents by combined score for intelligent assignment - Support for recency-biased scoring (recent_success_rate) - Methods: rank_agents, select_best, rank_agents_with_recency ### KG Integration (✅ Complete) - KGPersistence::get_executions_for_task_type() - query by agent + task type - KGPersistence::get_agent_executions() - all executions for agent - Coordinator::load_learning_profile_from_kg() - core KG→Learning integration - Coordinator::load_all_learning_profiles() - batch load for multiple agents - Convert PersistedExecution → ExecutionData for learning calculations ### Agent Assignment Integration (✅ Complete) - AgentCoordinator uses learning profiles for task assignment - extract_task_type() infers task type from title/description - assign_task() scores candidates using AgentScoringService - Fallback to load-based selection if no learning data available - Learning profiles stored in coordinator.learning_profiles RwLock ### Profile Adapter Enhancements (✅ Complete) - create_learning_profile() - initialize empty profiles - add_task_type_expertise() - set task-type expertise - update_profile_with_learning() - update swarm profiles from learning ## Files Modified ### vapora-knowledge-graph/src/persistence.rs (+30 lines) - get_executions_for_task_type(agent_id, task_type, limit) - get_agent_executions(agent_id, limit) ### vapora-agents/src/coordinator.rs (+100 lines) - load_learning_profile_from_kg() - core KG integration method - load_all_learning_profiles() - batch loading for agents - assign_task() already uses learning-based scoring via AgentScoringService ### Existing Complete Implementation - vapora-knowledge-graph/src/learning.rs - calculation functions - vapora-agents/src/learning_profile.rs - data structures and expertise - vapora-agents/src/scoring.rs - unified scoring service - vapora-agents/src/profile_adapter.rs - adapter methods ## Tests Passing - learning_profile: 7 tests ✅ - scoring: 5 tests ✅ - profile_adapter: 6 tests ✅ - coordinator: learning-specific tests ✅ ## Data Flow 1. Task arrives → AgentCoordinator::assign_task() 2. Extract task_type from description 3. Query KG for task-type executions (load_learning_profile_from_kg) 4. Calculate expertise with recency bias 5. Score candidates (SwarmCoordinator + learning) 6. Assign to top-scored agent 7. Execution result → KG → Update learning profiles ## Key Design Decisions ✅ Recency bias: 7-day half-life with 3x weight for recent performance ✅ Confidence scoring: min(1.0, total_executions / 20) prevents overfitting ✅ Hierarchical scoring: 30% base load, 50% expertise, 20% confidence ✅ KG query limit: 100 recent executions per task-type for performance ✅ Async loading: load_learning_profile_from_kg supports concurrent loads ## Next: Phase 5.4 - Cost Optimization Ready to implement budget enforcement and cost-aware provider selection.
2026-01-11 13:03:53 +00:00
// vapora-backend: REST API server for VAPORA v1.0
// Phase 1: Complete backend with SurrealDB integration
mod api;
mod config;
mod services;
use anyhow::Result;
use axum::{
routing::{delete, get, post, put},
Extension, Router,
};
use std::sync::Arc;
use std::net::SocketAddr;
use tower_http::cors::{Any, CorsLayer};
use tracing::{info, Level};
use vapora_swarm::{SwarmCoordinator, SwarmMetrics};
use crate::api::AppState;
use crate::config::Config;
use crate::services::{AgentService, ProjectService, TaskService};
#[tokio::main]
async fn main() -> Result<()> {
// Load environment variables from .env file if present
dotenv::dotenv().ok();
// Initialize logging
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.with_target(false)
.compact()
.init();
info!("VAPORA Backend v{}", env!("CARGO_PKG_VERSION"));
info!("Phase 1: Backend Core + SurrealDB");
// Load configuration
let config = Config::load("config/vapora.toml")?;
info!("Configuration loaded successfully");
// Connect to SurrealDB
info!("Connecting to SurrealDB at {}", config.database.url);
let db = surrealdb::Surreal::new::<surrealdb::engine::remote::ws::Ws>(&config.database.url)
.await?;
// Sign in to database
db.signin(surrealdb::opt::auth::Root {
username: "root",
password: "root",
})
.await?;
// Use namespace and database
db.use_ns("vapora").use_db("main").await?;
info!("Connected to SurrealDB");
// Initialize services
let project_service = ProjectService::new(db.clone());
let task_service = TaskService::new(db.clone());
let agent_service = AgentService::new(db.clone());
// Create application state
let app_state = AppState::new(project_service, task_service, agent_service);
// Create SwarmMetrics for Prometheus monitoring
let metrics = match SwarmMetrics::new() {
Ok(m) => {
info!("SwarmMetrics initialized for Prometheus monitoring");
m
}
Err(e) => {
tracing::warn!("Failed to initialize SwarmMetrics: {:?}, continuing without metrics", e);
// Create new registry and metrics as fallback
SwarmMetrics::new().unwrap()
}
};
// Create SwarmCoordinator for multi-agent coordination
let mut swarm_coordinator = SwarmCoordinator::new();
swarm_coordinator.set_metrics(Arc::clone(&metrics));
let swarm_coordinator = Arc::new(swarm_coordinator);
info!("SwarmCoordinator initialized for Phase 5.2");
// Configure CORS
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
// Build router
let app = Router::new()
// Health endpoint
.route("/health", get(api::health::health))
// Metrics endpoint (Prometheus)
.route("/metrics", get(api::metrics::metrics_handler))
// Project endpoints
.route("/api/v1/projects", get(api::projects::list_projects).post(api::projects::create_project))
.route(
"/api/v1/projects/:id",
get(api::projects::get_project)
.put(api::projects::update_project)
.delete(api::projects::delete_project),
)
.route("/api/v1/projects/:id/features", post(api::projects::add_feature))
.route("/api/v1/projects/:id/features/:feature", delete(api::projects::remove_feature))
.route("/api/v1/projects/:id/archive", post(api::projects::archive_project))
// Task endpoints
.route("/api/v1/tasks", get(api::tasks::list_tasks).post(api::tasks::create_task))
.route(
"/api/v1/tasks/:id",
get(api::tasks::get_task)
.put(api::tasks::update_task)
.delete(api::tasks::delete_task),
)
.route("/api/v1/tasks/:id/reorder", put(api::tasks::reorder_task))
.route("/api/v1/tasks/:id/status", put(api::tasks::update_task_status))
.route("/api/v1/tasks/:id/assign", put(api::tasks::assign_task))
.route("/api/v1/tasks/:id/priority", put(api::tasks::update_priority))
// Agent endpoints (specific routes before parameterized routes)
.route("/api/v1/agents", get(api::agents::list_agents).post(api::agents::register_agent))
.route("/api/v1/agents/available", get(api::agents::get_available_agents))
.route(
"/api/v1/agents/:id",
get(api::agents::get_agent)
.put(api::agents::update_agent)
.delete(api::agents::deregister_agent),
)
.route("/api/v1/agents/:id/health", get(api::agents::check_agent_health))
.route("/api/v1/agents/:id/status", put(api::agents::update_agent_status))
.route("/api/v1/agents/:id/capabilities", post(api::agents::add_capability))
.route("/api/v1/agents/:id/capabilities/:capability", delete(api::agents::remove_capability))
.route("/api/v1/agents/:id/skills", post(api::agents::add_skill))
// Tracking endpoints
.route("/api/v1/tracking/entries", get(api::tracking::list_tracking_entries))
.route("/api/v1/tracking/summary", get(api::tracking::get_tracking_summary))
.route("/api/v1/tracking/health", get(api::tracking::tracking_health))
// Swarm endpoints (Phase 5.2)
.route("/api/v1/swarm/stats", get(api::swarm::swarm_statistics))
.route("/api/v1/swarm/health", get(api::swarm::swarm_health))
// Apply CORS, state, and extensions
.layer(Extension(swarm_coordinator))
.layer(cors)
.with_state(app_state);
// Start server
let addr = SocketAddr::from(([127, 0, 0, 1], config.server.port));
info!("Server listening on {}", addr);
info!("Health check: http://{}/health", addr);
info!("API documentation: http://{}/api/v1", addr);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}