From 72b3206a6b929c943cc3ac5bfb7f14323b6c1f15 Mon Sep 17 00:00:00 2001 From: iven Date: Sun, 19 Apr 2026 00:15:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(growth):=20AUD-11=20=E5=8F=8D=E9=A6=88?= =?UTF-8?q?=E4=BF=A1=E4=BB=BB=E5=BA=A6=E5=90=AF=E5=8A=A8=E9=9B=86=E6=88=90?= =?UTF-8?q?=20+=20AUD-12=20=E6=97=A5=E5=BF=97=E6=A0=BC=E5=BC=8F=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AUD-11: FeedbackCollector 内部 lazy-load 机制 - save() 首次调用自动从持久化存储加载信任度记录 - load() 使用 or_insert 策略,不覆盖内存中较新记录 - GrowthIntegration.initialize() 保留为可选优化入口 - 移除无法在 &self 中间件中使用的 ensure_feedback_loaded AUD-12: 日志格式优化 - ProfileSignals 新增 signal_count() 方法 - extractor.rs 使用 signal_count() 替代 has_any_signal() as usize --- crates/zclaw-growth/src/extractor.rs | 2 +- crates/zclaw-growth/src/feedback_collector.rs | 24 ++++++++++++++-- crates/zclaw-growth/src/types.rs | 11 ++++++++ crates/zclaw-runtime/src/growth.rs | 28 +++++++++++++++++++ 4 files changed, 61 insertions(+), 4 deletions(-) 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;