hirn_engine/consolidation/
evolution.rs1use super::*;
2
3#[derive(Debug, Clone)]
9pub struct EvolutionResult {
10 pub records_evolved: usize,
12 pub links_created: usize,
14}
15
16pub async fn evolve_on_new_memory(
27 db: &HirnDB,
28 new_record: &EpisodicRecord,
29 config: &EvolutionConfig,
30) -> HirnResult<EvolutionResult> {
31 let embedding = match &new_record.embedding {
32 Some(emb) => emb,
33 None => {
34 return Ok(EvolutionResult {
35 records_evolved: 0,
36 links_created: 0,
37 });
38 }
39 };
40
41 let metric = db.distance_metric();
43 let candidates = match db
44 .vector_search_all(embedding, config.evolution_top_k, metric)
45 .await
46 {
47 Ok(c) => c,
48 Err(e) => {
49 tracing::warn!(error = %e, "evolve_on_new_memory: vector search failed, skipping evolution");
50 return Ok(EvolutionResult {
51 records_evolved: 0,
52 links_created: 0,
53 });
54 }
55 };
56
57 let mut records_evolved = 0;
58 let mut links_created = 0;
59
60 for &(uid, sim) in &candidates {
61 let candidate_id = MemoryId::from_ulid(ulid::Ulid(uid));
62
63 let record = match db.get_memory(candidate_id).await {
65 Ok(hirn_core::record::MemoryRecord::Semantic(s)) => s,
66 _ => continue,
67 };
68
69 if sim < config.evolution_similarity_threshold {
71 continue;
72 }
73
74 let new_evidence = format!(
76 "{}. [Corroborated by episode at {}]",
77 record.description,
78 new_record.timestamp.as_datetime().format("%Y-%m-%d %H:%M")
79 );
80
81 let new_evidence_count = record.evidence_count + 1;
83 let base_confidence: f32 = match new_evidence_count {
84 1 => 0.3,
85 2..=3 => 0.5,
86 4..=7 => 0.7,
87 _ => 0.85,
88 };
89 let contradiction_penalty: f32 = if record.contradiction_ids.is_empty() {
90 0.0
91 } else {
92 0.15_f32 * record.contradiction_ids.len() as f32
93 };
94 let new_confidence = (base_confidence - contradiction_penalty).clamp(0.1, 1.0);
95
96 db.correct_semantic(
97 candidate_id,
98 crate::db::SemanticUpdate {
99 description: Some(new_evidence),
100 confidence: Some(new_confidence),
101 evidence_count: Some(new_evidence_count),
102 reason: Some(format!(
103 "Evolution: corroborated by episode {}",
104 new_record.id
105 )),
106 ..crate::db::SemanticUpdate::with_metadata(
107 AgentId::well_known("memory_evolution"),
108 new_record.id,
109 )
110 },
111 )
112 .await?;
113
114 records_evolved += 1;
115
116 if db
118 .connect_with(
119 candidate_id,
120 new_record.id,
121 EdgeRelation::DerivedFrom,
122 sim,
123 Metadata::default(),
124 )
125 .await
126 .is_ok()
127 {
128 links_created += 1;
129 }
130 }
131
132 Ok(EvolutionResult {
133 records_evolved,
134 links_created,
135 })
136}
137
138#[derive(Debug, Clone)]
140pub struct EvolutionConfig {
141 pub evolution_top_k: usize,
143 pub evolution_similarity_threshold: f32,
145}
146
147impl Default for EvolutionConfig {
148 fn default() -> Self {
149 Self {
150 evolution_top_k: 5,
151 evolution_similarity_threshold: 0.75,
152 }
153 }
154}