# ZCLAW 自我进化系统审查与修复计划 ## 背景 自我进化系统是 ZCLAW 的核心能力,包括四个组件: - **心跳引擎** - 定期主动检查 - **反思引擎** - 分析模式并生成改进建议 - **身份管理** - 管理人格文件和变更提案 - **记忆存储** - 持久化对话和经验 --- ## 审查结果摘要 ### 实现状态 | 组件 | 函数/功能 | 状态 | |------|----------|------| | **心跳引擎** | check_pending_tasks | ✅ 完整 | | | check_memory_health | ✅ 完整 | | | check_correction_patterns | ✅ 完整 | | | check_learning_opportunities | ✅ 完整 | | | check_idle_greeting | ⚠️ 占位符 | | **反思引擎** | analyze_patterns | ✅ 完整 | | | generate_improvements | ✅ 完整 | | | propose_identity_changes | ✅ 完整 | | **身份管理** | 提案处理 | ✅ 完整 | | | 持久化 | ✅ 完整 | | **前端** | Intelligence Client | ✅ 完整 | | | IdentityChangeProposal UI | ✅ 完整 | | | 提案通知系统 | ✅ 存在 | ### 发现的问题 | 优先级 | 问题 | 影响 | |--------|------|------| | HIGH | MemoryStatsCache 同步问题 | 心跳检查依赖前端主动更新,可能跳过检查 | | HIGH | API 命名不一致 | `updateMemoryStats` 参数名不匹配(camelCase vs snake_case) | | MEDIUM | check_idle_greeting 占位符 | 空闲问候功能不可用 | | MEDIUM | 类型定义不一致 | `totalEntries` vs `total_memories` 命名不统一 | | MEDIUM | 提案审批错误处理 | 缺少详细的错误反馈 | | LOW | storageSizeBytes fallback 为 0 | localStorage 模式下无法计算 | | LOW | 硬编码配置值 | 历史限制、快照数量不可配置 | --- ## 修复计划 ### Phase 1: 修复 HIGH 优先级问题 #### Fix 1.1: API 参数命名修正 ⚡ 5分钟 **文件**: [intelligence-client.ts](desktop/src/lib/intelligence-client.ts) **问题**: `updateMemoryStats` 使用 camelCase 参数,但 Rust 后端期望 snake_case **修改位置**: 第 989-1011 行 ```typescript // 修改前 await invoke('heartbeat_update_memory_stats', { agentId, taskCount, totalEntries, storageSizeBytes, }); // 修改后 await invoke('heartbeat_update_memory_stats', { agent_id: agentId, task_count: taskCount, total_entries: totalEntries, storage_size_bytes: storageSizeBytes, }); ``` #### Fix 1.2: 添加周期性记忆统计同步 ⚡ 15分钟 **文件**: [App.tsx](desktop/src/App.tsx) **问题**: 记忆统计仅在启动时同步一次,之后数据可能陈旧 **修改位置**: 第 213 行后(heartbeat.start 之后) ```typescript // 添加周期性同步(每 5 分钟) const MEMORY_STATS_SYNC_INTERVAL = 5 * 60 * 1000; const statsSyncInterval = setInterval(async () => { try { const stats = await intelligenceClient.memory.stats(); const taskCount = stats.byType?.['task'] || 0; await intelligenceClient.heartbeat.updateMemoryStats( defaultAgentId, taskCount, stats.totalEntries, stats.storageSizeBytes ); console.log('[App] Memory stats synced (periodic)'); } catch (err) { console.warn('[App] Periodic memory stats sync failed:', err); } }, MEMORY_STATS_SYNC_INTERVAL); ``` #### Fix 1.3: 心跳检查容错处理 ⚡ 20分钟 **文件**: [heartbeat.rs](desktop/src-tauri/src/intelligence/heartbeat.rs) **问题**: 当缓存为空时,检查函数直接跳过,无告警 **修改**: 在 `check_pending_tasks` 和 `check_memory_health` 中添加缓存缺失告警 ```rust fn check_pending_tasks(agent_id: &str) -> Option { match get_cached_memory_stats(agent_id) { Some(stats) if stats.task_count >= 5 => { /* 现有逻辑 */ }, Some(_) => None, None => Some(HeartbeatAlert { title: "记忆统计未同步".to_string(), content: "心跳引擎未能获取记忆统计信息,部分检查被跳过".to_string(), urgency: Urgency::Low, source: "pending-tasks".to_string(), timestamp: chrono::Utc::now().to_rfc3339(), }), } } ``` ### Phase 2: 修复 MEDIUM 优先级问题 #### Fix 2.1: 统一类型定义命名 ⚡ 10分钟 **文件**: [intelligence-backend.ts](desktop/src/lib/intelligence-backend.ts) **问题**: 前端使用 `totalEntries`,后端返回 `total_memories` **修改**: 更新接口定义以匹配后端 ```typescript export interface MemoryStats { total_entries: number; // 匹配后端 by_type: Record; by_agent: Record; oldest_entry: string | null; newest_entry: string | null; storage_size_bytes: number; } ``` **同时更新** [intelligence-client.ts](desktop/src/lib/intelligence-client.ts) 中的转换函数 #### Fix 2.2: 增强提案审批错误处理 ⚡ 10分钟 **文件**: [IdentityChangeProposal.tsx](desktop/src/components/IdentityChangeProposal.tsx) **添加错误解析函数**: ```typescript function parseProposalError(err: unknown, operation: 'approval' | 'rejection' | 'restore'): string { const errorMessage = err instanceof Error ? err.message : String(err); if (errorMessage.includes('not found')) { return `提案不存在或已被处理`; } if (errorMessage.includes('not pending')) { return '该提案已被处理,请刷新页面'; } if (errorMessage.includes('network') || errorMessage.includes('fetch')) { return '网络连接失败,请检查网络后重试'; } return `${operation === 'approval' ? '审批' : operation === 'rejection' ? '拒绝' : '恢复'}失败: ${errorMessage}`; } ``` #### Fix 2.3: 实现 check_idle_greeting(可选)⚡ 30分钟 **文件**: [heartbeat.rs](desktop/src-tauri/src/intelligence/heartbeat.rs) **添加最后交互时间追踪**: ```rust static LAST_INTERACTION: OnceLock>> = OnceLock::new(); 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()); } } fn check_idle_greeting(agent_id: &str) -> Option { let map = get_last_interaction_map(); let last_interaction = map.read().ok()?.get(agent_id).cloned()?; let last_time = chrono::DateTime::parse_from_rfc3339(&last_interaction).ok()?; let idle_hours = (chrono::Utc::now() - last_time).num_hours(); if idle_hours >= 24 { Some(HeartbeatAlert { title: "用户长时间未互动".to_string(), content: format!("距离上次互动已过去 {} 小时", idle_hours), urgency: Urgency::Low, source: "idle-greeting".to_string(), timestamp: chrono::Utc::now().to_rfc3339(), }) } else { None } } ``` **同时添加 Tauri 命令**: ```rust #[tauri::command] pub async fn heartbeat_record_interaction(agent_id: String) -> Result<(), String> ``` ### Phase 3: 修复 LOW 优先级问题(可选) #### Fix 3.1: localStorage fallback 存储大小计算 **文件**: [intelligence-client.ts](desktop/src/lib/intelligence-client.ts) ```typescript // 在 fallbackMemory.stats() 中添加 let storageSizeBytes = 0; try { const serialized = JSON.stringify(store.memories); storageSizeBytes = new Blob([serialized]).size; } catch { /* ignore */ } ``` --- ## 实现顺序 | 顺序 | 修复项 | 优先级 | 预估时间 | |------|--------|--------|----------| | 1 | Fix 1.1 - API 参数命名 | HIGH | 5 分钟 | | 2 | Fix 1.2 - 周期性同步 | HIGH | 15 分钟 | | 3 | Fix 1.3 - 心跳容错 | HIGH | 20 分钟 | | 4 | Fix 2.1 - 类型统一 | MEDIUM | 10 分钟 | | 5 | Fix 2.2 - 错误处理 | MEDIUM | 10 分钟 | | 6 | Fix 2.3 - 空闲问候 | MEDIUM | 30 分钟 | | 7 | Fix 3.1 - 存储大小 | LOW | 5 分钟 | **总计**: 约 1.5 小时(不含可选项) --- ## 关键文件 | 文件 | 修改内容 | |------|----------| | [intelligence-client.ts](desktop/src/lib/intelligence-client.ts) | API 参数命名、类型转换、存储大小计算 | | [App.tsx](desktop/src/App.tsx) | 周期性记忆统计同步 | | [heartbeat.rs](desktop/src-tauri/src/intelligence/heartbeat.rs) | 缓存容错、空闲问候 | | [intelligence-backend.ts](desktop/src/lib/intelligence-backend.ts) | 类型定义统一 | | [IdentityChangeProposal.tsx](desktop/src/components/IdentityChangeProposal.tsx) | 错误处理增强 | | [lib.rs](desktop/src-tauri/src/lib.rs) | 注册新 Tauri 命令(如实现 Fix 2.3) | --- ## 验证方法 ### Fix 1.1 验证 ```bash # 启动应用,检查控制台 pnpm start:dev # 观察 Tauri invoke 调用参数是否正确 ``` ### Fix 1.2 验证 ```bash # 启动后等待 5 分钟,检查控制台 # 应看到 "[App] Memory stats synced (periodic)" 日志 ``` ### Fix 1.3 验证 ```bash # 清除缓存后触发心跳 # 应看到 "记忆统计未同步" 告警 ``` ### 全量验证 ```bash # TypeScript 类型检查 pnpm tsc --noEmit # 运行测试 pnpm vitest run # 启动开发环境 pnpm start:dev ``` ### 人工验证清单 - [ ] 应用启动无错误 - [ ] 心跳引擎正常初始化 - [ ] 记忆统计同步正常(启动 + 周期) - [ ] 提案审批流程正常 - [ ] 错误信息清晰可读 --- ## 深度复验报告 (2026-03-24) ### 前端修复验证 | 修复项 | 文件 | 状态 | 说明 | |--------|------|------|------| | Fix 1.1 | intelligence-client.ts | ✅ 已正确实现 | `updateMemoryStats` 和 `recordCorrection` 使用 snake_case | | Fix 1.2 | App.tsx | ✅ 已正确实现 | 5分钟周期同步 + 清理逻辑完整 | | Fix 2.1 | intelligence-backend.ts | ✅ 已正确实现 | `MemoryStats` 使用 `total_entries` 等 | | Fix 2.1 | intelligence-client.ts | ✅ 已正确实现 | `toFrontendStats` 正确映射字段 | | Fix 2.2 | IdentityChangeProposal.tsx | ✅ 已正确实现 | `parseProposalError` 函数 + 3处调用 | | Fix 3.1 | intelligence-client.ts | ✅ 已正确实现 | `fallbackMemory.stats()` 计算 `storageSizeBytes` | **前端修复验证结果**: 全部 6/6 通过 ✅ ### 后端修复验证 | 修复项 | 文件 | 状态 | 说明 | |--------|------|------|------| | Fix 1.3 | heartbeat.rs:451-480 | ✅ 已正确实现 | `check_pending_tasks` 缓存为空时返回告警 | | Fix 1.3 | heartbeat.rs:484-521 | ⚠️ 设计偏差 | `check_memory_health` 缓存为空时返回 None(避免重复告警) | | Fix 2.3 | heartbeat.rs:347 | ✅ 已正确实现 | `LAST_INTERACTION` 静态变量 | | Fix 2.3 | heartbeat.rs:366-368 | ✅ 已正确实现 | `get_last_interaction_map()` 函数 | | Fix 2.3 | heartbeat.rs:371-376 | ✅ 已正确实现 | `record_interaction()` 函数 | | Fix 2.3 | heartbeat.rs:524-559 | ✅ 已正确实现 | `check_idle_greeting` 完整实现(24小时阈值) | | Fix 2.3 | heartbeat.rs:736-741 | ✅ 已正确实现 | `heartbeat_record_interaction` Tauri 命令 | | 命令注册 | lib.rs:1432 | ✅ 已正确注册 | `heartbeat_record_interaction` 已注册 | **后端修复验证结果**: 7/8 完全通过,1/8 设计偏差(合理) ### 设计偏差说明 **`check_memory_health` 缓存为空时返回 None** 原计划要求:返回告警 实际实现:返回 None 设计理由(代码注释): > Cache is empty - skip check (already reported in check_pending_tasks) **评估**: 这是合理的设计决策,避免在缓存为空时产生重复告警。`check_pending_tasks` 已经报告了缓存未同步的问题,`check_memory_health` 无需再次告警。 ### 复验结论 | 类别 | 结果 | |------|------| | 前端修复 | 6/6 ✅ 通过 | | 后端修复 | 7/8 ✅ 通过 | | 设计偏差 | 1 ⚠️ 合理(不视为问题)| | 总体评估 | **全部修复已正确实现** | **编译验证**: - TypeScript 类型检查: ✅ 通过 - Rust 编译: ✅ 通过(仅有预先存在的警告)