Persist memory scores, use them for eviction in trim_entries
Scores are saved to memory-scores.json alongside the conversation log after each scoring run, and loaded on startup — no more re-scoring on restart. trim_entries now evicts lowest-scored memories first (instead of oldest-first) when memories exceed 50% of context. The 50% threshold stays as a heuristic for memory-vs-conversation balance until we have a scoring signal for conversation entries too. Unscored memories get 0.0, so they're evicted before scored ones. save_memory_scores rebuilds from current entries, so evicted memories are automatically expired from the scores file. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
bef1bfbb33
commit
df62b7ceaa
2 changed files with 68 additions and 5 deletions
|
|
@ -28,6 +28,49 @@ use crate::subconscious::learn;
|
|||
|
||||
pub use dmn::{SubconsciousSnapshot, Subconscious};
|
||||
|
||||
use crate::agent::context::ConversationEntry;
|
||||
|
||||
/// Load persisted memory scores from disk and apply to Memory entries.
|
||||
fn load_memory_scores(entries: &mut [ConversationEntry], path: &std::path::Path) {
|
||||
let data = match std::fs::read_to_string(path) {
|
||||
Ok(d) => d,
|
||||
Err(_) => return,
|
||||
};
|
||||
let scores: std::collections::BTreeMap<String, f64> = match serde_json::from_str(&data) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return,
|
||||
};
|
||||
let mut applied = 0;
|
||||
for entry in entries.iter_mut() {
|
||||
if let ConversationEntry::Memory { key, score, .. } = entry {
|
||||
if let Some(&s) = scores.get(key.as_str()) {
|
||||
*score = Some(s);
|
||||
applied += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if applied > 0 {
|
||||
dbglog!("[scoring] loaded {} scores from {}", applied, path.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Save all memory scores to disk.
|
||||
fn save_memory_scores(entries: &[ConversationEntry], path: &std::path::Path) {
|
||||
let scores: std::collections::BTreeMap<String, f64> = entries.iter()
|
||||
.filter_map(|e| {
|
||||
if let ConversationEntry::Memory { key, score: Some(s), .. } = e {
|
||||
Some((key.clone(), *s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if let Ok(json) = serde_json::to_string_pretty(&scores) {
|
||||
let _ = std::fs::write(path, json);
|
||||
dbglog!("[scoring] saved {} scores to {}", scores.len(), path.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Which pane streaming text should go to.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StreamTarget {
|
||||
|
|
@ -267,6 +310,11 @@ impl Mind {
|
|||
// Restore conversation
|
||||
let mut ag = self.agent.lock().await;
|
||||
ag.restore_from_log();
|
||||
|
||||
// Restore persisted memory scores
|
||||
let scores_path = self.config.session_dir.join("memory-scores.json");
|
||||
load_memory_scores(&mut ag.context.entries, &scores_path);
|
||||
|
||||
ag.changed.notify_one();
|
||||
drop(ag);
|
||||
|
||||
|
|
@ -335,6 +383,7 @@ impl Mind {
|
|||
pub fn start_memory_scoring(&self) {
|
||||
let agent = self.agent.clone();
|
||||
let bg_tx = self.bg_tx.clone();
|
||||
let scores_path = self.config.session_dir.join("memory-scores.json");
|
||||
let cfg = crate::config::get();
|
||||
let max_age = cfg.scoring_interval_secs;
|
||||
let response_window = cfg.scoring_response_window;
|
||||
|
|
@ -362,6 +411,8 @@ impl Mind {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Persist all scores to disk
|
||||
save_memory_scores(&ag.context.entries, &scores_path);
|
||||
}
|
||||
}
|
||||
let _ = bg_tx.send(BgEvent::ScoringDone);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue