--- title: 记忆管道 updated: 2026-04-22 status: active tags: [module, memory, fts5, growth] --- # 记忆管道 (Memory Pipeline) > 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[butler]] > 详细提取逻辑归档: [[archive/memory-extraction-details]] ## 1. 设计决策 **核心问题: LLM 无状态,每次对话从零开始。需要从历史对话中积累知识。** | 决策 | WHY | |------|-----| | 闭环架构 | 对话→提取→索引→检索→注入,形成正向循环。每次聊天都积累,每次提问都利用 | | 双数据库 | memories.db (FTS5 全文索引) + data.db (结构化画像)。前者处理模糊语义检索,后者处理精确字段查询 | | FTS5+TF-IDF+Embedding 三层 | FTS5 粗筛 + TF-IDF 加权 + Embedding 精排(当前 NoOp,配置后激活)。不依赖外部服务即可工作 | | 进化引擎 | 从对话中检测行为模式变化,生成进化候选项(技能建议/工作流优化),通过 EvolutionMiddleware@78 注入 system prompt | | Token 预算控制 | 注入 system prompt 时有 token 上限,防止记忆注入挤占用户实际对话空间 | **Hermes 核心借鉴** (详见 [[archive/hermes-analysis]]): 经验库 FTS5 全文检索 + 用户画像结构化建模 + 自然语言 cron 调度 + 轨迹记录压缩。4 Chunk 已交付: ExperienceStore(10 tests) + UserProfileStore(14 tests) + NlScheduleParser(16 tests) + TrajectoryRecorder(18 tests)。 ## 2. 关键文件 + 数据流 ### 核心文件 | 文件 | 职责 | |------|------| | `crates/zclaw-growth/src/extractor.rs` | LLM 记忆提取 (偏好/知识/经验) | | `crates/zclaw-growth/src/retriever.rs` | 语义检索 (FTS5 + TF-IDF + 意图分类) | | `crates/zclaw-growth/src/injector.rs` | Prompt 注入 (token 预算) | | `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 核心 (memories.db) | | `crates/zclaw-runtime/src/middleware/memory.rs` | 记忆中间件 (提取+注入编排) | | `crates/zclaw-runtime/src/growth.rs` | GrowthIntegration 闭环编排 | | `crates/zclaw-memory/src/user_profile_store.rs` | UserProfileStore (data.db) | ### 闭环数据流 ``` [提取] 对话完成 → MemoryMiddleware.after_completion → MemoryExtractor.extract_combined() → LLM 单次调用 → CombinedExtraction { memories, experiences, profile_signals (含 agent_name/user_name) } → VikingAdapter → SqliteStorage → memories.db (FTS5 索引) → UserProfileStore → data.db (结构化画像) → [身份信号] identity/* → VikingStorage → post_conversation_hook → soul.md + Tauri event [检索] 新请求 → MemoryMiddleware.before_completion → MemoryRetriever.retrieve(agent_id, user_input) → QueryAnalyzer 意图分类 (5类: Preference/Knowledge/Experience/Code/General) → FTS5 全文搜索 + TF-IDF 评分 + IdentityRecall 43+ 模式 → 弱身份 fallback: <3 结果 → 补充 broad retrieval → PromptInjector.inject_with_format(system_prompt, memories) ``` ### 集成契约 | 方向 | 模块 | 触发点 | |------|------|--------| | Called by | middleware: Memory@150 | Every chat completion (after) + new request (before) | | Calls | zclaw-memory: FTS5 store, TF-IDF scorer | Memory extraction + retrieval | | Calls | zclaw-growth: GrowthIntegration, EvolutionEngine | Pattern detection + skill generation | | Provides | loop_runner: Context injection into system prompt | Before LLM call | ### Tauri 命令 | 分类 | 命令数 | 关键命令 | |------|--------|----------| | Memory CRUD (`memory_commands.rs`) | 11 | `memory_store`, `memory_search`, `memory_build_context` | | VikingStorage (`viking_commands.rs`) | 14 | `viking_add`, `viking_find`, `viking_inject_prompt` | | Intelligence (`intelligence/`) | ~40 | identity(13), heartbeat(11), pain(5), reflection(6) | | 提取+Embedding | 5 | `extract_session_memories`, `embedding_create` | ## 3. 代码逻辑 ### 跨会话记忆完整链路 ``` [初始化] kernel_init → init_storage(memories.db) → MemoryStore(data.db) → set_viking(SqliteStorage) → set_extraction_driver(LLM) → [首次聊天] create_middleware_chain() 重建 GrowthIntegration: VikingAdapter + ExtractionDriver + UserProfileStore(data.db) → MemoryMiddleware@150 注册到中间件链 [写入] after_completion → 30秒去重 → extract_combined(LLM) → memories → memories.db | profile_signals → data.db [读取] before_completion → retrieve(agent_id, query) → FTS5 + TF-IDF + IdentityRecall → inject_with_format() [展示] 管家Tab → viking_ls/viking_read(memories.db) + agent_get(data.db) ``` ### 不变量 - memories.db 和 data.db 是独立的 SQLite 数据库,跨库查询需使用正确连接 - 记忆注入在中间件@150,AFTER ButlerRouter@80,BEFORE SkillIndex@200 - Embedding 当前为 NoOpEmbeddingClient,用户配置 provider 后替换为真实实现 - 30 秒去重窗口:同一 agent 30 秒内跳过重复提取 - URI scheme: `agent://{agent_id}/{type}/{category}` ### 非显然逻辑 - TF-IDF 语义路由:70% embedding 权重 + 30% TF-IDF(embedding 未激活时退化为纯 TF-IDF) - IdentityRecall: 43+ 模式匹配("你记得我"/"上次说的"/"我的偏好"等)触发记忆检索 - 弱身份 fallback: 检索结果 <3 且含弱身份关键词时,补充 broad retrieval 扩大搜索范围 ## 4. 活跃问题 + 陷阱 ### 活跃 | 问题 | 状态 | 说明 | |------|------|------| | Embedding 未激活 | 🚧 长期观察 | NoOpEmbeddingClient 是默认值,用户配置 provider 后替换 | | SaaS pgvector | 🚧 deferred | HNSW 索引就绪 (knowledge_chunks, vector(1536)),生成逻辑未实现 | ### 历史陷阱 (已修复) - 跨会话记忆断裂 (profile_store 未连接 + 双数据库不一致 + 缺日志) → 04-22 已修复 (adf0251) - 记忆去重失败 (同 URI+content 重复添加) → 04-17 已修复 (a504a40) - 记忆非 Agent 隔离 (viking_find 返回所有 Agent 记忆) → 04-17 已修复 (a504a40) - 跨会话注入断裂 (新会话报 "no conversation history found") → 04-17 已修复 (a504a40) ### 警告 > memories.db / data.db 连接池隔离。修改存储层代码务必确认目标数据库。 > GrowthIntegration 缓存在 `set_viking()` / `set_extraction_driver()` 时会被清除,首次聊天时重建。 ## 5. 变更日志 | 日期 | 变更 | 关联 | |------|------|------| | 2026-04-23 | 身份信号提取: ProfileSignals+agent_name/user_name + VikingStorage identity 存储 + soul.md 写回 | commit 08812e5+e64a3ea | | 2026-04-22 | 跨会话记忆断裂修复: profile_store 连接 + 双数据库统一 + 诊断日志 | commit adf0251 | | 2026-04-22 | Wiki 5-section 重构: 363→~190 行,详细逻辑归档 | wiki/ | | 2026-04-21 | Embedding 接通 + 自学习自动化 A线+B线 (SemanticScorer + EvolutionMiddleware) | 934 tests PASS | | 2026-04-17 | E2E 全系统验证 129 链路: 记忆去重+注入+跨会话 修复 | 79.1% 通过率 | | 2026-04-15 | Heartbeat 统一健康系统 (health_snapshot + 健康快照集成) | 183 commands | ## 测试概览 | Crate | 测试数 | 覆盖 | |-------|--------|------| | zclaw-growth | 181 (29 文件) | 全覆盖 | | zclaw-memory | 54 (4 文件) | 全覆盖 | | **合计** | **235** | |