智能体系统中的记忆机制

Memory in agent systems

记忆是 AI 智能体架构的核心组件,负责将上下文从一次交互延续到下一次交互,支撑多轮连贯对话与自适应行为。若缺乏记忆机制,绝大多数智能体都只能停留在无状态的单次响应模式。本课介绍短期记忆与长期记忆的划分、基于 LangGraph 的具体实现模式,以及 Mem0 等独立记忆服务层,帮助你构建能够跨轮次、跨会话保持上下文的智能体。

记忆在推理中的作用

记忆机制使智能体能够在对话或任务推进过程中持续传递信息、回溯过往交互、引用先前确认的事实,并完成多步复杂推理(例如用户在早期提问中说明了个人偏好,智能体即可在后续回答中直接沿用)。其核心作用包括:

  • 保持对话连续性:使多轮回答始终围绕当前主题展开,并与前序内容保持逻辑一致。
  • 学习与适应:记录用户的纠错反馈或补充信息,减少重复犯错,逐步适应用户需求(对话期内的这种适应性调整也可视为上下文学习 In-Context Learning 的体现)。
  • 支撑多步推理:保存中间计算结果与阶段性决策,供后续步骤读取使用。
  • 实现个性化交互:记住用户姓名、关注领域及历史问题等细节,使后续交互更贴合具体用户的语境。

简言之,记忆机制将原本无状态的 LLM 转化为能够持续交互的有状态智能体,提升了交互效率与自然度。

短期记忆与长期记忆

短期记忆(工作记忆):当前会话或任务期间维护的工作上下文,通常表现为在决策循环中持续传给 LLM 的最近几轮用户与助手对话消息及最新状态。短期记忆以单个会话线程(thread)为边界,归属于特定对话或会话(本课实战即实现该机制)。

图 1 · 会话状态与持久化边界
已认证用户
租户、用户与会话;服务端生成 thread_id
短期对话状态
当前消息、工具结果和引用;控制历史长度
InMemorySaver
进程内保存;重启后状态消失
PostgresSaver
数据库检查点;初始化、权限和恢复
知识索引
Qdrant 保存知识分块;与对话检查点分别管理
撤权与删除
历史、摘要和索引同步处理;授权不明时停止返回资料
对话状态可以使用进程内存或 PostgreSQL 持久化。知识索引与会话历史分别维护权限和删除。

长期记忆跨会话保存信息,当前对话结束后仍可读取,包括用户资料、历史对话事实或积累的领域知识。它可以跨不同对话线程使用;需要时也可以按系统设计与访问权限在不同用户之间共享。

两者的主要区别:

  • 作用范围:短期记忆仅限于当前活动会话与上下文;长期记忆通过持久化存储跨会话保留。
  • 存储方式:短期记忆通常是传给模型的近期消息列表或内存状态变量;长期记忆则存储在数据库、文件系统或向量索引中,生命周期独立于单次会话。
  • 读取与更新方式:短期记忆通常随每轮交互追加消息或更新状态,并直接拼入每轮提示词(也可按需过滤无关状态);长期记忆则需要按需检索读取,可在智能体识别到重要信息时主动写入,也可在会话结束后批量提取关键事实入库。
  • 容量限制:短期记忆受限于模型的上下文窗口上限,历史过长容易超限或拉低推理性能;长期记忆可借助外部大容量存储扩展,工程难点在于如何在需要时精准检索出应注入当前上下文的信息。

短期记忆提供当前聊天历史,长期记忆提供跨会话知识连续性。下面以 LangGraph 为例介绍实现。

实现短期记忆

实现短期记忆最直接的方式是维护对话历史列表:每次收到新用户消息时,将其与此前若干轮的用户及助手消息一同传给 LLM。在 LangGraph 中,短期历史保存在图状态(State)的消息列表中(每条消息包含角色与内容),并通过检查点保存器(Checkpointer)在多次调用之间维护状态。开发调试阶段可使用内存保存器,生产环境则通常使用数据库保存器。以下示例使用进程内内存保存器:

Python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph

checkpointer = InMemorySaver()  # 在内存中暂存状态
workflow = StateGraph(...)
graph = workflow.compile(checkpointer=checkpointer)
# 图将按 thread 保存并加载对话状态

上述代码中的 StateGraph(...) 代表已定义好状态结构与节点的应用图。InMemorySaver 将状态暂存在当前进程内存中;真实生产部署则通常采用 PostgresSaver 等持久化保存器,以便在服务重启或用户稍后重连时恢复短期对话历史。调用图时,只需在配置参数中传入 thread_id,Checkpointer 就会按该线程 ID 加载或更新对应的会话状态,从而隔离不同用户或不同会话的历史记录。

每次运行时,应用将最新用户问题和助手回答加入状态的消息列表,checkpointer 按 thread ID 保存。下一轮用户输入到来后,加载历史并提供给 LLM,让模型知道此前说过什么。

然而,无限制地向列表追加消息会带来问题:模型上下文窗口有限,过长的提示词不仅会增加延迟与 token 费用,还可能导致模型忽略关键信息。因此需要配合以下策略管理短期记忆的保留、裁剪与压缩:

  • 截断旧消息(Truncation):仅保留最近 NNN 条消息,超出上限时直接丢弃更早的记录。实现最简单,但若早期关键信息未提前转存,截断后就会丢失。
  • 摘要压缩(Summarization):用简短摘要替代冗长的早期对话(例如累积满 50 条消息后生成一段摘要,后续仅携带“摘要 + 最近几条原始消息”继续对话),在压缩体积的同时保留核心脉络。
  • 相关性过滤(Filtering):借助启发式规则或模型相关性评分,省略或压缩与当前查询关联度较低的历史消息。
  • 归档后重置(Save & Reset):当一个子话题结束时,先提取关键事实写入长期记忆,随后清空短期状态以开启新话题。

具体策略需结合业务场景选择:客服系统可在工单解决后将旧对话压缩为摘要;而长篇互动叙事智能体则可能需要保留完整的叙事上下文直至故事结束。在 LangGraph 中配置好 messages 状态与 Checkpointer 后,底层状态的存取与追加均由框架接管,开发者只需重点把控向消息列表中注入什么内容(如系统提示词)以及何时触发摘要或裁剪。

长期记忆与检索模式

长期记忆为智能体提供跨会话知识库,从用户事实到大量文档或历史事件都可以保存。常见实现是数据库或索引,按需要检索相关内容,再加入提示词。

长期记忆为智能体提供跨会话的持久化知识存储(通常基于支持读写与检索的文档数据库或向量索引,以 JSON 文档或文本形式保存用户事实、历史事件或领域资料)。如何将长期记忆接入推理循环,主要有以下几种设计模式:

生成前预检索(Retrieve Before Generation):在每轮新问题或对话开始时,先以用户原始消息(或从中提取的关键词)查询长期存储,将匹配到的记忆作为 system 内容或补充上下文注入提示词。例如当用户问“我叫什么名字?”时,系统检索 name 或“名字是”相关记录,若命中此前保存的“用户名为 Aurimas”,即可将该事实放入上下文供模型准确作答。

按需工具调用(On-Demand Tool Call):让智能体在推理循环中自主判断何时需要查询长期记忆(与前文将向量库封装为工具的做法一致)。在 LangGraph 中可挂载 search_memory 工具节点或子图,并在提示词中说明该工具的适用时机,供模型在发现当前上下文不足时主动调用。

回答后写入记忆(Write After Response):在生成回答或完成任务后,从交互中提炼新事实写入长期存储,可分为同步写入与异步写入。例如当用户说“我叫 Aurimas,请记住”时,同步写入会在返回响应前立即落库(信息即刻生效,但会增加当次响应耗时);异步写入则交由后台任务扫描对话日志提取事实(前端响应更快,但新信息存在短暂的生效延迟)。

定期概括与整理(Consolidation):随着长期存储不断膨胀,需定期合并与提炼记录(例如将 100 条零散的用户行为流水汇总为高层画像,或在每日夜间批处理中将当天的聊天记录提炼为关键事实条目)。

从认知功能角度,长期记忆与短期记忆还可进一步细分:

图 2 · Agent的多种记忆与工作上下文
情景和语义信息可进入可检索的记忆,程序性记忆提供提示与工具;检索结果与执行历史共同组成工作记忆。

长期记忆:

  • 情景记忆(Episodic):保存过去交互与智能体行动。操作完成后,应用写入持久存储,需要时再读取。例如,通过向量数据库保存交互的语义表示。
  • 语义记忆(Semantic):智能体可用的外部信息,以及关于自身的知识。可以理解为 RAG 中的知识上下文,包括内部资料,或从互联网资料中选择的相关依据。
  • 程序性记忆(Procedural):System Prompt 结构、可用工具、安全限制等系统性信息,通常保存在 Git、提示词注册中心或工具注册中心。

短期记忆:

  • 任务需要时,应用从长期记忆取得信息,暂存在当前工作环境。
  • 当前本地信息与检索到的长期信息共同组成短期或工作记忆,再组织为提示词交给 LLM,模型由此决定后续行动。

Mem0 与独立记忆服务层

除了在应用层通过 LangGraph 状态自行管理记忆外,另一种思路是将记忆抽离为独立的服务层。Mem0 是一个面向 AI 智能体的开源模块化记忆层,将长期记忆的存储、检索、压缩与更新封装为标准接口(后续课程还将进一步介绍)。无论底层采用 LangGraph、LangChain 还是直接调用模型 API,应用都可以通过 Mem0 存取记忆。其主要特点包括:

它将长期记忆封装为可接入不同 LLM 应用的服务。无论采用 LangGraph、LangChain 还是直接 API 调用,都可以通过 Mem0 保存和读取记忆,并使用自动压缩、语义关联和个性化上下文管理等能力。Mem0 将自身描述为能够持续改进的记忆层,后续课程还将进一步介绍。

主要特点包括:

  • 框架无关的轻量集成:可作为中间层无缝对接 OpenAI API、LangGraph、CrewAI 等,负责在调用前补充上下文或根据模型输出更新记忆,无需大幅重构现有智能体代码。
  • 自动压缩与 Token 效率:自动将冗长的原始对话历史提炼为紧凑摘要或向量表示,在保留核心事实的同时减少提示词 token 消耗(Mem0 官方报告称在部分场景下最高可减少 80% 的提示词 token,具体节省比例取决于实际业务数据,需通过实验测量)。
  • 动态上下文更新:跨多次交互关联同一实体,优先召回近期及高相关度事实(如用户最新补充或更正的信息),并衰减过期内容权重。
  • 融合多类型记忆:同时管理保存客观事实与用户画像的语义记忆,以及记录交互轨迹的情景记忆。
  • 服务层解耦抽象:提供自托管与云托管模式,应用只需调用 API 获取上下文或写入新信息,底层的向量编码、索引维护与摘要压缩均由 Mem0 接管(在简化开发的同时,也相应减少了对底层存储细节的直接控制)。

除 Mem0 外,业界还有 MemoryBank、Ret-LLM、HippoRAG 等项目在探索融合遗忘曲线或受生物神经系统启发的长期记忆机制。

持久保存对话与访问权限

InMemorySaver 保存的状态会随进程退出而消失;要在服务重启后恢复会话状态,需使用官方 PostgresSaver(首次初始化时需调用 .setup() 建表,生产环境后续变更则按数据库迁移流程管理)。注意,thread_id 必须由服务端根据已认证的租户、用户身份与会话标识拼接生成——客户端仅知道某个 thread_id 字符串并不代表拥有该会话的读取权限。

Python
import os
from langgraph.checkpoint.postgres import PostgresSaver

# workflow 为已定义状态、节点与边的 StateGraph。
with PostgresSaver.from_conn_string(os.environ["CHECKPOINT_DATABASE_URL"]) as checkpointer:
    checkpointer.setup()  # 首次初始化执行;已有部署按迁移流程管理。
    graph = workflow.compile(checkpointer=checkpointer)
    graph.invoke({"messages": [{"role": "user", "content": "继续保修问题"}]},
                 {"configurable": {"thread_id": "demo-tenant:demo-user:demo-session"}})

在企业知识助手架构中,PostgreSQL 检查点负责保存对话状态,Qdrant 负责保存可检索的知识分块,两套存储必须各自独立维护删除与访问权限:一旦某份飞书资料的访问权限被撤回或资料被删除,不仅要从 Qdrant 索引中屏蔽,此前已经进入对话历史记录或对话摘要的相关内容也必须同步清理。若采用自托管 Mem0,需显式配置抽取模型、embedding 模型与底层存储;若评估云托管模式,则需额外审查对话内容外发传输与留存的合规策略。

练习:对比进程重启后 InMemorySaver 与 PostgresSaver 的会话恢复结果;随后撤回一份已在历史会话中被引用过的飞书资料权限,检查历史消息与摘要是否已同步清理。

搜索课程

正在加载课程目录…