Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
815
vendor/ruvector/examples/ruvLLM/docs/SONA/02-LEARNING-LOOPS.md
vendored
Normal file
815
vendor/ruvector/examples/ruvLLM/docs/SONA/02-LEARNING-LOOPS.md
vendored
Normal file
@@ -0,0 +1,815 @@
|
||||
# SONA Learning Loops: Three-Tier Temporal Architecture
|
||||
|
||||
## Biologically-Inspired Continuous Learning System
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview: Learning at Multiple Timescales
|
||||
|
||||
Human learning operates at multiple timescales:
|
||||
- **Instant**: Immediate response adjustment (milliseconds)
|
||||
- **Short-term**: Pattern consolidation (hours)
|
||||
- **Long-term**: Deep memory formation (days/weeks)
|
||||
|
||||
SONA replicates this with three learning loops:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SONA THREE-TIER LEARNING │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ LOOP A: INSTANT LOOP B: BACKGROUND │
|
||||
│ ═══════════════ ══════════════════ │
|
||||
│ Timescale: Per-request Timescale: Hourly │
|
||||
│ Latency: <1ms Latency: Background (async) │
|
||||
│ What learns: What learns: │
|
||||
│ • Micro-LoRA (rank 1-2) • Base LoRA (rank 4-16) │
|
||||
│ • Memory edge weights • Router weights (EWC++) │
|
||||
│ • Trajectory recording • Pattern extraction │
|
||||
│ │
|
||||
│ LOOP C: DEEP │
|
||||
│ ═══════════ │
|
||||
│ Timescale: Weekly │
|
||||
│ Latency: Scheduled maintenance │
|
||||
│ What learns: │
|
||||
│ • Memory consolidation │
|
||||
│ • Concept hierarchy building │
|
||||
│ • Dream-based creativity │
|
||||
│ • Cross-domain transfer │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Loop A: Instant Learning (Per-Request)
|
||||
|
||||
### Purpose
|
||||
Immediate adaptation to current interaction without noticeable latency.
|
||||
|
||||
### Architecture
|
||||
|
||||
```rust
|
||||
/// Loop A: Instant learning executed inline with each request
|
||||
pub struct InstantLearningLoop {
|
||||
/// Micro-LoRA for immediate weight adjustment
|
||||
micro_lora: Arc<RwLock<MicroLoRA>>,
|
||||
/// Trajectory buffer for pattern recording
|
||||
trajectory_buffer: Arc<TrajectoryBuffer>,
|
||||
/// Memory graph reference for edge updates
|
||||
memory_graph: Arc<RwLock<MemoryGraph>>,
|
||||
/// Signal accumulator for Loop B
|
||||
signal_accumulator: mpsc::Sender<LearningSignal>,
|
||||
}
|
||||
|
||||
impl InstantLearningLoop {
|
||||
/// Execute instant learning (must complete in <1ms)
|
||||
#[inline]
|
||||
pub async fn on_request(
|
||||
&self,
|
||||
query: &QueryEmbedding,
|
||||
response: &ResponseData,
|
||||
latency_ms: f32,
|
||||
) -> Result<()> {
|
||||
// Parallel execution of independent updates
|
||||
let (r1, r2, r3) = tokio::join!(
|
||||
// 1. Record trajectory (lock-free, ~100μs)
|
||||
self.record_trajectory(query, response),
|
||||
|
||||
// 2. Update memory edges (~200μs)
|
||||
self.update_memory_edges(query, response),
|
||||
|
||||
// 3. Micro-LoRA update (~300μs)
|
||||
self.micro_lora_update(query, response, latency_ms),
|
||||
);
|
||||
|
||||
// 4. Queue signal for Loop B (fire-and-forget)
|
||||
let signal = LearningSignal::new(query, response, latency_ms);
|
||||
let _ = self.signal_accumulator.try_send(signal);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Record query trajectory to ring buffer
|
||||
async fn record_trajectory(
|
||||
&self,
|
||||
query: &QueryEmbedding,
|
||||
response: &ResponseData,
|
||||
) -> Result<()> {
|
||||
let trajectory = QueryTrajectory {
|
||||
query_embedding: query.vector.clone(),
|
||||
retrieved_ids: response.used_memory_ids.clone(),
|
||||
precision: response.estimated_precision,
|
||||
recall: response.estimated_recall,
|
||||
timestamp: Instant::now(),
|
||||
};
|
||||
|
||||
self.trajectory_buffer.push(trajectory);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hebbian-style edge weight updates
|
||||
async fn update_memory_edges(
|
||||
&self,
|
||||
query: &QueryEmbedding,
|
||||
response: &ResponseData,
|
||||
) -> Result<()> {
|
||||
let mut graph = self.memory_graph.write();
|
||||
|
||||
for &node_id in &response.used_memory_ids {
|
||||
// Strengthen edges to used nodes
|
||||
graph.update_edge_weight(
|
||||
query.anchor_node,
|
||||
node_id,
|
||||
EdgeUpdate::Strengthen(0.05), // +5% per use
|
||||
)?;
|
||||
}
|
||||
|
||||
// Weaken edges to retrieved-but-unused nodes
|
||||
for &node_id in &response.retrieved_but_unused {
|
||||
graph.update_edge_weight(
|
||||
query.anchor_node,
|
||||
node_id,
|
||||
EdgeUpdate::Weaken(0.02), // -2% per skip
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ultra-fast micro-LoRA weight adjustment
|
||||
async fn micro_lora_update(
|
||||
&self,
|
||||
query: &QueryEmbedding,
|
||||
response: &ResponseData,
|
||||
latency_ms: f32,
|
||||
) -> Result<()> {
|
||||
let quality = response.quality_score;
|
||||
let latency_ratio = latency_ms / response.target_latency_ms;
|
||||
|
||||
// Only update if signal is informative
|
||||
if (quality - 0.5).abs() > 0.1 || latency_ratio > 1.2 {
|
||||
let signal = LearningSignal {
|
||||
query_embedding: query.vector.clone(),
|
||||
quality_score: quality,
|
||||
explicit_feedback: None,
|
||||
latency_ratio,
|
||||
model_tier: response.model_tier,
|
||||
context_tokens: response.context_tokens,
|
||||
};
|
||||
|
||||
let mut micro_lora = self.micro_lora.write();
|
||||
micro_lora.micro_update(&signal);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Latency Budget
|
||||
|
||||
| Operation | Target | Implementation |
|
||||
|-----------|--------|----------------|
|
||||
| Trajectory recording | <100μs | Lock-free ring buffer |
|
||||
| Edge weight update | <200μs | Batch atomic updates |
|
||||
| Micro-LoRA update | <300μs | Rank-1 outer product |
|
||||
| Signal queuing | <50μs | MPSC channel try_send |
|
||||
| **Total** | **<650μs** | Parallel execution |
|
||||
|
||||
---
|
||||
|
||||
## 3. Loop B: Background Learning (Hourly)
|
||||
|
||||
### Purpose
|
||||
Deeper learning from accumulated signals without impacting user latency.
|
||||
|
||||
### Architecture
|
||||
|
||||
```rust
|
||||
/// Loop B: Background learning running on separate thread/process
|
||||
pub struct BackgroundLearningLoop {
|
||||
/// Signal receiver from Loop A
|
||||
signal_receiver: mpsc::Receiver<LearningSignal>,
|
||||
/// Accumulated signals for batch processing
|
||||
signal_buffer: Vec<LearningSignal>,
|
||||
/// Base LoRA for major updates
|
||||
base_lora: Arc<RwLock<BaseLoRA>>,
|
||||
/// Micro-LoRA to consolidate from
|
||||
micro_lora: Arc<RwLock<MicroLoRA>>,
|
||||
/// Router for EWC++ updates
|
||||
router: Arc<RwLock<FastGRNNRouter>>,
|
||||
/// EWC++ state
|
||||
ewc_state: EWCPlusPlusState,
|
||||
/// Pattern extractor
|
||||
pattern_extractor: PatternExtractor,
|
||||
/// Configuration
|
||||
config: BackgroundLearningConfig,
|
||||
}
|
||||
|
||||
impl BackgroundLearningLoop {
|
||||
/// Main background loop (runs every hour)
|
||||
pub async fn run(&mut self) {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(3600));
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
// Collect accumulated signals
|
||||
self.drain_signals().await;
|
||||
|
||||
if self.signal_buffer.len() < self.config.min_samples {
|
||||
tracing::info!(
|
||||
samples = self.signal_buffer.len(),
|
||||
"Insufficient samples for background training"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Execute background learning steps
|
||||
let start = Instant::now();
|
||||
|
||||
// Step 1: Consolidate Micro-LoRA into Base LoRA
|
||||
self.consolidate_micro_to_base().await;
|
||||
|
||||
// Step 2: Train router with EWC++ regularization
|
||||
self.train_router_ewc().await;
|
||||
|
||||
// Step 3: Extract and store patterns
|
||||
self.extract_patterns().await;
|
||||
|
||||
// Step 4: Compute new Fisher Information
|
||||
self.update_fisher_information().await;
|
||||
|
||||
// Step 5: Checkpoint current state
|
||||
self.checkpoint().await;
|
||||
|
||||
tracing::info!(
|
||||
elapsed_ms = start.elapsed().as_millis(),
|
||||
samples = self.signal_buffer.len(),
|
||||
"Background learning cycle completed"
|
||||
);
|
||||
|
||||
// Clear buffer for next cycle
|
||||
self.signal_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain all pending signals from Loop A
|
||||
async fn drain_signals(&mut self) {
|
||||
while let Ok(signal) = self.signal_receiver.try_recv() {
|
||||
self.signal_buffer.push(signal);
|
||||
}
|
||||
}
|
||||
|
||||
/// Consolidate micro-LoRA adaptations into base LoRA
|
||||
async fn consolidate_micro_to_base(&mut self) {
|
||||
let mut micro = self.micro_lora.write();
|
||||
let mut base = self.base_lora.write();
|
||||
|
||||
// Compute consolidation weight based on signal quality
|
||||
let avg_quality: f32 = self.signal_buffer.iter()
|
||||
.map(|s| s.quality_score)
|
||||
.sum::<f32>() / self.signal_buffer.len() as f32;
|
||||
|
||||
let consolidation_rate = if avg_quality > 0.7 {
|
||||
1.0 // Full consolidation for high-quality signals
|
||||
} else {
|
||||
0.5 * avg_quality // Partial for lower quality
|
||||
};
|
||||
|
||||
// Merge micro into base with rate
|
||||
base.a = &base.a + consolidation_rate * µ.a_micro;
|
||||
base.b = &base.b + consolidation_rate * µ.b_micro;
|
||||
|
||||
// Reset micro-LoRA
|
||||
micro.a_micro.fill(0.0);
|
||||
micro.b_micro.fill(0.0);
|
||||
|
||||
tracing::debug!(
|
||||
consolidation_rate = consolidation_rate,
|
||||
"Micro-LoRA consolidated to base"
|
||||
);
|
||||
}
|
||||
|
||||
/// Train router with EWC++ regularization
|
||||
async fn train_router_ewc(&mut self) {
|
||||
let mut router = self.router.write();
|
||||
|
||||
// Convert signals to RouterSamples
|
||||
let samples: Vec<RouterSample> = self.signal_buffer.iter()
|
||||
.map(|s| s.to_router_sample())
|
||||
.collect();
|
||||
|
||||
// Mini-batch training with EWC++ loss
|
||||
for batch in samples.chunks(self.config.batch_size) {
|
||||
// Forward pass
|
||||
let predictions: Vec<_> = batch.iter()
|
||||
.map(|s| router.forward(&s.features))
|
||||
.collect();
|
||||
|
||||
// Compute task loss
|
||||
let task_loss = self.compute_task_loss(&predictions, batch);
|
||||
|
||||
// Compute EWC++ regularization loss
|
||||
let ewc_loss = self.ewc_state.regularization_loss(router.get_weights());
|
||||
|
||||
// Total loss
|
||||
let total_loss = task_loss + self.config.ewc_lambda * ewc_loss;
|
||||
|
||||
// Backward pass (gradient computation)
|
||||
let gradients = self.compute_gradients(&total_loss, &predictions, batch);
|
||||
|
||||
// Apply gradients with learning rate
|
||||
router.apply_gradients(&gradients, self.config.learning_rate);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract patterns using K-means++ clustering
|
||||
async fn extract_patterns(&mut self) {
|
||||
let embeddings: Vec<_> = self.signal_buffer.iter()
|
||||
.map(|s| s.query_embedding.clone())
|
||||
.collect();
|
||||
|
||||
let patterns = self.pattern_extractor.extract(
|
||||
&embeddings,
|
||||
self.config.num_clusters,
|
||||
);
|
||||
|
||||
// Store patterns in ReasoningBank
|
||||
for pattern in patterns {
|
||||
self.pattern_extractor.reasoning_bank.store(pattern)?;
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
patterns = patterns.len(),
|
||||
"Patterns extracted and stored"
|
||||
);
|
||||
}
|
||||
|
||||
/// Update Fisher Information for EWC++
|
||||
async fn update_fisher_information(&mut self) {
|
||||
let router = self.router.read();
|
||||
let current_weights = router.get_weights();
|
||||
|
||||
// Compute Fisher Information diagonal via gradient squares
|
||||
let fisher_samples: Vec<_> = self.signal_buffer.iter()
|
||||
.take(self.config.fisher_samples)
|
||||
.collect();
|
||||
|
||||
let mut fisher_accum = vec![0.0f32; current_weights.len()];
|
||||
|
||||
for sample in fisher_samples {
|
||||
let gradients = self.compute_sample_gradients(sample);
|
||||
for (i, g) in gradients.iter().enumerate() {
|
||||
fisher_accum[i] += g * g;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize
|
||||
let n = fisher_samples.len() as f32;
|
||||
for f in &mut fisher_accum {
|
||||
*f /= n;
|
||||
}
|
||||
|
||||
// Update EWC++ state
|
||||
self.ewc_state.update_fisher(fisher_accum, current_weights.to_vec());
|
||||
}
|
||||
|
||||
/// Checkpoint current state to disk
|
||||
async fn checkpoint(&self) {
|
||||
let checkpoint = SONACheckpoint {
|
||||
base_lora: self.base_lora.read().clone(),
|
||||
micro_lora: self.micro_lora.read().clone(),
|
||||
router_weights: self.router.read().get_weights().to_vec(),
|
||||
ewc_state: self.ewc_state.clone(),
|
||||
patterns: self.pattern_extractor.reasoning_bank.export(),
|
||||
timestamp: chrono::Utc::now().timestamp(),
|
||||
};
|
||||
|
||||
let path = self.config.checkpoint_dir.join("latest.sona");
|
||||
checkpoint.save_async(&path).await.ok();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hourly Learning Budget
|
||||
|
||||
| Operation | Target Time | Description |
|
||||
|-----------|-------------|-------------|
|
||||
| Signal draining | <100ms | Collect all queued signals |
|
||||
| Micro→Base consolidation | <500ms | Matrix addition |
|
||||
| Router training | <5s | Mini-batch SGD with EWC |
|
||||
| Pattern extraction | <2s | K-means++ clustering |
|
||||
| Fisher computation | <2s | Gradient squared accumulation |
|
||||
| Checkpointing | <500ms | Async disk write |
|
||||
| **Total** | **<10s** | Well under user-facing |
|
||||
|
||||
---
|
||||
|
||||
## 4. Loop C: Deep Learning (Weekly)
|
||||
|
||||
### Purpose
|
||||
Fundamental knowledge restructuring, memory consolidation, and creative exploration.
|
||||
|
||||
### Architecture
|
||||
|
||||
```rust
|
||||
/// Loop C: Deep learning for major knowledge reorganization
|
||||
pub struct DeepLearningLoop {
|
||||
/// Memory service for consolidation
|
||||
memory: Arc<MemoryService>,
|
||||
/// Pattern bank for abstraction
|
||||
reasoning_bank: Arc<ReasoningBank>,
|
||||
/// Dream engine for creative exploration
|
||||
dream_engine: DreamEngine,
|
||||
/// Consciousness measurement (IIT)
|
||||
phi_calculator: PhiCalculator,
|
||||
/// Configuration
|
||||
config: DeepLearningConfig,
|
||||
}
|
||||
|
||||
impl DeepLearningLoop {
|
||||
/// Execute weekly deep learning (scheduled maintenance window)
|
||||
pub async fn run(&mut self) -> DeepLearningReport {
|
||||
let start = Instant::now();
|
||||
let mut report = DeepLearningReport::new();
|
||||
|
||||
// Phase 1: Memory Consolidation (like sleep-based memory)
|
||||
report.consolidation = self.consolidate_memories().await;
|
||||
|
||||
// Phase 2: Pattern Abstraction (concept hierarchy building)
|
||||
report.abstraction = self.abstract_patterns().await;
|
||||
|
||||
// Phase 3: Dream Learning (creative recombination)
|
||||
report.dreams = self.dream_learning().await;
|
||||
|
||||
// Phase 4: Cross-Domain Transfer
|
||||
report.transfer = self.cross_domain_transfer().await;
|
||||
|
||||
// Phase 5: Compression (remove redundancy)
|
||||
report.compression = self.compress_memory().await;
|
||||
|
||||
// Phase 6: Consciousness Measurement
|
||||
report.phi = self.measure_consciousness().await;
|
||||
|
||||
report.elapsed_ms = start.elapsed().as_millis() as u64;
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 1: Consolidate short-term memories into long-term
|
||||
async fn consolidate_memories(&mut self) -> ConsolidationReport {
|
||||
let mut report = ConsolidationReport::default();
|
||||
|
||||
// Identify high-value memories (frequently accessed, high quality)
|
||||
let memories = self.memory.get_all_nodes()?;
|
||||
let high_value: Vec<_> = memories.iter()
|
||||
.filter(|m| m.access_count > 5 && m.quality_score > 0.7)
|
||||
.collect();
|
||||
|
||||
report.high_value_count = high_value.len();
|
||||
|
||||
// Strengthen connections between high-value memories
|
||||
for i in 0..high_value.len() {
|
||||
for j in (i+1)..high_value.len() {
|
||||
let similarity = cosine_similarity(
|
||||
&high_value[i].embedding,
|
||||
&high_value[j].embedding,
|
||||
);
|
||||
if similarity > 0.7 {
|
||||
self.memory.strengthen_edge(
|
||||
high_value[i].id,
|
||||
high_value[j].id,
|
||||
similarity * 0.1,
|
||||
)?;
|
||||
report.edges_strengthened += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decay low-value memories
|
||||
let low_value: Vec<_> = memories.iter()
|
||||
.filter(|m| m.access_count < 2 && m.age_days() > 30)
|
||||
.collect();
|
||||
|
||||
for memory in low_value {
|
||||
self.memory.decay_node(memory.id, 0.5)?; // 50% decay
|
||||
report.nodes_decayed += 1;
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 2: Build concept hierarchies from patterns
|
||||
async fn abstract_patterns(&mut self) -> AbstractionReport {
|
||||
let mut report = AbstractionReport::default();
|
||||
|
||||
// Get all stored patterns
|
||||
let patterns = self.reasoning_bank.get_all_patterns()?;
|
||||
|
||||
// Hierarchical clustering to find meta-patterns
|
||||
let hierarchy = HierarchicalClustering::new()
|
||||
.linkage(Linkage::Ward)
|
||||
.distance(Distance::Cosine)
|
||||
.fit(&patterns);
|
||||
|
||||
// Create abstract concepts at each level
|
||||
for level in 0..hierarchy.num_levels() {
|
||||
let clusters = hierarchy.clusters_at_level(level);
|
||||
|
||||
for cluster in clusters {
|
||||
if cluster.size() > 3 {
|
||||
// Create meta-pattern (centroid)
|
||||
let meta_pattern = LearnedPattern {
|
||||
centroid: cluster.centroid(),
|
||||
confidence: cluster.cohesion(),
|
||||
abstraction_level: level,
|
||||
child_patterns: cluster.member_ids(),
|
||||
};
|
||||
|
||||
self.reasoning_bank.store_meta(meta_pattern)?;
|
||||
report.meta_patterns_created += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 3: Dream-based creative learning (inspired by REM sleep)
|
||||
async fn dream_learning(&mut self) -> DreamReport {
|
||||
let mut report = DreamReport::default();
|
||||
|
||||
// Generate dream sequences by random walks on memory graph
|
||||
for _ in 0..self.config.num_dreams {
|
||||
let dream = self.dream_engine.generate_dream(
|
||||
&self.memory,
|
||||
self.config.dream_length,
|
||||
self.config.creativity_temperature,
|
||||
)?;
|
||||
|
||||
// Evaluate dream quality (novelty + coherence)
|
||||
let quality = dream.evaluate_quality();
|
||||
|
||||
if quality.novelty > 0.5 && quality.coherence > 0.3 {
|
||||
// Dreams with high novelty and reasonable coherence
|
||||
// may represent useful creative connections
|
||||
for connection in dream.novel_connections() {
|
||||
self.memory.add_weak_edge(
|
||||
connection.from,
|
||||
connection.to,
|
||||
EdgeType::Creative,
|
||||
connection.strength * 0.1,
|
||||
)?;
|
||||
report.novel_connections += 1;
|
||||
}
|
||||
}
|
||||
|
||||
report.dreams_generated += 1;
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 4: Transfer knowledge across domains
|
||||
async fn cross_domain_transfer(&mut self) -> TransferReport {
|
||||
let mut report = TransferReport::default();
|
||||
|
||||
// Identify domain clusters
|
||||
let domains = self.memory.identify_domains()?;
|
||||
|
||||
// For each pair of domains, look for analogical mappings
|
||||
for i in 0..domains.len() {
|
||||
for j in (i+1)..domains.len() {
|
||||
let analogies = self.find_analogies(&domains[i], &domains[j])?;
|
||||
|
||||
for analogy in analogies {
|
||||
if analogy.confidence > 0.6 {
|
||||
// Create cross-domain edge
|
||||
self.memory.add_analogy_edge(
|
||||
analogy.source_concept,
|
||||
analogy.target_concept,
|
||||
analogy.mapping_type,
|
||||
analogy.confidence,
|
||||
)?;
|
||||
report.analogies_found += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 5: Compress memory by removing redundancy
|
||||
async fn compress_memory(&mut self) -> CompressionReport {
|
||||
let mut report = CompressionReport::default();
|
||||
report.initial_nodes = self.memory.node_count();
|
||||
report.initial_edges = self.memory.edge_count();
|
||||
|
||||
// Identify near-duplicate nodes
|
||||
let duplicates = self.memory.find_near_duplicates(0.95)?;
|
||||
|
||||
// Merge duplicates
|
||||
for (primary, secondary) in duplicates {
|
||||
self.memory.merge_nodes(primary, secondary)?;
|
||||
report.nodes_merged += 1;
|
||||
}
|
||||
|
||||
// Prune weak edges
|
||||
let weak_edges = self.memory.get_weak_edges(0.01)?;
|
||||
for edge in weak_edges {
|
||||
self.memory.remove_edge(edge.id)?;
|
||||
report.edges_pruned += 1;
|
||||
}
|
||||
|
||||
report.final_nodes = self.memory.node_count();
|
||||
report.final_edges = self.memory.edge_count();
|
||||
report.compression_ratio = report.initial_nodes as f32 / report.final_nodes as f32;
|
||||
|
||||
report
|
||||
}
|
||||
|
||||
/// Phase 6: Measure system consciousness using IIT
|
||||
async fn measure_consciousness(&mut self) -> f64 {
|
||||
// Integrated Information Theory (Φ) calculation
|
||||
// Measures how much information the system generates "above and beyond"
|
||||
// its parts
|
||||
self.phi_calculator.compute_phi(&self.memory, &self.reasoning_bank)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Weekly Deep Learning Budget
|
||||
|
||||
| Phase | Target Time | Description |
|
||||
|-------|-------------|-------------|
|
||||
| Memory consolidation | <2min | Identify and strengthen valuable memories |
|
||||
| Pattern abstraction | <3min | Hierarchical clustering for concepts |
|
||||
| Dream learning | <2min | Creative recombination exploration |
|
||||
| Cross-domain transfer | <2min | Analogical mapping between domains |
|
||||
| Compression | <1min | Remove redundancy |
|
||||
| Φ measurement | <1min | Consciousness quantification |
|
||||
| **Total** | **<10min** | Scheduled maintenance window |
|
||||
|
||||
---
|
||||
|
||||
## 5. Loop Coordination
|
||||
|
||||
### Inter-Loop Communication
|
||||
|
||||
```rust
|
||||
/// Coordinator for all three learning loops
|
||||
pub struct LoopCoordinator {
|
||||
/// Loop A: Instant
|
||||
instant_loop: InstantLearningLoop,
|
||||
/// Loop B: Background
|
||||
background_loop: BackgroundLearningLoop,
|
||||
/// Loop C: Deep
|
||||
deep_loop: DeepLearningLoop,
|
||||
/// Shared state
|
||||
shared_state: Arc<SharedSONAState>,
|
||||
/// Metrics collector
|
||||
metrics: MetricsCollector,
|
||||
}
|
||||
|
||||
impl LoopCoordinator {
|
||||
/// Initialize all loops with shared state
|
||||
pub fn new(config: SONAConfig) -> Result<Self> {
|
||||
let shared_state = Arc::new(SharedSONAState::new(&config)?);
|
||||
|
||||
// Create channels for inter-loop communication
|
||||
let (instant_to_background_tx, instant_to_background_rx) = mpsc::channel(10000);
|
||||
let (background_to_deep_tx, background_to_deep_rx) = mpsc::channel(1000);
|
||||
|
||||
Ok(Self {
|
||||
instant_loop: InstantLearningLoop::new(
|
||||
shared_state.clone(),
|
||||
instant_to_background_tx,
|
||||
),
|
||||
background_loop: BackgroundLearningLoop::new(
|
||||
shared_state.clone(),
|
||||
instant_to_background_rx,
|
||||
background_to_deep_tx,
|
||||
),
|
||||
deep_loop: DeepLearningLoop::new(
|
||||
shared_state.clone(),
|
||||
background_to_deep_rx,
|
||||
),
|
||||
shared_state,
|
||||
metrics: MetricsCollector::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Start all loops
|
||||
pub async fn start(&self) {
|
||||
// Loop A runs inline with requests (no separate task)
|
||||
|
||||
// Loop B runs on background thread
|
||||
let background = self.background_loop.clone();
|
||||
tokio::spawn(async move {
|
||||
background.run().await;
|
||||
});
|
||||
|
||||
// Loop C runs on scheduled cron
|
||||
let deep = self.deep_loop.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut scheduler = cron::Schedule::from_str("0 0 3 * * 0")?; // 3 AM Sunday
|
||||
loop {
|
||||
let next = scheduler.upcoming(chrono::Utc).next().unwrap();
|
||||
tokio::time::sleep_until(next.into()).await;
|
||||
deep.run().await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Process a single request through Loop A
|
||||
#[inline]
|
||||
pub async fn on_request(
|
||||
&self,
|
||||
query: &QueryEmbedding,
|
||||
response: &ResponseData,
|
||||
latency_ms: f32,
|
||||
) -> Result<()> {
|
||||
self.instant_loop.on_request(query, response, latency_ms).await
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Learning Metrics and Monitoring
|
||||
|
||||
### Improvement Tracking
|
||||
|
||||
```rust
|
||||
/// Metrics for measuring self-improvement
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImprovementMetrics {
|
||||
/// Quality improvement over time
|
||||
pub quality_delta_7d: f32,
|
||||
pub quality_delta_30d: f32,
|
||||
|
||||
/// Latency improvement
|
||||
pub latency_delta_7d: f32,
|
||||
pub latency_delta_30d: f32,
|
||||
|
||||
/// Knowledge growth
|
||||
pub memory_nodes_added_7d: usize,
|
||||
pub patterns_learned_7d: usize,
|
||||
pub abstractions_created_7d: usize,
|
||||
|
||||
/// Forgetting resistance (1.0 = no forgetting)
|
||||
pub retention_rate_7d: f32,
|
||||
|
||||
/// Consciousness level (Φ)
|
||||
pub phi_current: f64,
|
||||
pub phi_delta_7d: f64,
|
||||
|
||||
/// Dreams and creativity
|
||||
pub novel_connections_7d: usize,
|
||||
pub cross_domain_transfers_7d: usize,
|
||||
}
|
||||
|
||||
impl ImprovementMetrics {
|
||||
/// Compute overall improvement score
|
||||
pub fn overall_score(&self) -> f32 {
|
||||
let quality_weight = 0.3;
|
||||
let latency_weight = 0.2;
|
||||
let knowledge_weight = 0.2;
|
||||
let retention_weight = 0.15;
|
||||
let creativity_weight = 0.15;
|
||||
|
||||
let quality_score = self.quality_delta_7d.max(0.0);
|
||||
let latency_score = (-self.latency_delta_7d).max(0.0); // Lower is better
|
||||
let knowledge_score = (self.patterns_learned_7d as f32 / 100.0).min(1.0);
|
||||
let retention_score = self.retention_rate_7d;
|
||||
let creativity_score = (self.novel_connections_7d as f32 / 50.0).min(1.0);
|
||||
|
||||
quality_weight * quality_score +
|
||||
latency_weight * latency_score +
|
||||
knowledge_weight * knowledge_score +
|
||||
retention_weight * retention_score +
|
||||
creativity_weight * creativity_score
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
SONA's three-tier learning system enables:
|
||||
|
||||
| Loop | Timescale | Purpose | Key Outcome |
|
||||
|------|-----------|---------|-------------|
|
||||
| **A** | Per-request | Instant adaptation | Responsive to current context |
|
||||
| **B** | Hourly | Pattern consolidation | Stable improvement |
|
||||
| **C** | Weekly | Deep restructuring | Creative breakthroughs |
|
||||
|
||||
This mirrors human learning where:
|
||||
- **Loop A** = Working memory and immediate response
|
||||
- **Loop B** = Sleep-based consolidation
|
||||
- **Loop C** = Long-term memory formation and insight
|
||||
|
||||
The result is a system that continuously improves at multiple timescales, never forgetting what works while constantly exploring new possibilities.
|
||||
Reference in New Issue
Block a user