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: 添加测试文件验证工作区路径
361 lines
12 KiB
Markdown
361 lines
12 KiB
Markdown
# 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 编译: ✅ 通过(仅有预先存在的警告)
|