Context上下文
一. Context、Memory、Prompt与Token的区别
Token:模型处理信息的单位
Token 是文本经过分词器处理后得到的基本单位,可能是一个字、一个词的一部分或标点符号。
原始文本
↓
Tokenizer分词
↓
Token序列
↓
模型处理
注意:
- Token 数不等于字符数。
- 同一段文本使用不同分词器,Token 数可能不同。
- 中文、英文、代码的 Token 与字符比例不同。
- 工程中应使用对应分词器计数,不能固定认为一个 Token 等于几个汉字。
模型的上下文容量通常以 Token 衡量,调用时还需要为输出预留空间。
Prompt:给模型的指令和输入
Prompt 用于告诉模型"要做什么、如何做"。
它不仅是用户的问题,还可以包含:
- 系统指令
- 用户任务
- 示例
- 输出格式
- 参考资料
例如:
系统指令:
你是一名Java开发助手,请简洁回答。
用户问题:
HashMap和ConcurrentHashMap有什么区别?
输出要求:
使用表格比较。
实际使用中,"Prompt"也常泛指发送给模型的整个输入,因此它与 Context 的概念存在重叠。
Context:本次调用中模型能看到的信息
Context 是模型生成当前回答时能够使用的输入信息。
通常包括:
系统指令
当前用户问题
选入的历史消息
工具定义
工具调用结果
RAG检索资料
检索到的记忆
当前任务状态
关键点:只有实际传入当前调用的信息,模型才能使用。
数据库里保存了信息
↓
没有读取并加入请求
↓
模型本次看不到这些信息
上下文可以按用途分类:(重点)
| 分类 | 内容 |
|---|---|
| Instructions:指令 | 角色、任务、输出要求 |
| Knowledge:知识 | 参考资料、检索到的记忆 |
| Tools:工具 | 工具名称、参数、能力描述 |
| State:状态 | 历史消息、执行结果、计划进度 |
这些分类不要求严格互斥。
Memory:保存和复用信息的机制
Memory 是应用为了跨调用保留信息而实现的存储、更新和读取机制。
普通模型调用不会自动记住上一次请求,需要应用把相关信息重新带入上下文。
之前的对话或任务
↓
保存到Memory
↓
后续请求读取相关信息
↓
加入当前Context
↓
模型利用这些信息回答
短期记忆
短期记忆主要服务于当前会话或任务,例如:
-
最近几轮对话
-
当前计划
-
已收集的用户需求
-
最近的工具结果
用户:我准备去北京。
用户:玩5天。
用户:预算5000元。
↓
短期记忆保留这些信息
↓
模型生成旅行方案
短期记忆不一定保存在内存中,也可以持久化到数据库。它是否保留,与存储方式及清理策略有关。
长期记忆
长期记忆用于跨会话复用信息,例如:
-
用户偏好
-
长期目标
-
历史任务经验
-
稳定的个人信息
本次会话:
用户表示喜欢安静、少走路的旅行方式。
↓
提取偏好并保存
↓
下次规划旅行时检索偏好
↓
加入当前上下文
长期记忆通常保存在数据库、文件或向量存储中,但不需要每次全部加载。
Context和Memory的区别
Memory:
保存了哪些信息,以及如何读取和更新。
Context:
本次模型调用实际看到了哪些信息。
例如,系统保存了1000条历史消息,但当前只发送最近10条:
Memory:1000条历史消息
Context:最近10条 + 当前问题 + 系统指令等
所以:
- Memory 不等于 Context。
- Memory 中被选取并传入模型的内容,才成为当前 Context 的一部分。
- Context 也包含工具定义、当前问题等不一定来自 Memory 的内容。
四者之间的关系
Prompt中的指令与问题
+
Memory中选取的信息
+
RAG资料、工具定义和执行状态
↓
构成本次Context
↓
编码为Token序列
↓
模型生成回答
↓
按需要更新Memory
对比总结
| 概念 | 关注点 | 示例 |
|---|---|---|
| Token | 信息如何编码、占用多少容量 | 一段文本被分成多少Token |
| Prompt | 告诉模型做什么 | "请总结这份报告" |
| Context | 本次模型能看到什么 | 问题、历史、资料、工具结果 |
| Memory | 哪些信息被保存和复用 | 历史对话、用户偏好 |
重点总结
-
Token 是模型处理文本的单位,不能与字符数直接画等号。
-
Prompt 不只是用户问题,也包括系统指令、示例和输出要求。
-
Context 是当前调用实际提供给模型的信息,受上下文容量限制。
-
Memory 负责跨调用保存和复用信息;只有加载到当前请求中的部分才进入 Context。
-
短期、长期描述的是记忆的用途和保留范围,不等于内存存储、数据库存储。
-
可以这样记:
Token:信息占多少容量。
Prompt:要求模型做什么。
Context:模型本次看到什么。
Memory:系统保存并复用什么。
二. 上下文太长会带来什么问题?
上下文为什么会膨胀
随着对话和 Agent 执行持续进行,上下文会不断累积:
系统指令
用户问题
历史消息
工具定义
工具调用结果
RAG检索资料
任务计划与执行状态
如果没有筛选和清理,上下文可能变得过长、重复或混乱。
上下文过长的影响
-
输入 Token 增多,调用成本和处理耗时可能增加。
-
超出上下文窗口,导致请求失败或部分内容被截断。
-
无关内容干扰任务,重要信息容易被忽略。
-
历史错误、过时状态和冲突信息影响后续判断。
上下文更多 ≠ 回答更准确
关键是提供与当前任务相关、准确且必要的信息。
Lost in the Middle:中间信息被忽略
《Lost in the Middle》研究指出,在其测试任务和模型中,关键信息的位置会影响模型表现:
上下文开头 → 较容易被利用
上下文中间 → 较容易被忽略
上下文结尾 → 较容易被利用
即使模型能够接收长上下文,也不代表它能同样有效地利用每个位置的信息。
这里的"中间迷失"主要指中间位置的信息利用不足,不等同于所有情况下都会忘记最初目标。
具体表现取决于模型、任务和输入内容,不能把某个 Token 数作为统一的性能下降阈值。
Context Poisoning:上下文中毒
错误信息进入上下文后,被后续步骤当作事实使用,导致错误持续传播。
模型生成错误结论
↓
错误结论被保存到上下文
↓
后续步骤将其当作事实
↓
错误不断放大
例如:
工具实际结果:
订单未支付。
错误摘要:
订单已支付。
后续Agent:
根据"已支付"安排发货。
核心问题:错误内容污染了后续判断的依据。
Context Distraction:上下文分散
上下文包含大量无关信息,干扰模型对当前目标的关注,导致答非所问或偏离任务。
当前任务:总结文章
+
大量无关聊天和历史记录
↓
模型关注了无关内容
↓
没有完成总结任务
核心问题:信息太杂,分散了对当前任务的注意力。
Context Confusion:上下文混乱
上下文中的工具、选项或说明过多、相似度过高,导致模型混淆它们的用途,选错执行方式。
例如:
query_order
query_order_history
query_archived_order
query_refund_order
query_test_order
如果工具说明不清晰,模型可能无法区分应该调用哪一个。
工具过多、职责重叠
↓
难以判断适用范围
↓
选错工具或传错参数
核心问题:难以区分信息、工具或选项的用途。
Context Clash:上下文冲突
上下文同时存在互相矛盾的信息,模型无法正确判断应以哪一条为准。
例如:
旧计划:
用户准备去北京。
新要求:
用户已经改为去上海。
上下文没有明确更新:
北京和上海两份计划同时保留。
模型可能混用两个城市的行程。
核心问题:新旧信息或不同来源的信息相互矛盾。
四类问题对比
| 问题 | 核心表现 | 示例 |
|---|---|---|
| 上下文中毒 | 错误信息被当作事实 | 将"未支付"记成"已支付" |
| 上下文分散 | 无关信息干扰目标 | 总结文章时转而讨论闲聊内容 |
| 上下文混乱 | 混淆工具和信息用途 | 多个相似工具中选错一个 |
| 上下文冲突 | 矛盾信息同时存在 | 旧目的地与新目的地混用 |
这些问题可能同时出现,也不是只有长上下文才会发生;上下文累积会增加管理难度。
常见处理方式
| 问题 | 处理思路 |
|---|---|
| 上下文过长 | 裁剪历史、按需检索、压缩冗长结果 |
| 中间信息被忽略 | 合理组织内容,突出当前目标和关键证据 |
| 上下文中毒 | 核验关键结果,区分事实、推测和模型结论 |
| 上下文分散 | 删除与当前任务无关的信息 |
| 上下文混乱 | 按需加载工具,明确工具职责和适用范围 |
| 上下文冲突 | 更新任务状态,明确最新信息和来源 |
压缩上下文时也要检查摘要质量,避免把错误结论保留下来。
重点总结
-
上下文越长不代表效果越好,相关性和准确性更重要。
-
模型支持的最大上下文长度,不等于有效利用信息的能力。
-
四类典型问题:
中毒:错误信息被相信。
分散:无关信息让模型偏题。
混乱:工具和信息难以区分。
冲突:相互矛盾的信息同时存在。 -
上下文管理的目标:
保留关键事实
删除无关内容
更新过时状态
减少重复信息
按需提供工具
明确当前目标
三. 什么是上下文工程?
核心概念
上下文工程(Context Engineering)是管理模型每次调用时能够看到的信息,让上下文保持相关、准确、充分,并控制 Token 开销。
上下文不仅包含提示词,还包括:
历史消息
工具定义
工具执行结果
任务计划
记忆
RAG检索资料
因此,除了优化提示词,还需要决定哪些信息加载、保留、压缩或移出当前上下文。
与提示词工程的区别
| 类型 | 关注点 | 示例 |
|---|---|---|
| 提示词工程 | 如何表达指令和要求 | 指定角色、示例、输出格式 |
| 上下文工程 | 本次调用应提供哪些信息 | 选择历史、检索资料、筛选工具 |
提示词工程:和模型怎么说。
上下文工程:让模型看到什么。
提示词工程是上下文工程的一部分,两者可以共同使用。
上下文窗口与工作内存
可以将上下文窗口类比为工作内存:
外部存储:保存大量资料和历史信息
↓ 按需加载
上下文窗口:当前任务所需的信息
↓
模型处理
上下文工程负责管理:
- 哪些信息现在需要加载。
- 哪些信息暂时保存在外部。
- 哪些内容需要压缩。
- 哪些任务应该拆开处理。
四种主要方法
| 方法 | 作用 |
|---|---|
| Write:写入 | 将重要信息保存到上下文之外 |
| Select:选择 | 按需加载与当前任务相关的信息 |
| Compress:压缩 | 减少输入体积,保留关键内容 |
| Isolate:隔离 | 将不同任务的上下文分开管理 |
它们可以组合使用,不是必须依次执行的四个阶段。
Write:写入
将重要信息保存到文件、状态存储或数据库中,方便后续使用。
执行中产生重要信息
↓
保存到外部存储
↓
后续需要时再读取
常见方式:
- Scratchpad:任务草稿板。
- Long-Term Memory:长期记忆。
Scratchpad:草稿板
保存工作过程中的临时信息,例如:
当前目标
任务计划
已完成步骤
待办事项
关键发现
失败原因
示例:
目标:分析接口变慢的原因。
已完成:
1. 检查网络,无明显异常。
2. 发现数据库存在慢查询。
下一步:
检查慢查询SQL的执行计划。
草稿板不必保存全部执行过程,主要记录继续任务所需的信息。
Checkpoint:检查点
检查点用于保存图的执行状态,支持恢复、中断续跑等操作。草稿板可以作为状态的一部分被保存。
Checkpoint:保存执行状态。
Scratchpad:记录任务过程中的关键笔记。
两者不是同一个概念。
thread_id 和 conversation_id 都可以用于关联会话,但关联的数据不同:
thread_id:
通常用于关联图执行状态和检查点。
conversation_id:
通常用于关联聊天消息。
Long-Term Memory:长期记忆
保存跨会话仍有价值的信息,例如:
-
用户偏好
-
稳定的业务知识
-
历史任务经验
-
长期目标
本次发现重要信息
↓
筛选并保存长期记忆
↓
下次任务按需读取
写入外部存储后,如果仍将全部内容发送给模型,并不会减少当前上下文占用。
Select:选择
从已保存的信息中,选择当前任务需要的部分加入上下文。
典型做法:
-
检索相关的长期记忆。
-
召回相关文档。
-
加载适用的规则文件。
-
筛选本次需要的工具。
-
读取必要的任务状态。
大量外部信息
↓
根据当前任务筛选
↓
只加载相关内容
↓
加入当前Context
例如:
系统共有100个工具。
当前任务:查询订单。
↓
只提供订单查询、物流查询等相关工具。
选择不仅要考虑相似度,也要考虑信息来源、时效性和适用范围。
Compress:压缩
减少上下文中的 Token,同时尽量保留完成任务所需的信息。
主要方式:
| 方法 | 处理方式 |
|---|---|
| Summarization:摘要 | 将长内容概括成简短记录 |
| Trimming:裁剪 | 按规则删除部分内容 |
| Pruning:按相关性删减 | 移除对当前任务没有帮助的内容 |
摘要
几十轮对话
↓
提取目标、约束、进展和关键结论
↓
生成精简摘要
摘要应重点保留:
用户目标
明确约束
已确认事实
关键结果
未解决问题
下一步行动
摘要本身可能遗漏或误写信息,因此不能仅追求字数少。
裁剪
按固定规则保留部分消息:
保留系统指令
保留最近若干轮对话
删除过时或重复内容
处理工具调用消息时,应保持调用请求与对应结果的完整关联。
按相关性删减
根据当前任务,删除无关内容。
当前任务:排查数据库性能。
保留:
慢查询、执行计划、数据库指标。
移除:
无关聊天、重复日志和其他任务记录。
Isolate:隔离
将任务拆分,让不同执行单元只接收自己需要的上下文,减少信息混杂。
多智能体隔离
主Agent
├── 数据Agent:只处理数据分析
├── 文档Agent:只处理资料检索
└── 写作Agent:只处理结果表达
↓
返回必要结果
↓
主Agent汇总
关键是给子 Agent 精简输入,并让它返回有用结果;如果每个 Agent 都接收完整历史,隔离收益就会降低。
执行环境隔离
将大文件、完整数据集等保留在执行环境中,通过代码处理,只向模型返回必要结果。
完整数据集保存在文件中
↓
代码读取并计算
↓
返回统计结果或少量样本
↓
模型继续分析
减少上下文占用依靠的是"外部处理、精简返回",并非使用沙盒就会自动减少 Token。
结构化状态
将信息分成不同字段,分别管理:
{
"messages": [],
"plan": [],
"search_results": [],
"file_path": "/data/report.csv"
}
构建模型请求时只选择必要字段。
State保存的信息
≠
全部发送给模型的信息
仅定义状态字段不会自动实现隔离,还需要控制请求构造逻辑。
渐进式披露
渐进式披露是先提供少量概览,模型确定需要后,再加载详细内容。
先提供能力名称和简介
↓
判断是否与任务相关
↓
加载详细说明
↓
需要时继续读取参考文件
例如技能文档:
第一层:技能名称、用途
第二层:具体操作说明
第三层:参考资料、脚本和模板
这样可以避免一开始把所有说明和资料塞入上下文。
四种方法如何配合
以长时间故障排查为例:
Write:
把排查进展写入任务笔记。
Select:
只读取当前问题相关的日志和工具。
Compress:
把大量日志概括成关键异常。
Isolate:
让独立Agent分析某个子系统。
渐进式披露:
先读取日志概览,再按需查看详细记录。
重点总结
-
上下文工程管理的是模型本次调用实际接收的信息。
-
提示词工程关注指令表达,是上下文工程的一部分。
-
四种主要方法:
Write:存到外部。
Select:按需取用。
Compress:精简内容。
Isolate:分开处理。 -
外部存储和执行状态不等于模型上下文,只有实际传入请求的内容才会被模型看到。
-
检查点负责保存执行状态,草稿板负责记录任务笔记。
-
渐进式披露是先加载概览,再按需展开详细信息。
-
目标不是让上下文越短越好,而是让它包含完成当前任务所需的准确信息。
四. Manus的上下文工程实践
核心思路
Manus 的实践重点是管理 Agent 执行过程中不断增长的上下文,让模型获得必要信息,同时控制延迟和成本。
主要包括:
提高缓存复用
限制可选工具
使用文件系统保存大内容
持续提醒任务目标
保留有价值的失败记录
围绕 KV Cache 设计上下文
模型生成文本时,会计算 Token 对应的 Query、Key、Value。
KV Cache 保存已经处理过的历史 Token 的 Key 和 Value,后续生成时可以复用。
历史Token
↓
计算并缓存K、V
↓
生成新Token时复用历史K、V
↓
减少重复计算
注意:新 Token 仍需要计算自己的 Q、K、V,并关注历史内容。不能简单理解为整个推理过程从 O(n²) 变成了 O(n)。
在支持前缀缓存的服务中,多次请求还可以复用相同前缀的计算结果。
第1次请求:
系统指令 + 工具定义 + 用户问题
第2次请求:
系统指令 + 工具定义 + 用户问题 + 新增执行结果
└──────── 可复用的相同前缀 ────────┘
如何提高缓存命中率
- 保持系统提示词和工具定义稳定。
- 将变化频繁的信息放在稳定内容之后。
- 使用确定性的序列化方式,避免字段顺序反复变化。
- 尽量追加新消息,避免频繁改写前面的历史内容。
- 根据实际服务的缓存机制配置路由和缓存边界。
例如:
不利于前缀复用:
系统提示词开头每次插入新的时间戳。
更有利于复用:
稳定系统指令 + 稳定工具定义 + 当前时间和任务信息。
JSON 字段排序只是保证序列化稳定的一种方法,关键是相同内容应产生一致的 Token 序列。
前缀发生变化时,变化位置之前的部分仍可能复用,不一定全部缓存都失效。
工具遮蔽:保留定义,限制选择
动态删除或调整工具列表,可能改变请求前缀,影响缓存复用。
这类方案选择保留稳定的工具定义,再限制当前步骤允许调用的工具。
完整工具定义保持稳定
↓
当前步骤只允许浏览器工具
↓
模型从允许的工具中选择
工具可以使用统一前缀命名:
browser_search
browser_navigate
file_read
file_write
shell_run
shell_check
这样方便按工具类别进行限制。
实现方式取决于模型和推理服务,可以涉及工具选择约束、受约束解码或响应预填充。
需要注意:
- 仅在提示词中要求"只用某类工具",不等于强制约束。
- 普通 JSON 文本前缀不一定能触发原生工具调用。
- 缓存边界和特殊 Token 不是所有服务通用的标准。
- 工具定义依然占用上下文,缓存不会让它们免费或消失。
文件系统作为外部上下文
网页、PDF和日志等内容可能非常大,直接加入上下文会增加开销和干扰。
可以将完整内容保存在文件中,上下文只保留摘要和文件位置。
读取大量网页或日志
↓
保存到文件
↓
上下文保留摘要、路径和关键发现
↓
需要细节时再读取对应内容
例如:
搜索结果已保存到:/workspace/results.html
关键发现:资料主要涉及缓存设计和工具调度。
需要时读取相关章节。
与直接删除内容相比,这种方式保留了重新获取细节的能力。
但"可恢复"需要满足条件:文件仍然存在、路径有效,并且 Agent 有读取能力。仅保留一个可能失效的远程 URL,不保证信息可恢复。
通过任务清单提醒目标
长任务执行过程中,模型可能逐渐偏离最初目标。
可以维护 todo.md,记录目标、进展和下一步行动,并在关键阶段将当前清单加入上下文。
# 任务目标
分析接口响应变慢的原因。
## 已完成
- [x] 检查网络连接
- [x] 收集接口耗时日志
## 待处理
- [ ] 分析慢查询
- [ ] 检查索引
- [ ] 整理诊断报告
执行流程:
完成一个阶段
↓
更新任务清单
↓
读取并提醒当前目标和剩余事项
↓
继续下一步
仅修改文件不会自动影响模型,相关内容需要被读取或加入后续请求。
任务清单应保持简洁,避免大量重复提醒再次挤占上下文。
保留错误记录,帮助恢复
工具调用失败后,保留错误原因和已尝试的方法,可以帮助模型调整策略,避免重复犯错。
尝试读取文件
↓
返回"路径不存在"
↓
记录失败路径
↓
检查实际目录
↓
使用正确路径重试
可以保存:
失败动作:读取/data/report.csv
错误原因:文件不存在
已知信息:实际文件位于/workspace/report.csv
下一步:使用正确路径读取
这里的"学习"是模型利用当前上下文中的反馈调整行为,不是更新模型参数。
也不需要将完整错误堆栈反复塞入上下文,可以保留关键错误,把完整日志存到文件。
稳定缓存与动态上下文的平衡
保持前缀稳定有利于缓存,清理历史有利于上下文质量,两者需要权衡。
短期执行:
追加新消息,尽量复用缓存。
历史过长:
压缩或重建上下文,接受必要的缓存失效。
不能为了缓存一直保留过时、错误或无关的信息。
重点总结
- 保持请求前缀稳定,有利于复用缓存、减少重复计算。
- 动态信息尽量放在后面;修改历史前缀可能影响后续缓存复用。
- 工具遮蔽是在保留工具定义的同时限制当前可选工具,具体实现依赖服务能力。
- 大内容保存到文件,上下文保留摘要和位置,需要时再读取。
- 通过简洁的任务清单持续提醒目标、进度和下一步。
- 保留关键失败信息,帮助模型恢复和调整策略,而不是反复重复错误。
- 对应上下文工程方法:
| 实践 | 主要作用 |
|---|---|
| 文件保存完整内容 | Write:外部写入 |
| 按需读取文件 | Select:选择信息 |
| 用摘要和路径替代全文 | Compress:减少上下文 |
| 提醒当前任务清单 | 保持目标聚焦 |
| 保留关键错误记录 | 支持执行恢复 |
最终目标是让 Agent 每一步都获得足够、相关、可信的信息,并减少不必要的计算和上下文占用。
五. 什么是长期记忆?
Agent 的记忆
Agent 的记忆是存储、读取和利用历史信息的机制,可以包括:
- 历史对话
- 工具调用结果
- 任务状态
- 用户偏好
- 事实和经验
模型使用这些信息时,需要将相关内容加载到当前上下文,而不是把全部历史一次性传入。
历史信息
↓
保存为记忆
↓
当前任务按需读取
↓
加入模型上下文
↓
生成回答
短期记忆
短期记忆主要服务于当前会话或任务,保存最近的消息和执行状态。
例如:
用户:我准备去北京。
用户:玩5天,预算5000元。
用户:帮我安排一下。
模型结合当前会话的信息生成行程。
短期记忆可以保存在:
- 内存
- Redis
- 关系型数据库
- 执行检查点
存储位置不决定它属于短期还是长期记忆。
长期记忆
长期记忆保存具有持续价值的信息,供后续不同会话或任务按需使用。
例如:
一年前:
用户说自己喜欢吃香菜。
↓
提取并保存饮食偏好。
↓
一年后:
用户要求推荐餐馆。
↓
读取相关偏好,辅助推荐。
不需要把这一年的全部对话传给模型,只需要提取与当前任务有关的信息。
长期记忆的作用
| 作用 | 示例 |
|---|---|
| 个性化服务 | 用户喜欢清淡饮食、偏好简洁回答 |
| 知识积累 | 保存业务事实、项目约定 |
| 经验复用 | 记录某类故障的有效处理方式 |
| 补充上下文 | 从历史中召回当前任务需要的信息 |
长期记忆不仅是用户画像,也可以保存任务经验、操作流程和业务知识。
长期记忆与持久化记忆
这两个概念关注的角度不同:
持久化:
数据能否在进程退出或重启后保留。
长期记忆:
信息是否用于跨会话、跨任务的长期复用。
例如:
将某个会话的消息保存到数据库
↓
重启后继续原会话
↓
持久化的会话记忆
从不同会话中提取用户偏好
↓
保存为用户画像
↓
在新的会话中按需使用
↓
长期记忆
长期记忆通常需要持久化存储,但仅把聊天记录存进数据库,不代表已经实现了长期记忆管理。
短期记忆与长期记忆对比
| 对比项 | 短期记忆 | 长期记忆 |
|---|---|---|
| 主要范围 | 当前会话或任务 | 跨会话、跨任务 |
| 常见内容 | 最近消息、当前进度 | 偏好、事实、经验 |
| 使用方式 | 最近消息、摘要、任务状态 | 根据任务检索或精确读取 |
| 管理重点 | 保持当前任务连贯 | 提取、更新、筛选和复用 |
| 是否可以持久化 | 可以 | 通常需要 |
为什么不能携带全部历史
用户历史不断增加
↓
全部加入上下文
↓
Token开销增加、可能超出窗口
↓
无关信息干扰当前任务
因此,长期记忆需要两个关键能力:
写入时:判断什么值得记住。
读取时:判断本次需要什么。
实现方式一:向量存储与检索
将记忆条目或摘要转换成向量,查询时召回语义相关的内容。
历史对话
↓
提取有价值的记忆
↓
Embedding向量化
↓
保存到向量数据库
↓
根据当前问题检索相关记忆
适合:
- 历史经验
- 对话摘要
- 语义相关的事实
- 不容易用固定字段描述的信息
检索时还需要限制用户、权限和适用范围,避免召回其他用户的记忆。
实现方式二:结构化数据库
将用户偏好或明确事实保存为字段,方便精确查询和更新。
{
"userId": "user-001",
"preferences": {
"likesCoriander": true,
"answerStyle": "concise",
"preferredLanguage": "zh-CN"
},
"updatedAt": "2026-09-14"
}
适合:
- 用户画像
- 偏好设置
- 身份信息
- 明确的时间、状态和属性
例如:
当前任务:生成回答
↓
根据userId读取answerStyle
↓
发现偏好为concise
↓
提供简洁回答
实现方式三:图数据库
使用实体和关系表达记忆,适合关联信息较多的场景。
用户A ── 任职于 ── 公司B
用户A ── 参与 ── 项目C
项目C ── 使用 ── 技术D
适合:
- 人物关系
- 项目关系
- 组织结构
- 多实体关联查询
实现方式四:混合存储
根据记忆类型选择不同存储方式。
用户ID、明确偏好 → 结构化数据库
历史摘要、任务经验 → 向量数据库
人物、项目关联 → 图数据库
这样可以同时支持精确查询和语义检索,不必把所有记忆都放进向量库。
实现方式五:配置或规则文件
将用户明确提出的偏好、约束和项目规范保存为文件,后续按需加载。
# 用户偏好
- 使用中文回答。
- 解释尽量简洁。
- Java代码块标注java。
# 项目约定
- Controller按业务模块分类。
- 配置中的密钥通过环境变量读取。
这类文件可以充当持久的偏好或规则来源,但不会自动学习,需要明确的写入和更新机制。
微调与长期记忆的区别
微调会改变模型参数,可以用于学习稳定的风格、能力或领域模式。
但它不适合充当日常用户事实的主要记忆库:
-
单条信息不容易精确读取。
-
更新和删除成本较高。
-
难以保证模型准确复述某个事实。
-
不适合频繁变化的用户偏好。
长期记忆存储:
外部保存信息,需要时加载。微调:
修改模型参数,改变模型行为。
用户画像和动态事实通常更适合使用外部存储。
长期记忆的完整流程
用户交互
↓
识别值得保存的信息
↓
去重、核验并处理冲突
↓
保存或更新记忆
↓
新的会话或任务
↓
按用户、相关性和时效性读取
↓
加入当前上下文
↓
模型完成任务
例如,用户更新偏好:
旧记忆:喜欢香菜。
新信息:现在不吃香菜了。
↓
更新偏好,标记旧信息失效。
不能只不断追加,而不处理新旧信息冲突。
重点总结
-
短期记忆服务当前会话,长期记忆支持跨会话、跨任务复用。
-
持久化是存储属性,不等于长期记忆。
-
长期记忆不需要加载全部历史,只读取当前任务相关的信息。
-
常见存储方式:
向量库 → 语义检索
结构化数据库 → 精确事实和用户画像
图数据库 → 实体关系
规则文件 → 明确偏好和约束 -
长期记忆需要管理提取、更新、去重、冲突、过期和删除。
-
基于记忆调整回答,不等于模型参数发生了学习或更新。
-
核心流程:
提取重要信息 → 长期保存 → 按需读取 → 加入Context
六. 长期记忆实现方案:Mem0
Mem0 简介
Mem0 是为 AI 应用提供长期记忆能力的框架,可以从对话中提取用户信息、偏好和事实,保存后供后续会话检索使用。
用户对话
↓
提取重要事实和偏好
↓
与已有记忆比较
↓
新增、更新、删除或保留
↓
保存记忆
↓
后续对话按需检索
它不仅保存原始聊天记录,还负责提取和维护有价值的记忆。
核心模块
| 模块 | 作用 |
|---|---|
| LLM | 提取事实、判断记忆是否需要更新 |
| Embedder | 将记忆和查询转换成向量 |
| Vector Store | 保存向量并检索相关记忆 |
| 用户标识与元数据 | 区分记忆归属、记录来源等信息 |
记忆提取
从对话中提取适合长期保存的信息。
用户:
我叫Hollis,喜欢编程和游戏。
提取结果:
姓名是Hollis。
喜欢编程和游戏。
这些记忆通常是简短的自然语言条目,再通过 ID、用户标识和时间等字段进行组织。
记忆存储与检索
记忆条目
↓
Embedding向量化
↓
关联user_id并存入向量数据库
新的会话中:
当前问题 + user_id
↓
检索该用户的相关记忆
↓
返回记忆条目
user_id 用于限定记忆归属,查询内容用于寻找相关信息。
记忆更新
新信息可能与旧记忆重复或冲突,Mem0 可以借助模型判断如何处理。
旧记忆:
用户住在北京。
新信息:
用户表示刚搬到上海。
处理:
更新居住地信息。
模型判断仍可能出错,因此重要信息需要保留来源,并支持查询、纠正和删除。
本例技术组合
Mem0:记忆提取和管理
Ollama:本地运行模型
聊天模型:提取事实、判断更新
Embedding模型:生成向量
PostgreSQL + pgvector:存储和检索向量
课程中的模型准备说明和代码配置不一致。下面统一使用代码中的组合:
聊天模型:qwen3:1.7b
Embedding模型:nomic-embed-text:v1.5
向量维度:768
实际提取效果取决于模型能力,小模型可能无法稳定生成符合要求的结果。
准备本地模型
ollama pull qwen3:1.7b
ollama pull nomic-embed-text:v1.5
确保 Ollama 服务可以访问:
http://localhost:11434
准备数据库
连接已经安装 pgvector 的 PostgreSQL,创建数据库:
CREATE DATABASE mem0_memory;
切换连接到 mem0_memory 后启用扩展:
CREATE EXTENSION IF NOT EXISTS vector;
本例假设数据库连接信息为:
主机:127.0.0.1
端口:5433
数据库:mem0_memory
用户名:pgvector
密码:pgvector
这些是示例配置,应以实际部署为准。
创建 Python 项目
Windows PowerShell:
uv init --no-package hello_mem0
cd hello_mem0
uv venv
.\.venv\Scripts\Activate.ps1
添加依赖:
uv add mem0ai ollama psycopg2-binary
注意包名与导入名不同:
安装包:mem0ai
导入名:mem0
配置并使用 Mem0
以下沿用课程中本地 Memory 的 API 写法,保存为 hello.py。
python
from mem0 import Memory
config = {
"vector_store": {
"provider": "pgvector",
"config": {
"user": "pgvector",
"password": "pgvector",
"host": "127.0.0.1",
"port": 5433,
"dbname": "mem0_memory",
"embedding_model_dims": 768,
},
},
"llm": {
"provider": "ollama",
"config": {
"model": "qwen3:1.7b",
"temperature": 0,
"max_tokens": 2000,
"ollama_base_url": "http://localhost:11434",
},
},
"embedder": {
"provider": "ollama",
"config": {
"model": "nomic-embed-text:v1.5",
"ollama_base_url": "http://localhost:11434",
"embedding_dims": 768,
},
},
}
print("初始化Memory...")
memory = Memory.from_config(config)
messages = [
{
"role": "user",
"content": "Hi, I'm Hollis. I love Coding and Gaming.",
},
{
"role": "assistant",
"content": "Hey Hollis! I'll remember your interests.",
},
]
print("添加记忆...")
add_result = memory.add(
messages,
user_id="Hollis666",
)
print(add_result)
print("查询记忆...")
search_result = memory.search(
"What do you know about me?",
user_id="Hollis666",
)
print(search_result)
三部分配置的区别
"llm": {
"provider": "ollama"
}
负责理解对话、提取记忆和判断记忆变更。
"embedder": {
"provider": "ollama"
}
负责将文本转换成向量。
"vector_store": {
"provider": "pgvector"
}
负责保存向量并进行相似度检索。
LLM:记什么、怎么更新。
Embedder:将内容转换成向量。
Vector Store:保存并查找相关记忆。
添加记忆
result = memory.add(
messages,
user_id="Hollis666",
)
概念流程:
分析对话
↓
提取候选事实
↓
查找相关旧记忆
↓
判断是否新增或更新
↓
保存结果
示意返回:
{
"results": [
{
"id": "memory-001",
"memory": "Name is Hollis",
"event": "ADD"
},
{
"id": "memory-002",
"memory": "Loves Coding and Gaming",
"event": "ADD"
}
]
}
实际条目和措辞由模型处理结果决定,不保证每次完全相同。
检索记忆
results = memory.search(
"What are my interests?",
user_id="Hollis666",
)
示意结果:
{
"results": [
{
"id": "memory-002",
"memory": "Loves Coding and Gaming",
"user_id": "Hollis666",
"score": 0.8
}
]
}
检索结果是相关记忆,不是最终聊天回答。
将记忆加入模型上下文
应用需要读取检索结果,再将记忆加入聊天模型的输入。
python
results = memory.search(
"推荐一些适合我的休闲活动",
user_id="Hollis666",
)
memory_text = "\n".join(
item["memory"]
for item in results.get("results", [])
)
prompt = f"""
请根据用户问题和相关记忆给出建议。
相关记忆:
{memory_text}
用户问题:
推荐一些适合我的休闲活动。
"""
print(prompt)
完整应用流程:
用户提问
↓
Mem0检索相关记忆
↓
记忆 + 当前问题构成Prompt
↓
调用聊天模型生成回答
↓
按需将新的有效信息写入Mem0
用户标识的注意事项
本例使用:
memory.search(
"What do you know about me?",
user_id="Hollis666",
)
课程中遇到的错误:
At least one of 'user_id', 'agent_id', or 'run_id' must be provided.
表示该调用没有按接口要求提供记忆作用域。
不同版本、本地接口与托管服务的过滤参数可能不同,不能直接混用示例。这里保持与 add() 一致,显式传入 user_id。
向量维度必须一致
本例配置:
"embedding_model_dims": 768
"embedding_dims": 768
需要保持:
模型实际输出维度
=
向量存储配置维度
=
已有数据库向量列的维度
否则可能出现:
expected 1536 dimensions, not 768
配置中的数字不会自动把模型向量转换成对应维度。
如果已有表使用1536维,仅修改配置不一定能解决问题,需要迁移到匹配的存储结构,并重新生成向量。
运行程序
uv run hello.py
也可以在已安装依赖的 Python 环境中执行:
python hello.py
pip 用于管理依赖,不能使用 pip hello.py 运行脚本。
重点总结
-
Mem0 管理的是从交互中提取的记忆,不只是完整聊天记录。
-
三个核心模块:
LLM → 提取和更新记忆
Embedder → 生成向量
Vector Store → 保存和检索 -
两个主要操作:
memory.add(messages, user_id="Hollis666")
memory.search("当前问题", user_id="Hollis666") -
保存和检索需要使用正确的用户标识;真实应用应由身份认证确定归属。
-
模型实际向量维度、配置维度和数据库维度必须一致。
-
查询到记忆后,仍需要应用将其加入聊天模型的上下文。
-
整体流程:
对话 → 提取记忆 → 更新存储
↓
新问题 → 检索相关记忆 → 加入Prompt → 生成回答
七. Mem0的实现原理
核心定位
Mem0 是记忆管理框架,负责协调模型和存储组件,完成记忆提取、更新、保存和检索。
应用调用Mem0
↓
协调LLM、Embedding和存储
↓
完成记忆管理
核心组件
| 组件 | 作用 |
|---|---|
| LLM | 提取事实,判断记忆是否需要新增、更新或删除 |
| Embedding模型 | 将记忆内容和查询转换成向量 |
| 向量数据库 | 保存记忆文本、向量和元数据,支持语义检索 |
| 图数据库(可选) | 保存实体关系,支持关联检索 |
| SQLite历史存储 | 在课程所述本地实现中记录记忆变更历史 |
注意:向量化由 Embedding 模型完成,向量数据库负责保存和检索。
这些组件需要相应配置,但不一定都要独立部署,也可以使用本地库或托管服务。
添加记忆:add
memory.add(
messages,
user_id="Hollis666",
)
默认的记忆推断流程可以理解为:
输入对话和用户标识
↓
LLM提取候选事实
↓
Embedding生成向量,检索相关旧记忆
↓
LLM比较新旧信息
├── 新信息 → 新增
├── 信息变化 → 更新
├── 旧信息失效 → 删除
└── 重复或无需处理 → 不变
↓
更新向量存储
↓
记录记忆变更历史
例如:
旧记忆:
用户住在北京。
新对话:
我刚搬到上海。
处理结果:
更新用户当前居住地为上海。
模型不一定每次都判断正确,重要记忆仍需要支持纠正和删除。
图记忆:可选能力
启用图存储后,可以提取并保存实体之间的关系。
原始信息:
爱丽丝的朋友是约翰。
图关系:
爱丽丝 ── 朋友 ──→ 约翰
适合保存:
- 人物关系
- 组织关系
- 项目关联
- 实体之间的联系
未配置图数据库时,仍然可以通过向量记忆完成主要功能。
检索记忆:search
memory.search(
"我住在哪里?",
user_id="Hollis666",
)
基础流程:
当前问题 + 用户标识
↓
Embedding将问题向量化
↓
在对应用户范围内检索相关记忆
↓
筛选并返回结果
如果启用了图记忆,还可以结合实体关系检索。
需要区分:
- 基础向量检索不一定需要先调用 LLM 改写问题。
- 图检索、重排序等能力取决于配置和实现。
- 不能默认认为每次检索都会综合时间、重要性等全部因素。
SQLite历史与向量存储的区别
向量存储:
当前有哪些记忆,哪些与问题相关。
历史存储:
某条记忆何时新增、更新或删除。
例如:
当前记忆:
用户住在上海。
变更历史:
首次添加:用户住在北京。
后续更新:用户搬到上海。
记忆变更历史也不等于完整聊天记录。
检索结果如何参与回答
Mem0 返回的是记忆条目,应用再将它们加入聊天模型的上下文。
用户提问
↓
Mem0检索相关记忆
↓
记忆 + 当前问题
↓
传给聊天模型
↓
生成个性化回答
例如:
相关记忆:
用户不喜欢香菜。
当前问题:
推荐一道晚餐。
模型根据偏好给出建议。
重点总结
-
Mem0 是记忆管理框架,底层数据由配置的存储组件保存。
-
各组件分工:
LLM:提取事实、判断变更。
Embedding:生成向量。
向量数据库:保存和语义检索。
图数据库:保存实体关系,可选。
历史存储:记录记忆变更。 -
add()的核心:提取新事实 → 检索旧记忆 → 比较并决策 → 更新存储
-
search()的核心:问题向量化 → 按用户范围检索 → 返回相关记忆
-
图检索和重排序属于可选能力,不是所有配置都会启用。
-
是否完全本地运行取决于模型和存储的部署方式;使用远程模型时,相关数据仍会发送到对应服务。
八. Spring AI Alibaba接入Mem0做长期记忆
整体思路
Java 应用通过 HTTP 调用 Mem0 REST API Server,由 Python 服务执行记忆提取、更新和检索。
Spring AI ChatClient
↓
Mem0ChatMemoryAdvisor
↓
Mem0MemoryStore
↓
Mem0ServiceClient
↓ HTTP
Mem0 REST API Server
↓
LLM + Embedding + 向量数据库
本节按课程提供的服务和 1.1.0.0-M5 扩展版本整理,其他版本的配置与接口可能不同。
Mem0 REST API Server
REST 服务将 Mem0 的能力暴露为 HTTP 接口,Java 无需直接调用 Python 代码。
主要能力:
- 添加记忆
- 检索相关记忆
- 获取已有记忆
- 更新和删除记忆
- 查看记忆变更历史
具体接口、参数和请求方法以部署服务的 Swagger 文档为准。
课程服务的文档地址:
http://localhost:8888/docs
部署课程中的服务
课程使用的是修改过的部署文件,需要先取得并解压对应项目。
Windows PowerShell:
Copy-Item .env.example .env
修改 .env:
OPENAI_API_KEY=你的模型服务APIKey
启动服务:
docker compose up -d
课程部署同时启动 Mem0 服务及 PostgreSQL、Neo4j 等依赖,实际组成由 docker-compose.yml 决定。
OPENAI_API_KEY 是该部署的配置变量名,不代表只能使用 OpenAI 官方服务,仍需与服务端配置的地址和模型对应。
初始化服务
课程中的定制服务需要先通过 /configure 初始化 Memory,才能执行记忆操作。
启动REST服务
↓
调用/configure
↓
创建Memory及相关组件
↓
执行记忆增删改查
没有初始化时,可能出现:
AttributeError: 'NoneType' object has no attribute 'add'
初始化请求的参数以该服务 /docs 中的定义为准,不能直接假设所有 Mem0 服务都需要相同的初始化步骤。
验证记忆写入
PowerShell:
$body = @{
messages = @(
@{
role = "user"
content = "Hi, I am Hollis. I love Coding and Gaming."
}
)
user_id = "Hollis678"
} | ConvertTo-Json -Depth 10
Invoke-RestMethod `
-Uri "http://localhost:8888/memories" `
-Method Post `
-ContentType "application/json; charset=utf-8" `
-Body ([System.Text.Encoding]::UTF8.GetBytes($body))
Mem0 分析对话,提取有价值的信息并保存。
获取用户记忆
Invoke-RestMethod `
-Uri "http://localhost:8888/memories?user_id=Hollis678" `
-Method Get
这里是获取用户已有记忆,不等于根据问题进行语义搜索。语义检索应使用该版本服务提供的搜索接口。
引入长期记忆依赖
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-long</artifactId>
<version>1.1.0.0-M5</version>
</dependency>
课程中相关组件的职责:
| 组件 | 作用 |
|---|---|
Mem0ServiceClient |
封装 HTTP 请求 |
Mem0MemoryStore |
适配 Spring AI 的存储接口 |
Mem0ChatMemoryAdvisor |
调用前读取记忆,调用后写入交互 |
| 自动配置类 | 创建相关 Bean,并配置 Mem0 服务 |
注意与项目中已有的 Spring AI、Spring AI Alibaba 版本保持兼容。
最小配置
如果服务端已经配置好模型和数据库,Java 端可以使用课程中的最小配置:
spring:
ai:
alibaba:
mem0:
client:
base-url: http://127.0.0.1:8888
timeout-seconds: 120
server:
version: v1.0.0
其中 version 是该集成传给服务端的配置项,不是 Maven 依赖版本。
自定义服务端配置
也可以由 Java 配置模型、向量库和可选的图存储,然后通过 /configure 传给服务端。
以下沿用课程中的配置字段:
spring:
ai:
alibaba:
mem0:
client:
base-url: http://127.0.0.1:8888
timeout-seconds: 120
server:
version: v1.0.0
vector-store:
provider: pgvector
config:
host: postgres
port: 5432
dbname: postgres
user: postgres
password: ${MEM0_DB_PASSWORD}
collection-name: memories
graph-store:
provider: neo4j
config:
url: bolt://neo4j:7687
username: neo4j
password: ${MEM0_GRAPH_PASSWORD}
llm:
provider: openai
config:
api-key: ${MEM0_LLM_API_KEY}
model: ${MEM0_LLM_MODEL}
temperature: 0.2
openai-base-url: ${MEM0_LLM_BASE_URL}
embedder:
provider: openai
config:
api-key: ${MEM0_EMBEDDING_API_KEY}
model: ${MEM0_EMBEDDING_MODEL}
openai-base-url: ${MEM0_EMBEDDING_BASE_URL}
需要区分两个地址视角:
client.base-url:
Java应用访问Mem0服务的地址。
server中的数据库和模型地址:
Mem0服务访问这些依赖时使用的地址。
例如,postgres、neo4j 通常是 Docker 网络内的服务名。
另外,Embedding 模型的实际输出维度需要与向量存储一致。
给 ChatClient 添加长期记忆
import com.alibaba.cloud.ai.memory.mem0.advisor.Mem0ChatMemoryAdvisor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import static com.alibaba.cloud.ai.memory.mem0.advisor
.Mem0ChatMemoryAdvisor.USER_ID;
@RestController
@RequestMapping("/longTermMemory")
public class LongTermMemoryController {
private final ChatClient chatClient;
public LongTermMemoryController(
ChatModel chatModel,
@Qualifier("mem0MemoryStore") VectorStore memoryStore) {
Mem0ChatMemoryAdvisor advisor =
Mem0ChatMemoryAdvisor.builder(memoryStore)
.build();
this.chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(advisor)
.build();
}
@GetMapping("/chat")
public String chat(
@RequestParam String message,
@RequestParam String userId) {
return chatClient.prompt(message)
.advisors(spec ->
spec.params(Map.of(USER_ID, userId))
)
.call()
.content();
}
}
这里的三个关键步骤:
创建Mem0ChatMemoryAdvisor
↓
注册到ChatClient
↓
每次调用传入USER_ID
如果项目存在多个 ChatModel,需要再通过 @Qualifier 指定聊天模型。
Advisor 的执行流程
用户问题 + userId
↓
Advisor调用Mem0检索相关记忆
↓
将记忆加入当前Prompt
↓
调用聊天模型
↓
获得回答
↓
Advisor将本次交互提交给Mem0
↓
Mem0提取并更新记忆
长期记忆和当前上下文的关系:
Mem0保存的记忆
↓
本次检索出的相关部分
↓
加入ChatClient请求
↓
模型才能使用
调用示例
第一次告诉模型:
GET /longTermMemory/chat?userId=hollis666&message=我元旦去了三亚
后续再次使用同一个用户标识:
GET /longTermMemory/chat?userId=hollis666&message=我元旦去哪里了?
模型收到的上下文可以理解为:
用户问题:
我元旦去哪里了?
相关长期记忆:
用户元旦去了三亚。
Java 应用重启后,只要 Mem0 后端数据仍保留,就可以继续检索。
但"重启后还能读取"只能证明数据持久化;长期记忆的关键还在于跨会话提取和按需复用。
用户记忆不等于确定偏好
如果只保存:
用户元旦去过三亚。
不能直接断言:
用户冬天总是喜欢去三亚。
一次旅行经历是事实,不一定代表长期偏好。模型应区分记录中的事实与基于事实做出的推测。
Mem0 不可用对启动的影响
按课程中的自动配置实现,启动时会创建客户端并调用 /configure。如果 Mem0 不可用,可能导致初始化失败。
Spring Boot启动
↓
创建Mem0相关Bean
↓
调用/configure
↓
连接失败
↓
应用启动受到影响
仅在 Controller 上添加 @Lazy,不能阻止其他自动配置 Bean 提前初始化。
延迟初始化方案
课程方案分为三步:
- 排除原有自动配置。
- 自定义懒加载配置,创建所需 Bean。
- 将使用这些 Bean 的 Controller 设为懒加载。
排除自动配置:
@SpringBootApplication(exclude = {
com.alibaba.cloud.ai.autoconfigure.memory
.Mem0ChatMemoryAutoConfiguration.class
})
public class SpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(
SpringAiApplication.class,
args
);
}
}
自定义懒加载配置,相关类型来自课程版本的 Mem0 扩展:
@Lazy
@Configuration
@EnableConfigurationProperties(Mem0ChatMemoryProperties.class)
public class Mem0LazyConfiguration {
@Bean
public Mem0ServiceClient mem0ServiceClient(
Mem0ChatMemoryProperties properties,
ResourceLoader resourceLoader) {
Mem0ServiceClient client = new Mem0ServiceClient(
properties.getClient(),
properties.getServer(),
resourceLoader
);
client.configure(properties.getServer());
return client;
}
@Bean
public VectorStore mem0MemoryStore(
Mem0ServiceClient mem0ServiceClient) {
return Mem0MemoryStore.builder(mem0ServiceClient)
.build();
}
}
Controller 添加:
@Lazy
@RestController
@RequestMapping("/longTermMemory")
public class LongTermMemoryController {
// 使用前面的Controller实现
}
执行效果:
应用启动
↓
暂不初始化Mem0相关Bean
↓
首次需要长期记忆功能
↓
初始化客户端和存储
↓
调用Mem0服务
懒加载只是推迟初始化;首次访问时服务仍不可用,调用依然会失败。如果其他非懒加载 Bean 提前依赖这些组件,也可能触发初始化。
重点总结
-
Java 通过 Mem0 REST API Server 使用 Python 侧的记忆管理能力。
-
核心调用链:
ChatClient
↓
Mem0ChatMemoryAdvisor
↓
Mem0MemoryStore
↓
Mem0ServiceClient
↓
Mem0 REST API -
Advisor 在调用前检索记忆、补充上下文,调用后提交交互供 Mem0 提取和更新。
-
USER_ID用于关联用户记忆,实际业务中应从登录身份取得。 -
课程中的
/configure、配置字段和自动初始化行为与具体服务、扩展版本有关。 -
Java 访问 Mem0 的地址,与 Mem0 访问数据库和模型的地址,需要分别配置。
-
延迟初始化需要同时处理自动配置、相关 Bean 和使用方;仅给 Controller 加
@Lazy不一定有效。 -
最终流程:
读取相关记忆 → 加入Prompt → 生成回答 → 提交新交互 → 更新记忆