feat: 新增技能编排引擎和工作流构建器组件
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
refactor: 统一Hands系统常量到单个源文件 refactor: 更新Hands中文名称和描述 fix: 修复技能市场在连接状态变化时重新加载 fix: 修复身份变更提案的错误处理逻辑 docs: 更新多个功能文档的验证状态和实现位置 docs: 更新Hands系统文档 test: 添加测试文件验证工作区路径
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
//! Phase 2 of Intelligence Layer Migration.
|
||||
//! Reference: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md §6.4.1
|
||||
|
||||
use chrono::{DateTime, Local, Timelike};
|
||||
use chrono::{Local, Timelike};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -342,6 +342,10 @@ static CORRECTION_COUNTERS: OnceLock<RwLock<StdHashMap<String, usize>>> = OnceLo
|
||||
/// Key: agent_id, Value: (task_count, total_memories, storage_bytes)
|
||||
static MEMORY_STATS_CACHE: OnceLock<RwLock<StdHashMap<String, MemoryStatsCache>>> = OnceLock::new();
|
||||
|
||||
/// Global last interaction timestamps
|
||||
/// Key: agent_id, Value: last interaction timestamp (RFC3339)
|
||||
static LAST_INTERACTION: OnceLock<RwLock<StdHashMap<String, String>>> = OnceLock::new();
|
||||
|
||||
/// Cached memory stats for an agent
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MemoryStatsCache {
|
||||
@@ -359,6 +363,18 @@ fn get_memory_stats_cache() -> &'static RwLock<StdHashMap<String, MemoryStatsCac
|
||||
MEMORY_STATS_CACHE.get_or_init(|| RwLock::new(StdHashMap::new()))
|
||||
}
|
||||
|
||||
fn get_last_interaction_map() -> &'static RwLock<StdHashMap<String, String>> {
|
||||
LAST_INTERACTION.get_or_init(|| RwLock::new(StdHashMap::new()))
|
||||
}
|
||||
|
||||
/// Record an interaction for an agent (call from frontend when user sends message)
|
||||
pub fn record_interaction(agent_id: &str) {
|
||||
let map = get_last_interaction_map();
|
||||
if let Ok(mut map) = map.write() {
|
||||
map.insert(agent_id.to_string(), chrono::Utc::now().to_rfc3339());
|
||||
}
|
||||
}
|
||||
|
||||
/// Update memory stats cache for an agent
|
||||
/// Call this from frontend via Tauri command after fetching memory stats
|
||||
pub fn update_memory_stats_cache(agent_id: &str, task_count: usize, total_entries: usize, storage_size_bytes: usize) {
|
||||
@@ -433,10 +449,10 @@ fn check_correction_patterns(agent_id: &str) -> Vec<HeartbeatAlert> {
|
||||
/// Check for pending task memories
|
||||
/// Uses cached memory stats to detect task backlog
|
||||
fn check_pending_tasks(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
if let Some(stats) = get_cached_memory_stats(agent_id) {
|
||||
// Alert if there are 5+ pending tasks
|
||||
if stats.task_count >= 5 {
|
||||
return Some(HeartbeatAlert {
|
||||
match get_cached_memory_stats(agent_id) {
|
||||
Some(stats) if stats.task_count >= 5 => {
|
||||
// Alert if there are 5+ pending tasks
|
||||
Some(HeartbeatAlert {
|
||||
title: "待办任务积压".to_string(),
|
||||
content: format!("当前有 {} 个待办任务未完成,建议处理或重新评估优先级", stats.task_count),
|
||||
urgency: if stats.task_count >= 10 {
|
||||
@@ -446,51 +462,102 @@ fn check_pending_tasks(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
},
|
||||
source: "pending-tasks".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
});
|
||||
})
|
||||
},
|
||||
Some(_) => None, // Stats available but no alert needed
|
||||
None => {
|
||||
// Cache is empty - warn about missing sync
|
||||
tracing::warn!("[Heartbeat] Memory stats cache is empty for agent {}, waiting for frontend sync", agent_id);
|
||||
Some(HeartbeatAlert {
|
||||
title: "记忆统计未同步".to_string(),
|
||||
content: "心跳引擎未能获取记忆统计信息,部分检查被跳过。请确保记忆系统正常运行。".to_string(),
|
||||
urgency: Urgency::Low,
|
||||
source: "pending-tasks".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
})
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check memory storage health
|
||||
/// Uses cached memory stats to detect storage issues
|
||||
fn check_memory_health(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
if let Some(stats) = get_cached_memory_stats(agent_id) {
|
||||
// Alert if storage is very large (> 50MB)
|
||||
if stats.storage_size_bytes > 50 * 1024 * 1024 {
|
||||
return Some(HeartbeatAlert {
|
||||
title: "记忆存储过大".to_string(),
|
||||
content: format!(
|
||||
"记忆存储已达 {:.1}MB,建议清理低重要性记忆或归档旧记忆",
|
||||
stats.storage_size_bytes as f64 / (1024.0 * 1024.0)
|
||||
),
|
||||
urgency: Urgency::Medium,
|
||||
source: "memory-health".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
});
|
||||
}
|
||||
match get_cached_memory_stats(agent_id) {
|
||||
Some(stats) => {
|
||||
// Alert if storage is very large (> 50MB)
|
||||
if stats.storage_size_bytes > 50 * 1024 * 1024 {
|
||||
return Some(HeartbeatAlert {
|
||||
title: "记忆存储过大".to_string(),
|
||||
content: format!(
|
||||
"记忆存储已达 {:.1}MB,建议清理低重要性记忆或归档旧记忆",
|
||||
stats.storage_size_bytes as f64 / (1024.0 * 1024.0)
|
||||
),
|
||||
urgency: Urgency::Medium,
|
||||
source: "memory-health".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
});
|
||||
}
|
||||
|
||||
// Alert if too many memories (> 1000)
|
||||
if stats.total_entries > 1000 {
|
||||
return Some(HeartbeatAlert {
|
||||
title: "记忆条目过多".to_string(),
|
||||
content: format!(
|
||||
"当前有 {} 条记忆,可能影响检索效率,建议清理或归档",
|
||||
stats.total_entries
|
||||
),
|
||||
urgency: Urgency::Low,
|
||||
source: "memory-health".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
});
|
||||
// Alert if too many memories (> 1000)
|
||||
if stats.total_entries > 1000 {
|
||||
return Some(HeartbeatAlert {
|
||||
title: "记忆条目过多".to_string(),
|
||||
content: format!(
|
||||
"当前有 {} 条记忆,可能影响检索效率,建议清理或归档",
|
||||
stats.total_entries
|
||||
),
|
||||
urgency: Urgency::Low,
|
||||
source: "memory-health".to_string(),
|
||||
timestamp: chrono::Utc::now().to_rfc3339(),
|
||||
});
|
||||
}
|
||||
None
|
||||
},
|
||||
None => {
|
||||
// Cache is empty - skip check (already reported in check_pending_tasks)
|
||||
None
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if user has been idle (placeholder)
|
||||
fn check_idle_greeting(_agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
// In full implementation, this would check last interaction time
|
||||
None
|
||||
/// Check if user has been idle and might benefit from a greeting
|
||||
fn check_idle_greeting(agent_id: &str) -> Option<HeartbeatAlert> {
|
||||
let map = get_last_interaction_map();
|
||||
|
||||
// Try to get the last interaction time
|
||||
let last_interaction = {
|
||||
let read_result = map.read();
|
||||
match read_result {
|
||||
Ok(map) => map.get(agent_id).cloned(),
|
||||
Err(_) => return None, // Skip if lock fails
|
||||
}
|
||||
};
|
||||
|
||||
// If no interaction recorded yet, skip
|
||||
let last_interaction = last_interaction?;
|
||||
|
||||
// Parse the timestamp and convert to UTC for comparison
|
||||
let last_time = chrono::DateTime::parse_from_rfc3339(&last_interaction)
|
||||
.ok()?
|
||||
.with_timezone(&chrono::Utc);
|
||||
let now = chrono::Utc::now();
|
||||
let idle_hours = (now - last_time).num_hours();
|
||||
|
||||
// Alert if idle for more than 24 hours
|
||||
if idle_hours >= 24 {
|
||||
Some(HeartbeatAlert {
|
||||
title: "用户长时间未互动".to_string(),
|
||||
content: format!(
|
||||
"距离上次互动已过去 {} 小时,可以考虑主动问候或检查用户是否需要帮助",
|
||||
idle_hours
|
||||
),
|
||||
urgency: Urgency::Low,
|
||||
source: "idle-greeting".to_string(),
|
||||
timestamp: now.to_rfc3339(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for personality improvement opportunities
|
||||
@@ -665,6 +732,16 @@ pub async fn heartbeat_record_correction(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Record a user interaction for idle greeting detection
|
||||
/// Call this from frontend whenever user sends a message
|
||||
#[tauri::command]
|
||||
pub async fn heartbeat_record_interaction(
|
||||
agent_id: String,
|
||||
) -> Result<(), String> {
|
||||
record_interaction(&agent_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
//! Phase 3 of Intelligence Layer Migration.
|
||||
//! Reference: ZCLAW_AGENT_INTELLIGENCE_EVOLUTION.md §6.2.3
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::Utc;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use tracing::{error, info, warn};
|
||||
use tracing::{error, warn};
|
||||
|
||||
// === Types ===
|
||||
|
||||
|
||||
@@ -29,24 +29,10 @@ pub mod reflection;
|
||||
pub mod identity;
|
||||
|
||||
// Re-export main types for convenience
|
||||
pub use heartbeat::{
|
||||
HeartbeatConfig, HeartbeatEngine, HeartbeatEngineState,
|
||||
HeartbeatAlert, HeartbeatResult, HeartbeatStatus,
|
||||
Urgency, NotifyChannel, ProactivityLevel,
|
||||
};
|
||||
pub use compactor::{
|
||||
CompactionConfig, ContextCompactor, CompactableMessage,
|
||||
CompactionResult, CompactionCheck, CompactionUrgency,
|
||||
estimate_tokens, estimate_messages_tokens,
|
||||
};
|
||||
pub use heartbeat::HeartbeatEngineState;
|
||||
pub use reflection::{
|
||||
ReflectionConfig, ReflectionEngine, ReflectionEngineState,
|
||||
ReflectionResult, ReflectionState, ReflectionResult as ReflectionOutput,
|
||||
PatternObservation, ImprovementSuggestion, IdentityChangeProposal as ReflectionIdentityChangeProposal,
|
||||
Sentiment, Priority, MemoryEntryForAnalysis,
|
||||
ReflectionEngine, ReflectionEngineState,
|
||||
};
|
||||
pub use identity::{
|
||||
AgentIdentityManager, IdentityManagerState,
|
||||
IdentityFiles, IdentityChangeProposal, IdentitySnapshot,
|
||||
IdentityFile, ProposalStatus,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user