Skip to content

记忆系统功能设计文档

一、产品定位

MJ Studio 的记忆系统旨在让 AI 助手真正"认识"用户,实现跨对话的智能记忆和个性化服务。不同于简单的对话历史,记忆系统能够:

  • 持久化用户偏好:记住编程风格、技术栈偏好、工作习惯
  • 积累项目知识:理解用户的项目背景、技术选型、业务目标
  • 学习失败教训:避免重复相同的错误决策和配置
  • 跨会话连续性:从上次对话无缝延续,无需重复背景介绍

二、用户感知到的效果

场景 1:编程偏好记忆

第一天对话

用户:"我喜欢用 TypeScript,代码风格偏向函数式编程,多用组合而不是继承"
助手:"好的,我记住了你的偏好!"

一周后对话

用户:"帮我重构这个用户管理模块"
助手:"基于你喜欢的函数式风格,我建议使用纯函数和组合模式,
      用 TypeScript 的类型系统保证安全性..."

场景 2:失败教训学习

第一次失败

用户:"Docker 构建又失败了,网络超时"
助手:"我记录了这个问题,你的环境需要使用 proxy-env 来运行 docker 命令"

后续提醒

用户:"我要启动 docker compose"
助手:"提醒:你的环境需要代理,建议使用 'proxy-env docker compose up -d'"

场景 3:项目知识积累

多次对话后

用户:"帮我写个数据库查询"
助手:"根据你的项目使用 Drizzle ORM + SQLite,这里是查询代码:..."
(无需每次重复说明技术栈)

三、记忆的分类和层级

3.1 按作用域分类

作用域说明示例命名空间
用户级跨所有助手、对话共享的长期记忆编程习惯、技术栈偏好、项目背景user:{userId}

设计原则

  • 助手特定记忆 → 使用助手的 System Prompt 实现(如角色设定、专业领域)
  • 对话上下文 → 依靠 LLM 工作记忆(最近消息)和对话压缩功能

3.2 按内容类型分类

类型说明示例重要性权重
偏好用户的主观喜好"喜欢TypeScript"、"偏好简洁代码"0.9
事实客观的用户信息"正在开发MJ Studio"、"使用Nuxt 4"0.8
教训失败经验和解决方案"Docker需要代理才能构建"0.85
目标用户的待办和计划"计划添加视频生成功能"0.7
情境临时的上下文信息"刚才提到的那个API"0.4

3.3 按时间维度分类

维度生命周期遗忘策略
核心记忆长期(永久)不遗忘,除非用户删除
工作记忆中期(30天)按访问频率衰减
临时记忆短期(7天)快速衰减

四、用户界面设计

4.1 记忆管理页面

位置:设置页面 → 记忆管理(/settings/memories

页面结构

┌─────────────────────────────────────────────┐
│  [搜索记忆...]              [清空所有记忆]  │
├─────────────────────────────────────────────┤
│  筛选:                                     │
│  [ 全部 | 偏好 | 事实 | 教训 | 目标 ]       │
├─────────────────────────────────────────────┤
│  ┌─────────────────────────────────────┐   │
│  │ 🎨 偏好 | 3天前                      │   │
│  │ 我喜欢函数式编程,多用组合少用继承│   │
│  │ 访问:5次 | 重要性:90%        [删除] │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │ 💡 事实 | 1周前                      │   │
│  │ 正在开发 MJ Studio 多模态 AI 工作台  │   │
│  │ 访问:12次 | 重要性:85%       [删除] │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │ ⚠️ 教训 | 5天前                      │   │
│  │ Docker 构建需要使用 proxy-env 代理  │   │
│  │ 访问:3次 | 重要性:80%        [删除] │   │
│  └─────────────────────────────────────┘   │
│                                             │
│            [加载更多]                       │
└─────────────────────────────────────────────┘

4.2 对话中的记忆提示

助手回复时的记忆引用

助手:"基于你的偏好(函数式编程 🧠),我建议..."
      ────────────────
      点击可查看完整记忆

记忆更新确认

助手:"我记住了这个偏好!✅
      已添加到记忆:「喜欢使用 TypeScript 严格模式」
      [查看] [撤销]"

4.3 记忆对用户的呈现维度

用户在管理界面看到的每条记忆包含:

  1. 内容:记忆的文本描述
  2. 类型:偏好/事实/教训/目标(图标 + 标签)
  3. 时间:创建时间(相对时间显示)
  4. 活跃度:访问次数
  5. 重要性:0-100% 的评分
  6. 来源:从哪次对话提取(可点击跳转)
  7. 状态:活跃/已遗忘(已遗忘的记忆置灰但保留)

4.4 隐私控制

临时对话模式

  • 位置:对话输入框旁边的开关
  • 效果:开启后,本次对话不提取记忆,也不使用已有记忆
  • 用途:敏感话题、测试场景

记忆开关

  • 位置:设置页面
  • 选项:
    • ✅ 启用记忆系统
    • ✅ 自动提取记忆(关闭则需手动确认)
    • ✅ 对话中显示记忆引用

五、记忆的提取和召回机制

5.1 技术选型:Zep

我们使用 Zep 作为记忆系统的核心引擎,原因如下:

  • 时序感知:每个记忆追踪生效时间和失效时间,避免返回过时信息
  • 知识图谱:维护实体和关系,支持复杂的记忆推理
  • 混合检索:结合语义搜索(向量)+ 关键词搜索(BM25)+ 图遍历
  • 自动提取:内置 LLM 驱动的记忆提取逻辑
  • 开源自托管:通过 Docker 部署,数据完全可控

5.2 记忆提取机制

提取时机

触发条件说明
异步批量提取每 5 轮对话提取一次(推荐,降低成本)
流式完成后AI 回复生成完成后立即提取
手动触发用户点击"记住这个"按钮

提取流程

用户发送消息

AI 生成回复(流式输出)

流式完成后,触发记忆提取(异步,不阻塞用户)

1. 获取对话上下文(最近 10 条消息,~4000 tokens)

2. 调用 LLM 进行记忆提取
   System Prompt: "从以下对话中提取值得长期记住的信息..."
   输入: 最近 10 条消息
   输出: 结构化记忆列表(JSON)

3. LLM 返回记忆候选
   [
     { content: "用户喜欢函数式编程", type: "preference", importance: 0.9 },
     { content: "项目使用 Nuxt 4", type: "fact", importance: 0.8 }
   ]

4. 将记忆存储到 Zep
   - 生成 embedding(向量化)
   - 存储到知识图谱
   - 检测并解决冲突(相同主题的旧记忆)

5. 同步元数据到本地数据库(memories_metadata)

6. 通过全局事件通知前端(memory.created)

提取窗口策略

配置项理由
窗口大小最近 10 条消息(5 轮对话)平衡上下文完整性和成本
Token 限制~4000 tokens足够提取关键信息,成本可控
提取频率每 5 轮对话避免过度提取,降低 API 成本

为什么不用完整对话历史

  • 成本问题:100k tokens 的对话历史每次提取成本高昂
  • 噪声问题:大部分对话内容是临时性的,不值得长期记忆
  • LLM 能力:最近 10 条消息已足够 LLM 理解上下文并提取关键信息

5.3 记忆召回机制

召回时机

每次用户发送新消息时,在生成 AI 回复之前执行召回。

召回流程

用户发送新消息

1. 构建召回查询
   查询内容 = 用户当前消息 + 最近 3 条上下文消息
   示例: "用户: 帮我重构这个模块\n上下文: 刚才讨论了用户管理..."

2. 向 Zep 发起混合检索
   - 语义搜索:query embedding 与记忆 embedding 的相似度
   - 关键词搜索:BM25 算法匹配关键词
   - 图遍历:基于实体关系查找相关记忆
   - 时序过滤:只返回有效记忆(validUntil 为 null 或未来)

3. Zep 返回 Top-3 相关记忆
   [
     { content: "用户喜欢函数式编程", relevance: 0.92, type: "preference" },
     { content: "项目使用 TypeScript", relevance: 0.85, type: "fact" },
     { content: "避免使用 class 继承", relevance: 0.78, type: "lesson" }
   ]

4. 将记忆注入到 System Prompt
   System Prompt:
   """
   你是一个 AI 助手。

   关于用户的背景信息:
   - 用户喜欢函数式编程
   - 项目使用 TypeScript
   - 避免使用 class 继承

   (原有的助手提示词...)
   """

5. 组合完整上下文,调用 LLM 生成回复
   上下文 = System Prompt(含记忆)+ 最近 10 条消息 + 用户新消息

6. 流式返回 AI 回复

召回策略

配置项理由
召回数量Top-3避免噪声,聚焦最相关的记忆
查询增强当前消息 + 最近 3 条上下文提升召回准确性
时序过滤只返回有效记忆避免过时信息干扰
相关性阈值0.7低于此分数的记忆不注入

为什么不用 MCP/函数调用让 LLM 主动搜索

方案优点缺点结论
被动注入(我们的方案)确定性强、延迟低、成本可控LLM 无法主动搜索✅ 适合我们
MCP/函数调用LLM 自主决策何时搜索不确定性高、多次往返、成本高❌ 不适合
小模型路由成本低召回质量依赖小模型能力🤔 可作为优化方向

我们选择被动注入方案,因为:

  1. 每次对话都需要记忆:用户期望 AI 始终"记得"他们的偏好
  2. 延迟优化:提前注入记忆,无需等待 LLM 决策是否搜索
  3. 成本可控:Top-3 记忆注入固定成本,而函数调用可能多次往返

5.4 核心组件

Zep 服务(Docker)
  ├── 时序知识图谱存储(Postgres + pgvector)
  ├── 向量检索引擎(混合搜索)
  └── 记忆提取/更新 API

应用层封装(server/services/memory.ts)
  ├── MemoryService 类
  ├── 提取逻辑(extractFromConversation)
  ├── 召回逻辑(buildRecallQuery + search)
  ├── 冲突解决(时序优先)
  └── 遗忘机制(软删除 + 时间衰减)

前端组合式函数(app/composables/useMemory.ts)
  ├── 记忆检索
  ├── 记忆管理
  └── 实时更新(通过全局事件)

5.5 数据库扩展(本地元数据)

表:memories_metadata

字段类型说明
idinteger主键
zepMemoryIdstringZep 中的记忆 ID
userIdinteger用户 ID
typeenumpreference/fact/lesson/goal/context
importancefloat重要性评分 0-1
confidencefloat置信度 0-1
accessCountinteger访问次数
lastAccessedAttimestamp最后访问时间
isForgottenboolean是否已遗忘(软删除)
validFromtimestamp事实生效时间(时序关键)
validUntiltimestamp事实失效时间(可为 null)
supersedesinteger[]覆盖了哪些旧记忆的 ID
sourceConversationIdinteger来源对话 ID
sourceMessageIdinteger来源消息 ID
createdAttimestamp创建时间
updatedAttimestamp更新时间

设计说明

  • Zep 负责核心存储和检索
  • 本地数据库存储元数据(类型、统计)
  • 时序字段(validFrom/validUntil):处理偏好变化,确保时序正确性
  • 关系字段(supersedes):追踪记忆演化链条
  • 软删除通过 isForgotten 字段实现
  • 仅用户级记忆:移除了 scope/scopeId 字段,所有记忆都是用户级

5.6 API 端点

端点方法功能
/api/memoriesGET获取记忆列表(分页、筛选)
/api/memories/searchPOST语义搜索记忆
/api/memoriesPOST手动添加记忆
/api/memories/:idDELETE删除记忆(软删除)
/api/memories/:id/restorePOST恢复已遗忘的记忆
/api/memories/extractPOST从对话提取记忆(内部)
/api/memories/statsGET记忆统计(总数、类型分布)

5.7 核心服务方法

MemoryService 类

  • extractFromConversation(messages) - 从对话提取记忆(固定窗口:最近 10 条)
  • buildRecallQuery(userMessage, recentContext) - 构建召回查询(消息 + 最近 3 条上下文)
  • search(query, userId, options) - 混合搜索(语义 + BM25 + 图遍历)
  • add(content, type, metadata) - 添加记忆
  • update(id, data) - 更新记忆(访问次数、重要性)
  • resolveConflict(newMemory, oldMemory) - 冲突解决(时序优先)
  • softDelete(id) - 软删除记忆
  • restore(id) - 恢复记忆
  • list(userId, filters, pagination) - 列出记忆(支持时间范围过滤)
  • calculateRelevance(memory) - 计算记忆相关性(时间衰减 + 访问频率)
  • markForForgetting(threshold) - 批量标记低价值记忆
  • getStats(userId) - 获取统计信息

5.8 记忆冲突解决机制

问题示例

第1天:用户说"我喜欢用 Vue 3"
第30天:用户说"我现在更喜欢用 React"

处理策略(时序优先原则):

  1. 检测冲突:同类型、同主题的记忆
  2. 标记失效:旧记忆的 validUntil = 新记忆的 validFrom
  3. 建立关系:新记忆的 supersedes = [旧记忆 ID]
  4. 保留历史:旧记忆不删除,支持时间推理("你5个月前喜欢Vue")

召回优先级

  • 有效记忆(validUntil 为 null 或未来)> 已失效记忆
  • 最近的记忆 > 旧记忆
  • 高置信度 > 低置信度

5.9 前端 Composable

useMemory

  • memories - 响应式记忆列表
  • search(query, filters) - 搜索记忆
  • list(filters, page) - 分页列出
  • remove(id) - 删除记忆
  • restore(id) - 恢复记忆
  • stats - 统计信息

六、遗忘机制

6.1 遗忘策略

相关性评分公式

相关性 = 时间衰减系数 × (1 + 访问频率权重) × 重要性权重

其中:
- 时间衰减系数 = exp(-0.01 × 天数)
- 访问频率权重 = log(1 + 访问次数)
- 重要性权重 = importance (0-1)

遗忘阈值

  • 相关性 < 0.1:标记为已遗忘
  • 相关性 < 0.3:降低重要性评分
  • 相关性 > 0.7:视为活跃记忆

6.2 定时任务

每日凌晨执行server/tasks/memory-maintenance.ts):

  1. 计算所有记忆的相关性评分
  2. 标记低于阈值的记忆为已遗忘
  3. 检测矛盾的记忆(如偏好冲突)
  4. 生成记忆质量报告

6.3 软删除设计

已遗忘的记忆

  • ✅ 保留在数据库中(isForgotten = true
  • ✅ 不参与检索和推理
  • ✅ 用户可在管理界面查看和恢复
  • ✅ 支持导出备份

真正删除的触发条件(可选,暂不实现):

  • 用户手动"永久删除"
  • 已遗忘超过 365 天且从未恢复

七、MVP 阶段计划

7.1 目标

  • ✅ 部署 Zep 服务(Docker)
  • ✅ 实现基础的记忆提取和检索
  • ✅ 使用测试对话观察记忆质量
  • ❌ 不集成到生产对话流程

7.2 测试场景

准备 3 类测试对话:

  1. 编程偏好对话

    • "我喜欢函数式编程"
    • "我常用 TypeScript 严格模式"
    • "我不喜欢用 class 继承"
  2. 项目信息对话

    • "我在开发 MJ Studio"
    • "项目用 Nuxt 4 + SQLite"
    • "遇到了 Docker 网络问题"
  3. 失败教训对话

    • "Docker 需要使用 proxy-env"
    • "SQLite 不支持某些复杂查询"
    • "上次用这个方案失败了"

7.3 观察指标

  • 提取准确性:是否正确识别出关键信息?
  • 细节保留:专有名词、具体参数是否被记住?
  • 类型分类:自动分类为偏好/事实/教训是否准确?
  • 去重效果:相似表述是否被合并?
  • 检索质量:搜索"编程偏好"能否找到相关记忆?

7.4 MVP 不包含的功能

  • ❌ 自动遗忘(手动测试遗忘逻辑)
  • ❌ 对话中的实时记忆引用
  • ❌ 用户管理界面
  • ❌ 与助手的集成

八、正式实施阶段

8.1 集成点

对话生成流程(详细):

用户发送消息

1. 获取工作记忆(最近 10 条消息)

2. 构建召回查询(当前消息 + 最近 3 条上下文)

3. 检索长期记忆(Top-3 相关记忆,时序过滤)

4. 组合上下文:
   - System Prompt(基于长期记忆)
   - Messages(工作记忆)

5. LLM 生成回复(流式输出)

6. 异步提取记忆(流式完成后,最近 10 条消息窗口)

7. 检测冲突并解决(时序优先)

8. 发送全局事件通知前端

关键实现细节

  • 工作记忆和长期记忆分离
  • 召回查询增强(避免"好的按第一个方案办"无法召回)
  • 提取不阻塞流式输出
  • 每 5 轮批量提取一次(降低成本)

8.2 全局事件集成

新增事件类型

  • memory.created - 记忆创建
  • memory.updated - 记忆更新
  • memory.deleted - 记忆删除
  • memory.forgotten - 记忆被遗忘
  • memory.restored - 记忆恢复

事件订阅app/composables/useMemory.ts):

typescript
globalEvents.on('memory.created', (memory) => {
  // 更新本地记忆列表
})

8.3 用户设置

设置页面新增选项

  • 记忆系统总开关
  • 自动提取记忆(关闭则需手动确认)
  • 对话中显示记忆引用
  • 临时对话模式(全局快捷键 Ctrl+Shift+T)

九、性能和成本考虑

9.1 延迟控制

  • 记忆检索:< 200ms(Zep 优化后)
  • 记忆提取:异步执行,不阻塞对话
  • 批量操作:使用后台任务

9.2 存储成本

  • Zep 存储:向量数据库(Postgres + pgvector)
  • 本地元数据:SQLite(轻量)
  • 预估:1000 条记忆约 10MB(含向量)

9.3 API 调用成本

实际成本估算(GPT-4o Mini,$0.15/1M input):

操作频率Token/次月调用量月成本
记忆提取每 5 轮4k tokens80,000$48
召回查询每次对话Embedding only300,000$39
冲突检测提取时触发向量计算(免费)-$0
总计---$87/月

对比全上下文方案

  • 全上下文:~$750/月(每次发送完整历史)
  • 我们的方案:$87/月
  • 节省 88%

十、后续演进方向

10.1 短期(3 个月内)

  • ✅ 完成 MVP 测试
  • ✅ 集成到对话流程
  • ✅ 上线记忆管理界面
  • ✅ 收集用户反馈

10.2 中期(6 个月内)

  • ✅ 自动遗忘机制上线
  • ✅ 记忆质量自动评估
  • ✅ 多助手间记忆共享策略
  • ✅ 记忆导出/导入功能

10.3 长期(1 年内)

  • ✅ 基于用户反馈训练记忆选择策略
  • ✅ 多模态记忆(图片、视频)
  • ✅ 记忆可视化(知识图谱展示)
  • ✅ 记忆推理能力(从多条记忆推导新知识)

十一、风险和挑战

11.1 技术风险

  • Zep 稳定性:开源项目维护不确定性
  • 细节丢失:尽管双重索引,仍可能丢失极细节信息
  • 向量检索准确性:依赖 Embedding 模型质量

缓解措施

  • 本地备份所有记忆(SQLite 元数据)
  • 关键记忆人工审核
  • 定期测试检索质量

11.2 产品风险

  • 用户困惑:记忆系统可能让用户感到"被监视"
  • 错误记忆:AI 误解用户意图,存储错误信息
  • 隐私担忧:敏感信息被记录

缓解措施

  • 首次使用引导和说明
  • 记忆可见化,用户随时删除
  • 临时对话模式
  • 敏感信息自动过滤(正则 + LLM 检测)

11.3 成本风险

  • 向量存储成本:长期运行后可能膨胀
  • LLM 调用成本:记忆提取增加 API 调用

缓解措施

  • 遗忘机制控制总量
  • 使用小模型进行记忆提取
  • 批量处理降低调用频率

十二、成功指标

12.1 MVP 阶段

  • ✅ 提取准确率 > 80%
  • ✅ 检索相关性 > 75%
  • ✅ 细节保留率 > 70%(专有名词、数字)

12.2 正式上线后

  • ✅ 用户启用率 > 60%
  • ✅ 记忆系统带来的对话满意度提升 > 20%
  • ✅ 平均每用户记忆数稳定在 100-500 条
  • ✅ 遗忘率 < 30%(大部分记忆保持活跃)

12.3 长期目标

  • ✅ 对话重复说明背景的频率降低 > 50%
  • ✅ 用户感知到的"AI 理解我"程度显著提升

MJ-Studio - 多模型 AI 工作台