从零设计一个 AI 记忆系统

一、为什么需要记忆?

现在的 AI 对话有个大问题:聊完就忘

你跟 AI 说"我喜欢吃火锅",下次再问"我上次说喜欢吃什么?",它完全不知道。

记忆系统就是解决这个问题的:让 AI 能记住用户说过的话,下次对话时能想起来


二、记忆系统的核心流程

整个系统就三步:

复制代码
用户说话 → 存起来 → 下次能想起来

拆开来说:

复制代码
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   用户说话   │ ──→ │   存到数据库  │ ──→ │  下次能查到  │
└─────────────┘     └─────────────┘     └─────────────┘
     输入                存储                输出

三、数据怎么存?

3.1 原始消息(用户说了什么)

最简单的就是把用户说的话原封不动存下来:

复制代码
用户: "我叫张三,今年25岁,在北京工作"
AI: "你好张三,很高兴认识你"
用户: "我喜欢打篮球"

这些就是原始消息,存在数据库里,每条消息有:

  • 谁说的(user 还是 AI)

  • 说了什么(内容)

  • 什么时候说的(时间)

3.2 但是光存原文有问题

如果用户说了 1000 条消息,你不可能每次都把 1000 条全发给 AI,因为:

  • 太长了,AI 处理不了

  • 太慢了,用户等不及

  • 太贵了,按 token 收费

所以需要提炼:把长对话变成简短的摘要。


四、怎么提炼记忆?

4.1 方法一:AI 帮你总结

每隔一段时间,把用户的对话丢给 AI,让它总结:

复制代码
输入(用户最近的对话):
  - "我叫张三"
  - "我在北京工作"
  - "我喜欢打篮球"

AI 总结输出:
  - 张三,25岁,在北京工作
  - 兴趣爱好:打篮球

这就是情景记忆(Episodic Memory):记录用户经历的事情。

4.2 方法二:提取用户画像

从对话中提取用户的固定信息:

复制代码
对话: "我叫张三,今年25岁"

提取结果:
  - 姓名: 张三
  - 年龄: 25岁

这就是用户画像(Profile):记录用户的固定属性。

4.3 什么时候触发总结?

两种方式:

方式一:定时触发

  • 每积累 10 条消息,自动总结一次

  • 或者每 30 分钟总结一次

方式二:手动触发

  • 用户主动说"总结一下我们刚才的对话"

  • 或者调用 API 手动触发(Flush)


五、怎么查找记忆?

5.1 问题:用户问"我喜欢什么?"

你需要从数据库里找到相关的记忆,但是:

  • 数据库可能有几千条记忆

  • 不能一条一条看

5.2 解决方案:关键词搜索

最简单的方式:用关键词匹配

复制代码
用户问: "我喜欢什么?"

搜索关键词: "喜欢"

数据库里匹配到:
  - "用户喜欢打篮球"
  - "用户喜欢火锅"
  - "用户喜欢看电影"

返回给 AI: "根据记忆,你喜欢打篮球、火锅和看电影"

5.3 进阶:语义搜索

关键词搜索有个问题:用户说"我爱吃什么",但数据库里存的是"喜欢吃",关键词对不上。

语义搜索就是让 AI 理解"爱"和"喜欢"是一个意思:

复制代码
用户问: "我爱吃什么?"

语义搜索理解: "爱" ≈ "喜欢"

匹配到:
  - "喜欢吃火锅" ✅
  - "喜欢打篮球" ✅

六、整体架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        用户对话                              │
│  "我叫张三,喜欢打篮球"                                       │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                     1. 原始消息存储                           │
│  把用户说的话原封不动存到数据库                                 │
│  表: messages (user_id, role, content, timestamp)            │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   2. 记忆提取(AI 总结)                      │
│  定期把对话丢给 AI,生成简短摘要                               │
│                                                             │
│  输入: 最近 10 条对话                                         │
│  输出: "张三,喜欢打篮球"                                     │
│                                                             │
│  表: memories (user_id, summary, type, timestamp)            │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   3. 用户画像提取                             │
│  从对话中提取用户的固定属性                                    │
│                                                             │
│  输入: "我叫张三,25岁"                                       │
│  输出: {name: "张三", age: 25}                               │
│                                                             │
│  表: profiles (user_id, key, value)                          │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                    4. 记忆查找                                │
│  用户问问题时,从数据库找相关记忆                               │
│                                                             │
│  方式一: 关键词搜索(简单但不准)                              │
│  方式二: 语义搜索(准确但复杂)                                │
│                                                             │
│  返回: 相关记忆列表                                          │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                    5. 组装回复                                │
│  把记忆塞进 AI 的提示词里,生成回复                            │
│                                                             │
│  提示词:                                                     │
│    "你是张三的助手。以下是关于张三的记忆:                      │
│     - 喜欢打篮球                                             │
│     - 在北京工作                                              │
│     请根据这些记忆回答用户的问题。"                            │
│                                                             │
│  用户问: "我喜欢什么运动?"                                   │
│  AI 回答: "你喜欢打篮球!"                                    │
└─────────────────────────────────────────────────────────────┘

七、数据库设计

十、进阶优化

10.1 记忆去重

用户可能在不同时间说了同样的话:

  • 第1天:"我喜欢火锅"

  • 第7天:"我最爱吃火锅"

需要去重,避免重复记忆。

10.2 记忆更新

用户的喜好可能变化:

  • 以前:"喜欢打篮球"

  • 现在:"现在更喜欢踢足球了"

需要更新记忆,而不是简单追加。

10.3 记忆遗忘

太久远的记忆可能不重要了,需要定期清理:

  • 3个月前的记忆:降权

  • 1年前的记忆:归档或删除

10.4 多用户隔离

不同用户的记忆要完全隔离,不能串:

  • 张三的记忆只能张三看到

  • 李四的记忆只能李四看到


十一、技术栈详解

11.1 RAG(检索增强生成)

RAG 全称是 Retrieval-Augmented Generation,翻译过来就是"先搜再答"。

没有 RAG 的 AI:

复制代码
用户: "我喜欢什么?"
AI: "我不知道你喜欢什么"(因为它没记住)

有 RAG 的 AI:

复制代码
用户: "我喜欢什么?"
   ↓
系统先去数据库搜: 找到"用户喜欢打篮球"
   ↓
把搜到的内容塞进提示词: "根据记忆,用户喜欢打篮球。请回答用户的问题。"
   ↓
AI: "你喜欢打篮球!"

RAG 的核心流程:

复制代码
用户提问 → 搜索相关记忆 → 把记忆塞进提示词 → AI 带着记忆回答

11.2 Elasticsearch(ES)--- 搜索引擎

为什么需要 ES?

普通数据库(MySQL)搜索太慢了,而且功能有限:

复制代码
-- MySQL 的 LIKE 搜索:慢,不支持语义
SELECT * FROM memories WHERE summary LIKE '%篮球%';

ES 是专门做搜索的数据库,支持:

  • 全文搜索:分词后搜索,"打篮球" 能匹配到 "篮球"

  • 模糊搜索:拼写错了也能搜到

  • 语义搜索:理解"喜欢"和"爱"是一个意思

ES 的分词原理:

复制代码
原始文本: "我喜欢打篮球"
     ↓ 分词
["我", "喜欢", "打", "篮球"]
     ↓ 存储到倒排索引

倒排索引:
  "我"     → [文档1, 文档3, 文档5]
  "喜欢"   → [文档1, 文档2]
  "打"     → [文档1, 文档4]
  "篮球"   → [文档1, 文档3]

搜索"篮球" → 快速找到 [文档1, 文档3]

ES 在记忆系统中的作用:

  • 存储记忆的文本内容

  • 提供快速的关键词搜索

  • 支持中文分词(需要安装 IK 分词器)

11.3 Milvus --- 向量数据库

为什么需要向量数据库?

ES 的关键词搜索有个问题:

复制代码
用户问: "我爱吃什么运动?"
ES 搜索: 关键词"爱"和"运动" → 匹配不到"喜欢打篮球"

因为"爱"≠"喜欢","运动"≠"篮球",关键词对不上。

向量搜索的原理:

把文本转换成一串数字(向量),意思相近的文本,数字也相近:

复制代码
"喜欢打篮球" → [0.2, 0.8, 0.1, 0.5, ...]
"爱打篮球"   → [0.21, 0.79, 0.11, 0.49, ...]  ← 很接近!
"喜欢吃火锅" → [0.6, 0.3, 0.7, 0.2, ...]      ← 差很远

搜索时,把用户的问题也转成向量,然后找最近的:

复制代码
"我爱吃什么" → [0.22, 0.78, 0.12, 0.48, ...]

匹配结果:
  1. "喜欢打篮球" (相似度 0.98) ✅
  2. "喜欢吃火锅" (相似度 0.45)

Milvus 在记忆系统中的作用:

  • 存储记忆的向量表示

  • 提供语义搜索能力

  • 解决关键词搜索匹配不到的问题

11.4 Redis --- 缓存队列

为什么需要 Redis?

记忆提取(调用 AI 总结)是个慢操作,可能需要几秒钟。如果用户发一条消息就等几秒,体验太差了。

Redis 的作用:

复制代码
用户发消息 → 存到消息队列(Redis)→ 立即返回"已收到"
                    ↓
            后台慢慢处理(AI总结)
                    ↓
              存到数据库(ES/MongoDB)

Redis 在记忆系统中的三个用途:

  1. 消息队列:把待处理的消息放队列,后台慢慢消费

    队列: [消息1, 消息2, 消息3, ...]
    ↓ 消费
    处理并存储

  2. 缓存:把常用的记忆缓存起来,查询更快

    查询: "张三的记忆"
    ↓ 先查 Redis 缓存
    ↓ 命中 → 直接返回(快!)
    ↓ 未命中 → 查 ES → 结果存到缓存 → 返回

  3. 会话状态:记住当前对话的状态

    当前会话: 张三的第3次对话
    累积消息数: 5条
    上次总结时间: 2分钟前

11.5 分词器 --- 让搜索更准

什么是分词?

把一句话拆成一个个词:

复制代码
中文: "我喜欢打篮球" → ["我", "喜欢", "打", "篮球"]
英文: "I like basketball" → ["I", "like", "basketball"]

为什么分词重要?

不分词的话,搜索"篮球"匹配不到"打篮球",因为它们是不同的字符串。

分词后,"篮球"和"打篮球"都包含"篮球"这个词,就能匹配上了。

常用分词器:

分词器 特点 适用场景
IK 分词器 中文专用,支持细粒度和粗粒度 中文搜索
jieba Python 中文分词库 Python 项目
Standard ES 默认,按空格分 英文搜索

IK 分词器示例:

复制代码
细粒度: "中华人民共和国" → ["中华", "人民", "共和国", "中华人民共和国"]
粗粒度: "中华人民共和国" → ["中华人民共和国"]

11.6 MongoDB --- 文档存储

为什么需要 MongoDB?

有些数据不适合存到 ES(ES 主要是用来搜索的),比如:

  • 用户的原始对话记录

  • 用户画像的结构化数据

  • 系统配置

MongoDB 是文档数据库,存 JSON 很方便:

复制代码
{
    "user_id": "zhangsan",
    "messages": [
        {"role": "user", "content": "我叫张三"},
        {"role": "assistant", "content": "你好张三"}
    ],
    "created_at": "2024-01-01"
}

11.7 技术栈总结

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        用户对话                              │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                     FastAPI (Python)                         │
│                    接收请求,返回响应                          │
└─────────────────────────┬───────────────────────────────────┘
                          │
          ┌───────────────┼───────────────┐
          │               │               │
          ▼               ▼               ▼
    ┌──────────┐    ┌──────────┐    ┌──────────┐
    │  Redis   │    │ MongoDB  │    │   ES     │
    │  消息队列  │    │ 文档存储  │    │  搜索    │
    │  缓存     │    │ 原始对话  │    │  分词    │
    └──────────┘    └──────────┘    └──────────┘
                          │
                          ▼
                   ┌──────────────┐
                   │   Milvus     │
                   │  向量数据库   │
                   │  语义搜索    │
                   └──────────────┘

数据流向:

复制代码
1. 用户发消息
   → 存到 Redis 队列
   → 存到 MongoDB(原始记录)

2. 后台处理(AI总结)
   → 从 Redis 取消息
   → 调用 AI 生成摘要
   → 存到 ES(全文搜索)
   → 存到 Milvus(语义搜索)

3. 用户查询
   → 先查 Redis 缓存
   → 没有就查 ES(关键词搜索)
   → 同时查 Milvus(语义搜索)
   → 合并结果返回

十二、总结

做一个记忆系统,核心就是:

  1. :把用户说的话存到数据库

  2. 提炼:用 AI 把长对话总结成简短记忆

  3. :用户问问题时,能找到相关记忆

  4. :把记忆塞进提示词,让 AI 带着记忆回答

技术栈选择:

  • MongoDB:存原始对话

  • Elasticsearch:存记忆,支持关键词搜索

  • Milvus:存向量,支持语义搜索

  • Redis:消息队列 + 缓存

  • FastAPI:Web 框架

  • 分词器:让中文搜索更准

就这么简单,核心就是存、提炼、查、用四个字,技术栈只是实现手段。

相关推荐
凯丨1 小时前
把“计划“搬出上下文窗口:拆解 Claude Opus 4.8 的 Dynamic Workflows
人工智能
__log1 小时前
Codex默认调用本地Ollama模型配置指南
人工智能·知识图谱
黄啊码1 小时前
【黄啊码】为什么AI写不出阿嬷的情书?
人工智能
rit84324991 小时前
基于POCS的超分辨率重建(Keren配准)MATLAB实现
人工智能·matlab·超分辨率重建
星辰AI1 小时前
AI Agent 记忆系统设计与实现:让 AI 记住一切
人工智能·ai·语言模型
AwakeFantasy1 小时前
聊聊近况和最近做的踩坑项目
人工智能·python·gpt·ocr
路人甲3261 小时前
Moravec‘s Paradox and the Robot Olympics
人工智能·深度学习·计算机视觉·机器人·具身智能
黄啊码1 小时前
【黄啊码】拉勾倒了,但你的简历早就不该在招聘软件上了
人工智能·面试
头歌实践平台1 小时前
头歌 卷积神经网络卷积核和结构设计实验
人工智能·深度学习·cnn