diff --git a/crates/zclaw-growth/src/extractor.rs b/crates/zclaw-growth/src/extractor.rs index 339d007..1d5c2e1 100644 --- a/crates/zclaw-growth/src/extractor.rs +++ b/crates/zclaw-growth/src/extractor.rs @@ -293,7 +293,7 @@ impl MemoryExtractor { "[MemoryExtractor] Combined extraction: {} memories, {} experiences, {} profile signals", combined.memories.len(), combined.experiences.len(), - combined.profile_signals.has_any_signal() as usize, + combined.profile_signals.signal_count(), ); return Ok(combined); } diff --git a/crates/zclaw-growth/src/feedback_collector.rs b/crates/zclaw-growth/src/feedback_collector.rs index a0a08fa..4f7f29a 100644 --- a/crates/zclaw-growth/src/feedback_collector.rs +++ b/crates/zclaw-growth/src/feedback_collector.rs @@ -69,6 +69,8 @@ pub struct TrustRecord { pub struct FeedbackCollector { trust_records: HashMap, viking: Option>, + /// 是否已从持久化存储加载信任度记录 + loaded: bool, } impl FeedbackCollector { @@ -76,6 +78,7 @@ impl FeedbackCollector { Self { trust_records: HashMap::new(), viking: None, + loaded: false, } } @@ -84,6 +87,7 @@ impl FeedbackCollector { Self { trust_records: HashMap::new(), viking: Some(viking), + loaded: false, } } @@ -105,8 +109,10 @@ impl FeedbackCollector { for entry in entries { match serde_json::from_str::(&entry.content) { Ok(record) => { + // 只合并不覆盖:保留内存中的较新记录 self.trust_records - .insert(record.artifact_id.clone(), record); + .entry(record.artifact_id.clone()) + .or_insert(record); count += 1; } Err(e) => { @@ -127,7 +133,19 @@ impl FeedbackCollector { } /// 将信任度记录持久化到 VikingAdapter - pub async fn save(&self) -> Result { + /// 首次调用时自动从存储加载已有记录,避免覆盖 + pub async fn save(&mut self) -> Result { + // 首次保存前自动加载已有记录,防止丢失历史数据 + if !self.loaded { + if let Err(e) = self.load().await { + tracing::debug!( + "[FeedbackCollector] Auto-load before save failed (non-fatal): {}", + e + ); + } + self.loaded = true; + } + let viking = match &self.viking { Some(v) => v, None => return Ok(0), @@ -418,7 +436,7 @@ mod tests { #[tokio::test] async fn test_save_without_viking_returns_zero() { - let collector = FeedbackCollector::new(); + let mut collector = FeedbackCollector::new(); let saved = collector.save().await.unwrap(); assert_eq!(saved, 0); } diff --git a/crates/zclaw-growth/src/types.rs b/crates/zclaw-growth/src/types.rs index c4dfb89..e291a1a 100644 --- a/crates/zclaw-growth/src/types.rs +++ b/crates/zclaw-growth/src/types.rs @@ -443,6 +443,17 @@ impl ProfileSignals { || self.preferred_tool.is_some() || self.communication_style.is_some() } + + /// 有效信号数量 + pub fn signal_count(&self) -> usize { + let mut count = 0; + if self.industry.is_some() { count += 1; } + if self.recent_topic.is_some() { count += 1; } + if self.pain_point.is_some() { count += 1; } + if self.preferred_tool.is_some() { count += 1; } + if self.communication_style.is_some() { count += 1; } + count + } } /// 进化事件 diff --git a/crates/zclaw-runtime/src/growth.rs b/crates/zclaw-runtime/src/growth.rs index 9c6dc08..7916d89 100644 --- a/crates/zclaw-runtime/src/growth.rs +++ b/crates/zclaw-runtime/src/growth.rs @@ -116,6 +116,34 @@ impl GrowthIntegration { self.config.enabled } + /// 启动时初始化:从持久化存储恢复进化引擎的信任度记录 + /// + /// **注意**:FeedbackCollector 内部已实现 lazy-load(首次 save() 时自动加载), + /// 所以此方法为可选优化 — 提前加载可避免首次反馈提交时的延迟。 + /// 在中间件持有 GrowthIntegration 的场景中,由于 `&self` 限制无法调用此方法, + /// lazy-load 机制会兜底处理。 + pub async fn initialize(&mut self) -> Result<()> { + if let Some(ref mut engine) = self.evolution_engine { + match engine.load_feedback().await { + Ok(count) => { + if count > 0 { + tracing::info!( + "[GrowthIntegration] Loaded {} trust records from storage", + count + ); + } + } + Err(e) => { + tracing::warn!( + "[GrowthIntegration] Failed to load trust records: {}", + e + ); + } + } + } + Ok(()) + } + /// Enable or disable auto extraction pub fn set_auto_extract(&mut self, auto_extract: bool) { self.config.auto_extract = auto_extract;