Phase 1 - Security: - Add AES-GCM encryption for localStorage fallback - Enforce WSS protocol for non-localhost WebSocket connections - Add URL sanitization to prevent XSS in markdown links Phase 2 - Domain Reorganization: - Create Intelligence Domain with Valtio store and caching - Add unified intelligence-client for Rust backend integration - Migrate from legacy agent-memory, heartbeat, reflection modules Phase 3 - Core Optimization: - Add virtual scrolling for ChatArea with react-window - Implement LRU cache with TTL for intelligence operations - Add message virtualization utilities Additional: - Add OpenFang compatibility test suite - Update E2E test fixtures - Add audit logging infrastructure - Update project documentation and plans Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
367 lines
11 KiB
Markdown
367 lines
11 KiB
Markdown
# ZCLAW 智能层统一实现方案
|
||
|
||
## 概述
|
||
|
||
本方案旨在消除前后端智能层代码重复,统一使用 Rust 后端 + TypeScript 适配器 (`intelligence-backend.ts`)。
|
||
|
||
## 现状分析
|
||
|
||
### 已有的 Rust 后端命令(通过 `intelligence-backend.ts` 封装)
|
||
|
||
| 模块 | 命令 | 状态 |
|
||
|------|------|------|
|
||
| Memory | memory_init, memory_store, memory_get, memory_search, memory_delete, memory_delete_all, memory_stats, memory_export, memory_import, memory_db_path | 完整 |
|
||
| Heartbeat | heartbeat_init, heartbeat_start, heartbeat_stop, heartbeat_tick, heartbeat_get_config, heartbeat_update_config, heartbeat_get_history | 完整 |
|
||
| Compactor | compactor_estimate_tokens, compactor_estimate_messages_tokens, compactor_check_threshold, compactor_compact | 完整 |
|
||
| Reflection | reflection_init, reflection_record_conversation, reflection_should_reflect, reflection_reflect, reflection_get_history, reflection_get_state | 完整 |
|
||
| Identity | identity_get, identity_get_file, identity_build_prompt, identity_update_user_profile, identity_append_user_profile, identity_propose_change, identity_approve_proposal, identity_reject_proposal, identity_get_pending_proposals, identity_update_file, identity_get_snapshots, identity_restore_snapshot, identity_list_agents, identity_delete_agent | 完整 |
|
||
|
||
### 需要迁移的前端 TS 实现
|
||
|
||
| 文件 | 代码行数 | 引用位置 |
|
||
|------|----------|----------|
|
||
| `agent-memory.ts` | ~487行 | chatStore, memoryGraphStore, MemoryPanel, memory-extractor, agent-swarm, skill-discovery |
|
||
| `agent-identity.ts` | ~351行 | chatStore, reflection-engine, memory-extractor, ReflectionLog |
|
||
| `reflection-engine.ts` | ~678行 | chatStore, ReflectionLog |
|
||
| `heartbeat-engine.ts` | ~347行 | HeartbeatConfig |
|
||
| `context-compactor.ts` | ~443行 | chatStore |
|
||
|
||
### 类型差异分析
|
||
|
||
前端 TS 和 Rust 后端的类型有细微差异,需要创建适配层:
|
||
|
||
```
|
||
前端 MemoryEntry.importance: number (0-10)
|
||
后端 PersistentMemory.importance: number (相同)
|
||
|
||
前端 MemoryEntry.type: MemoryType ('fact' | 'preference' | ...)
|
||
后端 PersistentMemory.memory_type: string
|
||
|
||
前端 MemoryEntry.tags: string[]
|
||
后端 PersistentMemory.tags: string (JSON 序列化)
|
||
```
|
||
|
||
---
|
||
|
||
## 实施计划
|
||
|
||
### Phase 0: 准备工作(环境检测 + 降级策略)
|
||
|
||
**目标**: 创建环境检测机制,支持 Tauri/浏览器双环境
|
||
|
||
**修改文件**:
|
||
- 新建 `desktop/src/lib/intelligence-client.ts`
|
||
|
||
**实现内容**:
|
||
```typescript
|
||
// intelligence-client.ts
|
||
import { intelligence } from './intelligence-backend';
|
||
|
||
// 检测是否在 Tauri 环境中
|
||
const isTauriEnv = typeof window !== 'undefined' && '__TAURI__' in window;
|
||
|
||
// 降级策略:非 Tauri 环境使用 localStorage 模拟
|
||
const fallbackMemory = {
|
||
store: async (entry) => { /* localStorage 模拟 */ },
|
||
search: async (options) => { /* localStorage 模拟 */ },
|
||
// ... 其他方法
|
||
};
|
||
|
||
export const intelligenceClient = {
|
||
memory: isTauriEnv ? intelligence.memory : fallbackMemory,
|
||
heartbeat: isTauriEnv ? intelligence.heartbeat : fallbackHeartbeat,
|
||
compactor: isTauriEnv ? intelligence.compactor : fallbackCompactor,
|
||
reflection: isTauriEnv ? intelligence.reflection : fallbackReflection,
|
||
identity: isTauriEnv ? intelligence.identity : fallbackIdentity,
|
||
};
|
||
```
|
||
|
||
**验证方法**:
|
||
- 在 Tauri 桌面端启动,确认 `isTauriEnv === true`
|
||
- 在浏览器中访问 Vite dev server,确认降级逻辑生效
|
||
|
||
---
|
||
|
||
### Phase 1: 迁移 Memory 模块(最关键)
|
||
|
||
**优先级**: 最高(其他模块都依赖 Memory)
|
||
|
||
**修改文件**:
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `chatStore.ts` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
| `memoryGraphStore.ts` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
| `MemoryPanel.tsx` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
| `memory-extractor.ts` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
| `agent-swarm.ts` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
| `skill-discovery.ts` | 将 `getMemoryManager()` 替换为 `intelligenceClient.memory` |
|
||
|
||
**详细修改示例** (chatStore.ts):
|
||
|
||
```typescript
|
||
// 修改前
|
||
import { getMemoryManager } from '../lib/agent-memory';
|
||
|
||
// 在 sendMessage 中
|
||
const memoryMgr = getMemoryManager();
|
||
const relevantMemories = await memoryMgr.search(content, {
|
||
agentId,
|
||
limit: 8,
|
||
minImportance: 3,
|
||
});
|
||
|
||
// 修改后
|
||
import { intelligenceClient } from '../lib/intelligence-client';
|
||
|
||
// 在 sendMessage 中
|
||
const relevantMemories = await intelligenceClient.memory.search({
|
||
agent_id: agentId,
|
||
query: content,
|
||
limit: 8,
|
||
min_importance: 3,
|
||
});
|
||
```
|
||
|
||
**类型适配**:
|
||
```typescript
|
||
// 创建类型转换函数
|
||
function toFrontendMemory(backend: PersistentMemory): MemoryEntry {
|
||
return {
|
||
id: backend.id,
|
||
agentId: backend.agent_id,
|
||
content: backend.content,
|
||
type: backend.memory_type as MemoryType,
|
||
importance: backend.importance,
|
||
source: backend.source as MemorySource,
|
||
tags: JSON.parse(backend.tags || '[]'),
|
||
createdAt: backend.created_at,
|
||
lastAccessedAt: backend.last_accessed_at,
|
||
accessCount: backend.access_count,
|
||
conversationId: backend.conversation_id || undefined,
|
||
};
|
||
}
|
||
|
||
function toBackendMemoryInput(frontend: Omit<MemoryEntry, 'id' | 'createdAt' | 'lastAccessedAt' | 'accessCount'>): MemoryEntryInput {
|
||
return {
|
||
agent_id: frontend.agentId,
|
||
memory_type: frontend.type,
|
||
content: frontend.content,
|
||
importance: frontend.importance,
|
||
source: frontend.source,
|
||
tags: frontend.tags,
|
||
conversation_id: frontend.conversationId,
|
||
};
|
||
}
|
||
```
|
||
|
||
**验证方法**:
|
||
1. 启动桌面端,发送消息
|
||
2. 检查记忆是否正确存储到 SQLite
|
||
3. 搜索记忆是否返回正确结果
|
||
4. MemoryPanel 组件是否正确显示记忆列表
|
||
|
||
**回滚方案**:
|
||
- 保留 `agent-memory.ts` 文件
|
||
- 通过 feature flag 切换新旧实现
|
||
|
||
---
|
||
|
||
### Phase 2: 迁移 Compactor 模块
|
||
|
||
**优先级**: 高(依赖 Memory,但影响范围较小)
|
||
|
||
**修改文件**:
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `chatStore.ts` | 将 `getContextCompactor()` 替换为 `intelligenceClient.compactor` |
|
||
|
||
**详细修改**:
|
||
|
||
```typescript
|
||
// 修改前
|
||
import { getContextCompactor } from '../lib/context-compactor';
|
||
|
||
const compactor = getContextCompactor();
|
||
const check = compactor.checkThreshold(messages);
|
||
|
||
// 修改后
|
||
import { intelligenceClient } from '../lib/intelligence-client';
|
||
|
||
const check = await intelligenceClient.compactor.checkThreshold(
|
||
messages.map(m => ({ role: m.role, content: m.content }))
|
||
);
|
||
```
|
||
|
||
**验证方法**:
|
||
1. 发送大量消息触发 compaction 阈值
|
||
2. 检查是否正确压缩上下文
|
||
3. 验证压缩后消息正常显示
|
||
|
||
---
|
||
|
||
### Phase 3: 迁移 Reflection + Identity 模块
|
||
|
||
**优先级**: 中(关联紧密,需要一起迁移)
|
||
|
||
**修改文件**:
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `chatStore.ts` | 将 `getReflectionEngine()` 替换为 `intelligenceClient.reflection` |
|
||
| `ReflectionLog.tsx` | 将 `ReflectionEngine` 和 `getAgentIdentityManager()` 替换为 intelligenceClient |
|
||
| `memory-extractor.ts` | 将 `getAgentIdentityManager()` 替换为 `intelligenceClient.identity` |
|
||
|
||
**详细修改**:
|
||
|
||
```typescript
|
||
// 修改前 (chatStore.ts)
|
||
import { getReflectionEngine } from '../lib/reflection-engine';
|
||
|
||
const reflectionEngine = getReflectionEngine();
|
||
reflectionEngine.recordConversation();
|
||
if (reflectionEngine.shouldReflect()) {
|
||
reflectionEngine.reflect(agentId);
|
||
}
|
||
|
||
// 修改后
|
||
import { intelligenceClient } from '../lib/intelligence-client';
|
||
|
||
await intelligenceClient.reflection.recordConversation();
|
||
if (await intelligenceClient.reflection.shouldReflect()) {
|
||
const memories = await intelligenceClient.memory.search({ agent_id: agentId, limit: 100 });
|
||
await intelligenceClient.reflection.reflect(agentId, memories.map(m => ({
|
||
memory_type: m.memory_type,
|
||
content: m.content,
|
||
importance: m.importance,
|
||
access_count: m.access_count,
|
||
tags: JSON.parse(m.tags || '[]'),
|
||
})));
|
||
}
|
||
```
|
||
|
||
**验证方法**:
|
||
1. 完成多轮对话后检查 reflection 是否触发
|
||
2. 检查 ReflectionLog 组件是否正确显示反思历史
|
||
3. 验证身份变更提案的审批流程
|
||
|
||
---
|
||
|
||
### Phase 4: 迁移 Heartbeat 模块
|
||
|
||
**优先级**: 低(独立模块,无依赖)
|
||
|
||
**修改文件**:
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `HeartbeatConfig.tsx` | 将 `HeartbeatEngine` 替换为 `intelligenceClient.heartbeat` |
|
||
| `SettingsLayout.tsx` | 如有引用,同样替换 |
|
||
|
||
**详细修改**:
|
||
|
||
```typescript
|
||
// 修改前
|
||
import { HeartbeatEngine, DEFAULT_HEARTBEAT_CONFIG } from '../lib/heartbeat-engine';
|
||
|
||
const engine = new HeartbeatEngine(agentId, config);
|
||
engine.start();
|
||
|
||
// 修改后
|
||
import { intelligenceClient } from '../lib/intelligence-client';
|
||
import type { HeartbeatConfig } from '../lib/intelligence-backend';
|
||
|
||
await intelligenceClient.heartbeat.init(agentId, config);
|
||
await intelligenceClient.heartbeat.start(agentId);
|
||
```
|
||
|
||
**验证方法**:
|
||
1. 在 HeartbeatConfig 面板中启用心跳
|
||
2. 等待心跳触发,检查是否生成 alert
|
||
3. 验证配置更新是否正确持久化
|
||
|
||
---
|
||
|
||
### Phase 5: 清理遗留代码
|
||
|
||
**优先级**: 最低(在所有迁移验证完成后)
|
||
|
||
**删除文件**:
|
||
- `desktop/src/lib/agent-memory.ts`
|
||
- `desktop/src/lib/agent-identity.ts`
|
||
- `desktop/src/lib/reflection-engine.ts`
|
||
- `desktop/src/lib/heartbeat-engine.ts`
|
||
- `desktop/src/lib/context-compactor.ts`
|
||
- `desktop/src/lib/memory-index.ts` (agent-memory 的依赖)
|
||
|
||
**更新文档**:
|
||
- 更新 `CLAUDE.md` 中的架构说明
|
||
- 更新相关组件的注释
|
||
|
||
---
|
||
|
||
## 风险与缓解措施
|
||
|
||
| 风险 | 缓解措施 |
|
||
|------|----------|
|
||
| Tauri invoke 失败 | 实现完整的降级策略,fallback 到 localStorage |
|
||
| 类型不匹配 | 创建类型转换层,隔离前后端类型差异 |
|
||
| 性能差异 | Rust 后端应该更快,但需要测试 SQLite 查询性能 |
|
||
| 数据迁移 | 提供 localStorage -> SQLite 迁移工具 |
|
||
| 回滚困难 | 使用 feature flag,可快速切换回旧实现 |
|
||
|
||
---
|
||
|
||
## 测试检查清单
|
||
|
||
### 每个阶段必须验证
|
||
|
||
- [ ] TypeScript 编译通过 (`pnpm tsc --noEmit`)
|
||
- [ ] 相关单元测试通过 (`pnpm vitest run`)
|
||
- [ ] 桌面端启动正常
|
||
- [ ] 聊天功能正常
|
||
- [ ] 记忆存储/搜索正常
|
||
- [ ] 无控制台错误
|
||
|
||
### Phase 1 额外验证
|
||
|
||
- [ ] MemoryPanel 正确显示记忆列表
|
||
- [ ] 记忆图谱正确渲染
|
||
- [ ] skill-discovery 推荐功能正常
|
||
|
||
### Phase 3 额外验证
|
||
|
||
- [ ] ReflectionLog 正确显示反思历史
|
||
- [ ] 身份变更提案审批流程正常
|
||
- [ ] USER.md 自动更新正常
|
||
|
||
---
|
||
|
||
## 时间估算
|
||
|
||
| 阶段 | 预计时间 | 累计 |
|
||
|------|----------|------|
|
||
| Phase 0 | 2h | 2h |
|
||
| Phase 1 | 4h | 6h |
|
||
| Phase 2 | 1h | 7h |
|
||
| Phase 3 | 3h | 10h |
|
||
| Phase 4 | 1h | 11h |
|
||
| Phase 5 | 1h | 12h |
|
||
| 测试与修复 | 3h | 15h |
|
||
|
||
**总计**: 约 2 个工作日
|
||
|
||
---
|
||
|
||
## 执行顺序建议
|
||
|
||
1. **先完成 Phase 0** - 这是所有后续工作的基础
|
||
2. **然后 Phase 1** - Memory 是核心依赖
|
||
3. **接着 Phase 2** - Compactor 依赖 Memory
|
||
4. **然后 Phase 3** - Reflection + Identity 关联紧密
|
||
5. **然后 Phase 4** - Heartbeat 独立,可最后处理
|
||
6. **最后 Phase 5** - 确认一切正常后再删除旧代码
|
||
|
||
每个阶段完成后都应该进行完整的功能验证,确保没有引入 bug。
|