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

refactor: 统一Hands系统常量到单个源文件
refactor: 更新Hands中文名称和描述

fix: 修复技能市场在连接状态变化时重新加载
fix: 修复身份变更提案的错误处理逻辑

docs: 更新多个功能文档的验证状态和实现位置
docs: 更新Hands系统文档

test: 添加测试文件验证工作区路径
This commit is contained in:
iven
2026-03-25 08:27:25 +08:00
parent 9c781f5f2a
commit aa6a9cbd84
110 changed files with 12384 additions and 1337 deletions

View File

@@ -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::*;

View File

@@ -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 ===

View File

@@ -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,
};