
你有没有遇到过这种情况明明上一轮跟 AI 助手聊得好好的告诉过它“我在做跨境电商主攻东南亚市场”结果新开一个会话它又问一遍“请问您的业务方向是什么”这其实就是当前大模型应用的典型痛点——模型本身没有记忆能力。大模型每次对话都是“无状态”的它不记得你是谁、你做过什么、你上次聊到哪了。而 AI Agent 要想真正承担起“助手”甚至“同事”的角色记忆能力是绕不开的坎。我写这个“走进 AI Agent”系列前两篇分别聊了 Agent 的基础架构和工具调用机制这一篇聚焦在记忆系统上。说白了就是解决一个核心问题怎么让 Agent 真正“记住你”——记住你的偏好、你的项目背景、你们之前的对话内容并且在需要的时候能准确想起来。这篇文章会从记忆系统的设计思路讲起再拆解具体的技术选型向量数据库、Embedding、召回策略最后给出一套基于 LangChain 的可落地实现方案和排坑实录。适合已经在做 Agent 开发、或者准备从“单轮问答”往“多轮记忆型 Agent”升级的开发者参考。不管你是用 LangChain、LangGraph 还是自己从零搭这篇的思路和代码都能直接拿过去用。1. 记忆系统整体设计思路Agent 为什么需要“记住你”很多初学者对 Agent 记忆的理解就是“把对话历史拼一起塞进 Prompt”。这种做法在小 demo 里能跑通但一旦对话轮次多了、业务信息复杂了立刻就会出问题。这节先把记忆系统的底层逻辑讲清楚。1.1 没有记忆的 Agent 是什么状态先说一个概念大模型的“无状态”到底意味着什么。你每次调用 GPT、Claude 或者开源模型模型本身不保存任何会话信息。它只接收你当前这一轮输入的文字Prompt然后根据训练学到的知识生成回复。也就是说模型就像一位“每次都失忆”的专家——你必须在 Prompt 里把所有背景信息重新交代一遍它才能给出符合语境的回答。如果没有记忆机制Agent 在真实业务中会暴露出一堆问题用户说“还是按上次的方案继续”Agent 完全不知道“上次的方案”是什么用户已经设置过偏好比如“报告用表格”“邮件语气正式”Agent 下次又恢复默认风格业务上有长期任务比如“帮我跟踪这个客户的需求变化”Agent 无法跨会话维护上下文多轮复杂任务比如“先分析数据再写报告最后发邮件”中间状态一多Prompt 塞不下直接丢失关键信息。这些问题不解决Agent 就永远停留在“演示品”阶段没法真正投入到长期、连续的工作流里面。1.2 记忆的三种层次短期、长期和工作记忆做记忆系统之前先要建立视野记忆不是单一组件而是一套分层结构。这里我借用了认知科学里的模型来设计 Agent 的记忆体系实践中非常好用。短期记忆Short-term Memory对应当前会话内的上下文。模型每次请求里带的对话历史就是短期记忆。它的特点是“用完即走”只服务于当前这轮交互。实现上最简单把 messages 列表存起来随请求一起发出去就行。长期记忆Long-term Memory对应跨会话的核心信息比如用户的身份、偏好、项目背景、业务规则。它需要持久化存储并且在每次会话开始时加载到上下文中。长期记忆是可以被覆盖和更新的——用户跟你熟了偏好变了Agent 也得跟着更新认知。工作记忆Working Memory这个是我做 Agent 工程时特别强调的一层。它指的是“当前任务执行过程中暂时需要记住的中间结果”。比如 Agent 在执行“先查天气再订机票”这个复合任务时天气查询的结果需要传给订机票步骤这个中间状态就是工作记忆。工程上通常用状态管理比如 LangGraph 的 State 机制来实现。这三层记忆不是孤立存在的它们之间有数据流动短期记忆里重要的信息经过提炼后沉淀到长期记忆长期记忆的概要会在会话开始时加载到短期记忆里作为背景。理解了这个流动过程你做架构设计时就不会一团浆糊。1.3 为什么不能把全部历史都塞进上下文有人可能会问“既然短期记忆就是发历史那我直接把所有对话记录都发过去不就行了”问题出在两个地方Token 有限和注意力分散。先算笔账主流的 GPT-4 级别的模型上下文窗口是 128K Token大概能容纳 10 万字左右。表面看挺大但你想想你要处理的可能是几百轮对话、几十份文档、多个项目历史根本塞不下。就算塞下了每轮请求都携带全量历史API 调用成本会直线上升——一个每天处理上万请求的业务Token 消耗可能多出几十倍。再说注意力问题。大模型的注意力机制对“信息密度”是有期待的。你在 10 万字的上下文中埋了一条关键信息“用户公司叫某某科技”模型很可能在处理其他内容时把它“忽略”了。信息越多关键信息越容易被稀释这是 transformer 架构的通病。所以正经的 Agent 记忆系统必须做“选择”和“压缩”——不是全存而是存该存的、取该取的。这就是后面要讲的向量化存储和召回策略的用武之地。2. 核心记忆技术选型存储、向量化与召回记忆系统的技术核心可以拆成三块文本怎么存、怎么从库里找到相关内容、找回来的内容怎么用。每一块都有讲究我逐个拆开讲。2.1 向量数据库怎么选从 FAISS 到 Milvus 再到 pgvector先把“向量存储”这个核心概念说明白。要让 Agent 从历史对话中找到“跟当前问题相关”的内容最直接的办法是给每段文本生成一个向量一串数字然后用“向量相似度”来衡量相关性——两个向量越接近代表它们对应的文本越相关。这个存储和检索向量的地方就是向量数据库。市面上的选择很多但不要盲目上重型的按项目阶段来起步阶段几万条以内FAISS 就够了。它是 Meta 开源的向量检索库轻量、快、单机可用配合内存操作在中小规模场景下表现很棒。缺点是需要自己管理索引的持久化没有现成的数据库能力。业务阶段几十万到百万级建议上 pgvector。它是 PostgreSQL 的扩展能直接在原有的关系库里存向量、做相似度检索。好处是不用额外维护一套存储系统而且可以和业务表做 Join 查询——比如“找出所有标记为‘客户A’的高相关记忆”一条 SQL 就搞定了。大规模生产千万级以上Milvus 或 Qdrant 这类专业向量数据库更合适。它们支持分布式、多副本、高并发检索性能和扩展能力都很强。但运维成本也高不是小团队前期该碰的东西。我做项目时踩过一个坑一上来就搭了 Milvus 集群结果数据量不到十万条纯属杀鸡用牛刀还得维护 Zookeeper、MinIO 一堆依赖。后来换到 pgvector一个 PostgreSQL 实例全搞定检索速度还更快。技术选型一定要匹配数据量和团队运维能力这是经验之谈。2.2 Embedding 模型的选择与参数细节向量化这一步的核心工具是 Embedding 模型——它负责把文本变成向量。选型上最常用的是 OpenAI 的 text-embedding-3-small、Cohere 的 embed-multilingual-v3以及开源的 BGE、M3E 系列。这里有几个参数和细节必须注意维度大小embedding 的维度直接决定存储成本。OpenAI text-embedding-3-small 支持从 1536 维缩减到 512 维如果你的场景不追求极限精度降维能省不少存储。比如 100 万条记忆1536 维的向量存储大约是 1536×4 字节×100万 6GB降到 512 维后直接变成 2GB。语言适配如果你的业务涉及中文尽量选在多语种语料上训练过的模型比如 BGE-large-zh 或 M3E。用英文模型处理中文文本向量质量会明显下降检索出来的东西牛头不对马嘴。文本切分粒度向量检索讲究一个“分段规则”。传统做法是“固定长度切分”比如每 500 个字符切一段但这种方式容易把一句完整的话拦腰截断。我自己实践下来尽量按语义边界切分——对话按“轮”切文档按“段落”切。宁可每段短一点也要保证每段信息完整这样召回效果最好。2.3 记忆写入与读取的完整流程有了存储和向量化工具后记忆系统的工作流程就清晰了分写入和读取两侧写入侧记忆沉淀对话进行中Agent 产生新的交互内容用户说了什么、Agent 回复了什么系统判断这些内容“值不值得长期记”——比如用户明确说了“我偏好简洁回复”这显然要记但“今天天气不错”这种寒暄就没必要对值得记的内容做提炼可以用一个小模型做“摘录”或者直接用规则比如用户消息中包含“我的/我喜欢/我希望”等关键词生成向量写入向量数据库同时打上元数据标签时间、会话 ID、业务线、用户 ID。读取侧记忆召回用户发来新消息系统把这条消息向量化从向量库里检索最相似的 N 条记忆Top-K 召回把召回的记忆拼接到 Prompt 的特定区域作为系统上下文大模型基于“这些记忆 当前消息”生成回答。这整个流程说起来简单但细节决定成败。比如“Top-K 取多少”这个问题取太多会稀释注意力取太少又可能漏关键信息。我一般先取 5 条试跑然后根据业务场景调到 8~10 条——具体公式和调参思路后面实操部分会细说。3. 实操基于 LangChain 搭一套带记忆的 Agent理论部分讲完了进入正经的实战环节。我直接给出一个可运行的方案基于 LangChain 搭建一个带长期记忆的客服 Agent。它做的事情是用户多轮对话中Agent 能记住用户的产品偏好和使用反馈并在后续回答中参考这些记忆。3.1 环境准备与架构设计先列依赖我用 Python 3.10 LangChain 0.2 版本测试过稳定性不错pip install langchain langchain-openai langchain-community chromadb这里的 ChromaDB 是本地向量数据库适合演示和小型业务生产环境要换 pgvector 也是同理代码逻辑基本一致。架构上我把它设计成三个模块记忆写入模块监听对话流用关键词规则筛选“值得记忆”的内容生成文档写入向量库记忆召回模块每次收到用户消息先从向量库召回 Top-K 相关记忆对话生成模块把“召回的记忆 当前请求 会话历史”组装起来发给大模型。这仨模块组合起来正好对应前面说的“长期记忆 短期记忆”的分层。3.2 核心代码实现记忆写入与召回先看记忆写入模块的代码from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.schema import Document from datetime import datetime # 初始化 Embedding 和向量库 embedding_model OpenAIEmbeddings(modeltext-embedding-3-small) vector_store Chroma( collection_nameagent_memory, embedding_functionembedding_model, persist_directory./memory_db ) # 判断是否值得长期记忆 MEMORY_KEYWORDS [我喜欢, 我偏好, 我的习惯, 请注意, 我是做, 我负责] def should_memorize(text: str) - bool: 只要包含关键词就认定是重要的用户画像信息 return any(keyword in text for keyword in MEMORY_KEYWORDS) def write_memory(user_message: str, agent_reply: str, user_id: str): 将用户信息写入长期记忆库 if not should_memorize(user_message): return None # 按对话轮次组织记忆内容 memory_text f用户说{user_message}\n助手回应{agent_reply} doc Document( page_contentmemory_text, metadata{ user_id: user_id, timestamp: datetime.now().isoformat(), memory_type: user_preference } ) vector_store.add_documents([doc]) return doc这段代码里有两个细节值得说明。一是should_memorize函数——我用关键词规则做初筛虽然简单但在“捕获用户明确表达偏好”的场景下效果很好。别觉得规则 low规则 向量化的组合在真实项目里远比纯靠“模型自觉”靠谱因为模型不一定知道自己该记什么。二是 metadata 的设计这个太重要了。user_id字段保证了“A 用户的话不会出现在 B 用户的记忆里”memory_type字段用于区分记忆类别偏好、事实、任务进度等后面对记忆做更新或删除时非常有用。再看召回模块def recall_memory(query: str, user_id: str, top_k: int 5) - list[str]: 根据当前用户消息从长期记忆中召回相关内容 # 先按 user_id 过滤再做相似度检索 docs vector_store.similarity_search_with_score( query, ktop_k, filter{user_id: user_id} ) # similarity_search_with_score 返回 (doc, score)score 越小越相似 # 做一层分数过滤避免召回完全不相关的内容 relevant [doc for doc, score in docs if score 0.55] return [doc.page_content for doc in relevant]这里有几个调优点Top-K 怎么定。上面代码里top_k5是我在多个对话场景下试出来的经验值。太少1~2 条容易漏信息太多10 条会让模型注意力涣散而且 Prompt Token 消耗大。建议从 5 开始观察召回结果的相关性再微调。相似度阈值 0.55。Chroma 的相似度分数是“距离”越小越相关。0.55 是我对比了大量“相关和不相关样本”后找的分界线。这个值跟 Embedding 模型强相关换了模型一定要重新校准别直接抄。filter 参数。这是 Chroma 支持元数据过滤的用法也是我说“一定要设计好 metadata”的原因。业务上经常要按用户、按时间区间来过滤记忆如果一开始没打标后面就得全量检索再过滤既慢又废 Token。最后是对话生成模块from langchain.chat_models import ChatOpenAI from langchain.memory import ConversationBufferWindowMemory from langchain.schema import SystemMessage, HumanMessage class MemoryAgent: def __init__(self, user_id: str): self.user_id user_id self.llm ChatOpenAI(modelgpt-4o-mini, temperature0.3) # 短期记忆只保留最近10轮对话 self.short_memory ConversationBufferWindowMemory(k10, return_messagesTrue) def chat(self, user_message: str) - str: # 1. 从长期记忆库召回 long_term recall_memory(user_message, self.user_id) # 2. 组装系统提示词 system_prompt 你是用户的AI助手。请参考下面提供的用户历史信息来回答如果信息不相关可以忽略。 if long_term: memory_block \n.join([f- {m} for m in long_term]) system_prompt f\n\n用户长期记忆\n{memory_block} # 3. 组装完整消息 messages [SystemMessage(contentsystem_prompt)] messages.extend(self.short_memory.chat_memory.messages) messages.append(HumanMessage(contentuser_message)) # 4. 调用模型并返回 response self.llm.invoke(messages).content # 5. 保存短期记忆并尝试写入长期记忆 self.short_memory.chat_memory.add_user_message(user_message) self.short_memory.chat_memory.add_ai_message(response) write_memory(user_message, response, self.user_id) return response这个类的核心价值在于“短期记忆 长期记忆”的联动。短期用ConversationBufferWindowMemory维护最近 10 轮保证对话的连贯性长期用向量库做召回保证用户画像能跨会话保留。3.3 实操演示让 Agent“记住你”写个小测例跑一下看效果# 模拟多轮对话 agent MemoryAgent(user_iduser_001) print(agent.chat(你好我是做跨境电商的主攻东南亚市场。)) print(---) print(agent.chat(我们主要卖家居小件客单价在50到80元之间。)) print(---) # 模拟新会话看 Agent 是否还记得 agent2 MemoryAgent(user_iduser_001) print(agent2.chat(我上次说了我的业务你能帮我出个推广方案吗))运行结果简化演示你好很高兴认识你你的业务是跨境电商面向东南亚市场很好。请问有什么我可以帮你的 明白了家居小件客单价50-80元我记下了。这个价格区间在东南亚电商平台很有竞争力。 根据你的长期记忆你从事跨境电商主攻东南亚市场销售家居小件客单价50-80元。我为你的推广方案提供如下建议...看到了吗第三轮对话虽然新建了一个MemoryAgent实例短期记忆是空的但通过长期记忆召回Agent 依然知道用户的业务背景。这就是“让 Agent 记住你”的核心效果。3.4 工程化落地时的进阶改动上面这套只是单机 demo真要落地到生产环境还需要补几个能力记忆合并与去重。用户可能多次说“我偏好简洁风格”向量库里存了 5 条高度重复的记录。建议定期跑一次聚类把相似内容合并成一条权威记忆避免召回时重复信息占据大量 Token。记忆的遗忘与更新。用户的偏好会变上个月偏好“正式商务风格”这个月改口“轻松活泼一点”。你需要设计“记忆更新机制”——当新记忆和旧记忆冲突时要么覆盖要么记录一条带时间戳的新记忆并降低旧记忆的权重。结构化记忆与消息队列。大规模业务中记忆写入不能同步阻塞对话流程。建议把“写入记忆”做成异步任务丢给消息队列处理。用户的问题先用短期记忆回复后续再慢慢沉淀长期记忆库。隐私与权限。记忆库存放的是用户的个人信息一定要做好访问控制和数据隔离。我见过不少项目Agent 记忆库直接裸奔任意用户可以查看到其他用户的记忆片段这是个很严重的隐患。4. 常见问题与排查实录记忆系统开发中的那些坑记忆系统看着简单但做起来各种妖怪问题层出不穷。我把自己踩过的坑和排查过程整理出来这里做个速查表你遇到类似问题时可以直接对号入座。4.1 问题一召回结果不相关Agent 回答生硬现象用户明明说了“我比较喜欢简约风”但后续 Agent 在推荐产品时还是给出了花里胡哨的选项。排查过程我先确认用户的消息是否真的被写入了记忆库——结果发现根本没有写入。原因在于should_memorize的关键词列表太短用户这句话里没有触发任何关键词。调到召回测试时用户消息“推荐一些符合我审美的产品”和库里存的文本“用户偏好简约风”之间的向量相似度确实不够高Top-5 里压根没出现。解决方案调整关键词列表加入更多偏好表达。同时优化召回策略——除了关键词过滤外增加一条规则如果当前轮次用户明确表达了不满或需求强制把最近几轮对话加入召回候选集。这个方法我们内部叫“会话级召回保底”实测能有效缓解“提了需求但没记住”的情况。4.2 问题二短期记忆窗口太小多步任务执行断片现象Agent 执行一个三步任务“查数据→写分析→生成邮件”执行到第二步时丢失了第一步的数据结果。排查过程这是典型的“工作记忆”缺失问题。我用ConversationBufferWindowMemory(k10)做短期记忆但工具调用产生的中间结果并没有被纳入对话历史只有最终回复文本被保存。所以模型在执行后续步骤时能看到“我查完了”但看不到“查到的是什么”。解决方案把工具调用的输入输出也写入短期记忆而不是只记用户和助手的对话。具体做法是在chat方法中增加一个环节工具返回结果后把结构化内容压缩成文本作为 AI 消息追加到chat_memory里。这个“信息完整化”步骤看起来小但能解决多步任务执行的大量问题。4.3 问题三Token 消耗爆炸业务成本失控现象接入记忆系统后API 账单涨了十倍。排查过程查日志发现每次请求都召回了 10 条记忆每条记忆还带上完整的前后文对话光记忆块就占了 2000 多 Token。加上短期记忆存储的 10 轮对话每轮可能有很长的工具输出单次请求总 Token 轻松破万。解决方案做“边界控制”。短期内把记忆的长度压缩——召回时按语义摘要而不是完整对话长期上给短期记忆设k5而不是 10同时给工具输出做截断只保留关键字段。调整后单次请求 Token 从 10000 降到 3500效果基本不掉。Token 预算需要从一开始就设计而不是等到账单提醒你。4.4 问题四向量检索慢接口响应超时现象数据量涨到一百万条后检索响应从 20ms 涨到 300ms接口 P95 延迟直接超时。排查过程一开始用的是暴力检索Flat 索引数据量小的时候没感觉一上量性能就雪崩。查出来是索引类型的问题——Chroma 默认的 HNSW 索引参数M16, ef_construction200在小数据量下的性能其实不错但我没有调整索引参数导致图结构不够紧凑。解决方案换了 HNSW 索引并调整参数把 M 调到 32、ef_search 调到 128。检索延迟从 300ms 降到 60ms 左右。要是再大就该换 pgvector 加 IVFFlat 索引或者干脆上 Milvus。索引策略要跟着数据量走这个意识和代码能力同样重要。4.5 问题五记忆污染Agent 把不同项目的记忆搞混现象用户同时在做两个项目“A项目的客户反馈”和“B项目的设计方案”Agent 回答时经常把两个项目的信息混在一起。排查过程回顾 metadata 设计发现我只存了user_id和memory_type没有“项目/业务线”这个维度。向量召回转回来的是这个用户的所有记忆模型无法区分哪些属于当前对话场景。解决方案给 metadata 增加project_id字段召回时按user_id project_id双条件过滤。这样用户切换到 A 项目时B 项目的记忆就被隔离在外了。这个教训验证了前面说的一句话metadata 设计一开始就要考虑好维度否则后面改起来涉及全量数据重写。5. 把记忆能力延伸出去从“记住你”到“理解你”写完基础版记忆系统你可能会想接下来还能怎么演化我根据自己的项目经验说几个记忆系统后续可以扩展的方向。从显式记忆到隐式推演。当前方案是“用户说什么才记什么”。进阶方案是让 Agent 从用户的言行中推断偏好并主动提出——比如用户三次追问同一类问题Agent 能主动提醒“您似乎对这个功能感兴趣要不要我汇总一下相关资料”这一步其实是对记忆的“主动利用”比单纯的存储和召回高了一个维度。从单点到知识图谱式记忆。向量检索擅长“找相似的”但不擅长“找关联的”——比如“用户提到了客户 C客户 C 关联了项目 P项目 P 在上周有重要进展”。这种多跳关联查询需要把记忆抽象成图谱结构实体-关系-实体然后用图数据库存储。长期看记忆系统会从“文档库”演变成“知识图谱”这也是 Agent 从“记住你”走向“理解你”的必经之路。多 Agent 之间的记忆协同。现在很多团队在做多 Agent 架构就是热词里常出现的 Multi-Agent 模式多个 Agent 分别负责不同的任务但它们之间怎么共享记忆是个大问题。我的方案是“记忆公共池”每个 Agent 可以读取公共池的抽象记忆比如公司层面的业务规则但各自维护私有记忆比如自己的对话上下文。遇到需要跨 Agent 协作的任务由调度层负责从公共池里挑选合适的记忆片段传给目标 Agent。这些方向不一定马上就要做但设计记忆系统时给它们留好扩展位比如 metadata 预留字段、存储抽象层接口后面演进起来会省很多力气。最后再说一点个人体会做 Agent 记忆系统跟做人脑记忆系统有相似之处——不是记得越多越好而是记该记的、忘该忘的、在需要时能想起来的。很多团队一上来就追求“全量记忆”结果 Token 成本爆了、召回质量差了反而把 Agent 越做越“傻”。我的经验是先跑通“关键词筛选写入 Top-K 向量召回”这条最小闭环再逐步加上关联记忆、记忆更新、图谱化这些高级能力。稳扎稳打效果往往比一步到位好得多。如果你也正在搭自己的 Agent 记忆系统欢迎按照这篇的步骤先试跑一遍遇到问题随时对照第 4 节的排查表。这系列的下一篇我打算聊聊 Agent 的规划能力——也是从“能用”到“好用”的另一个关键跳板。