use super::*;
#[derive(Debug, Clone)]
pub struct EvolutionResult {
pub records_evolved: usize,
pub links_created: usize,
}
pub async fn evolve_on_new_memory(
db: &HirnDB,
new_record: &EpisodicRecord,
config: &EvolutionConfig,
) -> HirnResult<EvolutionResult> {
let embedding = match &new_record.embedding {
Some(emb) => emb,
None => {
return Ok(EvolutionResult {
records_evolved: 0,
links_created: 0,
});
}
};
let metric = db.distance_metric();
let candidates = match db
.vector_search_all(embedding, config.evolution_top_k, metric)
.await
{
Ok(c) => c,
Err(e) => {
tracing::warn!(error = %e, "evolve_on_new_memory: vector search failed, skipping evolution");
return Ok(EvolutionResult {
records_evolved: 0,
links_created: 0,
});
}
};
let mut records_evolved = 0;
let mut links_created = 0;
for &(uid, sim) in &candidates {
let candidate_id = MemoryId::from_ulid(ulid::Ulid(uid));
let record = match db.get_memory(candidate_id).await {
Ok(hirn_core::record::MemoryRecord::Semantic(s)) => s,
_ => continue,
};
if sim < config.evolution_similarity_threshold {
continue;
}
let new_evidence = format!(
"{}. [Corroborated by episode at {}]",
record.description,
new_record.timestamp.as_datetime().format("%Y-%m-%d %H:%M")
);
let new_evidence_count = record.evidence_count + 1;
let base_confidence: f32 = match new_evidence_count {
1 => 0.3,
2..=3 => 0.5,
4..=7 => 0.7,
_ => 0.85,
};
let contradiction_penalty: f32 = if record.contradiction_ids.is_empty() {
0.0
} else {
0.15_f32 * record.contradiction_ids.len() as f32
};
let new_confidence = (base_confidence - contradiction_penalty).clamp(0.1, 1.0);
db.correct_semantic(
candidate_id,
crate::db::SemanticUpdate {
description: Some(new_evidence),
confidence: Some(new_confidence),
evidence_count: Some(new_evidence_count),
reason: Some(format!(
"Evolution: corroborated by episode {}",
new_record.id
)),
..crate::db::SemanticUpdate::with_metadata(
AgentId::well_known("memory_evolution"),
new_record.id,
)
},
)
.await?;
records_evolved += 1;
if db
.connect_with(
candidate_id,
new_record.id,
EdgeRelation::DerivedFrom,
sim,
Metadata::default(),
)
.await
.is_ok()
{
links_created += 1;
}
}
Ok(EvolutionResult {
records_evolved,
links_created,
})
}
#[derive(Debug, Clone)]
pub struct EvolutionConfig {
pub evolution_top_k: usize,
pub evolution_similarity_threshold: f32,
}
impl Default for EvolutionConfig {
fn default() -> Self {
Self {
evolution_top_k: 5,
evolution_similarity_threshold: 0.75,
}
}
}