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
|
|
|
use crate::config::ProviderConfig;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
/// Provider cost and efficiency score for decision making.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ProviderCostScore {
|
|
|
|
|
/// Provider name (claude, gpt4, gemini, ollama)
|
|
|
|
|
pub provider: String,
|
|
|
|
|
/// Estimated cost in cents for the token count
|
|
|
|
|
pub estimated_cost_cents: u32,
|
|
|
|
|
/// Quality score (0.0-1.0) from KG success rate for task type
|
|
|
|
|
pub quality_score: f64,
|
|
|
|
|
/// Cost efficiency: (quality_score * 100) / (cost_cents + 1)
|
|
|
|
|
/// Prevents division by zero for Ollama (free = $0)
|
|
|
|
|
pub cost_efficiency: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Service for ranking providers by cost efficiency.
|
|
|
|
|
pub struct CostRanker;
|
|
|
|
|
|
|
|
|
|
impl CostRanker {
|
|
|
|
|
/// Estimate cost in cents for token usage on provider.
|
|
|
|
|
/// Formula: (input_tokens * rate_in + output_tokens * rate_out) / 1M * 100
|
|
|
|
|
/// Costs are stored in dollars, converted to cents for calculation.
|
2026-01-11 21:32:56 +00:00
|
|
|
pub fn estimate_cost(config: &ProviderConfig, input_tokens: u64, output_tokens: u64) -> u32 {
|
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
|
|
|
// Convert dollar rates to cents
|
|
|
|
|
let input_cost_cents = config.cost_per_1m_input * 100.0;
|
|
|
|
|
let output_cost_cents = config.cost_per_1m_output * 100.0;
|
|
|
|
|
|
|
|
|
|
let input_total = (input_tokens as f64 * input_cost_cents) / 1_000_000.0;
|
|
|
|
|
let output_total = (output_tokens as f64 * output_cost_cents) / 1_000_000.0;
|
|
|
|
|
|
|
|
|
|
(input_total + output_total).round() as u32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get quality score for provider + task type.
|
|
|
|
|
/// In practice, queries KG for success rate. For now, uses provided value.
|
|
|
|
|
pub fn get_quality_score(provider: &str, task_type: &str, _quality_data: Option<f64>) -> f64 {
|
|
|
|
|
// Default quality scores until KG integration provides actual metrics
|
|
|
|
|
match (provider, task_type) {
|
2026-01-11 21:32:56 +00:00
|
|
|
("claude", _) => 0.95, // Highest quality
|
|
|
|
|
("gpt4", _) => 0.92, // Very good
|
|
|
|
|
("gemini", _) => 0.88, // Good
|
|
|
|
|
("ollama", _) => 0.75, // Decent for local
|
|
|
|
|
(_, _) => 0.5, // Unknown
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Rank providers by cost efficiency.
|
|
|
|
|
/// Formula: efficiency = (quality_score * 100) / (cost_cents + 1)
|
|
|
|
|
/// Higher efficiency = better value for money.
|
|
|
|
|
/// Ordered by efficiency descending (best value first).
|
|
|
|
|
pub fn rank_by_efficiency(
|
|
|
|
|
providers: Vec<(String, ProviderConfig)>,
|
|
|
|
|
task_type: &str,
|
|
|
|
|
input_tokens: u64,
|
|
|
|
|
output_tokens: u64,
|
|
|
|
|
) -> Vec<ProviderCostScore> {
|
|
|
|
|
let mut scores: Vec<ProviderCostScore> = providers
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|(provider_name, config)| {
|
|
|
|
|
let cost = Self::estimate_cost(&config, input_tokens, output_tokens);
|
|
|
|
|
let quality = Self::get_quality_score(&provider_name, task_type, None);
|
|
|
|
|
let efficiency = (quality * 100.0) / (cost as f64 + 1.0);
|
|
|
|
|
|
|
|
|
|
ProviderCostScore {
|
|
|
|
|
provider: provider_name,
|
|
|
|
|
estimated_cost_cents: cost,
|
|
|
|
|
quality_score: quality,
|
|
|
|
|
cost_efficiency: efficiency,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// Sort by efficiency descending (best value first)
|
|
|
|
|
scores.sort_by(|a, b| {
|
|
|
|
|
b.cost_efficiency
|
|
|
|
|
.partial_cmp(&a.cost_efficiency)
|
|
|
|
|
.unwrap_or(std::cmp::Ordering::Equal)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
scores
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Select cheapest provider when budget is tight.
|
|
|
|
|
/// Orders by cost ascending (cheapest first).
|
|
|
|
|
pub fn rank_by_cost(
|
|
|
|
|
providers: Vec<(String, ProviderConfig)>,
|
|
|
|
|
input_tokens: u64,
|
|
|
|
|
output_tokens: u64,
|
|
|
|
|
) -> Vec<ProviderCostScore> {
|
|
|
|
|
let mut scores: Vec<ProviderCostScore> = providers
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|(provider_name, config)| {
|
|
|
|
|
let cost = Self::estimate_cost(&config, input_tokens, output_tokens);
|
|
|
|
|
let quality = Self::get_quality_score(&provider_name, "generic", None);
|
|
|
|
|
let efficiency = (quality * 100.0) / (cost as f64 + 1.0);
|
|
|
|
|
|
|
|
|
|
ProviderCostScore {
|
|
|
|
|
provider: provider_name,
|
|
|
|
|
estimated_cost_cents: cost,
|
|
|
|
|
quality_score: quality,
|
|
|
|
|
cost_efficiency: efficiency,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// Sort by cost ascending (cheapest first)
|
|
|
|
|
scores.sort_by_key(|s| s.estimated_cost_cents);
|
|
|
|
|
|
|
|
|
|
scores
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Calculate cost-benefit ratio for task.
|
|
|
|
|
/// Returns tuple: (provider, cost_cents, efficiency_score)
|
|
|
|
|
pub fn cost_benefit_ratio(
|
|
|
|
|
providers: Vec<(String, ProviderConfig)>,
|
|
|
|
|
task_type: &str,
|
|
|
|
|
input_tokens: u64,
|
|
|
|
|
output_tokens: u64,
|
|
|
|
|
) -> Vec<(String, u32, f64)> {
|
|
|
|
|
let ranked = Self::rank_by_efficiency(providers, task_type, input_tokens, output_tokens);
|
|
|
|
|
ranked
|
|
|
|
|
.into_iter()
|
2026-01-11 21:32:56 +00:00
|
|
|
.map(|score| {
|
|
|
|
|
(
|
|
|
|
|
score.provider,
|
|
|
|
|
score.estimated_cost_cents,
|
|
|
|
|
score.cost_efficiency,
|
|
|
|
|
)
|
|
|
|
|
})
|
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
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_get_quality_score() {
|
|
|
|
|
let claude_quality = CostRanker::get_quality_score("claude", "coding", None);
|
|
|
|
|
assert_eq!(claude_quality, 0.95);
|
|
|
|
|
|
|
|
|
|
let ollama_quality = CostRanker::get_quality_score("ollama", "coding", None);
|
|
|
|
|
assert_eq!(ollama_quality, 0.75);
|
|
|
|
|
|
|
|
|
|
let unknown_quality = CostRanker::get_quality_score("unknown", "coding", None);
|
|
|
|
|
assert_eq!(unknown_quality, 0.5);
|
|
|
|
|
}
|
|
|
|
|
}
|