# 语义记忆 vs OpenViking 差距审计记录 **审计日期**: 2026-03-26 **审计范围**: ZCLAW 语义记忆子系统 vs OpenViking 原始设计 **审计结果**: 发现 2 个核心差距,已全部修复 --- ## 一、审计背景与方法 ### 1.1 审计动机 ZCLAW 的记忆系统基于 OpenViking 设计构建,但在实际落地过程中可能存在功能缺失或实现降级。本次审计目的是系统性比对 ZCLAW 实现与 OpenViking 设计规格之间的差距。 ### 1.2 审计方法(可复用模板) 本次采用的五步审计流程可作为其他功能点的审计模板: ``` Step 1: 文档对齐 — 将 OpenViking 设计文档与 ZCLAW 实际代码逐一比对 Step 2: 追踪数据流 — 从前端 UI 到后端存储,确认每个环节是否连通 Step 3: 识别 dead_code — 搜索 #[allow(dead_code)]、#![allow(dead_code)]、未注册的命令 Step 4: 检查 trait 实现 — 确认 trait 方法是否被实际调用(而非仅定义) Step 5: 验证端到端 — 从用户操作角度验证功能是否真正可用 ``` ### 1.3 关键发现 ZCLAW 存在**两套并行存储系统**,这是很多差距的根因: | 存储系统 | 代码位置 | 使用场景 | 调用入口 | |----------|----------|----------|----------| | `SqliteStorage` | `crates/zclaw-growth/src/storage/sqlite.rs` | VikingPanel 记忆管理 | VikingCommands | | `PersistentMemoryStore` | `desktop/src-tauri/src/memory/persistent.rs` | 聊天流程记忆 | MemoryCommands | 两套系统功能不对等,导致某些功能在一个路径可用但另一个不可用。 --- ## 二、发现的差距与修复 ### 差距 1: 向量嵌入搜索未启用 **严重程度**: 高 **差距描述**: OpenViking 设计中包含向量嵌入语义搜索,ZCLAW 代码中 `EmbeddingClient` trait、`index_entry_with_embedding()`、`score_similarity_with_embedding()` 已写好但从未被调用。搜索仅依赖 TF-IDF。 **根因分析**: - `llm::EmbeddingClient` HTTP 客户端存在,Settings 页面有测试按钮 - 但前端保存 Embedding 配置后未调用 `viking_configure_embedding` 将配置传递到后端 - `SqliteStorage.store()` 未在存储时自动生成 embedding - `SqliteStorage.find()` 未使用混合评分(embedding + TF-IDF) **修复方案**: 1. 创建 `embedding_adapter.rs` — 将 `llm::EmbeddingClient` 适配为 `zclaw_growth::EmbeddingClient` trait 2. `SqliteStorage` 添加 `configure_embedding()` 方法 3. 修改 `store()` — embedding 可用时自动生成向量 4. 修改 `find()` — 混合评分(70% embedding + 30% TF-IDF),降级到纯 TF-IDF 5. 前端 `ModelsAPI.tsx` 保存后调用 `viking_configure_embedding` 6. `App.tsx` bootstrap 阶段自动恢复配置 **经验教训**: > **"代码写好不等于功能可用"** — trait 实现、适配器、前端接入、启动恢复,任何一环缺失都会导致功能不可用。审计时不能只看代码是否存在,必须追踪完整调用链。 --- ### 差距 2: L0/L1/L2 分层加载未接入 **严重程度**: 高 **差距描述**: OpenViking 设计了三层记忆加载策略以节省 token: - L0(Quick Scan): ~100 tokens,3-5 个关键词 - L1(Standard): ~200 tokens,1-2 句话摘要 - L2(Deep): 完整内容 ZCLAW 中 `context_builder.rs` 已有完整 L0→L1→L2 加载逻辑,但整个模块标记为 `#![allow(dead_code)]`,从未接入实际聊天流程。 **根因分析**: - `MemoryEntry` 缺少 `overview` 和 `abstract_summary` 字段 - 没有 LLM 驱动来生成摘要 - `ContextBuilder.build_context()` 未被任何 Tauri 命令调用 - `chatStore.ts` 使用简单的 `memory.search()` + 手动拼接,无分层概念 **修复方案**: 1. `MemoryEntry` 添加 `overview: Option` 和 `abstract_summary: Option` 2. 两套存储系统均做 schema migration(`ALTER TABLE ADD COLUMN`) 3. 新建 `summarizer.rs` — `SummaryLlmDriver` trait + prompt 模板 4. 新建 `summarizer_adapter.rs` — 用 OpenAI 兼容 API 实现摘要生成 5. 添加 `memory_build_context` Tauri 命令 — 优先使用 L1 overview,尊重 token 预算 6. `chatStore.ts` 替换为调用 `buildContext()` 获取分层上下文 7. `VikingPanel.tsx` 支持 L1→L2 展开 8. `extract_and_store_memories` 存储后异步生成 L0/L1 **经验教训**: > **"#![allow(dead_code) 是最大的技术债信号"** — 完整的功能实现被标记为死代码,说明设计阶段考虑周全但集成阶段被跳过。审计时应优先搜索 `dead_code` 标记。 > > **"后台异步生成 + 即时存储"模式** — L2 完整内容同步存储(不阻塞),L0/L1 摘要通过 `tokio::spawn` 后台异步生成并回写。这保证了用户体验不受影响。 --- ## 三、集成审计中发现的 5 个 GAP 在修复核心差距后,进行了端到端集成审计,发现以下连通性问题: | GAP | 描述 | 状态 | |-----|------|------| | GAP 1 | Settings 保存 Embedding 配置后未通知后端 | 已修复 | | GAP 2 | Bootstrap 阶段未自动恢复 Embedding 配置 | 已修复 | | GAP 3 | `extract_and_store_memories` 未触发 L0/L1 生成 | 已修复 | | GAP 4 | Summary Driver 需要单独配置 | 不需要 — 已改为自动从活跃 LLM 配置 | | GAP 5 | `context_builder.rs` 仍标记 dead_code | 已清理注释说明现状 | --- ## 四、遗留问题(后续工作) ### 4.1 双存储系统统一 当前 SqliteStorage 和 PersistentMemoryStore 功能不对等: | 能力 | SqliteStorage | PersistentMemoryStore | |------|--------------|----------------------| | FTS5 全文搜索 | 有 | 无(仅 LIKE 查询) | | TF-IDF 语义评分 | 有 | 无 | | L0 abstract_summary | 有 | 无 | | L0/L1 自动生成 | 有 | 无 | | 导出/导入 | 无 | 有 | | 记忆统计 | 无 | 有 | **建议**: 长期应统一为一套存储系统,或在 PersistentMemoryStore 补齐缺失能力。 ### 4.2 测试覆盖不足 | 模块 | 测试状态 | |------|----------| | `embedding_adapter.rs` | 无测试 | | `memory_commands.rs` | 无测试 | | `persistent.rs`(store/search/embedding) | 仅 1 个 ID 生成测试 | | 端到端 embedding + 搜索流程 | 无集成测试 | | 端到端 L0/L1 生成流程 | 无集成测试 | ### 4.3 ContextBuilder 完整版未启用 `context_builder.rs` 中的完整 L0→L1→L2 渐进加载逻辑仍是 dead_code。当前聊天流程使用简化的 `memory_build_context`。完整版预留了 retrieval trace、分阶段 token 预算等高级能力,待后续启用。 ### 4.4 Intelligence 子系统大面积 dead_code 以下模块均为 `#![allow(dead_code)]`: - `validation.rs` — 验证函数 - `identity.rs` — 身份管理 - `recommender.rs` — 推荐系统 - `heartbeat.rs` — 心跳引擎 - `pattern_detector.rs` — 模式检测 - `persona_evolver.rs` — 人格演化 - `compactor.rs` — 记忆压缩 - `trigger_evaluator.rs` — 触发评估 - `mesh.rs` — Agent 群体 - `reflection.rs` — 自我反思 --- ## 五、审计方法论总结(供后续复用) ### 5.1 通用审计清单 对任何功能点进行审计时,按以下维度检查: ``` [ ] 代码存在性 — trait/struct/function 是否已定义 [ ] 实现完整性 — trait 方法是否有具体实现(非空/panic) [ ] 调用链连通 — 从 UI 触发到后端执行的完整路径是否打通 [ ] 配置传递 — 前端配置是否能传递到后端生效 [ ] 启动恢复 — 应用重启后配置/状态是否能自动恢复 [ ] 降级策略 — 依赖不可用时是否有合理降级 [ ] 数据流闭环 — 写入的数据能否被正确读取和使用 [ ] dead_code 清理 — 无 dead_code 或有明确原因保留 [ ] 双系统一致性 — 多套实现是否功能对等 [ ] 测试覆盖 — 关键路径是否有测试 ``` ### 5.2 常见差距模式 本次审计中发现的差距模式可归纳为: 1. **"写了没接"** — 代码已实现但未接入实际流程(ContextBuilder、EmbeddingClient) 2. **"接了没传"** — 后端命令存在但前端未调用(viking_configure_embedding) 3. **"传了没存"** — 配置保存了但未持久化或恢复(Embedding 配置丢失) 4. **"存了没用"** — 数据已存储但读取时未使用(overview 字段未被搜索返回) 5. **"双系统不同步"** — 一套系统有功能另一套没有(SqliteStorage vs PersistentMemoryStore) ### 5.3 审计命令速查 ```bash # 搜索 dead_code 标记 rg "allow\(dead_code\)" --type rust # 搜索未使用的函数 rg "#\[allow(dead_code)\]" crates/ desktop/src-tauri/src/ # 搜索未注册的 Tauri 命令 rg "#\[tauri::command\]" desktop/src-tauri/src/ -l # 对比 lib.rs 中的 .invoke_handler() 注册列表 # 搜索 TODO/FIXME rg "TODO|FIXME" --type rust --type ts # 搜索前端 invoke 调用 rg "invoke\(" desktop/src/ --type ts -l # 编译检查 cargo check 2>&1 | grep -i "warning\|error" # 测试 cargo test -p zclaw-growth 2>&1 | tail -5 ``` --- ## 六、关键文件索引 | 文件 | 职责 | 审计阶段 | |------|------|----------| | `crates/zclaw-growth/src/types.rs` | MemoryEntry 数据模型 | Phase 2.1 | | `crates/zclaw-growth/src/storage/sqlite.rs` | SqliteStorage 存储 | Phase 1.3, 2.1 | | `crates/zclaw-growth/src/retrieval/semantic.rs` | TF-IDF + Embedding 评分 | Phase 1 | | `crates/zclaw-growth/src/summarizer.rs` | L0/L1 摘要生成 trait | Phase 2.2 | | `crates/zclaw-growth/src/injector.rs` | Prompt 注入器 | Phase 2.3 | | `desktop/src-tauri/src/embedding_adapter.rs` | Embedding 适配器 | Phase 1.2 | | `desktop/src-tauri/src/summarizer_adapter.rs` | 摘要 LLM 驱动 | Phase 2.2 | | `desktop/src-tauri/src/viking_commands.rs` | Viking Tauri 命令 | Phase 1.4, 2.4 | | `desktop/src-tauri/src/memory_commands.rs` | Memory Tauri 命令 | Phase 2.3 | | `desktop/src-tauri/src/memory/persistent.rs` | PersistentMemoryStore | Phase 1.6, 2.1 | | `desktop/src-tauri/src/memory/context_builder.rs` | L0/L1/L2 渐进加载 | Phase 2.3 | | `desktop/src-tauri/src/memory/extractor.rs` | 会话记忆提取 | Phase 2.2 | | `desktop/src/lib/intelligence-client.ts` | 前端记忆客户端 | Phase 2.3 | | `desktop/src/store/chatStore.ts` | 聊天状态管理 | Phase 2.3 | | `desktop/src/components/VikingPanel.tsx` | 记忆管理面板 | Phase 2.4 | | `desktop/src/App.tsx` | 应用启动引导 | Phase 1.5, 2.2 |