FEATURED · 精选文章

LLM长期记忆架构全解析:从摘要到向量检索的实践指南

发布时间 / 2026/8/30 15:40:03
来源 / 创域科博编辑部
栏目 / 资讯中心
LLM长期记忆架构全解析:从摘要到向量检索的实践指南 在 LLM 应用真正进入生产场景之后长期记忆Long term memory很快从一个可选项变成核心治理问题。很多开发者在做 RAG 聊天助手、Agent 或多轮任务系统时都会遇到同一个现象对话超过几十轮后要么上下文窗口被占满要么前面的关键信息被新内容挤出注意力范围导致模型重复提问、前后矛盾、丢失用户偏好。这篇文章基于我做过的一组架构实验梳理 LLM 长期记忆的几种主流设计思路、各自取舍、最小可运行案例和踩坑路径希望能给正在做记忆模块的读者一份可参考的架构地图。需要先说明的是这篇文章不会给出一套“标准答案”因为长期记忆本质上没有一个通用最优解。它取决于你的业务形态、上下文长度、检索精度、延迟和成本约束。文章要讨论的重点是当你想给 LLM 增加长期记忆时有哪些架构选择每种选择解决什么问题实验阶段应该怎么验证生产阶段还需要补哪些工程能力。1. 先理解 LLM 长期记忆的难点不是存储而是有效召回长期记忆这个名词看起来很好理解让模型记住之前的内容不要每次对话都从零开始。但在架构设计上它并不是简单的加一张数据库表或一个 Redis 缓存也不是把历史消息无限塞进 prompt。真正的难点在于模型本身是一个无状态函数它只有当前这一轮输入而记忆要从历史数据中重组出“当前这个时刻最值得被模型看到的信息”。1.1 长期记忆要解决的核心场景先列出几个典型场景这些场景可以用来判断你的应用是否真的需要长期记忆多轮对话中用户在前几轮提到“我住在上海预算两万以内”后面几轮问“推荐一台适合我通勤场景的笔记本”。如果模型忘了前面的约束推荐结果就可能跑偏。Agent 执行任务时用户说“先看一下订单状态然后帮我把异常订单汇总给运营”。这个任务内部有多个步骤步骤之间需要共享中间状态。长期使用场景下用户希望系统记住自己的偏好比如标题风格、日报格式、代码注释习惯而不是每次重新设置。知识型应用里用户希望模型能记住之前整理过的笔记、标签和结论并在后续问答中主动引用。这些场景的共同点是记忆不是一次性的上下文而是跨会话、跨任务、跨时间复用的状态。1.2 为什么不能把所有历史都塞进上下文最容易想到的做法是把全部历史消息原样拼接到 prompt 后面。这样做在小规模实验里是可行的但随着对话轮数增加会遇到四个问题。第一是上下文窗口限制。即使模型支持 128k 或 200k 上下文总会被某个阈值卡住而且输入越长单次请求成本越高。第二是注意力稀释。模型对长文本不同位置的信息关注度并不均匀关键信息夹在大量闲聊文本中间时容易被忽略。第三是信息冗余。用户重复表达、寒暄、纠错等过程并不都有记忆价值全部保留只会增加噪声。第四是安全和权限问题。把 A 的历史数据原样塞给 B 的请求会带来严重的越权风险。所以长期记忆架构的核心不是“把历史存下来”而是“从完整历史中提炼出有价值的信息并在需要的时候快速、准确地找回来”。1.3 长期记忆的三个基本步骤写入、索引、召回不管采用哪种记忆架构都绕不开三个环节写入从原始对话、文档或行为日志中抽取记忆片段。这一步要决定什么内容值得记、以什么格式存储。索引给记忆打标签、建向量、建关系目的是让后续召回有据可查。召回根据当前输入从记忆库中选择最相关的一部分拼接到上下文中。实验阶段最容易犯的错误是只关注存储忽略了召回策略。实际上召回策略才决定记忆是否真正被模型用到。如果召回结果不够准确或者召回了大量无关内容记忆反而会成为噪声。2. 四种常见的长期记忆架构路线对比在我做的实验中长期记忆架构大体可以归为四条路线原始文本拼接、摘要式记忆、向量检索记忆、结构化知识记忆。实际项目里往往不是二选一而是组合使用。2.1 原始文本拼接最简单但最先被淘汰原始文本拼接就是把对话历史按时间顺序存下来在每次请求前取最近 N 轮拼进 prompt。实现成本极低没有额外的存储和索引逻辑在 Demo 阶段可以快速验证记忆能力。但它的问题也很明显。它是基于窗口的“短时记忆”不是真正意义上的长期记忆。当会话数量超过窗口大小后最早的记忆会被丢弃。如果窗口设得很大成本和时间都会上升。另外它没有筛选能力闲聊、重复、无意义内容全部保留。实验结论原始文本拼接只适合用来做基线不适合作为长生命周期的记忆方案。2.2 摘要式记忆用降维换取可控成本摘要式记忆的思路是把多轮对话周期性地交给 LLM 进行压缩总结提炼出用户偏好、关键决策、待办事项等信息然后只保存摘要文本。每次请求时可以把当前轮的新消息和最近一次摘要合并再一起交给模型。这种架构最大的好处是能显著控制上下文膨胀。如果对话持续很长系统会定期把之前的原文替换成摘要原始文本可以归档不参与每次请求。缺点也很明显摘要意味着信息丢失细节越早越容易被压缩掉。比如用户在第 5 轮明确说过“不要推荐 AMD 显卡的笔记本”摘要可能写成“用户对笔记本有偏好”到第 30 轮时模型就无法准确判断这个约束。另一个问题是摘要本身有成本频繁做摘要会消耗大量 Token。实验时可以这样设计摘要触发条件对话轮数超过阈值比如 20 轮。对话长度超过阈值比如累计 4000 Token。检测到明确话题切换。2.3 向量检索记忆支持语义召回但需要谨慎做索引向量检索记忆是目前 RAG 类应用最常用的路线。思路是把对话片段、知识片段、用户偏好等文本切块用 embedding 模型转换成向量存入向量数据库。查询时把当前用户输入转成向量在数据库中做近似最近邻搜索取回 top-K 相似片段。它的优点是支持语义匹配用户不需要用完全一样的关键词描述历史信息。比如用户今天说“帮我写个冒泡排序”系统可以根据语义召回昨天记录的“数据结构算法练习”笔记。但向量检索有几个容易忽略的问题。第一切块方式决定召回上限如果按固定字符数切块很容易把完整语义切开。第二向量相似度不等于信息价值历史中相似内容很多召回回来的却不一定是有用的约束。第三embedding 模型更新后旧向量可能不再准确需要重建索引。实验阶段可以用一套最小代码验证把特定用户的历史消息存进向量库再模拟一次用户提问观察召回结果是否包含正确记忆。测试通过后再逐步增加切块、重排和过滤规则。2.4 结构化知识记忆适合实体关系明确的任务结构化知识记忆把信息抽取成实体、属性和关系存储在知识图谱或关系型数据库中。例如“用户张三住在上海”“张三偏好安静咖啡馆”“张三购买了商品 A”。每次请求前通过实体识别和关系查询把相关事实注入 prompt。这条路线适合信息密度高、关系明确的场景比如 CRM 系统、客服工单、医疗问诊、企业知识库。它的优势是精确、可控、便于审计还能与业务数据打通。但它的瓶颈明显依赖高质量的信息抽取需要额外设计实体识别和关系抽取流程而且文本抽取会有错误错误事实一旦进入知识库会对后续回答产生系统性污染。对于开放式对话很难把所有信息都结构化。2.5 混合式记忆生产环境最常用复杂度也最高我在实验中最终采用的架构是把这几种路线组合起来形成分层记忆池短期层保存最近 N 轮完整对话用于保证当前语境的连贯性。摘要层定期生成对话摘要保存长期偏好和关键进展。向量层把重要片段向量化支持语义召回覆盖摘要丢失的细节。知识层抽取明确的实体关系用于业务事实查询。查询时按优先级组合先用短期层保证当前轮上下文再用向量层召回历史相关片段最后用知识层补充实体事实。摘要层通常作为兜底当前面都召回不到时提供总体背景。混合方案可以避开单一方案的缺点但也引入了更多调度和成本问题。实验时不要一开始就做全量混合建议先跑通向量检索再叠加摘要最后才考虑知识层。下表是四种路线在实验维度上的对比架构路线实现成本上下文控制细节保真语义匹配适用场景原始文本拼接极低差高不支持Demo、基线摘要式记忆中好低弱长对话、总览型任务向量检索记忆中高好高强知识问答、偏好召回结构化知识记忆高好中中实体关系明确的业务混合式记忆高好高强复杂生产系统3. 实验系统搭建用最小代码验证记忆链路我建议先做一个小到不能再小的实验系统不引入复杂框架只用 Python、一个向量库和一个 embedding 接口跑通“写入、召回、拼接、生成”这条链路。下面示例用来说明思路实际项目要结合自己的框架和路径调整。3.1 实验目标设计实验前先明确目标不要只是“看看行不行”。我给这次实验设定的目标是第一次对话时用户告知一个偏好写作时要求使用 Markdown并且要加表格。第二次对话时用户说“帮我整理一份技术方案”模型应该自动生成带表格的 Markdown 文档。验证方式查看召回阶段是否取回偏好片段以及生成结果是否体现该偏好。这个目标很小但能完整验证记忆链路的写入、索引、召回和拼接。3.2 环境依赖最小实验只需要三类依赖对话生成调用 OpenAI、DeepSeek、Qwen 等模型的 OpenAI 兼容接口。向量化使用 text-embedding 系列模型或者本地 embedding 模型。向量存储可以使用 Chroma、FAISS、Milvus 等。实验阶段用 Chroma 最快。下面是示例依赖清单实际版本以官方文档为准openai chromadb python-dotenv安装命令pip install openai chromadb python-dotenv环境变量文件.env至少包含OPENAI_BASE_URLhttps://api.example.com/v1 OPENAI_API_KEYsk-your-api-key EMBEDDING_MODELtext-embedding-3-small CHAT_MODELgpt-4o-mini这里要注意不同服务商的 base_url、模型名和 API 格式存在差异落地前一定要先确认供应商文档。实验阶段不要硬编码密钥使用环境变量管理。3.3 关键模块记忆写入记忆写入的核心是决定“什么内容值得写入”。我的实验采用最简单的规则每轮对话结束后把用户消息和助手消息拼接成一个记忆片段并附带时间戳和会话 ID。import os import chromadb from openai import OpenAI client OpenAI( base_urlos.getenv(OPENAI_BASE_URL), api_keyos.getenv(OPENAI_API_KEY) ) chroma_client chromadb.PersistentClient(path./memory_db) collection chroma_client.get_or_create_collection( nameuser_memory, metadata{hnsw:space: cosine} ) def get_embedding(text: str) - list: resp client.embeddings.create( modelos.getenv(EMBEDDING_MODEL, text-embedding-3-small), inputtext ) return resp.data[0].embedding def write_memory(user_id: str, session_id: str, content: str): memory_id f{user_id}-{session_id}-{content[:20]} collection.upsert( ids[memory_id], documents[content], embeddings[get_embedding(content)], metadatas[{ user_id: user_id, session_id: session_id, timestamp: int(time.time()), source: conversation }] )这段代码用time模块需要单独导入。upsert按memory_id写入如果相同内容再次出现会覆盖旧记录。实验阶段这个逻辑是够用的但生产环境要避免用文本内容作为主键建议用消息 ID 或 UUID。为什么要把 user_id 放进 metadata因为生产环境必须做用户隔离检索时一定要按用户过滤否则会出现一个用户看到另一个用户记忆的严重事故。3.4 关键模块记忆召回召回阶段要完成三步过滤当前用户、语义检索、拼装成上下文片段。Chroma 支持 metadata 过滤直接传给query函数即可。def recall_memory(user_id: str, query: str, top_k: int 5) - list: results collection.query( query_embeddings[get_embedding(query)], n_resultstop_k, where{user_id: user_id} ) documents results.get(documents, [[]])[0] metadatas results.get(metadatas, [[]])[0] scores results.get(distances, [[]])[0] merged [] for doc, meta, score in zip(documents, metadatas, scores): merged.append({ content: doc, meta: meta, score: score }) return merged这里的score是距离不是相似度。余弦距离越小表示越相似。实验时要注意区分避免把“距离最小的”误判成“最不相似”。召回结果还需要做重排。最简单的方式是过滤掉距离大于阈值的记录。比如使用余弦距离时阈值设为 0.5 以上基本可以放弃。不同 embedding 模型的距离分布差异较大建议先跑一批真实查询统计分布后再确定阈值。3.5 组装成最小会话服务召回结果要转成 prompt 片段拼到系统提示词中。示例做法def build_messages(user_id: str, user_input: str, history: list): memories recall_memory(user_id, user_input, top_k3) memory_block if memories: memory_lines [m[content] for m in memories] memory_block \n.join(f- {line} for line in memory_lines) system_prompt f 你是用户长期助理。请优先参考下方记忆信息回答用户问题。 ## 用户历史记忆 {memory_block if memory_block else 暂无相关记忆} ## 回答要求 - 如果记忆与当前问题相关请主动引用并执行。 - 如果记忆不相关忽略它不要编造。 messages [{role: system, content: system_prompt}] messages.extend(history) messages.append({role: user, content: user_input}) return messages生成时再调用一次对话模型def chat(user_id: str, user_input: str, history: list): messages build_messages(user_id, user_input, history) resp client.chat.completions.create( modelos.getenv(CHAT_MODEL, gpt-4o-mini), messagesmessages, temperature0.3 ) return resp.choices[0].message.content到这里最小闭环已经跑通。第一轮用户说“以后写作都用 Markdown而且要加表格”系统写入记忆第二轮用户说“帮我写一份数据分析报告”系统召回该偏好并生成带表格的 Markdown 内容。实验时最常遇到的问题是模型明明看到了记忆却不用。这时要把 prompt 中的记忆区块打上更强指令比如“如果记忆与当前任务直接相关严格按照记忆中的偏好执行不要询问用户”。4. 评估实验效果不能只看对话能不能通只靠人工点几轮对话很难判断记忆架构是否有效。实验阶段需要给出一套可量化的评估方式。4.1 离线评估指标离线评估不调用对话模型只评估召回质量成本低且便于自动化。命中率构造一批“历史事实 当前问题”对判断目标事实是否出现在 top-K 召回结果中。精确率召回结果中与问题真正相关的比例。倒排质量目标记忆在 top-1、top-5 中的位置。向量距离分布统计命中与未命中的距离差异用于确定阈值。实验时构造 20 到 50 条测试用例就够了。每条用例包含三部分{ user_id: u001, history_content: 用户要求所有周报使用表格标题加上项目编号, query: 帮我生成这周的周报, expected_keyword: [表格, 项目编号] }判断命中时用关键词或人工配合。关键词判断会漏掉语义改写所以实验初期建议先人工看一轮结果再决定是否自动化。4.2 在线评估指标在线评估把模型输出也纳入检查范围。可以在测试环境中运行完整的对话流程然后用规则或另一个 LLM 对结果打分。可以记录三个维度记忆利用率假设真实输入包含某个约束检查输出是否体现了该约束。上下文污染率召回的记忆中有多少和当前请求无关是否干扰了模型判断。平均响应延迟和 Token 消耗这个指标决定方案是否有成本价值。这些指标不需要做得很重实验阶段用下面这种简单 JSON 记录每条测试用例的结果即可{ case_id: case-001, query: 帮我生成这周的周报, full_prompt_tokens: 1200, recall_count: 3, relevant_count: 2, irrelevant_count: 1, output_obeys_memory: true, latency_ms: 1450 }4.3 实验结果记录方式实验一定要保留可复现的记录。建议每次修改切块策略、embedding 模型、top-k 参数时都导出一份结果快照。快照包含向量库版本或索引版本。使用的 embedding 模型和维度。测试用例集合。召回阈值。命中率、精确率、平均延迟。典型失败案例。有了快照后面调整参数时才能判断变好还是变坏。否则很容易出现“参数调了一轮凭感觉觉得变好了实际上是因为测试用例变了”。5. 实验中最容易踩的四个坑下面这些坑在我实验过程中几乎都遇到过每一条都值得记录。5.1 召回不到任何记忆现象用户明明曾经说过某个偏好但当前查询召回的 top-K 里没有相关记忆。排查顺序确认向量库中确实写入了该用户的记录。按 user_id 直接查询 collection检查文档数量和内容。确认写入时使用的 embedding 模型和查询时是否一致。如果写入用了 A 模型查询用了 B 模型语义空间不同召回几乎必然失败。检查 metadata 过滤条件。where{user_id: user_id}如果大小写、类型不一致也可能查不到。打印查询向量的距离值。如果所有距离都在 0.7 以上说明语义空间整体不匹配需要考虑换模型或加大 top_k。预防建议把 embedding 模型名写入每条记忆的 metadata查询时校验模型一致。5.2 上下文污染记忆太多反而干扰模型现象召回确实返回了相关内容但因为 top-K 太大无关记忆也混进来模型反而忽略真正重要的信息。原因单纯靠向量相似度排序会把语义相近但不重要的内容排进来了。比如用户历史中多次提到过“表格”但都是闲聊当前这个场景需要的是硬约束却被闲聊内容淹没了。解决方案缩小 top-K从 5 改到 3观察效果。设定距离阈值过滤低相关片段。在 prompt 中给记忆排序越靠前越重要。增加“相关性判断”步骤先用轻量模型判断每条记忆是否与当前请求相关再决定是否注入。生产环境更推荐做两段式召回先用向量召回候选再用 LLM 或规则重排最终只保留前 2 到 3 条。5.3 记忆膨胀与过期现象记忆库里堆积了大量历史片段数量越来越多每次查询延迟越来越高同时旧记忆可能已经过时。原因写入阶段没有定义记忆生命周期所有内容都永久保存。解决方案为每条记忆增加timestamp和expire_at字段。定期清理过期记忆或者通过定期摘要压缩同类历史片段。对同一用户的同类偏好用新内容覆盖旧内容而不是无限追加。比如用户一开始说“简洁风格”后来改成“详细风格加示例代码”系统要识别这是同一类偏好的更新而不是两条并行的记忆。5.4 向量模型切换后没有重建索引现象本来效果正常切换 embedding 模型后召回结果变得混乱甚至完全无法命中。原因新旧模型的向量维度、语义空间完全不同旧向量和新向量无法直接比较。向量库会报维度不一致错误有些会静默失败。处理方式切换 embedding 模型后必须重建整个索引不能只重启服务。预防建议在向量库中记录embedding_model字段启动时校验当前模型与索引模型一致。下表汇总了这四类坑的快速排查路径问题现象首查内容次要检查推荐解法召回不到记忆记录是否写入、embedding 是否一致metadata 过滤条件校验模型一致性检查 where 条件上下文污染top_k 是否过大距离阈值缩小 top_k增加重排记忆膨胀过期是否有生命周期是否重复写入定时清理、摘要压缩、覆盖更新向量模型切换后失效索引是否重建维度是否匹配重建索引记录模型版本6. 从实验到生产长期记忆架构的工程化调整实验跑通只是第一步。进入生产环境后长期记忆模块的复杂度会明显上升下面这些点必须在架构设计阶段预留好位置。6.1 记忆写入异步化实验中的写入是同步完成的对话生成完成后才写入记忆。生产环境如果也这样做会明显拉高单次请求的延迟而且记忆写入失败会影响主流程。推荐方案是把写入改为异步任务对话完成响应后把原始对话抛到消息队列或任务队列中由后台服务负责切片、抽取、向量化和存储。这样用户感知不到记忆写入的耗时。如果原始对话包含敏感信息异步链路要额外考虑权限校验和数据脱敏不能简单地把全部原文丢到队列里。6.2 记忆隔离与权限这是最容易出严重事故的地方。用户 A 的记忆绝对不能出现在用户 B 的查询结果里。生产环境必须做到三层隔离存储层每个用户有独立的 memory namespace或者每条记忆都带 user_id。召回层查询时必须强制携带 user_id 过滤条件禁止全库检索。服务层接口层校验当前登录用户与请求携带的 user_id 一致。如果面向企业客户还需要增加 team_id、project_id 等多级隔离。审计日志也要记录谁在什么时间访问了哪条记忆。6.3 可观测性长期记忆模块的调试比普通接口更难因为问题可能出在写入、索引、召回、prompt 拼接任意一个环节。生产环境必须记录如下日志写入日志记录记忆片段来源、切块方式、向量模型、生成的主键。召回日志记录当前查询向量、召回的 top-K 结果、每条的相似度和通过还是被过滤。注入日志记录最终注入到 prompt 的记忆内容方便判断模型输出与记忆的因果关系。评估日志周期性记录命中率、精确率和平均延迟。没有这些日志排查记忆问题时只能靠猜。6.4 回滚与版本管理记忆代码和模型一样需要版本管理。当一次记忆 prompt 调整导致输出质量下降时需要能快速回滚到上一个版本。建议设计一个memory_strategy配置对象把 top_k、距离阈值、prompt 模板、重排策略都抽象成可配置项并支持按版本发布。发布前先在影子环境中跑一批测试用例对比新老版本在命中率和精确率上的差异。6.5 成本控制长期记忆会显著增加 Token 消耗。主要成本来自三块embedding 调用每次写入和查询都会消耗 embedding 的额度或算力。摘要生成周期性摘要需要额外调用对话模型。prompt 注入召回记忆增多会让每个请求的输入 Token 变多。控制成本的方法包括对写入做去重避免重复向量化摘要层只在会话达到阈值时触发召回结果限制在 top-2 到 top-3对低频用户的记忆定期归档。调研一个架构时建议把成本列入与准确性同等重要的评估维度。很多实验方案在准确率上表现不错但单位会话成本翻了一倍很难在真实业务中落地。7. 下一步扩展方向长期记忆还远没有发展到标准化的阶段实验和工程实践仍在快速演化。以下几个方向值得继续关注。7.1 从单轮记忆到会话队列目前的实验更多是“查询时召回历史”但没有充分处理多会话之间的关系。用户在同一周内的三个会话可能存在连续关系第一轮讨论技术选型第二轮写代码第三轮做方案评审。如果按独立会话处理记忆很难串起来。可以增加一个“会话队列”层按用户维度维护最近会话的主题、目标和状态让记忆召回具备时间连续性。7.2 基于 Agent 的记忆行为在 Agent 系统中记忆不只是给 LLM 看的文本还有工具调用记录、执行状态、失败原因和用户反馈。这类记忆更适合用事件结构存储而不是纯文本向量。Agent 需要区分哪些记忆是“事实”哪些是“步骤状态”哪些是“经验教训”。不同类型对应不同的读写策略和过期策略。7.3 标准化记忆接口与基准长期记忆方案的评估缺乏公开基准。不同的实验使用不同测试集很难横向比较。后续如果有机会可以像 RAG 领域的评测集一样逐步沉淀出覆盖长对话、偏好追踪、实体关联、遗忘更新等场景的评测集。有了统一评测集记忆架构的选择才能从“拍脑袋”变成“按数据说话”。7.4 混合记忆的分层调度混合式记忆虽然效果好但“什么时候用摘要、什么时候用向量、什么时候用知识图谱”需要更聪明的调度策略。简单的做法是全部召回然后重排更高级的做法是先用轻量分类器判断当前请求类型再决定走哪条记忆通道。例如用户问“昨天下午我们讨论了什么”优先召回当天会话用户问“我的笔记本预算上限是多少”优先查结构化偏好用户问“帮我写一份周报”优先用摘要加最近原始片段。这类调度逻辑是长期记忆架构从实验走向成熟的关键一步。长期记忆的工程价值不在于“存得多”而在于“在正确时刻提供正确的事实”。架构设计的核心也一直围绕这个目标展开写入时筛选索引时建模召回时取舍注入时克制。对刚开始做这块的团队建议先跑通最小闭环再逐层叠加摘要、向量、知识图谱和调度策略。每一次架构调整都要回到命中率、精确率、延迟和成本四个维度重新评估否则很容易陷入“记忆越多效果越差”的陷阱。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻