
最近在做 LLM 应用落地时最常被问到的一个能力不是“能不能调用工具”也不是“指令遵循得怎么样”而是“它能不能记住我上次说过的话”。这个问题听起来简单真正做起来却非常棘手。聊天记录一长上下文窗口很快就撑不住了超过窗口限制之后前面的信息就像被剪断的录像带一样再也找不回来。这个问题的本质是 LLM 缺少长期记忆。近期在 Hacker News 上看到有人分享 Long Term Memory for LLM 的架构实验结合自己的一些项目实践我整理了一篇系统性的实操笔记。这篇文章会先讲清楚 LLM 长期记忆的核心概念和架构难点再拆解常见的记忆设计模式最后用 Python 实现一个可运行的“外部记忆层”把“记忆”从对话框里搬出来放到可持久化、可检索的存储中。本文适合正在做 LLM 应用开发、Agent 搭建、知识库问答的开发者也适合刚接触大模型应用、想理解“记忆机制”到底是怎么回事的初学者。读完你会掌握LLM 长期记忆的核心问题与边界短期记忆、长期记忆、外部记忆的区别四种主流记忆架构设计模式一个基于向量检索的长期记忆层完整实现生产环境中记忆模块的排错与最佳实践1. 背景与核心概念1.1 为什么 LLM 需要长期记忆先来看一个最常见的场景。你正在和 AI 助手讨论一个项目方案前面已经确定了技术栈、模块划分、数据库选型甚至约定了命名规范。聊了 20 轮之后你随口问一句“那刚才说的用户表用软删除还是物理删除”模型却回答得模棱两可因为它已经记不清前面提到的数据库设计细节了。这不是模型“变笨”了而是模型的上下文窗口是有限的。目前主流的大模型上下文窗口虽然已经扩展到几十万 token但依然存在两个制约成本问题把整段历史对话全部塞进每次请求里token 消耗会随对话轮数线性增长长对话成本非常高。注意力衰减问题即便窗口放得下模型对中间部分内容的注意力也会明显下降历史信息容易被“稀释”。所以仅靠“把历史都拼进 prompt”不是一个可持续的解决方案。LLM 需要一种机制把重要的历史信息沉淀下来在需要时再快速、准确地取回这就是长期记忆要解决的问题。1.2 什么是长期记忆这里先做几个概念区分很多新手容易混在一起。短期记忆Short-term Memory指当前会话中模型可以直接“看到”的信息也就是上下文窗口内的内容。它的特点是读取快、无需额外处理但受限于窗口长度会话结束后往往就丢失了。长期记忆Long-term Memory指跨会话、跨时间维度的信息留存。它通常不直接放进 prompt而是存储在外部介质中需要时通过检索机制取回相关片段再注入当前上下文。外部记忆External Memory从架构角度看长期记忆往往以外部存储的形式存在可以是向量数据库、关系型数据库、Redis、文件系统甚至是一张结构化的 JSON 表。用一个比喻来理解短期记忆是办公桌上的便签手边就能看到但桌面空间有限长期记忆是资料室的档案柜容量大、可以持久保存但需要一套编码和检索系统才能快速找到有用的文件。1.3 长期记忆的难点在哪里长期记忆看起来只是“多存一份数据再查回来”实际上难点不少记忆的取舍不是所有对话都值得记如何区分“重要信息”和“一次性寒暄”记忆的结构化对话是自然语言但很多记忆其实是结构化的例如用户偏好、项目约束、实体关系怎么统一表达记忆的时效性一条记忆可能有时效比如“本周三的会议待办”过期了就不应该再被检索出来。检索的准确性从大量历史记忆中找到当前问题真正需要的那几条对检索策略要求很高。记忆的更新与遗忘用户修正了之前说过的话旧记忆如何失效需要一套写覆盖或冲突消解机制。理解这些难点之后再看各种架构实验思路就会清晰很多所有长期记忆方案本质上都是在“存储”和“检索”两端做设计取舍。2. LLM 记忆架构的常见设计模式近一年来社区里出现了很多长期记忆相关的实验方案比如 MemGPT、Generative Agents、Letta、Mem0以及各类知识库问答系统。从架构上归纳主流设计模式大致有四类。2.1 纯上下文扩展模式这是最朴素的方式也就是把所有历史记录拼进 prompt。系统提示词 最近对话历史 当前用户问题优点是不需要额外模块实现最简单。缺点是成本随对话长度线性上升超过窗口后必须截断截断策略很难做到“刚好去掉无关内容”超长上下文会稀释模型对关键信息的注意力适合短会话工具、一次性问答、模型能力演示。2.2 摘要压缩模式每次对话达到一定长度后用一个摘要模型把历史对话压缩成几句话然后用压缩后的摘要替代原始历史。[摘要] 用户正在开发一个Python项目确定了FastAPIReact技术栈... [最近对话] ...优点是可以保留较长时间跨度内的核心信息比直接截断优雅。缺点是摘要过程会丢失细节且重复摘要会产生“摘要的摘要”信息逐层衰减。适合需要跨轮次延续信息、但细节要求不高的场景。2.3 向量检索记忆模式这是目前最主流的长期记忆方案。核心思路是把记忆文本通过 embedding 模型转为向量。向量存入向量数据库。用户提问时把问题也转为向量在向量库中做相似度检索。将检索结果注入 prompt作为“记忆上下文”。向量检索 原始用户问题 → prompt优点是信息密度高、可扩展性强、支持跨会话检索是知识库问答和 Agent 长期记忆的主流基础方案。缺点是需要额外的 embedding 模型和向量存储组件检索质量直接受 embedding 模型影响。2.4 混合分层记忆模式更复杂的架构会把上面几种模式组合起来形成分层记忆。第一层最近几轮对话直接放窗口。第二层稍早的对话做摘要压缩。第三层历史中需要精确留存的事实写入记忆库需要时检索。第四层长期稳定的用户画像、偏好单独存储并定期更新。这种分层架构是目前社区实验的主要方向。Hacker News 上关于 LLM long term memory architecture 的讨论大多数也是围绕“如何组织这四层信息”展开的。分层的好处是兼顾成本、精度和时效代价是系统复杂度明显上升。3. 记忆架构关键组件拆解不管采用哪种模式一个可落地的长期记忆层通常包含四个关键组件输入处理、记忆存储、检索模块、写入策略。3.1 输入处理组件输入处理负责决定“哪些信息值得进入记忆系统”。原始对话往往是口语化的直接存储会浪费存储空间也会降低后续检索质量。常见做法先做对话清洗去除无意义的口头语、重复表达。再过滤敏感信息和无效内容。对长对话做信息抽取只保留关键实体、偏好、决策结论。可以调用一次 LLM 对记忆内容做规范化重写。3.2 记忆存储组件存储层决定了记忆的容量和查询方式。目前有以下几种选择存储方案优点缺点适用场景关系型数据库事务可靠结构清晰不适合语义检索用户画像、结构化配置Redis读速快支持过期无语义检索能力短期记忆、临时会话向量数据库支持语义相似度检索需要 embedding 与索引维护长期记忆核心存储本地 JSON/文件最简单无额外依赖查询能力弱小规模实验在长期记忆实验中向量数据库是检索记忆的主流存储方式常见的有 Chroma、Milvus、Qdrant、Weaviate、pgvector 等。选型时要考虑数据量、部署难度、是否支持持久化、是否有成熟客户端。3.3 检索模块检索模块把当前问题映射到历史记忆中。最基础的检索方式是向量相似度检索也就是把用户问题转为向量在向量库中查询最相似的若干条记忆。单纯向量检索的常见问题是“语义相近但实际无用”的干扰结果。一个更稳的做法是“向量召回 重排序”先用 embedding 相似度召回候选记忆数量可以多一些比如 20 条。再用一个重排模型reranker或基于规则的过滤逻辑从候选中筛选出最相关的 3-5 条。最后按时间权重调整排序兼顾“语义相关”和“信息时效”。3.4 写入策略写入策略决定了记忆系统如何新增、更新、遗忘。最简单的是“无条件写入”每条对话都存进去但这会让记忆库迅速膨胀检索噪声越来越大。推荐策略阈值写入对话轮次达到 N 轮后批量提取这次对话中的关键信息写入记忆库。去重更新写入前先检索是否有相似旧记忆如果有则更新原记录而不是新增一条。遗忘机制为记忆增加时间戳和重要度评分定期清理超过有效期的数据或者将低重要度数据归档。4. 环境准备与实验设计下面进入实操环节。我们来实现一个最简但完整的长期记忆层采用“向量检索 记忆库”模式不依赖重型组件方便你理解全链路。4.1 实验环境操作系统Windows 10 / macOS / Linux 均可Python3.10 或以上安装包openai、chromadb、pandaspip install openai chromadb pandas如果你的环境没有配置 openai 库请先安装。本文示例使用 OpenAI 兼容的 embedding 与 chat 接口如果你的模型是本地部署也支持 OpenAI 兼容协议思路是一样的。需要说明的是各家依赖版本迭代较快本文代码以常见版本为参考重点演示设计思路。如果某个 API 在你的版本中不存在请以官方文档为准调整。4.2 项目结构llm-memory-lab/ ├── memory/ │ ├── __init__.py │ ├── vector_memory.py │ ├── summarizer.py │ └── agent.py ├── data/ │ └── mem.db # Chroma 持久化目录 ├── demo.py └── requirements.txt4.3 设计要点我们要实现的记忆系统有三个模块vector_memory.py基于 Chroma 的记忆存取与检索。summarizer.py使用 LLM 将对话压缩成结构化记忆。agent.py把记忆检索结果与用户问题拼成 prompt交给 LLM 生成回复。实际运行流程用户问题 → 向量化 → 在记忆库中检索 top-k 条历史记忆 → 记忆 最近对话 用户问题 → LLM5. 核心代码实现5.1 向量记忆模块创建memory/vector_memory.py负责初始化向量数据库并提供增、查两个核心方法。# 文件路径memory/vector_memory.py import chromadb from chromadb.config import Settings class VectorMemory: 基于 Chroma 的长期记忆存储与检索。 def __init__(self, persist_directory: str ./data/mem_db): self.client chromadb.PersistentClient( pathpersist_directory, settingsSettings(anonymized_telemetryFalse), ) # collection 可以理解为一张记忆表 self.collection self.client.get_or_create_collection( namellm_long_term_memory, metadata{hnsw:space: cosine}, ) def add_memory( self, memory_id: str, text: str, embedding: list, metadata: dict None, ): 写入一条记忆。 self.collection.add( ids[memory_id], documents[text], embeddings[embedding], metadatas[metadata] if metadata else None, ) def search_memory( self, query_embedding: list, top_k: int 5, where: dict None, ): 根据 query 向量检索最相似的记忆。 result self.collection.query( query_embeddings[query_embedding], n_resultstop_k, wherewhere, ) return result这里有几个关键点PersistentClient会把数据持久化到本地目录重启后记忆还在。hnsw: space选择 cosine 余弦距离更适合语义相似度检索。documents保存记忆原文embeddings保存向量metadatas可以附加时间戳、来源、重要度等字段。5.2 Embedding 与对话压缩模块创建memory/summarizer.py这个模块负责两件事生成文本向量、把对话压缩成结构化记忆。请注意不同模型的向量维度可能不同使用前先确认你用的 embedding 模型维度与向量库索引维度一致。# 文件路径memory/summarizer.py from openai import OpenAI client OpenAI() # 读取环境变量中的 API key def get_embedding(text: str, model: str text-embedding-3-small) - list: 将文本转换为向量。 resp client.embeddings.create(modelmodel, inputtext) return resp.data[0].embedding def summarize_dialogue(dialogue: str, model: str gpt-4o-mini) - str: 将一段原始对话压缩为结构化记忆文本。 prompt f 你是一个记忆抽取助手。请从下面的对话中提取值得长期记住的信息 包括用户偏好、项目决策、重要事实、待办事项。 不要输出无关内容不要编造用简洁的中文条目输出。 对话 {dialogue} resp client.chat.completions.create( modelmodel, messages[ {role: system, content: 你是一个专业的记忆抽取助手。}, {role: user, content: prompt}, ], temperature0.2, ) return resp.choices[0].message.content def build_memory_text(meta_type: str, content: str) - str: 将抽取结果与记忆类型组合成规范化文本便于检索。 return f[{meta_type}] {content}build_memory_text的作用是给记忆加一个标签前缀比如[偏好]、[项目决策]、[待办]。这个前缀在向量检索时能提供额外的语义锚点提高命中率。5.3 Agent 主逻辑创建memory/agent.py这是整个记忆层与 LLM 交互的入口。# 文件路径memory/agent.py from .vector_memory import VectorMemory from .summarizer import get_embedding SYSTEM_PROMPT 你是一个有长期记忆能力的 AI 助手。 class MemoryAgent: 带长期记忆的 AI Agent。 def __init__(self, top_k: int 5): self.memory VectorMemory() self.top_k top_k def run(self, user_input: str, dialogue_id: str default): 接收用户输入检索相关记忆生成回复。 # 1. 将用户输入转成向量 query_vec get_embedding(user_input) # 2. 检索历史记忆 result self.memory.search_memory( query_embeddingquery_vec, top_kself.top_k, where{dialogue_id: dialogue_id}, ) # 3. 组织记忆上下文 memory_context if result[documents]: docs result[documents][0] memory_context \n.join(docs) # 4. 拼装 prompt prompt f 以下是与你对话用户的长期记忆请结合记忆回答问题。 如果记忆与问题无关请忽略记忆直接根据你的知识回答。 长期记忆 {memory_context} 用户问题{user_input} from .summarizer import client resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: prompt}, ], temperature0.7, ) return resp.choices[0].message.content def save_dialogue(self, dialogue_text: str, dialogue_id: str default): 将一段对话抽取成记忆并写入向量库。 from .summarizer import summarize_dialogue, build_memory_text summary summarize_dialogue(dialogue_text) memory_text build_memory_text(对话记忆, summary) vec get_embedding(memory_text) memory_id f{dialogue_id}_{int(time.time())} self.memory.add_memory( memory_idmemory_id, textmemory_text, embeddingvec, metadata{dialogue_id: dialogue_id, timestamp: time.time()}, )注意上面用到了time模块记得在文件头部import time。这个 Agent 的核心思想是每次回答前先“回忆”再把回忆结果作为上下文的一部分交给 LLM。这就是外部记忆层的雏形。5.4 完整演示脚本创建根目录下的demo.py模拟一次完整的“写入记忆 → 检索记忆 → 生成回复”流程。# 文件路径demo.py from memory.agent import MemoryAgent agent MemoryAgent() # 第一步模拟一次对话并保存记忆 dialogue 用户我们项目准备用 Python 开发后端 API。 助手好的建议使用 FastAPI 框架性能好生态也成熟。 用户可以数据库就用 PostgreSQL。 助手明白PostgreSQL 加 FastAPI 是很常见的组合。 print( 正在保存对话记忆...) agent.save_dialogue(dialogue, dialogue_idproject_a) print(保存完成。) # 第二步新会话用户提问检索相关记忆 question 我们项目后端用什么框架 print(f用户问题{question}) answer agent.run(question, dialogue_idproject_a) print(fAI 回答{answer})运行python demo.py预期效果是即使这是一次全新的会话AI 也能根据历史记忆回答出“项目使用 FastAPI 和 PostgreSQL”。这就是长期记忆最基本的价值。6. 进阶架构思路与工具选型6.1 引入记忆总结与分层上面示例只做了最简单的“整段对话压缩后存入记忆”。实际项目中对话可能很长直接压缩整段对话会丢失细节。更好的做法是将对话按轮次切分每 3-5 轮抽取一次。抽取结果先写入“候选记忆区”。当候选记忆区积累到一定规模后再做一次合并压缩写进“长期记忆区”。检索时优先从长期记忆区检索其次从候选区补充。这种分层方式把信息按生命周期切分避免把重要细节和临时内容混在一起。6.2 记忆更新与遗忘策略长期记忆最大的风险不是“没有记忆”而是“记住了错误信息后一直生效”。比如用户先说“数据库用 MySQL”后来改口“还是用 PostgreSQL 吧”如果记忆系统没有覆盖机制AI 会一直按旧信息回答。一个有效的做法是更新写入。# 伪代码更新写入逻辑 def upsert_memory(query_text: str, new_memory: str): # 先检索相似旧记忆 old_memories memory.search_memory(get_embedding(query_text), top_k3) for doc in old_memories: if similarity(doc, new_memory) 0.85: # 删除旧记忆用新记忆覆盖 memory.collection.delete(ids[doc[id]]) # 再写新记忆 memory.add_memory(...)同时还需要引入时间衰减检索结果不仅看相似度还要乘以时间因子让两周前的记忆权重自然降低。6.3 向量数据库选型参考不同规模项目的向量数据库选型策略项目规模推荐方案理由个人实验 / 小项目Chroma安装简单支持本地持久化中型项目Qdrant / Milvus检索性能好支持过滤部署可控已用 PostgreSQL 的团队pgvector避免额外引入组件复用现有数据库大规模生产Milvus / Weaviate分布式能力强运维生态成熟选型时不要只看宣传的性能数字还要关注团队的运维能力。嵌入在业务代码里的向量库往往比单独维护一套分布式系统更省心。7. 常见问题与排查思路在实现长期记忆的过程中我遇到和看到过很多问题下面整理成一张排查表。问题现象常见原因解决思路检索结果与问题完全无关embedding 模型选型不当或领域差异大换更强的 embedding 模型增加记忆标签如[偏好][决策]查询返回空结果collection 为空或 where 过滤条件写错检查记忆是否写入先去掉 where 条件测试记忆写入但重启后丢失Chroma 持久化路径未设置使用 PersistentClient 并指定 path检索到的记忆太旧缺乏时间衰减或过时清理机制在 metadata 中记录时间戳检索后按时间重排向量维度不一致导致报错embedding 模型换了向量维度变了统一 embedding 模型或者重建 collection对话太长导致压缩丢失关键信息一次性压缩整段对话按轮次切分分批压缩对关键实体单独抽取用户修改了旧信息但 AI 仍按旧信息回答没有更新覆盖机制写入前先查找相似记忆用新信息覆盖旧记忆Token 费用过高每次检索都塞入过多记忆条数降低 top_k增加过滤条件对记忆做重要度评分排查这类问题有一个通用思路把“写入”和“检索”两步分开验证。先在记忆库里直接查询某条内容确认已经写入再用同一个 embedding 模型对测试问题做相似度对比确认检索链路没问题最后才去检查 prompt 组装是否正确。8. 最佳实践与工程建议8.1 记忆内容要“结构化 标签化”不要把原始对话直接扔进向量库至少要抽取、格式化、加标签。通过[偏好]、[决策]、[待办]、[实体关系]这样的前缀能让检索命中率提升一个台阶。这种设计在检索时天然形成“类别过滤”的能力可以配合 metadatas 里的 type 字段做二次过滤。8.2 严格控制写入“记忆”的门槛记忆不是越多越好。建议设置写入门槛寒暄、问候、表情类内容不写入。单轮无信息量的内容不写入。明确包含用户偏好、项目决策、重要事实的内容才写入。写入前先让记忆抽取模型判断“这条对话是否有长期价值”。门槛越高记忆库越干净检索质量越高。8.3 上下文组装顺序有讲究在构建 prompt 时记忆内容不宜放在用户问题之后。推荐顺序系统提示词 注入记忆内容长期记忆 最近对话短期记忆 用户当前问题因为模型对输入内容的注意力分布并不均匀放在中间的内容容易被忽略放在开头和结尾的信息被利用得更充分。记忆内容最好放在 system 或对话开头的位置。8.4 单独管理记忆的版本与回滚长期记忆在生产环境中相当于“用户状态数据”和代码一样需要版本管理。可以给记忆增加类似memory_version的字段当算法或模型升级时可以按版本切换。涉及记忆批量修改的场景务必先在测试环境验证再对生产环境操作并保留数据备份。8.5 安全与隐私边界长期记忆一旦落地就会涉及用户数据的保密、删除、导出等合规问题。至少要考虑敏感信息密码、身份证号、密钥等不得写入记忆库。提供“清除指定用户记忆”的运维接口。记忆检索和写入需要做权限隔离不同用户不能互相看到对方的记忆数据。如果需要删除用户数据应同时清除向量库、关系库和备份中的数据。9. 总结与下一步这篇文章从 LLM 长期记忆的核心痛点出发梳理了长期记忆与短期记忆的区别、四种主流架构模式并实现了一个基于向量检索的完整记忆层示例。你现在应该掌握了LLM 长期记忆要解决的是“跨会话信息留存与准确恢复”问题向量检索记忆是最主流的长期记忆实现方案一个可运行的内存层至少包含存储、写入、检索、注入四部分记忆不是越多越好必须有抽取、过滤、更新、遗忘机制下一步可以沿着这几个方向继续深入给记忆层接入真实对话流接入 LangChain 或自研 Agent 框架尝试 Milvus 或 pgvector 等生产级向量数据库尝试引入 reranker 模型提升检索精度研究 MemGPT、Generative Agents 等论文中的分层记忆思想长期记忆是 LLM 走向 Agent 化、个性化的重要基础设施也是这个阶段最值得花时间研究的工程方向之一。建议你先跑通本文的示例再根据你的业务场景设计属于自己的记忆模型。代码写多了踩坑踩多了自然就有了自己的经验体系。