[控场AI]
· 6 分钟阅读· 3,373 字

LLM Agent 记忆系统全解析:从 Buffer 到语义记忆

LLM Agent 记忆系统全解析:从 Buffer 到语义记忆

Agent 记忆需应用层存储并回传历史,六种策略在成本与信息保留间权衡。

LLM 本身无状态,每次调用都需应用层重新传入历史问答,Agent 的记忆本质就是存储 Query-Response 组合并按需回传。文章梳理了六种主流记忆模式:Buffer 全量保存但成本随轮次线性增长;Window 只保留首尾固定条数;Token 按预算裁剪但可能截断语义;Summary 在删除旧消息前用便宜模型压缩成摘要;Long Term 由 Agent 决定存什么并跨会话共享;Semantic 借助 Embedding 存入向量库按相似度召回。工程上需区分 Checkpointer(管单次对话)与 Store(管跨会话事实),并确保 user_id 来自应用上下文。选型没有通吃方案,实际系统常组合使用,且记忆并非越多越好。

为什么 Agent 需要记忆?

构建 AI Agent 的终极目标,是让它成为一个能协调 LLM、调用工具并拥有记忆的编排器。LLM 本身每次调用都是“失忆”状态——模型不会记住上一轮你问了什么,它只是根据当前传入的上下文生成回答。要让对话连贯,应用层必须把历史问答重新发给模型。

记忆的本质,就是存储“我之前问了什么、它答了什么”这样的 Query-Response 组合,并在后续调用中按需回传。但这里立刻冒出一个工程问题:对话可以无限增长,我们不可能把所有内容都塞进上下文。于是产生了不同的记忆策略,各自在成本、上下文长度和信息保留之间做权衡。

对话记忆示意图

上下文窗口(Context Window) 指一次 LLM 调用中模型能够接收的 Token 总量上限,包括系统提示、历史对话、工具返回结果和当前用户输入。不同模型的窗口大小差异很大,从早期 4K 到如今常见的 128K 甚至更大。窗口越大,能塞进的历史越多,但注意力和成本也随之上升。更重要的是,即使窗口足够大,模型对长上下文中部信息的召回能力也会下降,这一现象常被称为“中间迷失”(Lost in the Middle)。因此,记忆策略不仅是在“放不放得下”的层面做取舍,还要考虑“放了模型是否用得上”。Agent 设计记忆时,需要把窗口容量视为一种有限预算,而非可以无限透支的免费资源。

六种主流记忆模式逐一拆解

原文将记忆方案归纳为六类:Buffer、Window、Token、Summary、Long Term 和 Semantic。下面结合 LangChain 的实现细节展开。

Buffer Memory:全量保存,简单但昂贵

Buffer 记忆对每次聊天存储完整对话,可以选择放在内存(RAM)或持久化存储(如 SQLite3、PostgreSQL)。每次调用都把全部历史发给 LLM,新对话则分配一个新的标识符。

代码层面使用 InMemorySaver() 把状态存在 RAM 中,容量受限于内存大小。问题也很直接:全量历史会持续消耗 Token 并逼近上下文上限,成本和延迟随对话轮次线性增长。

Token 是 LLM 处理文本的基本单位,可以粗略理解为词或子词片段。英文中一个 Token 大约对应 0.75 个单词,中文则因分词方式不同,一个汉字可能对应一到两个 Token。计费、上下文长度限制和裁剪预算都以 Token 为单位,因此“消息条数”和“Token 数量”并不等价——一条很长的消息可能比十条短消息更费预算。这也是 Token Memory 与 Window Memory 的核心差异:前者按实际消耗计量,后者按条数计量。开发者在估算成本时,应使用对应模型的分词器统计 Token,而不是简单按字符数或消息数折算。

Window Memory:只保留首尾

Window 记忆只保留固定数量的消息,典型做法是“第一条消息 + 最近 N 条”。LangChain 提供了一种中间件机制,请求在到达模型之前必须先穿过这层中间件,开发者可以在其中配置消息剔除逻辑,只保留首尾。

这种策略适合“最近对话比早期对话更重要”的场景,代价是中间部分被彻底遗忘。

Token Memory:按预算裁剪

Token 记忆与 Window 类似,但以 Token 数量而非消息条数作为预算上限,例如设定 2000 Token,超出部分不再保留。Token 的定义因模型和框架而异。

这里有个容易被忽略的细节:裁剪可能把一句话从中间截断,导致语义不完整。原文提到,即使正好用满 1000 Token,为了拿到完整语义,往往需要从句子开头重新截取。trim_messages() 函数负责处理这类边界。该方案的硬伤是:任何超出预算的旧内容会完全丢失。

Summary Memory:用摘要换记忆

Summary 记忆的思路是,在旧消息被删除之前,先把它们压缩成一段摘要存下来,这样即使原始数据没了,关键事实仍在。摘要用便宜的小模型生成以控制成本,对应的函数是 SummarizationMiddleware()。

它多了一次 AI 调用的开销,但换来的是长期事实的保留,适合需要跨多轮记住关键信息的助手场景。

Long Term Memory 与 Semantic Memory

长期记忆由 Agent 自己决定“存什么、存哪”,事实保存在 Store 中,可跨用户的多次对话共享。语义记忆则把整段对话压缩成一个点,借助 Embedding 存入向量数据库,检索时按语义相似度召回——本质上就是 RAG 的思路。区别在于,语义记忆需要额外的 Embedding 模型。

Embedding 是把文本映射为高维向量的过程,语义相近的文本在向量空间中距离更近。向量数据库(如 FAISS、Pinecone、pgvector)通过近似最近邻搜索,快速找出与查询向量最相似的若干条记录。在 Semantic Memory 中,每段对话被 Embedding 后存入向量库,检索时用当前问题生成查询向量并召回相关历史。这条链路与 RAG(检索增强生成)完全一致,区别只在于知识来源是“历史对话”而非“外部文档”。引入 Embedding 模型意味着额外的推理成本和维度管理问题,但换来的是按语义而非按时间或条数召回记忆的能力。

工程实现中的关键概念

理解了策略,还要理清 LangChain 生态里的几个基础构件,否则容易混淆。

  • Checkpointer:每轮对话后保存状态,InMemorySaver 存内存,SqliteSaver 或数据库用于持久化。
  • thread_id:相当于对话的“档案编号”,相同 id 延续同一段对话,新 id 开启全新对话。
  • Middleware(@before_model):在每次 AI 调用前运行,可编辑消息列表。
  • Store 与 Checkpointer 的区别:Checkpointer 管单次对话,Store 管跨对话的用户长期事实。
  • user_id 的安全来源:记忆工具中的 user_id 应来自应用上下文而非 AI 生成,防止用户越权读取他人记忆。

这些概念共同构成 Agent 记忆的骨架:Checkpointer 保证单会话连续性,Store 提供跨会话个性化,Middleware 负责在调用前做上下文裁剪。

LangGraph 是 LangChain 生态中用于构建有状态 Agent 的编排框架,它把 Agent 的执行流程建模为图(Graph),节点代表步骤、边代表流转。Checkpointer 和 Store 都是 LangGraph 的状态管理组件:Checkpointer 在每个节点执行后对图状态做快照,从而支持断点续跑和会话恢复;Store 则是独立于图的键值存储,用于跨会话共享数据。理解这一分层有助于判断状态该放在哪里——与单次对话流程强相关的放 Checkpointer,需要跨对话保留的用户画像或事实放 Store。

如何选择记忆方案

没有一种记忆策略通吃所有场景。短对话客服可以用 Buffer 或 Window,长文档问答倾向 Token 预算加 Summary 兜底,需要跨会话记住用户偏好的产品则要引入 Long Term 与 Semantic 记忆。实际系统往往是多策略组合:近期消息走 Window,远期事实走 Summary 或向量库。

值得提醒的是,记忆越多不等于效果越好——无关的历史会稀释模型注意力,反而降低回答质量。设计记忆系统的核心,是判断“哪些信息在未来对话中真正有价值”,并让这部分信息以最低的 Token 成本存活下来。

原文由开发者在 Dev.to 以笔记形式记录,包含部分泰米尔语注释和代码截图,本文在其基础上做了结构化梳理与解读。

#LLM Agent 记忆#Agent Memory#LangChain 记忆系统#Buffer Memory#Summary Memory#语义记忆#上下文管理
分享:

相关推荐