【第19篇】手把手教你搭建 AI 的长期记忆:Mem0
今天我们要聊的 Mem0,就是专门给 AI 智能体打造的"海马体"。我会带你从零开始,把它接入到 Spring AI Alibaba 项目中,让 AI 真正记住用户是谁、喜欢什么、之前聊过什么。
一、Mem0
1.1 概念
Mem0(Memory for AI Agents)是 AI 智能体的长期记忆层。 它不是一个简单的数据库,而是一个懂语义的记忆管家——它知道该记什么、怎么记、以及什么时候把合适的记忆调取出来。
1.2 为什么不用普通的数据库或 RAG?
很多人第一反应是:“我直接把聊天记录存进向量数据库不就行了?” 差得远呢。
| 普通向量存储 | Mem0 |
|---|---|
| 存的是原始文本片段 | 存的是提取后的语义事实(如"用户喜欢三角洲行动") |
| 检索靠关键词匹配 | 检索靠语义理解(你说"我喜欢玩什么游戏",它能找到"三角洲行动") |
| 没有用户维度 | 支持 user_id / agent_id / run_id 三维隔离 |
| 纯向量,无关系 | 向量(pgvector)+ 图(Neo4j)双引擎,能理解"用户-偏好-行为"之间的关系 |
打个比方: 普通向量库像把书页撕碎扔进抽屉,找的时候只能看碎片上的字;Mem0 则像一位图书管理员,读完书后做了摘要卡片,还知道哪些卡片之间有关联。
1.3 三种记忆类型
Mem0 把记忆分成了三个层次,对应真实世界中的三种"记得":
- 用户记忆(User Memory) —— 跨所有对话永久保存。比如"用户叫万能的喵,爱玩三角洲行动"。
- 智能体记忆(Agent Memory) —— 某个 AI 角色的专属知识。比如"这个客服代理负责处理退款"。
- 会话记忆(Session/Run Memory) —— 一次具体对话的上下文。比如"这次用户在咨询去日本的旅行"。
想象一下,你去理发店。Tony 老师记得你上次剪了什么发型(会话记忆),知道你喜欢短一点(用户记忆),而且他自己擅长剪寸头(智能体记忆)。Mem0 就是让 AI 也能拥有这种分层记忆能力。
1.4 本项目要做什么?
我们会搭建一个 Spring Boot 应用,暴露三个 REST 接口:
| 端点 | 方法 | 功能 |
|---|---|---|
/advisor/memory/mem0/call |
GET | 和 AI 对话,自动存取记忆 |
/advisor/memory/mem0/messages |
GET | 主动搜索某个用户的记忆 |
/advisor/memory/mem0/test |
GET | 演示长期记忆的存储与检索效果 |
二、技术架构:这些组件是怎么配合的?
2.1 整体架构一览
先上一张全景图,看看数据是怎么流的:
数据流向解读:
- 用户发送消息 → Spring 应用接收
Mem0ChatMemoryAdvisor拦截消息,先去 Mem0 Server 问:“关于这个用户,有什么相关的记忆?”- Mem0 Server 同时在 pgvector(向量相似度)和 Neo4j(关系图谱)里查找
- 找到的记忆被注入到 Prompt 上下文里,一起送给大模型
- 大模型生成回答后,Advisor 再把这次对话中的新事实送回 Mem0 Server 存储
- 用户收到回答
2.2 为什么需要 pgvector + Neo4j 双存储?
这是 Mem0 设计最精妙的地方,很多人一开始看不懂为什么要两个数据库。
pgvector(向量数据库)负责"像什么":
- 把记忆转成高维向量
- 当你问"我喜欢玩什么"时,通过语义相似度找到"用户爱玩三角洲行动"
- 解决的是模糊匹配问题
Neo4j(图数据库)负责"有什么关系":
- 存储"用户 → 喜欢 → 三角洲行动"这样的关系
- 能回答"这个用户还有什么其他爱好?"这类关联问题
- 解决的是关系推理问题
举个生活中的例子:向量库像是你通过"味道相似"来找菜(语义相似),图数据库像是你看菜单上的"搭配推荐"(关系关联)。两者结合,AI 既能理解你说的意思,又能联想出相关的信息。
2.3 核心组件速查表
| 组件 | 作用 | 端口 | 类比 |
|---|---|---|---|
| Mem0 Server | 记忆管理的"大脑",决定记什么、怎么记、怎么取 | 8888 | 图书管理员 |
| PostgreSQL + pgvector | 记忆的"内容仓库",支持语义搜索 | 8432 | 索引卡片盒 |
| Neo4j | 记忆的"关系地图",存储人-事-物关联 | 8687/7474 | 思维导图 |
| DashScope | 提供大模型能力,负责理解和生成 | 云端 | 聪明的大脑 |
三、核心代码:记忆是怎么自动工作的?
3.1 引入依赖
在 pom.xml 中加入 Spring AI Alibaba 的 Mem0 起步依赖:
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-mem0</artifactId>
<version>1.0.0-M6.3</version>
</dependency>
这个 starter 已经帮你封装好了和 Mem0 Server 的通信、记忆注入逻辑,你只需要配置就行。
3.2 控制器代码
@RestController
@RequestMapping("/advisor/memory/mem0")
public class Mem0MemoryController {
private final ChatClient chatClient;
private final VectorStore store;
private final Mem0ServiceClient mem0ServiceClient;
public Mem0MemoryController(
ChatClient.Builder builder,
VectorStore store,
Mem0ServiceClient mem0ServiceClient) {
// 创建 ChatClient,并挂上 Mem0 记忆增强器
this.chatClient = builder
.defaultAdvisors(
// 核心:Mem0ChatMemoryAdvisor 自动管理记忆生命周期
Mem0ChatMemoryAdvisor.builder(store).build()
)
.build();
}
/**
* 对话接口 - 自动存储和检索记忆
*
* 你不需要手动调用"存储记忆"或"查询记忆",
* Mem0ChatMemoryAdvisor 会在幕后自动完成一切。
*/
@GetMapping("/call")
public String call(
@RequestParam String message,
@RequestParam(defaultValue = "miao") String userId) {
return chatClient.prompt(message)
.advisors(a -> a.params(Map.of(USER_ID, userId)))
.call()
.content();
}
}
代码解读:
ChatClient是 Spring AI 的统一 LLM 调用入口Mem0ChatMemoryAdvisor是一个拦截器(Advisor),它会在每次对话前后自动干活- 你只需要传一个
userId,Mem0 就知道"这是谁在说话",从而找到对应的记忆
3.3 Mem0 的完整工作流程(重点)
这是整个系统最核心的机制,我用流程图帮你拆解每一步:
这个流程的精妙之处在于:
- 检索在生成之前:AI 回答前已经"想起"了用户是谁
- 存储在生成之后:新信息被实时提取并保存,下次就能用上
- 事实提取是自动的:不是存原始聊天记录,而是存"用户爱好:三角洲行动"这样的结构化事实
3.4 事实提取:Mem0 到底存了什么?
这是很多人困惑的地方。Mem0 存的不是聊天记录原文,而是 LLM 从对话中提炼出的事实。比如:
| 用户原话 | Mem0 存储的事实 |
|---|---|
| “你好,我是万能的喵,我爱玩三角洲行动” | {"type": "preference", "content": "用户爱玩三角洲行动", "entity": "万能的喵"} |
| “我每周三晚上都有空” | {"type": "habit", "content": "用户周三晚上有空", "entity": "万能的喵"} |
| “帮我推荐个游戏,不要射击类的” | {"type": "preference", "content": "用户不喜欢射击类游戏", "entity": "万能的喵"} |
这就是为什么 Mem0 比直接存聊天记录高效得多——它存的是"知识点",而不是"课本原文"。
四、配置详解:让系统跑起来
4.1 application.yml 完整配置
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
chat:
options:
# 强烈推荐使用 DeepSeek,原因见下文
model: deepseek-chat
temperature: 0.7
memory:
mem0:
# Mem0 服务器地址,本地开发用 localhost
base-url: http://localhost:8888
# 可选:自定义事实提取 Prompt
# 如果你希望提取特定领域的信息,可以写自己的模板
fact-extraction-prompt: classpath:prompts/custom_fact_extraction_prompt.st
# 禁用 Spring 的默认数据源自动配置
# 因为 Mem0 自己管理 pgvector 和 Neo4j 的连接,不需要 Spring Data JPA
spring.datasource.autoconfigure.enabled: false
# 调试日志:看看记忆是怎么被提取和检索的
logging:
level:
com.alibaba.cloud.ai.memory.mem0: DEBUG
4.2 环境变量清单
| 变量 | 必填 | 说明 |
|---|---|---|
DASHSCOPE_API_KEY |
✅ | 阿里云 DashScope 的 API Key,用于调用大模型 |
MEM0_BASE_URL |
✅ | Mem0 Server 的访问地址,默认 http://localhost:8888 |
4.3 为什么模型推荐 DeepSeek 而不是 Qwen?
这里有一个坑,原文件提了一嘴但没有细说。Mem0 内部需要 LLM 做结构化 JSON 输出(提取事实时必须按固定格式返回 JSON)。
# 推荐配置
model: deepseek-chat
# 不推荐(默认可能配置的是这个)
# model: qwen-turbo
原因很实际:
| 维度 | DeepSeek | Qwen(部分版本) |
|---|---|---|
| JSON 输出稳定性 | ✅ 非常稳定,格式严格遵守 | ⚠️ 偶尔会出现格式偏差 |
| 事实提取准确率 | ✅ 高 | ⚠️ 中等 |
| 价格 | ✅ 相对便宜 | 视版本而定 |
这不是说 Qwen 不好,而是在"结构化输出"这个特定任务上,DeepSeek 当前表现更稳。如果你的场景已经验证了 Qwen 可以稳定输出 JSON,当然也可以用。
五、Docker 部署:一步一步来
5.1 启动所有依赖服务
Mem0 需要三个服务协同工作,我们用 Docker Compose 一键拉起:
# 进入项目自带的 docker-compose 目录
cd /home/tht/examples-main/docker-compose/mem0
# 复制环境变量模板
cp .env.example .env
# 编辑 .env 填入必要配置(见下文)
vim .env
# 后台启动所有服务
docker-compose up -d
5.2 .env 文件模板
# Mem0 服务自身的 API Key(用于服务间认证)
MEM0_API_KEY=your-mem0-api-key
# PostgreSQL 配置
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# Neo4j 配置(格式:用户名/密码)
# 注意:Neo4j 的默认用户名必须是 neo4j,只能改密码
NEO4J_AUTH=neo4j/mem0graph
注意: Neo4j 有一个特殊约定——默认用户名固定为
neo4j,NEO4J_AUTH里设置的是密码。如果你写成NEO4J_AUTH=admin/123456,它会报错。
5.3 验证服务是否健康
启动后别急着跑应用,先检查每个服务是否正常:
# 查看所有容器状态
docker-compose ps
# 检查 Mem0 Server 是否就绪
curl http://localhost:8888/health
# 检查 PostgreSQL 是否接受连接
docker exec -it mem0-postgres-1 pg_isready -U postgres
# 期望输出:/var/run/postgresql:5432 - accepting connections
# 检查 Neo4j 是否可访问
curl -I http://localhost:7474
# 期望返回 HTTP 200
如果某个服务起不来,看日志:
# 实时跟踪 Mem0 日志
docker-compose logs -f mem0
# 查看 PostgreSQL 日志
docker-compose logs postgres
# 查看 Neo4j 日志
docker-compose logs neo4j
5.4 启动 Spring Boot 应用
依赖服务全部健康后,启动你的 Java 应用:
cd /home/tht/examples-main/spring-ai-alibaba-mem0-example
# 方式一:开发调试模式
mvn spring-boot:run
# 方式二:打包生产运行
mvn clean package -DskipTests
java -jar target/spring-ai-alibaba-mem0-example-1.0.0.jar
应用默认运行在 10009 端口。
六、API 使用指南:动手试一试
6.1 对话接口(记忆自动存取)
第一步:首次对话,让 AI 记住你
curl "http://localhost:10009/advisor/memory/mem0/call?message=你好,我是万能的喵,我爱玩三角洲行动&user_id=miao"
AI 可能会回答:“你好万能的喵!三角洲行动确实是一款很棒的战术射击游戏…”
第二步:换个话题,测试 AI 是否记得你
curl "http://localhost:10009/advisor/memory/mem0/call?message=推荐一款游戏给我&user_id=miao"
如果记忆生效,AI 会基于"你爱玩三角洲行动"推荐类似游戏,而不是随机推荐。
第三步:主动搜索记忆
curl "http://localhost:10009/advisor/memory/mem0/messages?query=我的爱好是什么?&user_id=miao"
返回示例:
[
{
"id": "doc-xxx",
"content": "用户爱好:玩三角洲行动",
"metadata": {
"user_id": "miao",
"memory_type": "user_preference",
"confidence": 0.95
}
}
]
6.2 测试接口(快速验证长期记忆)
curl "http://localhost:10009/advisor/memory/mem0/test"
如果看到日志输出:
用户和 agent 的长期记忆保存成功
agent 的长期记忆: [Document{...}]
说明记忆写入和读取链路都是通的。
6.3 记忆的三维层级(重要)
Mem0 支持按三个维度隔离和检索记忆,这是设计多租户、多智能体系统的关键:
检索示例:
# 获取某个用户的所有长期记忆(跨所有智能体和会话)
m.get_all(user_id="alice")
# 获取用户在特定智能体下的记忆
# 比如 alice 在"饮食助手"里的记忆
m.get_all(user_id="alice", agent_id="diet-assistant")
# 获取某次具体会话的记忆(短期记忆)
# 比如这次旅行咨询的对话
m.get_all(user_id="alice", run_id="trip-planning-2025")
# 语义搜索:用自然语言查找相关记忆
m.search("What do you know about me?", user_id="alice")
什么时候用什么维度?
| 场景 | 使用的维度 | 记忆类型 |
|---|---|---|
| AI 记住用户名字、爱好 | user_id |
长期记忆 |
| 不同 AI 角色有各自的专业知识 | user_id + agent_id |
长期记忆 |
| 一次具体的客服对话上下文 | user_id + run_id |
短期记忆 |
七、自定义 Prompt:让记忆提取更精准
7.1 默认提取的问题
Mem0 自带的事实提取 Prompt 是通用的,但你的业务可能有特殊需求。比如:
- 你是医疗助手,需要提取"过敏史"、“用药记录”
- 你是电商助手,需要提取"品牌偏好"、“价格敏感度”
- 你是教育助手,需要提取"薄弱知识点"、“学习目标”
7.2 自定义提取模板
在 src/main/resources/prompts/custom_fact_extraction_prompt.st 创建文件:
你是一个专业的记忆提取专家。请从以下对话中提取关键信息,
并严格按照 JSON 格式输出。
对话内容:
{{messages}}
提取规则:
1. 只提取明确陈述的事实,不要推测
2. 忽略寒暄、问候等无信息量的内容
3. 如果用户纠正了之前的信息,以最新说法为准
请提取以下类别的信息:
- preferences: 用户偏好(喜欢的、不喜欢的)
- habits: 用户习惯(时间规律、行为模式)
- facts: 重要事实(身份、职业、目标等)
- relationships: 关系信息(家人、同事、宠物等)
输出格式(必须是合法 JSON):
{
"preferences": [
{"subject": "游戏", "content": "爱玩三角洲行动", "sentiment": "positive"}
],
"habits": [],
"facts": [
{"subject": "名字", "content": "万能的喵"}
],
"relationships": []
}
7.3 启用自定义 Prompt
spring:
ai:
memory:
mem0:
fact-extraction-prompt: classpath:prompts/custom_fact_extraction_prompt.st
落地建议: 先跑一段时间默认 Prompt,观察提取效果,再针对漏提取或错提取的场景定制模板。不要一开始就写很复杂的模板。
八、常见问题与排查
8.1 Mem0 Server 连接失败
现象: 应用启动或调用时报 Connection refused: localhost:8888
排查步骤:
# 1. 先看 Mem0 容器是否在跑
docker-compose ps | grep mem0
# 2. 看 Mem0 日志找错误
docker-compose logs mem0 | tail -50
# 3. 常见原因:pgvector 或 Neo4j 还没就绪,Mem0 在重连
# 等 30 秒再试
# 4. 检查端口是否映射正确
curl http://localhost:8888/health
8.2 记忆检索不准确或为空
可能原因排查:
# 1. 检查 pgvector 是否健康
docker exec -it mem0-postgres-1 pg_isready -U postgres
# 2. 检查向量维度是否匹配(如果手动改过配置)
# 默认使用 1536 维或模型对应维度
# 3. 检查 LLM 模型
# 如果用的是 Qwen 且 JSON 输出不稳定,事实提取可能失败
# 建议切换到 deepseek-chat
调试技巧: 开启 DEBUG 日志,看记忆提取和检索的详细过程:
logging:
level:
com.alibaba.cloud.ai.memory.mem0: DEBUG
8.3 Neo4j 认证失败
现象: Unable to connect to Neo4j 或 Authentication failed
解决:
# 确认 .env 中的 NEO4J_AUTH 格式
# 必须是:neo4j/<你的密码>
# 用户名不能改,只能是 neo4j
# 如果之前启动过且改了密码,可能需要清理数据卷
docker-compose down -v
docker-compose up -d
8.4 内存占用过高
PostgreSQL + pgvector + Neo4j 一起跑比较吃资源,低配机器可能会卡。
# 在 docker-compose.yaml 中限制资源
services:
postgres:
shm_size: "64mb" # 减小共享内存
deploy:
resources:
limits:
memory: 512M
neo4j:
deploy:
resources:
limits:
memory: 1G
8.5 首次启动特别慢
正常现象。 首次启动需要:
- 拉取 Docker 镜像(几百 MB)
- Neo4j 初始化系统数据库(1-2 分钟)
- pgvector 创建扩展和表结构
# 耐心等待,看实时日志
docker-compose logs -f
# 当看到类似 "Mem0 server started" 或健康检查通过时即可
九、进阶用法:玩转三层记忆
9.1 短期记忆(按会话隔离)
适合需要记住"这次对话的上下文",但不需要永久保存的场景:
// 存储带 runId 的记忆(短期)
mem0ServiceClient.addMemory(
Mem0ServerRequest.MemoryCreate.builder()
.userId("test1")
.runId("trip-planning-2025") // 会话 ID,比如"日本旅行规划"
.messages(List.of(
new Message("user", "I'm planning a trip to Japan next month.")
))
.build()
);
// 检索时指定 runId,只查这次会话的记忆
mem0ServiceClient.search("What was my travel plan?", "test1", null, "trip-planning-2025");
9.2 长期记忆(跨会话共享)
适合用户画像、偏好等永久信息:
// 不指定 runId,就是长期记忆
mem0ServiceClient.addMemory(
Mem0ServerRequest.MemoryCreate.builder()
.userId("test2")
.agentId("agent2") // 可选:指定属于哪个智能体
.messages(List.of(
new Message("user", "I'm travelling to San Francisco"),
new Message("assistant", "That's great! I'm going to Dubai next month.")
))
.build()
);
9.3 记忆的更新与删除
记忆不是只增不减的,用户可能会说"我之前说错了":
// 更新某条具体记忆(需要知道 memoryId)
mem0ServiceClient.updateMemory(
memoryId,
Map.of("memory", "用户实际喜欢的是使命召唤,不是三角洲行动")
);
// 删除单条记忆
mem0ServiceClient.deleteMemory(memoryId);
// 清空某个用户的所有记忆(慎用!)
mem0ServiceClient.deleteAllMemories("user_id", null, null);
写在最后
给 AI 加记忆,不是简单地"把聊天记录存起来"。Mem0 的价值在于它让 AI 拥有了"提炼-存储-联想"的完整记忆能力——就像人类不是记住每一秒的画面,而是记住经过大脑加工后的知识和经验。
| 特性 | 价值 |
|---|---|
| 自动事实提取 | 不用写规则,LLM 自动从对话中提炼知识点 |
| 语义理解检索 | 用户换种说法问,也能找到相关记忆 |
| 向量 + 图双引擎 | 既懂"像什么",又懂"有什么关系" |
| 三维记忆隔离 | 用户级、智能体级、会话级灵活控制 |
| Spring AI 原生集成 | 一个 Advisor 搞定,对业务代码零侵入 |
按照本文,你应该能顺利地把 Mem0 集成到自己的 Spring AI 项目中。如果在部署过程中遇到问题,建议按照第八章的排查清单一步一步来,大部分问题都是服务依赖没就绪或配置项填错了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)