Files
wifi-densepose/crates/ruvector-nervous-system/examples/tiers/t4_agentic_self_model.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

934 lines
28 KiB
Rust

//! # Tier 4: Agentic Self-Model
//!
//! SOTA application: An agent that models its own cognitive state.
//!
//! ## The Problem
//! Traditional agents:
//! - Have no awareness of their own capabilities
//! - Cannot predict when they'll fail
//! - Don't know their own uncertainty
//! - Cannot explain "why I'm not confident"
//!
//! ## What Changes
//! - Nervous system scorecard tracks 5 health metrics
//! - Circadian phases indicate optimal task timing
//! - Coherence monitoring detects internal confusion
//! - Budget guardrails prevent resource exhaustion
//!
//! ## Why This Matters
//! - Agents can say: "I'm not confident, let me check"
//! - Agents can say: "I'm tired, defer this complex task"
//! - Agents can say: "I'm becoming unstable, need reset"
//! - Trustworthy autonomy through self-awareness
//!
//! This is the foundation for responsible AI agents.
use std::collections::HashMap;
use std::time::Instant;
// ============================================================================
// Cognitive State Model
// ============================================================================
/// The agent's model of its own cognitive state
#[derive(Clone, Debug)]
pub struct CognitiveState {
/// Current processing coherence (0-1)
pub coherence: f32,
/// Current confidence in outputs (0-1)
pub confidence: f32,
/// Current energy budget (0-1)
pub energy: f32,
/// Current focus level (0-1)
pub focus: f32,
/// Current circadian phase
pub phase: CircadianPhase,
/// Time to predicted degradation
pub ttd: Option<u64>,
/// Capabilities and their current availability
pub capabilities: HashMap<String, CapabilityState>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CircadianPhase {
/// Peak performance, all capabilities available
Active,
/// Transitioning up, some capabilities available
Dawn,
/// Transitioning down, reduce load
Dusk,
/// Minimal processing, consolidation only
Rest,
}
impl CircadianPhase {
pub fn duty_factor(&self) -> f32 {
match self {
Self::Active => 1.0,
Self::Dawn => 0.7,
Self::Dusk => 0.4,
Self::Rest => 0.1,
}
}
pub fn description(&self) -> &'static str {
match self {
Self::Active => "Peak performance",
Self::Dawn => "Warming up",
Self::Dusk => "Winding down",
Self::Rest => "Consolidating",
}
}
}
#[derive(Clone, Debug)]
pub struct CapabilityState {
/// Name of capability
pub name: String,
/// Is it available right now?
pub available: bool,
/// Current performance (0-1)
pub performance: f32,
/// Why unavailable (if not available)
pub reason: Option<String>,
/// Estimated recovery time
pub recovery_time: Option<u64>,
}
// ============================================================================
// Self-Model Components
// ============================================================================
/// Tracks coherence (internal consistency)
pub struct CoherenceTracker {
/// Module phases
phases: HashMap<String, f32>,
/// Recent coherence values
history: Vec<f32>,
/// Threshold for alarm
threshold: f32,
}
impl CoherenceTracker {
pub fn new(threshold: f32) -> Self {
Self {
phases: HashMap::new(),
history: Vec::new(),
threshold,
}
}
pub fn register_module(&mut self, name: &str) {
self.phases.insert(name.to_string(), 0.0);
}
pub fn update_module(&mut self, name: &str, phase: f32) {
self.phases.insert(name.to_string(), phase);
}
/// Compute current coherence (Kuramoto order parameter)
pub fn compute(&self) -> f32 {
if self.phases.is_empty() {
return 1.0;
}
let n = self.phases.len() as f32;
let sum_x: f32 = self.phases.values().map(|p| p.cos()).sum();
let sum_y: f32 = self.phases.values().map(|p| p.sin()).sum();
(sum_x * sum_x + sum_y * sum_y).sqrt() / n
}
pub fn record(&mut self) -> f32 {
let coherence = self.compute();
self.history.push(coherence);
if self.history.len() > 100 {
self.history.remove(0);
}
coherence
}
pub fn is_alarming(&self) -> bool {
self.compute() < self.threshold
}
pub fn trend(&self) -> f32 {
if self.history.len() < 10 {
return 0.0;
}
let recent: f32 = self.history.iter().rev().take(5).sum::<f32>() / 5.0;
let older: f32 = self.history.iter().rev().skip(5).take(5).sum::<f32>() / 5.0;
recent - older
}
}
/// Tracks confidence in outputs
pub struct ConfidenceTracker {
/// Running average confidence
average: f32,
/// Recent values
history: Vec<f32>,
/// Calibration factor (learned)
calibration: f32,
}
impl ConfidenceTracker {
pub fn new() -> Self {
Self {
average: 0.8,
history: Vec::new(),
calibration: 1.0,
}
}
pub fn record(&mut self, raw_confidence: f32) {
let calibrated = (raw_confidence * self.calibration).clamp(0.0, 1.0);
self.history.push(calibrated);
if self.history.len() > 100 {
self.history.remove(0);
}
self.average = self.history.iter().sum::<f32>() / self.history.len() as f32;
}
/// Calibrate based on feedback
pub fn calibrate(&mut self, predicted: f32, actual: f32) {
// If we predicted 0.9 but were actually 0.6, reduce calibration
let error = predicted - actual;
self.calibration = (self.calibration - error * 0.1).clamp(0.5, 1.5);
}
pub fn current(&self) -> f32 {
self.average
}
pub fn variance(&self) -> f32 {
if self.history.len() < 2 {
return 0.0;
}
let mean = self.average;
self.history
.iter()
.map(|&v| (v - mean).powi(2))
.sum::<f32>()
/ (self.history.len() - 1) as f32
}
}
/// Tracks energy budget
pub struct EnergyTracker {
/// Current energy (0-1)
current: f32,
/// Regeneration rate per hour
regen_rate: f32,
/// Consumption history
consumption_log: Vec<(u64, f32)>,
/// Budget per hour
budget_per_hour: f32,
}
impl EnergyTracker {
pub fn new(budget_per_hour: f32) -> Self {
Self {
current: 1.0,
regen_rate: 0.2, // 20% per hour
consumption_log: Vec::new(),
budget_per_hour,
}
}
pub fn consume(&mut self, amount: f32, timestamp: u64) {
self.current = (self.current - amount).max(0.0);
self.consumption_log.push((timestamp, amount));
// Trim old entries
let cutoff = timestamp.saturating_sub(3600);
self.consumption_log.retain(|(t, _)| *t > cutoff);
}
pub fn regenerate(&mut self, dt_hours: f32) {
self.current = (self.current + self.regen_rate * dt_hours).min(1.0);
}
pub fn current(&self) -> f32 {
self.current
}
pub fn hourly_rate(&self) -> f32 {
self.consumption_log.iter().map(|(_, a)| a).sum()
}
pub fn is_overspending(&self) -> bool {
self.hourly_rate() > self.budget_per_hour
}
pub fn time_to_exhaustion(&self) -> Option<u64> {
if self.hourly_rate() <= 0.0 {
return None;
}
let hours = self.current / self.hourly_rate();
Some((hours * 3600.0) as u64)
}
}
/// Tracks circadian phase
pub struct CircadianClock {
/// Current phase in cycle (0-1)
phase: f32,
/// Cycle duration in hours
cycle_hours: f32,
/// Current phase state
state: CircadianPhase,
}
impl CircadianClock {
pub fn new(cycle_hours: f32) -> Self {
Self {
phase: 0.0,
cycle_hours,
state: CircadianPhase::Active,
}
}
pub fn advance(&mut self, hours: f32) {
self.phase = (self.phase + hours / self.cycle_hours) % 1.0;
self.update_state();
}
fn update_state(&mut self) {
self.state = if self.phase < 0.5 {
CircadianPhase::Active
} else if self.phase < 0.6 {
CircadianPhase::Dusk
} else if self.phase < 0.9 {
CircadianPhase::Rest
} else {
CircadianPhase::Dawn
};
}
pub fn state(&self) -> CircadianPhase {
self.state.clone()
}
pub fn time_to_next_active(&self) -> f32 {
if self.phase < 0.5 {
0.0 // Already active
} else {
(1.0 - self.phase + 0.0) * self.cycle_hours
}
}
}
// ============================================================================
// Self-Aware Agent
// ============================================================================
/// An agent that models its own cognitive state
pub struct SelfAwareAgent {
/// Name of agent
pub name: String,
/// Coherence tracker
coherence: CoherenceTracker,
/// Confidence tracker
confidence: ConfidenceTracker,
/// Energy tracker
energy: EnergyTracker,
/// Circadian clock
clock: CircadianClock,
/// Registered capabilities
capabilities: HashMap<String, bool>,
/// Current timestamp
timestamp: u64,
/// Action history
actions: Vec<ActionRecord>,
}
#[derive(Clone, Debug)]
pub struct ActionRecord {
pub timestamp: u64,
pub action: String,
pub confidence: f32,
pub success: Option<bool>,
pub energy_cost: f32,
}
impl SelfAwareAgent {
pub fn new(name: &str) -> Self {
let mut agent = Self {
name: name.to_string(),
coherence: CoherenceTracker::new(0.7),
confidence: ConfidenceTracker::new(),
energy: EnergyTracker::new(0.5), // 50% per hour budget
clock: CircadianClock::new(24.0),
capabilities: HashMap::new(),
timestamp: 0,
actions: Vec::new(),
};
// Register standard modules
agent.coherence.register_module("perception");
agent.coherence.register_module("reasoning");
agent.coherence.register_module("planning");
agent.coherence.register_module("action");
// Register standard capabilities
agent
.capabilities
.insert("complex_reasoning".to_string(), true);
agent
.capabilities
.insert("creative_generation".to_string(), true);
agent
.capabilities
.insert("precise_calculation".to_string(), true);
agent.capabilities.insert("fast_response".to_string(), true);
agent
}
/// Get current cognitive state
pub fn introspect(&self) -> CognitiveState {
let coherence = self.coherence.compute();
let phase = self.clock.state();
// Determine capability availability based on state
let capabilities = self
.capabilities
.iter()
.map(|(name, baseline)| {
let (available, reason) =
self.capability_available(name, *baseline, &phase, coherence);
(
name.clone(),
CapabilityState {
name: name.clone(),
available,
performance: if available {
self.energy.current()
} else {
0.0
},
reason,
recovery_time: if available {
None
} else {
Some(self.time_to_recovery())
},
},
)
})
.collect();
CognitiveState {
coherence,
confidence: self.confidence.current(),
energy: self.energy.current(),
focus: self.compute_focus(),
phase,
ttd: self.energy.time_to_exhaustion(),
capabilities,
}
}
fn capability_available(
&self,
name: &str,
baseline: bool,
phase: &CircadianPhase,
coherence: f32,
) -> (bool, Option<String>) {
if !baseline {
return (false, Some("Capability disabled".to_string()));
}
match name {
"complex_reasoning" => {
if matches!(phase, CircadianPhase::Rest) {
(
false,
Some("Rest phase - complex reasoning unavailable".to_string()),
)
} else if coherence < 0.5 {
(
false,
Some("Low coherence - reasoning compromised".to_string()),
)
} else if self.energy.current() < 0.2 {
(false, Some("Low energy - reasoning expensive".to_string()))
} else {
(true, None)
}
}
"creative_generation" => {
if matches!(phase, CircadianPhase::Rest | CircadianPhase::Dusk) {
(
false,
Some(format!(
"{} phase - creativity reduced",
phase.description()
)),
)
} else {
(true, None)
}
}
"precise_calculation" => {
if coherence < 0.7 {
(
false,
Some("Coherence below precision threshold".to_string()),
)
} else {
(true, None)
}
}
"fast_response" => {
if self.energy.current() < 0.3 {
(
false,
Some("Insufficient energy for fast response".to_string()),
)
} else {
(true, None)
}
}
_ => (true, None),
}
}
fn compute_focus(&self) -> f32 {
let coherence = self.coherence.compute();
let energy = self.energy.current();
let phase_factor = self.clock.state().duty_factor();
(coherence * 0.4 + energy * 0.3 + phase_factor * 0.3).clamp(0.0, 1.0)
}
fn time_to_recovery(&self) -> u64 {
// Time until active phase + time to regen energy
let phase_time = self.clock.time_to_next_active();
let energy_time = if self.energy.current() < 0.3 {
(0.3 - self.energy.current()) / self.energy.regen_rate
} else {
0.0
};
((phase_time.max(energy_time)) * 3600.0) as u64
}
/// Express current state in natural language
pub fn express_state(&self) -> String {
let state = self.introspect();
let phase_desc = state.phase.description();
let coherence_desc = if state.coherence > 0.8 {
"clear"
} else if state.coherence > 0.6 {
"somewhat scattered"
} else {
"confused"
};
let energy_desc = if state.energy > 0.7 {
"energized"
} else if state.energy > 0.3 {
"adequate"
} else {
"depleted"
};
let confidence_desc = if state.confidence > 0.8 {
"confident"
} else if state.confidence > 0.5 {
"moderately confident"
} else {
"uncertain"
};
let unavailable: Vec<_> = state
.capabilities
.values()
.filter(|c| !c.available)
.map(|c| {
format!(
"{} ({})",
c.name,
c.reason.as_ref().unwrap_or(&"unavailable".to_string())
)
})
.collect();
let mut response = format!(
"I am {}. Currently {} ({}), feeling {} and {}.",
self.name,
phase_desc,
format!("{:.0}%", state.phase.duty_factor() * 100.0),
coherence_desc,
energy_desc
);
if !unavailable.is_empty() {
response.push_str(&format!(
"\n\nCurrently unavailable: {}",
unavailable.join(", ")
));
}
if state.ttd.is_some() && state.energy < 0.3 {
response.push_str(&format!(
"\n\nWarning: Energy low. Time to exhaustion: {}s",
state.ttd.unwrap()
));
}
response
}
/// Decide whether to accept a task
pub fn should_accept_task(&self, task: &Task) -> TaskDecision {
let state = self.introspect();
// Check required capabilities
for req_cap in &task.required_capabilities {
if let Some(cap) = state.capabilities.get(req_cap) {
if !cap.available {
return TaskDecision::Decline {
reason: format!(
"Required capability '{}' unavailable: {}",
req_cap,
cap.reason.as_ref().unwrap_or(&"unknown".to_string())
),
retry_after: cap.recovery_time,
};
}
}
}
// Check energy budget
if self.energy.current() < task.energy_cost {
return TaskDecision::Decline {
reason: format!(
"Insufficient energy: have {:.0}%, need {:.0}%",
self.energy.current() * 100.0,
task.energy_cost * 100.0
),
retry_after: Some(self.time_to_recovery()),
};
}
// Check coherence
if state.coherence < task.min_coherence {
return TaskDecision::Decline {
reason: format!(
"Coherence too low: {:.0}% < {:.0}% required",
state.coherence * 100.0,
task.min_coherence * 100.0
),
retry_after: None,
};
}
// Check phase
if task.requires_peak && !matches!(state.phase, CircadianPhase::Active) {
return TaskDecision::Defer {
reason: "Task requires peak performance phase".to_string(),
optimal_time: Some((self.clock.time_to_next_active() * 3600.0) as u64),
};
}
// Accept with confidence estimate
let confidence = self.estimate_confidence(&task, &state);
TaskDecision::Accept {
confidence,
warnings: self.generate_warnings(&task, &state),
}
}
fn estimate_confidence(&self, task: &Task, state: &CognitiveState) -> f32 {
let base = self.confidence.current();
let energy_factor = state.energy.powf(0.5); // Square root to soften impact
let coherence_factor = state.coherence;
let phase_factor = state.phase.duty_factor();
(base * energy_factor * coherence_factor * phase_factor).clamp(0.0, 1.0)
}
fn generate_warnings(&self, task: &Task, state: &CognitiveState) -> Vec<String> {
let mut warnings = Vec::new();
if state.energy < 0.4 {
warnings.push("Low energy may affect performance".to_string());
}
if state.coherence < 0.7 {
warnings.push("Reduced coherence - verify outputs".to_string());
}
if self.confidence.variance() > 0.1 {
warnings.push("High confidence variance - calibration recommended".to_string());
}
if matches!(state.phase, CircadianPhase::Dusk) {
warnings.push("Approaching rest phase - complex tasks may be deferred".to_string());
}
warnings
}
/// Execute an action (consumes energy, updates state)
pub fn execute(&mut self, action: &str, confidence: f32, energy_cost: f32) {
self.energy.consume(energy_cost, self.timestamp);
self.confidence.record(confidence);
self.actions.push(ActionRecord {
timestamp: self.timestamp,
action: action.to_string(),
confidence,
success: None,
energy_cost,
});
}
/// Record outcome and calibrate
pub fn record_outcome(&mut self, success: bool, predicted_confidence: f32) {
let actual = if success { 1.0 } else { 0.0 };
self.confidence.calibrate(predicted_confidence, actual);
if let Some(last) = self.actions.last_mut() {
last.success = Some(success);
}
}
/// Advance time
pub fn tick(&mut self, dt_seconds: u64) {
self.timestamp += dt_seconds;
self.clock.advance(dt_seconds as f32 / 3600.0);
self.energy.regenerate(dt_seconds as f32 / 3600.0);
self.coherence.record();
}
/// Simulate module activity (affects coherence)
pub fn module_activity(&mut self, module: &str, phase: f32) {
self.coherence.update_module(module, phase);
}
}
#[derive(Clone, Debug)]
pub struct Task {
pub name: String,
pub required_capabilities: Vec<String>,
pub energy_cost: f32,
pub min_coherence: f32,
pub requires_peak: bool,
}
#[derive(Clone, Debug)]
pub enum TaskDecision {
Accept {
confidence: f32,
warnings: Vec<String>,
},
Defer {
reason: String,
optimal_time: Option<u64>,
},
Decline {
reason: String,
retry_after: Option<u64>,
},
}
// ============================================================================
// Example Usage
// ============================================================================
fn main() {
println!("=== Tier 4: Agentic Self-Model ===\n");
let mut agent = SelfAwareAgent::new("Claude-Nervous");
println!("Initial state:");
println!("{}\n", agent.express_state());
// Define some tasks
let tasks = vec![
Task {
name: "Simple calculation".to_string(),
required_capabilities: vec!["precise_calculation".to_string()],
energy_cost: 0.05,
min_coherence: 0.7,
requires_peak: false,
},
Task {
name: "Complex reasoning problem".to_string(),
required_capabilities: vec!["complex_reasoning".to_string()],
energy_cost: 0.2,
min_coherence: 0.6,
requires_peak: false,
},
Task {
name: "Creative writing".to_string(),
required_capabilities: vec!["creative_generation".to_string()],
energy_cost: 0.15,
min_coherence: 0.5,
requires_peak: false,
},
Task {
name: "Critical system modification".to_string(),
required_capabilities: vec![
"complex_reasoning".to_string(),
"precise_calculation".to_string(),
],
energy_cost: 0.3,
min_coherence: 0.8,
requires_peak: true,
},
];
// Process tasks
println!("=== Task Processing ===\n");
for task in &tasks {
println!("Task: {}", task.name);
let decision = agent.should_accept_task(task);
match &decision {
TaskDecision::Accept {
confidence,
warnings,
} => {
println!(
" Decision: ACCEPT (confidence: {:.0}%)",
confidence * 100.0
);
if !warnings.is_empty() {
println!(" Warnings: {}", warnings.join("; "));
}
agent.execute(&task.name, *confidence, task.energy_cost);
}
TaskDecision::Defer {
reason,
optimal_time,
} => {
println!(" Decision: DEFER - {}", reason);
if let Some(time) = optimal_time {
println!(" Optimal time: in {}s", time);
}
}
TaskDecision::Decline {
reason,
retry_after,
} => {
println!(" Decision: DECLINE - {}", reason);
if let Some(time) = retry_after {
println!(" Retry after: {}s", time);
}
}
}
println!();
agent.tick(300); // 5 minutes between tasks
}
// Simulate degradation
println!("=== Simulating Extended Operation ===\n");
println!("Running for 12 hours...");
for hour in 0..12 {
// Simulate varying coherence
let phase = (hour as f32 * 0.3).sin() * 0.3;
agent.module_activity("perception", phase);
agent.module_activity("reasoning", phase + 0.1);
agent.module_activity("planning", phase + 0.2);
agent.module_activity("action", phase + 0.3);
// Consume energy
agent.execute("routine_task", 0.7, 0.08);
agent.tick(3600); // 1 hour
if hour % 4 == 3 {
println!("Hour {}: {}", hour + 1, agent.express_state());
println!();
}
}
// Final state
println!("=== Final State ===\n");
println!("{}", agent.express_state());
let state = agent.introspect();
println!("\n=== Detailed Capabilities ===");
for (name, cap) in &state.capabilities {
println!(
" {}: {} (perf: {:.0}%)",
name,
if cap.available {
"AVAILABLE"
} else {
"UNAVAILABLE"
},
cap.performance * 100.0
);
if let Some(reason) = &cap.reason {
println!(" Reason: {}", reason);
}
}
println!("\n=== Key Benefits ===");
println!("- Agent knows when to say 'I'm not confident'");
println!("- Agent knows when to defer complex tasks");
println!("- Agent predicts its own degradation");
println!("- Agent explains WHY capabilities are unavailable");
println!("\nThis is the foundation for trustworthy autonomous AI.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coherence_affects_capabilities() {
let mut agent = SelfAwareAgent::new("test");
// Desync modules
agent.module_activity("perception", 0.0);
agent.module_activity("reasoning", 3.14);
agent.module_activity("planning", 1.57);
agent.module_activity("action", 4.71);
let state = agent.introspect();
assert!(state.coherence < 0.5);
// Precise calculation should be unavailable
let cap = state.capabilities.get("precise_calculation").unwrap();
assert!(!cap.available);
}
#[test]
fn test_energy_affects_acceptance() {
let mut agent = SelfAwareAgent::new("test");
let expensive_task = Task {
name: "expensive".to_string(),
required_capabilities: vec![],
energy_cost: 0.9,
min_coherence: 0.0,
requires_peak: false,
};
// Deplete energy
agent.execute("drain", 0.8, 0.8);
let decision = agent.should_accept_task(&expensive_task);
assert!(matches!(decision, TaskDecision::Decline { .. }));
}
#[test]
fn test_phase_affects_capabilities() {
let mut agent = SelfAwareAgent::new("test");
// Advance to rest phase
agent.tick(12 * 3600); // 12 hours
let state = agent.introspect();
// Complex reasoning should be unavailable during rest
if matches!(state.phase, CircadianPhase::Rest) {
let cap = state.capabilities.get("complex_reasoning").unwrap();
assert!(!cap.available);
}
}
}