Agent记忆系统详解:从原理到代码实现长期记忆与向量检索

发布时间:2026/9/1 10:08:30
Agent记忆系统详解:从原理到代码实现长期记忆与向量检索 做 AI Agent 开发的朋友大概率都遇到过这样一个场景Agent 第一轮回答得逻辑清晰、方案完整到了第三轮它突然忘了自己两分钟前说过什么。用户问“我刚才让你保存的那个文件呢”Agent 一脸无辜地回答“抱歉我没有找到相关记录”。这不是模型不够聪明而是记忆没有打通。最近两年Agent 框架的版本迭代非常快但记忆模块始终是“看起来简单一上生产就翻车”的重灾区。很多人以为把历史消息全部拼进 Prompt 就是记忆结果 Token 成本翻了几倍效果反而更差也有人用向量数据库做了召回却发现召回的片段和当前问题毫无关系。这套标题虽然叫“B站 No.1”但记忆问题的解法其实是有共识的它不是一个模型能解决的问题而是一套工程架构问题。这篇文章会从原理到代码完整梳理 Agent 记忆的几种形态、主流方案的优缺点并用一套最小可运行的代码带你实现一个具备写入、召回、注入、遗忘能力的 Agent。读完你不仅能理解为什么 Agent 会“失忆”还能在自己的项目里真正解决它。1. 为什么 Agent 总是“翻脸不认人”1.1 先从一个真实场景说起假设你正在做一个客服 Agent。用户说“我想退掉上周买的蓝色耳机”Agent 第一轮回复了退货流程。用户接着说“收货地址也要改一下”如果 Agent 没有记忆它会问“请问您要改什么地址”——它已经不记得用户刚提到过订单和耳机了。更恼火的是跨会话场景。用户第二天再次打开应用问“我的退货申请处理到哪一步了”Agent 完全没有概念因为它根本不记得昨天的对话。用户只会觉得“这个 AI 真笨”而不是“这个系统缺少记忆模块”。从产品体验来看失忆是 Agent 最破坏信任感的问题之一。1.2 失忆的本质无状态请求 固定上下文窗口要解决失忆先要理解失忆的本质。大模型的 API 调用本质上是无状态的你发一个请求过去模型返回一个响应请求结束后模型什么都不记得。所有“记忆”都是外部系统在每次请求前把相关历史拼进 Prompt请求结束后再想办法把新内容存下来。同时模型还有一个物理限制上下文窗口是有限的。哪怕现在的模型已经支持几十万甚至百万 Token 的上下文也不可能无限制地塞入所有历史对话。一旦超过窗口长度最早的记忆就会被截断。所以 Agent 失忆不是“模型变傻了”而是两个工程问题叠加没有把重要信息持久化持久化之后没有在正确的时机把它找回。1.3 记忆问题的三层表现我在实际项目里会把 Agent 失忆分成三层层次表现典型原因短期对话遗忘同一次会话中多轮之后忘了早先的内容上下文被截断或完全没有传给模型跨会话遗忘用户下次进来Agent 不认识他没有长期存储会话之间完全隔离多 Agent 协作遗忘Agent A 做的事Agent B 完全不知道没有共享记忆只有各自的本地上下文很多人只处理了第一层把对话历史原样拼接进 Prompt就以为解决了记忆。第二层和第三层才是真正区分“玩具 Demo”和“生产可用系统”的分水岭。2. 先搞清楚Agent 记忆到底是什么2.1 短期记忆与工作记忆在 Agent 系统里短期记忆通常指当前会话中的上下文也就是最近几轮的用户消息和助手回复。它最直接也最容易实现。不过这里有一个容易被忽略的细节短期记忆并不一定要“全部保留”。把最近几轮完整消息传给模型是为了保持对话连贯性但如果会话已经进行了一百轮每一轮都传代价会越来越高。更合理的做法是短期记忆保留最近几轮更早的内容通过摘要或向量检索的方式“按需取用”。2.2 长期记忆长期记忆是解决跨会话失忆的关键。它的核心是把重要信息持久化到外部存储中例如数据库、向量数据库、文件系统或 Redis。长期记忆的内容不限于对话历史还包括用户偏好例如“用户喜欢简洁的回答风格”事实信息例如“用户的订单编号是 OD-20260501”任务状态例如“用户正在申请退货当前已进入审核阶段”Agent 自身的决策记录例如“上次已经给用户推荐过三款耳机”。这些信息如果只存在于对话历史里检索成本高、遗漏概率也高。更好的做法是把它们显示地抽取出来结构化存储。2.3 记忆不是简单地“拼对话历史”这是很多新手最容易踩的坑。如果只是把所有历史消息拼成一个超长字符串塞进 Prompt会带来三个问题Token 成本急剧上升且最终会被上下文窗口截断。无关信息会成为噪声干扰模型回答质量。隐私与权限风险历史消息里的敏感信息无法按需过滤。真正可用的记忆系统必须具备“筛选”和“检索”能力。它不是在每一轮把所有记忆都倒给模型而是只提取当前问题最需要的部分。2.4 双网络记忆模型与 LSTM 的启发很多人在理解 Agent 记忆时会想到神经网络里的 LSTM 和双网络记忆模型。LSTM 通过门控机制控制信息的写入和遗忘双网络模型则模拟了人类“快速学习新事实”和“缓慢巩固长期知识”两种机制。它们给 Agent 系统的启发是记忆应该分层写入和遗忘都应该有策略而不是“记住所有”或“忘记所有”。Agent 工程里更常见的对应关系是工作记忆 → 当前上下文中正在使用的信息长期记忆 → 外部存储中的事实、偏好和状态遗忘与巩固 → 定期对旧记忆做压缩、摘要合并或淘汰。理解了这一层你就不会把所有记忆方案都押注在“更大的上下文窗口”上。上下文再大也不如一个会筛选的记忆系统来得实用。3. 主流 Agent 记忆方案的架构对比3.1 全量拼接把所有历史消息直接拼成 Prompt。这是最简单的方案适合演示和超短会话。优点实现快不需要额外组件。缺点Token 成本高容易超窗历史越长越容易干扰模型注意力。适合场景内部工具、单轮或几轮内的轻量对话。3.2 摘要记忆每隔若干轮让模型把之前的对话浓缩成一段摘要下次请求时把摘要作为背景信息注入。优点压缩了历史信息解决长对话成本问题。缺点摘要会丢失细节错误会随摘要累积关键数字和用户原话容易被“提炼”掉。适合场景对话时间较长、但关键信息不密集的场景例如技术咨询。3.3 向量检索记忆把长期记忆存成向量在每次请求前用当前问题去检索最相近的若干条记忆再注入 Prompt。这其实就是把 RAG 的思路用到记忆上。优点检索精准成本可控可以应对大规模记忆。缺点召回质量依赖 Embedding 模型和分段策略如果查询语句和记忆表述差异过大可能检索不到。适合场景知识库助手、跨会话记忆、用户画像沉淀。3.4 混合记忆与多 Agent 共享记忆单靠一种记忆方案很难覆盖所有场景。生产环境中更常见的做法是混合式最近几轮用短期记忆关键事实用结构化存储模糊回忆用向量检索长时间跨度的总结用摘要记忆。当系统里有多个 Agent 协作时还需要共享记忆层。比如物流 Agent 查到了快递状态客服 Agent 要能直接使用这条信息而不是再查一次。这种场景下记忆的读写权限、数据隔离、一致性就变得非常关键。方案核心原理优点缺点适用场景全量拼接完整对话历史拼入 Prompt实现简单成本高、易超窗短对话、演示摘要记忆提炼历史摘要压缩时长对话易丢失细节长咨询、对话总结向量检索向量化并检索精准、可扩展依赖检索质量跨会话、知识库混合记忆分层组合多种方案全面、鲁棒实现复杂生产级 Agent4. 环境准备与前置条件本文的示例不依赖大型框架只使用两个 Python 库requests和numpy。你可以把它理解为一个极简的 Agent 记忆实现核心思路适用于 LangChain、LlamaIndex 或自研 Agent 框架。环境要求Python 3.9 或更高版本一个 OpenAI 兼容的 API 服务需要具备 Chat Completions 和 Embeddings 两个接口可以访问外部网络用于调用 API。建议在虚拟环境中操作mkdir agent-memory-demo cd agent-memory-demo python3 -m venv venv source venv/bin/activate pip install requests numpy也可以把依赖写入requirements.txtrequests2.31.0 numpy1.24.3版本号以你本机安装结果为准。本文重点演示的是通用思路而不是绑定某个库的最新版本。然后准备环境变量export OPENAI_API_KEYsk-你的-key export OPENAI_BASE_URLhttps://api.openai.com/v1 export LLM_MODELgpt-4o-mini export EMBED_MODELtext-embedding-3-small如果你使用的是国内兼容服务OPENAI_BASE_URL改成服务商提供的地址即可。注意如果你的基础 URL 已经包含/v1就不要在代码里重复拼接。5. 核心流程拆解写入、召回、注入、遗忘5.1 记忆写入记忆写入是第一步也是最需要设计的一步。写入什么内容、什么时候写入直接决定后续召回效果。推荐的做法是每一轮对话结束后将用户消息和助手回复都写入记忆库。为什么连助手回复也要写因为用户往往会引用助手之前的表述例如“你刚才说的那个方案再详细一点”如果只记用户消息Agent 就不知道“那个方案”指的是什么。写入时还可以附带一些元信息例如时间戳、会话 ID、业务场景。5.2 向量化与存储向量化是把文本转换成向量数组的过程通常由 Embedding 模型完成。之后将向量和原始文本一起存储便于后续检索。如果你只是做几十条记忆的演示使用 Python 列表即可如果要做生产系统建议换成向量数据库例如 Chroma、Milvus、Qdrant或者使用 Redis 加向量索引插件。5.3 召回与注入每次用户提问时先把问题向量化再与记忆库中的全部向量计算相似度取 Top-K 条最相关的记忆注入 Prompt。这里有一个重要细节如果当前请求是连续对话中的后续轮次还需要保留最近几轮的原始对话。否则向量库只提供“长期记忆”短期对话的衔接会断掉。5.4 遗忘与更新很多实现只考虑“写”和“读”没有考虑“遗忘”。但在生产环境里遗忘几乎是必须的用户撤回隐私信息后需要从记忆中删除旧的、错误的记忆需要被修正记忆数量过多时需要淘汰最旧或最不重要的内容。一个最简单的遗忘策略是“先进先出”当记忆数量超过阈值删除最早写入的记录。更高级的策略是定期把旧记忆交给模型做摘要合并只保留一个更概括的记忆条目。6. 完整示例给 Agent 装上长期记忆6.1 项目结构agent-memory-demo/ ├── memory_agent.py └── requirements.txt6.2 完整代码下面是一个可以直接运行的带记忆 Agent使用向量检索实现长期记忆同时保留最近 4 轮短期对话作为上下文。# 文件路径memory_agent.py # 功能一个带长期记忆的极简 Agent支持命令行交互 import os import json from datetime import datetime import numpy as np import requests # 配置区域 API_KEY os.getenv(OPENAI_API_KEY, ) BASE_URL os.getenv(OPENAI_BASE_URL, https://api.openai.com/v1) LLM_MODEL os.getenv(LLM_MODEL, gpt-4o-mini) EMBED_MODEL os.getenv(EMBED_MODEL, text-embedding-3-small) MAX_MEMORIES 200 # 长期记忆条数上限 SHORT_MEMORY_ROUNDS 4 # 短期记忆保留最近几轮 TOP_K 3 # 召回长期记忆条数 # 向量记忆类 class VectorMemory: def __init__(self): self.memories [] self.vectors [] def add(self, text: str, metadata: dict None): vector self._embed(text) self.memories.append({ text: text, metadata: metadata or {}, ts: datetime.now().isoformat(), }) self.vectors.append(vector) # 简单遗忘策略超过上限移除最早记录 if len(self.memories) MAX_MEMORIES: self.memories.pop(0) self.vectors.pop(0) def search(self, query: str, top_k: int TOP_K): if not self.memories: return [] query_vector self._embed(query) scores [] for vector in self.vectors: scores.append(self._cosine(query_vector, vector)) sorted_idx np.argsort(scores)[::-1][:top_k] results [] for i in sorted_idx: if scores[i] 0.1: continue results.append({ text: self.memories[i][text], score: float(scores[i]), ts: self.memories[i][ts], }) return results def _embed(self, text: str): url f{BASE_URL}/embeddings resp requests.post( url, headers{Authorization: fBearer {API_KEY}}, json{model: EMBED_MODEL, input: text}, timeout30, ) resp.raise_for_status() return np.array(resp.json()[data][0][embedding], dtypenp.float32) staticmethod def _cosine(a, b): a_norm np.linalg.norm(a) 1e-9 b_norm np.linalg.norm(b) 1e-9 return float(np.dot(a, b) / (a_norm * b_norm)) # 调用大模型 def chat_with_llm(messages): url f{BASE_URL}/chat/completions resp requests.post( url, headers{Authorization: fBearer {API_KEY}}, json{ model: LLM_MODEL, messages: messages, temperature: 0.7, }, timeout60, ) resp.raise_for_status() return resp.json()[choices][0][message][content] # 主程序 def main(): memory VectorMemory() history [] print(带记忆的 Agent 已启动输入 exit 退出。) print(示例你可以先告诉它一个偏好然后再问它这个问题。) while True: user_input input(\n你: ).strip() if user_input.lower() in (exit, quit): break # 1. 召回长期记忆 related memory.search(user_input, top_kTOP_K) memory_context if related: memory_context \n.join([f- {item[text]} for item in related]) # 2. 组装 Prompt system_prompt 你是一个有记忆能力的智能助手。 if memory_context: system_prompt \n以下是相关历史记忆请优先使用它们回答用户\n system_prompt memory_context messages [{role: system, content: system_prompt}] # 短期记忆最近几轮原始消息 messages.extend(history[-SHORT_MEMORY_ROUNDS:]) messages.append({role: user, content: user_input}) # 3. 调用模型 try: answer chat_with_llm(messages) except Exception as e: print(fAgent 调用出错: {e}) continue print(fAgent: {answer}) # 4. 写入长期记忆 memory.add(f用户说: {user_input}) memory.add(f助手回答: {answer}) # 5. 更新短期记忆 history.append({role: user, content: user_input}) history.append({role: assistant, content: answer}) if __name__ __main__: main()6.3 代码关键逻辑说明整个程序可以拆成 5 个步骤用户输入问题。memory.search(user_input)将当前问题向量化并和已有记忆向量计算余弦相似度返回最相关的TOP_K条记忆。把相关记忆放入 system prompt再拼接最近 4 轮原始对话作为短期记忆最后调用大模型。将用户输入和助手回复都写入长期记忆库。同时写入短期记忆history供下一轮拼接。第 4 步是长期记忆写入的核心。注意不是只写入助手回复用户原话同样重要因为后续检索都是基于“用户会怎么问”来匹配的。6.4 多 Agent 共享记忆扩展如果需要多个 Agent 共享记忆最简单的做法是抽出一个共享存储层。以下是一个伪代码示例展示思路class SharedMemory: 多 Agent 共享记忆以 Redis 向量索引为例 def __init__(self, namespace: str, redis_client, vector_search): self.namespace namespace self.redis redis_client self.vector_search vector_search def add(self, agent_id: str, text: str, metadata: dict None): entry { agent_id: agent_id, text: text, metadata: metadata or {}, ts: datetime.now().isoformat(), } self.redis.lpush(fmemory:{self.namespace}, json.dumps(entry, ensure_asciiFalse)) # 同时写入向量索引 self.vector_search.index(entry) def query(self, text: str, top_k: int 5): # 从向量索引检索权重可调整为 team_memory 优先 return self.vector_search.search(text, top_ktop_k)生产系统里多 Agent 共享记忆的难点在于权限隔离哪些 Agent 可以读哪些 Agent 可以写哪些记忆只对特定团队开放。如果全部共享容易造成信息污染。6.5 运行命令export OPENAI_API_KEYsk-你的-key export OPENAI_BASE_URLhttps://api.openai.com/v1 export LLM_MODELgpt-4o-mini export EMBED_MODELtext-embedding-3-small python3 memory_agent.py如果你的 API 服务商只提供了类似https://your-domain.com/v1的地址那么BASE_URL直接填写这个地址。代码里拼接 Embeddings 地址时会变成https://your-domain.com/v1/embeddingsChat Completions 同理。7. 运行结果与效果验证7.1 预期对话示例启动程序后输入以下内容你: 我喜欢简洁的回复不要太多废话。 Agent: 好的记住了。我会尽量用简洁的方式回答你。 你: 我昨天买了一个机械键盘但好像按键有点问题。 Agent: 收到你提到机械键盘按键可能有问题我会在后续帮助你排查建议。 你: 你记得我喜欢什么风格的回复吗 Agent: 根据我们的历史对话你偏好简洁的回复风格不喜欢太多废话。第三轮回答能准确引用第一轮的信息说明长期记忆已经生效。7.2 如何判断记忆生效判断记忆是否生效可以分三步检查当前进程内连续对话问一个前几轮提到过的具体信息模型能回答出来。重启程序后再次对话如果还能回答上一轮会话的信息说明记忆已经持久化。故意输入一个和所有历史记忆都不相关的问题观察系统 Prompt 中是否有检索结果。如果没有说明知识库没有受到污染。7.3 如果失败先看哪里程序运行失败时第一步要检查的是 API 返回信息而不是代码逻辑。401错误API Key 不对或过期。404错误BASE_URL拼接有问题很多兼容服务要求 URL 中带/v1。Timeout错误网络不稳定或模型响应过慢。8. 常见问题与排查思路问题现象可能原因排查方式解决方案启动时报SSL错误网络环境无法直接访问 API检查请求超时错误堆栈确认证书配置更换网络重试或使用服务商提供的专用地址调用模型返回401API Key 配置错误打印环境变量确认是否读取成功重新设置OPENAI_API_KEY返回404BASE_URL拼接错误检查最终请求 URL 是否重复/v1确保BASE_URL不含多余路径关键词查不到历史记忆Embedding 模型对短文本区分度不足打印检索分数观察 Top-K 结果调整TOP_K或换更强的 Embedding 模型记忆库增长后速度变慢全量向量计算太慢统计记忆条数和单次检索耗时替换为独立向量数据库增加索引模型开始引用错误记忆召回内容与当前问题语义相似但不相关查看检索结果中的文本和分数增加 score 阈值或对记忆条目做业务标签过滤短期对话中断history被截断检查SHORT_MEMORY_ROUNDS配置调大短期轮数或把关键信息写入长期记忆9. 最佳实践与工程建议9.1 记忆写入要有边界不是所有内容都值得写入长期记忆。对话中出现一次的口头禅、临时数据、验证码等不应该写入。更合理的做法是让模型在每轮对话结束后判断“这条信息是否值得长期保存”然后用结构化字段保存例如用户偏好、订单信息、任务状态。写代码时可以在每一轮对话后增加一个“记忆抽取”步骤让模型输出 JSON再决定是否写入{ save: true, type: user_preference, content: 用户偏好简洁回复, expire_at: null }这样比盲目写入所有消息更可控。9.2 记忆需要定期压缩与清理长期运行的系统记忆条目会不断膨胀。建议定期执行两类任务淘汰删除已经过期、错误或不再有价值的记忆合并把多条相似记忆交给模型整理成一条摘要降低冗余。压缩频率可以设为每天一次或者记忆条数达到阈值时触发。9.3 权限与隐私安全跨会话记忆最容易踩的坑是隐私泄露。用户在一段对话中透露了手机号下一次会话如果被另一个用户问出来就是严重事故。生产环境中记忆必须绑定用户 ID并在检索时强制加入权限过滤条件。同样多 Agent 共享记忆场景下要明确每个 Agent 的读写范围。客服 Agent 不应该读到内部库存系统的敏感状态。9.4 可观测性与回滚记忆模块一旦上线很难直接调试。建议在管理后台提供以下能力查看某个用户或会话的完整记忆列表查看每次请求实际召回了哪些记忆手动删除或修正某条记忆对记忆系统做 A/B 对比评估召回质量对最终回答质量的影响。9.5 不要把所有问题都抛给模型很多人喜欢让模型自己决定“记住什么”、“忘掉什么”但模型会犯错。更稳妥的设计是模型负责内容理解和抽取规则系统负责权限和生命周期管理。例如模型可以输出“这条记忆属于用户偏好”规则系统决定“保存 90 天后需要复核”而不是让模型直接执行删除操作。10. 总结与后续学习方向Agent 记忆问题不是单个模型能力能解决的它是“存储层 检索层 提示词组装层 生命周期管理”共同作用的结果。本文给出的最小实现虽然只有两百行代码但已经包含了生产级记忆系统的四个核心环节写入、向量化存储、召回注入、遗忘淘汰。你可以先把这个最小实现跑通再逐步替换组件把 Python 列表换成真正的向量数据库把命令行交互换成 Web API把简单的先进先出策略换成模型摘要合并。每一步替换都可以独立验证不会破坏整体架构。下一步值得深入的方向有三个一是记忆的评估体系如何量化“记忆对回答质量的影响”二是稀疏记忆与结构化记忆很多事实性信息不一定适合用向量检索三是多 Agent 场景下的记忆共享与权限隔离。建议你从第一个方向入手先建立一套自己的记忆评测集再谈优化。记忆系统一旦跑通Agent 的可用性会真正上一个台阶。