Files
zclaw_openfang/plans/nifty-inventing-valiant.md
iven aa6a9cbd84
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
feat: 新增技能编排引擎和工作流构建器组件
refactor: 统一Hands系统常量到单个源文件
refactor: 更新Hands中文名称和描述

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

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

test: 添加测试文件验证工作区路径
2026-03-25 08:27:25 +08:00

361 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<HeartbeatAlert> {
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<string, number>;
by_agent: Record<string, number>;
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<RwLock<StdHashMap<String, String>>> = 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<HeartbeatAlert> {
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 编译: ✅ 通过(仅有预先存在的警告)