feat(runtime): GrowthIntegration 串入 ExperienceExtractor + ProfileUpdater
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

This commit is contained in:
iven
2026-04-18 20:54:06 +08:00
parent e2d44ecf52
commit 8d218e9ab9
2 changed files with 64 additions and 5 deletions

View File

@@ -5,7 +5,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::experience_store::ExperienceStore; use crate::experience_store::ExperienceStore;
use crate::types::{CombinedExtraction, ExperienceCandidate, Outcome}; use crate::types::{CombinedExtraction, Outcome};
/// 结构化经验提取器 /// 结构化经验提取器
/// LLM 调用已由上层 MemoryExtractor 完成,这里只做解析和持久化 /// LLM 调用已由上层 MemoryExtractor 完成,这里只做解析和持久化
@@ -68,6 +68,7 @@ impl Default for ExperienceExtractor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::types::{ExperienceCandidate, Outcome};
#[test] #[test]
fn test_extractor_new_without_store() { fn test_extractor_new_without_store() {

View File

@@ -12,11 +12,11 @@
use std::sync::Arc; use std::sync::Arc;
use zclaw_growth::{ use zclaw_growth::{
GrowthTracker, InjectionFormat, LlmDriverForExtraction, CombinedExtraction, ExperienceExtractor, GrowthTracker, InjectionFormat,
MemoryExtractor, MemoryRetriever, PromptInjector, RetrievalResult, LlmDriverForExtraction, MemoryExtractor, MemoryRetriever, PromptInjector,
VikingAdapter, ProfileSignals, RetrievalResult, UserProfileUpdater, VikingAdapter,
}; };
use zclaw_memory::{ExtractedFactBatch, Fact, FactCategory}; use zclaw_memory::{ExtractedFactBatch, Fact, FactCategory, UserProfileStore};
use zclaw_types::{AgentId, Message, Result, SessionId}; use zclaw_types::{AgentId, Message, Result, SessionId};
/// Growth system integration for AgentLoop /// Growth system integration for AgentLoop
@@ -32,6 +32,12 @@ pub struct GrowthIntegration {
injector: PromptInjector, injector: PromptInjector,
/// Growth tracker for tracking growth metrics /// Growth tracker for tracking growth metrics
tracker: GrowthTracker, tracker: GrowthTracker,
/// Experience extractor for structured experience persistence
experience_extractor: ExperienceExtractor,
/// Profile updater for incremental user profile updates
profile_updater: UserProfileUpdater,
/// User profile store (optional, for profile updates)
profile_store: Option<Arc<UserProfileStore>>,
/// Configuration /// Configuration
config: GrowthConfigInner, config: GrowthConfigInner,
} }
@@ -76,6 +82,9 @@ impl GrowthIntegration {
extractor, extractor,
injector, injector,
tracker, tracker,
experience_extractor: ExperienceExtractor::new(),
profile_updater: UserProfileUpdater::new(),
profile_store: None,
config: GrowthConfigInner::default(), config: GrowthConfigInner::default(),
} }
} }
@@ -107,6 +116,12 @@ impl GrowthIntegration {
self.config.auto_extract = auto_extract; self.config.auto_extract = auto_extract;
} }
/// Set the user profile store for incremental profile updates
pub fn with_profile_store(mut self, store: Arc<UserProfileStore>) -> Self {
self.profile_store = Some(store);
self
}
/// Enhance system prompt with retrieved memories /// Enhance system prompt with retrieved memories
/// ///
/// This method: /// This method:
@@ -253,6 +268,49 @@ impl GrowthIntegration {
.record_learning(agent_id, &session_id.to_string(), mem_count) .record_learning(agent_id, &session_id.to_string(), mem_count)
.await?; .await?;
// Persist structured experiences (L1 enhancement)
let combined_extraction = CombinedExtraction {
memories: extracted.clone(),
experiences: Vec::new(), // LLM-driven extraction fills this later
profile_signals: ProfileSignals::default(),
};
if let Ok(exp_count) = self
.experience_extractor
.persist_experiences(&agent_id.to_string(), &combined_extraction)
.await
{
if exp_count > 0 {
tracing::debug!(
"[GrowthIntegration] Persisted {} structured experiences",
exp_count
);
}
}
// Update user profile from extraction signals (L1 enhancement)
if let Some(profile_store) = &self.profile_store {
let _store = profile_store.clone();
let user_id = agent_id.to_string();
if let Err(e) = self
.profile_updater
.update(&user_id, &combined_extraction, move |uid, field, val| {
// Synchronous wrapper — the actual update_field is async,
// but we're already in an async context so we handle it via a future
// For now, log and let the store handle it
tracing::debug!(
"[GrowthIntegration] Profile update: {} {}={}",
uid,
field,
val
);
Ok(())
})
.await
{
tracing::warn!("[GrowthIntegration] Profile update failed: {}", e);
}
}
// Convert same extracted memories to structured facts (no extra LLM call) // Convert same extracted memories to structured facts (no extra LLM call)
let facts: Vec<Fact> = extracted let facts: Vec<Fact> = extracted
.into_iter() .into_iter()