feat(growth,skills,saas,desktop): C线差异化全量实现 — C1日报+C2飞轮+C3引导
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
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
C3 零配置引导 (P0): - use-cold-start.ts: 4阶段→6阶段对话驱动状态机 (idle→greeting→industry→identity→task→completed) - cold-start-mapper.ts: 关键词行业检测 + 肯定/否定/名字提取 - cold_start_prompt.rs: Rust侧6阶段system prompt生成 + 7个测试 - FirstConversationPrompt.tsx: 动态行业卡片 + 行业任务引导 + 通用快捷操作 C1 管家日报 (P0): - kernel注册DailyReportHand (第8个Hand) - DailyReportPanel.tsx已存在,事件监听+持久化完整 C2 行业知识飞轮 (P1): - heartbeat.rs: 经验缓存(EXPERIENCE_CACHE) + check_unresolved_pains增强经验感知 - heartbeat_update_experiences Tauri命令 + VikingStorage持久化 - semantic_router.rs: 经验权重boost(0.05*ln(count+1), 上限0.15) + update_experience_boosts方法 - service.rs: auto_optimize_config() 基于使用频率自动优化行业skill_priorities 验证: tsc 0 errors, cargo check 0 warnings, 7 cold_start + 5 daily_report + 1 experience_boost tests PASS
This commit is contained in:
@@ -464,6 +464,27 @@ pub fn update_pain_points_cache(agent_id: &str, pain_points: Vec<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Global experience cache: high-reuse experiences per agent.
|
||||
/// Key: agent_id, Value: list of (tool_used, reuse_count) tuples.
|
||||
static EXPERIENCE_CACHE: OnceLock<RwLock<StdHashMap<String, Vec<(String, u32)>>>> = OnceLock::new();
|
||||
|
||||
fn get_experience_cache() -> &'static RwLock<StdHashMap<String, Vec<(String, u32)>>> {
|
||||
EXPERIENCE_CACHE.get_or_init(|| RwLock::new(StdHashMap::new()))
|
||||
}
|
||||
|
||||
/// Update experience cache (called from frontend or growth middleware)
|
||||
pub fn update_experience_cache(agent_id: &str, experiences: Vec<(String, u32)>) {
|
||||
let cache = get_experience_cache();
|
||||
if let Ok(mut cache) = cache.write() {
|
||||
cache.insert(agent_id.to_string(), experiences);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cached_experiences(agent_id: &str) -> Option<Vec<(String, u32)>> {
|
||||
let cache = get_experience_cache();
|
||||
cache.read().ok()?.get(agent_id).cloned()
|
||||
}
|
||||
|
||||
/// Get cached pain points for an agent
|
||||
fn get_cached_pain_points(agent_id: &str) -> Option<Vec<String>> {
|
||||
let cache = get_pain_points_cache();
|
||||
@@ -778,7 +799,8 @@ fn check_learning_opportunities(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
|
||||
/// Check for unresolved user pain points accumulated by the butler system.
|
||||
/// When pain points persist across multiple conversations, surface them as
|
||||
/// proactive suggestions.
|
||||
/// proactive suggestions. Also considers high-reuse experiences to generate
|
||||
/// contextual skill suggestions.
|
||||
fn check_unresolved_pains(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
let pains = get_cached_pain_points(agent_id)?;
|
||||
if pains.is_empty() {
|
||||
@@ -790,11 +812,25 @@ fn check_unresolved_pains(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
} else {
|
||||
format!("{}等 {} 项", pains[..3].join("、"), count)
|
||||
};
|
||||
|
||||
// Enhance with experience-based suggestions
|
||||
let experience_hint = if let Some(experiences) = get_cached_experiences(agent_id) {
|
||||
let high_use: Vec<&(String, u32)> = experiences.iter().filter(|(_, c)| *c >= 3).collect();
|
||||
if !high_use.is_empty() {
|
||||
let tools: Vec<&str> = high_use.iter().map(|(t, _)| t.as_str()).collect();
|
||||
format!(" 用户频繁使用{},可主动提供相关技能建议。", tools.join("、"))
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
Some(HeartbeatAlert {
|
||||
title: "未解决的用户痛点".to_string(),
|
||||
content: format!(
|
||||
"检测到 {} 个持续痛点:{}。建议主动提供解决方案或相关建议。",
|
||||
count, summary
|
||||
"检测到 {} 个持续痛点:{}。建议主动提供解决方案或相关建议。{}",
|
||||
count, summary, experience_hint
|
||||
),
|
||||
urgency: if count >= 3 { Urgency::High } else { Urgency::Medium },
|
||||
source: "unresolved-pains".to_string(),
|
||||
@@ -1098,6 +1134,28 @@ pub async fn heartbeat_update_pain_points(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update experience cache for heartbeat proactive suggestions.
|
||||
/// Called by frontend when high-reuse experiences are detected.
|
||||
// @reserved
|
||||
#[tauri::command]
|
||||
pub async fn heartbeat_update_experiences(
|
||||
agent_id: String,
|
||||
experiences: Vec<(String, u32)>,
|
||||
) -> Result<(), String> {
|
||||
update_experience_cache(&agent_id, experiences.clone());
|
||||
let key = format!("heartbeat:experiences:{}", agent_id);
|
||||
tokio::spawn(async move {
|
||||
if let Ok(storage) = crate::viking_commands::get_storage().await {
|
||||
if let Ok(json) = serde_json::to_string(&experiences) {
|
||||
if let Err(e) = zclaw_growth::VikingStorage::store_metadata_json(&*storage, &key, &json).await {
|
||||
tracing::warn!("[heartbeat] Failed to persist experiences: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user