一、为什么需要记忆?
现在的 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, 消息2, 消息3, ...]
↓ 消费
处理并存储 -
缓存:把常用的记忆缓存起来,查询更快
查询: "张三的记忆"
↓ 先查 Redis 缓存
↓ 命中 → 直接返回(快!)
↓ 未命中 → 查 ES → 结果存到缓存 → 返回 -
会话状态:记住当前对话的状态
当前会话: 张三的第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(语义搜索)
→ 合并结果返回
十二、总结
做一个记忆系统,核心就是:
-
存:把用户说的话存到数据库
-
提炼:用 AI 把长对话总结成简短记忆
-
查:用户问问题时,能找到相关记忆
-
用:把记忆塞进提示词,让 AI 带着记忆回答
技术栈选择:
-
MongoDB:存原始对话
-
Elasticsearch:存记忆,支持关键词搜索
-
Milvus:存向量,支持语义搜索
-
Redis:消息队列 + 缓存
-
FastAPI:Web 框架
-
分词器:让中文搜索更准
就这么简单,核心就是存、提炼、查、用四个字,技术栈只是实现手段。
