
做了大半年Agent项目被问得最多的一句不是“你这Agent能干嘛”而是“它怎么聊完就忘”。上午刚跟用户确认过公司团队规模下午再问就得重新介绍一遍昨天还盯着一个需求清单推进今天重启会话就全不记得了。这种体验放到产品里用户不会觉得“AI有上下文限制”只会觉得“这玩意儿记性真差”。这篇是“走进AI Agent”系列的第三篇前两篇分别聊了Agent的整体架构和任务编排这篇专门解决“记忆”问题。标题写得很直白——让Agent记住你。但真做起来这里面的坑比想象中多得多记忆放哪、什么时候写入、什么时候检索、存久了怎么压缩、检索错了怎么纠正每一环都是独立的工程问题。这篇文章会从记忆的本质讲起拆清楚记忆和上下文、知识库的关系再给出一套可以直接落地的记忆模块实现方案包含写入、检索、压缩、清理的完整逻辑和代码骨架最后整理我在实际项目中踩过的坑和排查思路。不管你是刚接触Agent开发还是已经在做生产级应用这篇都能让你少走不少弯路。1. Agent为什么需要记忆从“金鱼脑”到“记事本”1.1 大模型的“金鱼记忆”困境先说一个底层事实大语言模型本身是没有记忆的。模型在训练完成后参数就固定了它不保存任何用户对话状态。每次你调用接口它看到的就是你这次传过去的全部内容——包括系统提示词、历史消息、用户输入一句话不多也一句话不少。这不是模型“笨”而是它的工作原理决定的。Transformer架构通过注意力机制来处理输入模型生成回复时能“看到”的内容完全取决于当前上下文窗口里放进了什么。通俗点讲它就像是高峰期坐柜台的客服每来一位客户都是第一次见面所有背景信息都靠你递过去的那张单子。所以要做带记忆的Agent本质上不是“让模型记住”而是我们自己给它准备一个记事本。模型负责读、理解、推理我们负责把历史信息整理好、存起来、在合适的时候再塞回上下文里。这个记事本怎么设计就是记忆系统的全部学问。1.2 记忆、上下文、知识库到底啥关系很多刚开始做Agent的同学会把“上下文”“知识库”“记忆”混为一谈但它们是三码事。我先用一个比喻讲清楚上下文是“对话现场”。每次请求时放在Prompt里那几千几万个token是模型当前能看到的一切。知识库是“公司资料室”。里面存放的是产品文档、运营手册、FAQ这类静态资料通过检索把相关资料临时“复印”一份放到对话现场。记忆是“客户台账”。里面记录的是这个用户和你打交道的历史他喜欢什么、他说过什么、你上次答应了他什么、任务进展到哪一步。三者的核心区别在于时效性和归属。知识库是静态的、面向所有用户的记忆是动态的、跟着具体用户走的上下文则是它们汇合的地方——每次请求时我们从知识库检索相关文档片段从记忆库里检索相关历史信息和当前对话一起拼成Prompt。维度上下文知识库记忆内容来源本次会话动态拼接预先整理好的文档历史交互的记录更新频率每轮都变低频更新每次对话后可能更新归属范围单次请求内全体用户共享按用户/会话隔离存储位置Prompt/API参数中向量库源文件数据库向量库理解了这个关系你就能看懂市面上的Agent框架为什么都有“Memory”这个独立模块——它和RAG检索增强生成是两条线解决的问题完全不一样。2. 记忆的分类别把所有信息堆进一个篮子2.1 工作记忆与长期记忆做记忆系统之前先得学会给记忆分门别类。最基础的一层划分是工作记忆Working Memory和长期记忆Long-term Memory。工作记忆对应的是“当前这个对话任务里正在处理的信息”。比如用户正在做一个活动策划截止时间下周五、预算两万、目标人群是大学生——这些信息在本次会话里反复被引用但活动结束之后基本就没用了。这类信息的特点是高频访问、短期有效、量大。长期记忆则是对用户长期有价值的画像和事实。比如用户是产品经理、在SaaS公司工作、偏好简洁的回答风格、上个月做过一个类似的营销活动、当时踩过什么坑。这类信息的特点是低频访问、长期有效、量小但精。刚上手的时候很多人会犯一个错误把工作记忆和长期记忆混在同一个表里。结果就是长期记忆被大量临时信息污染检索时召回一堆没用的历史垃圾模型反而被带偏。我现在的做法是分两张表存工作记忆用Redis或内存缓存长期记忆落数据库加向量索引两张表通过会话ID和用户ID关联各管各的。2.2 情景记忆、语义记忆与程序记忆认知科学里把人类记忆分成好几类放在Agent里同样有对应的设计思路这里介绍最常用的三种**情景记忆Episodic Memory**记录的是“发生过什么”。对Agent来说就是过去的对话记录、用户的行为轨迹、任务的执行过程。比如用户上周问过“怎么优化广告投放预算”Agent给出了ABC三个方案用户最后选了B。这类记忆的价值在于让Agent能基于历史经历做个性化延续而不是每次都从零开始。**语义记忆Semantic Memory**记录的是“用户是什么样的人”。这是从情景记忆里提炼出来的事实、偏好、画像。比如通过用户多次询问预算问题可以提炼出“用户关注成本控制”这个特点通过用户每次都说“回复短一点”可以提炼出“偏好简短回答”。语义记忆不是原始记录的堆砌而是抽象出来的结论。**程序记忆Procedural Memory**记录的是“这类任务怎么做”。在Agent里表现为可复用的流程、模板、工具调用方式。比如用户每次做竞品分析都要求包含市场规模、竞品定位、SWOT三个部分Agent就可以把这个套路固化为一个流程模板下次直接按这个结构执行。在设计上我的建议是情景记忆存最原始的记录或经过去重后的摘要语义记忆存提炼后的用户画像程序记忆存可复用的流程模板和Tool调用经验。三者可以用标签区分也可以分成三个集合存储。分类越清楚检索时过滤条件就越精确召回质量越高。2.3 记忆的三大操作写入、检索、遗忘记忆系统本质上只有三个核心操作写入Write、检索Read、遗忘Forget。听起来简单但每个操作背后都有设计决策。写入要回答的问题是哪些信息值得记是每轮对话都记还是只有关键信息才记单轮记录要不要做摘要信息是原样存储还是提炼后存储我的经验是直接用大模型做“记忆提取”——每一轮对话结束后让模型从对话中提取出值得长期记住的事实和画像标签结构化后写入记忆库。这样避免把大量无关闲聊都存进去。检索要回答的问题是哪些记忆和当前相关怎么判断相关性一次性塞进去多少条合适核心方法是embedding相似度检索把当前对话向量化和记忆库里的条目做相似度比对然后取TopK条放入上下文。但这里有个细节单纯相似度检索会把大量重复的、过时的记忆捞出来所以还要加时间衰减和相关性重排。遗忘是最容易被忽略的。几乎所有初版记忆系统最后都会遇到同一个问题——记忆库膨胀。用户三个月聊了上千条向量检索越来越不准Prompt被塞得越来越长。这时候就需要“遗忘机制”按时间衰减权重、定期合并重复条目、把过时信息归档清理。好的记忆系统不是记得越多越好而是记得准、来得及、忘得掉。3. 手把手搭一个带记忆的Agent完整实现3.1 方案选型从零写还是用框架做记忆模块第一步是选存储方案。市面上常见的选择有四种按复杂度递增排列方案一内存字典。用一个全局Dict存用户ID到记忆列表的映射。优点是零依赖、实现最快缺点是一重启就全丢、不支持持久化、单机容量有限。适合本地demo验证不适合任何线上场景。方案二本地文件/SQLite。按用户ID存成JSON文件或SQLite表。优点是简单可靠、单文件备份方便缺点是百万级数据量后检索性能不够但中小项目完全够用。方案三关系型数据库向量检索。用MySQL/PostgreSQL存结构化信息用户画像、标签用向量数据库Milvus、Qdrant、Chroma存非结构化记忆片段。这是目前生产环境最常见的组合数据一致性好、检索性能强。方案四第三方记忆服务/框架内置Memory。比如LangChain的Memory模块、LangGraph的MemorySaver、字节的Coze记忆功能等。优点是省事但定制性差遇到复杂业务逻辑时会受框架限制。我个人推荐中小团队选“PostgreSQL向量插件业务表”这个组合。PostgreSQL的pgvector插件可以直接在熟悉的数据库里搞定向量检索不用额外维护一套向量库部署运维成本低。等数据量真正到了上亿规模再考虑单独上Milvus不迟。3.2 记忆写入哪些值得记、怎么记写入是整个记忆系统里最需要动脑子的部分。我见过最简单的做法是把每轮对话全部存进去稳是稳但后果是检索信噪比极低——用户随口说的一句“今天天气不错”也会被存进去后续检索时被反复召回浪费token还干扰模型判断。我推荐的做法是“两层写入”原始留痕、精华提取。原始留痕是指把每轮用户消息和Agent回复做清洗后存入工作记忆表主要用于短期会话内恢复上下文比如用户说“刚才给我推荐的那本书是哪个出版社的”。这类查询靠原始记录就能回答。精华提取是让模型对每一轮对话做一次“记忆提炼”判断这段对话里有没有值得长期记住的信息。如果值得就把信息结构化后写入长期记忆表。提炼逻辑我一般用结构化Prompt来约束模型输出extraction_prompt 请从以下对话中提取值得长期记忆的用户信息。 只提取客观事实和明确表达的用户偏好不要推测。 如果没有值得记住的信息返回空列表。 对话: {conversation} 请以JSON格式返回格式如下 {{ facts: [用户是产品经理, 用户公司规模约100人], preferences: [喜欢简洁回复, 倾向使用图表说明], pending_tasks: [下周三前完成竞品分析报告] }} 这段逻辑放在每次对话完成之后异步执行不阻塞主回复流程。注意几点只提取客观事实和明确偏好不要让模型从语气语调里过度推测用户画像。待办任务pending_tasks单独标记这类记忆时效性很强需要单独的时间提醒和过期清理机制。每条记忆打上时间戳和来源会话ID方便后续溯源和更新。3.3 核心代码一个可落地的记忆管理器下面给出一个基于Python的极简记忆管理器实现融合了向量检索和结构化存储。这里为了演示清晰我用SQLite存结构化信息用chromadb做向量索引实际生产可以替换成PostgreSQL的pgvector。import json import sqlite3 import uuid from datetime import datetime, timedelta from typing import List, Dict, Any import chromadb from chromadb.utils import embedding_functions class MemoryManager: def __init__(self, db_path: str memory.db, collection_name: str long_term): # 初始化SQLite存结构化记忆 self.conn sqlite3.connect(db_path, check_same_threadFalse) self._init_tables() # 初始化向量库存非结构化记忆 self.chroma_client chromadb.PersistentClient(path./chroma_data) self.embed_fn embedding_functions.DefaultEmbeddingFunction() self.collection self.chroma_client.get_or_create_collection( namecollection_name, embedding_functionself.embed_fn ) def _init_tables(self): self.conn.execute( CREATE TABLE IF NOT EXISTS long_term_memory ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, content TEXT NOT NULL, memory_type TEXT NOT NULL, importance INTEGER DEFAULT 5, created_at TIMESTAMP, last_accessed_at TIMESTAMP ) ) self.conn.execute( CREATE TABLE IF NOT EXISTS working_memory ( session_id TEXT NOT NULL, user_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP ) ) self.conn.commit() def save_working_memory(self, session_id: str, user_id: str, role: str, content: str): 保存工作记忆原始对话 self.conn.execute( INSERT INTO working_memory (session_id, user_id, role, content, created_at) VALUES (?, ?, ?, ?, ?), (session_id, user_id, role, content, datetime.utcnow()) ) self.conn.commit() def save_long_term_memory(self, user_id: str, content: str, memory_type: str fact, importance: int 5): 保存长期记忆同时写入SQLite和向量库 memory_id str(uuid.uuid4()) now datetime.utcnow() # 写入SQLite self.conn.execute( INSERT INTO long_term_memory (id, user_id, content, memory_type, importance, created_at, last_accessed_at) VALUES (?, ?, ?, ?, ?, ?, ?), (memory_id, user_id, content, memory_type, importance, now, now) ) self.conn.commit() # 写入向量库用于语义检索 self.collection.add( ids[memory_id], documents[content], metadatas[{ user_id: user_id, memory_type: memory_type, importance: importance, created_at: now.isoformat() }] ) return memory_id def retrieve_memory(self, user_id: str, query: str, top_k: int 5, min_recency_days: int 90) - List[Dict[str, Any]]: 混合检索向量找出语义相似的再过滤用户和时间衰减 results self.collection.query( query_texts[query], n_resultstop_k * 3, # 多召回一些用于后置过滤 where{user_id: user_id} ) memory_items [] now datetime.utcnow() if results[ids] and results[ids][0]: for idx, mem_id in enumerate(results[ids][0]): doc results[documents][0][idx] meta results[metadatas][0][idx] distance results[distances][0][idx] # 时间衰减超过min_recency_days的记忆相关性打个8折 created_at datetime.fromisoformat(meta[created_at]) days_old (now - created_at).days if days_old min_recency_days: distance * 1.2 # 重要度加权importance越高越容易被召回 adjusted_distance distance * (1 - int(meta[importance]) / 20) memory_items.append({ id: mem_id, content: doc, memory_type: meta[memory_type], importance: meta[importance], adjusted_distance: adjusted_distance }) # 根据调整后的距离排序取top_k memory_items.sort(keylambda x: x[adjusted_distance]) return memory_items[:top_k] def update_last_accessed(self, memory_id: str): 更新记忆的最后访问时间用于热度排序 self.conn.execute( UPDATE long_term_memory SET last_accessed_at ? WHERE id ?, (datetime.utcnow(), memory_id) ) self.conn.commit() def forget_old_memories(self, user_id: str, max_days: int 180): 遗忘机制清理长期未访问的旧记忆 cutoff datetime.utcnow() - timedelta(daysmax_days) rows self.conn.execute( SELECT id FROM long_term_memory WHERE user_id ? AND last_accessed_at ?, (user_id, cutoff) ).fetchall() ids_to_remove [row[0] for row in rows] if ids_to_remove: self.conn.execute( DELETE FROM long_term_memory WHERE id IN ({}).format( ,.join(? * len(ids_to_remove)) ), ids_to_remove ) self.conn.commit() self.collection.delete(idsids_to_remove) return len(ids_to_remove)这个类把记忆系统的核心能力都覆盖了工作记忆和长期记忆分表存储、向量检索加时间衰减和重要度加权的组合召回、长期未访问记忆的自动清理。代码可以直接跑起来做二次开发。3.4 把记忆模块接进Agent主循环有了记忆管理器接下来就是把它集成到Agent的处理流程里。标准的带记忆Agent主循环大概是这样的def agent_run(memory: MemoryManager, session_id: str, user_id: str, user_input: str): # 1. 检索相关记忆 relevant_memories memory.retrieve_memory(user_id, user_input, top_k5) # 2. 取最近工作记忆最近的对话历史 conn memory.conn recent_history conn.execute( SELECT role, content FROM working_memory WHERE session_id ? ORDER BY created_at ASC LIMIT 20, (session_id,) ).fetchall() # 3. 构建Prompt memory_block \n.join([ f[记忆] {mem[content]} for mem in relevant_memories ]) history_block \n.join([ f{role}: {content} for role, content in recent_history ]) prompt f 系统你是用户的专属助手请基于用户的历史记忆和对话历史回答问题。 如果历史记忆与当前问题矛盾以当前对话内容为准。 {memory_block} {history_block} 用户{user_input} 助手 # 4. 调用大模型 response call_llm(prompt) # 替换成你用的LLM API # 5. 保存当前对话到工作记忆 memory.save_working_memory(session_id, user_id, user, user_input) memory.save_working_memory(session_id, user_id, assistant, response) # 6. 异步执行长期记忆提炼 extracted extract_long_term_memory(user_input, response) for fact in extracted[facts]: memory.save_long_term_memory(user_id, fact, memory_typefact) return response这段主循环的编排逻辑是关键先查记忆再拼Prompt最后再更新记忆。顺序不能乱——如果先生成再查记忆那就没法用记忆来影响回复了如果先存再查会把刚存的当前轮对话也当历史检出来造成信息回流。另外注意第5步和第6步的区别工作记忆每轮都存长期记忆用模型提炼后存。工作记忆相当于“会议纪要”长期记忆相当于“客户档案”两者定位不同写入频率也不同。4. 落地过程中的几个关键工程问题4.1 记忆污染无价值信息越积越多记忆系统上线跑了两周后最常遇到的第一个问题就是“记忆污染”——用户画像里全是没用的碎信息。比如用户说过一句“今天加班到十一点”模型就提炼出一条“用户经常加班”。这其实只是一次偶发事件却被打成了长期标签后续所有推荐、回复都会受到这错误记忆的影响。要解决这个问题我总结了几条实操经验提高提炼门槛。在提炼Prompt里明确要求“只有用户明确表达过的、跨多次会话的一致性信息才值得记录”单次对话中的临时状态不记录。给记忆加上置信度。可以在记忆字段里加一个confidence同一事实被多次确认就增加置信度。低于阈值的记忆不参与检索召回。定期做记忆复核。用另一个“记忆审计Prompt”定期检查记忆库剔除那些矛盾、过时、低价值的信息。这个可以设计成每晚跑一次的批处理任务。4.2 检索召回质量差怎么调参数向量检索的效果受好几个因素影响embedding模型的选择、相似度阈值、TopK数量。我在项目里常用的调试思路是换更好的embedding模型。很多中文场景下默认的text-embedding-ada-002或bge-large-zh的表现各不相同。中文语义检索建议优先试BAAI的bge系列在中文语料上通常比OpenAI的默认向量效果好。用评测集跑一轮对比看哪个在你这批数据上RecallK最高。调整相似度阈值。Chroma返回的是距离距离越小越相似。距离阈值设置太严会漏召回太松会进大量噪声。我一般用一组历史问答样本来标定阈值画出精度-召回曲线取曲线拐点附近的值。TopK不要贪多。我见过有人一上来就取TopK20结果Prompt里塞了三千多token的“相关记忆”模型反而抓不住重点。大多数场景TopK3到5就够。如果觉得信息不够优先优化检索质量而不是单纯加数量。4.3 记忆冲突新旧信息打架怎么办用户可能今天说“我最喜欢简洁的回答”明天又说“这段能详细一点吗”。记忆库里的不同条目之间会产生冲突直接把互相矛盾的历史都塞进Prompt模型会无所适从。我的处理方式是给记忆加一个“最后更新时间”字段检索结果里增加“新旧权重”——更新的记忆权重更高。在Prompt里也做了说明注意如果用户当前表达的内容与历史记忆矛盾请优先遵循用户当前的表达。这个提示看着简单但能明显降低模型在矛盾信息面前“和稀泥”的概率优先响应用户当下最明确的需求。4.4 记忆的隐私与权限边界记忆系统存储的是用户的隐私信息这一点在设计和合规层面都不能忽视。至少要关注三件事数据隔离。不同用户之间的记忆绝对不能串。检索时一定要带用户ID做过滤我在代码里已经加了这个条件绝不能只按向量相似度检索全部集合。用户控制权。产品里要给用户提供“查看记忆”“清除记忆”的能力。让用户能看到Agent记住了自己什么可以一键删除。这既是合规要求也直接影响用户对产品的信任度。敏感信息识别。记忆提炼时要检测和过滤密码、银行卡号、身份证号等敏感信息。我一般会在提炼Prompt之后加一道基于规则的正则过滤双保险。5. 踩坑实录与排查技巧5.1 工作记忆无限膨胀请求越来越慢这是带记忆Agent上线后最经典的性能问题。用户聊了200轮早期代码把全部历史都塞进Prompt每次请求的token数直线上升响应延迟从1秒一路涨到10秒账单也撑不住。排查思路很简单看每一轮请求的输入token数如果随时间持续增长说明工作记忆没有做截断管理。解决方法是给工作记忆加窗口策略——只保留最近N轮对话更早的对话通过离线摘要压缩成“早期对话梗概”存入长期记忆既保留关键信息又控制Prompt体积。def summarize_old_history(session_id: str, keep_rounds: int 20): 把超过keep_rounds轮的历史摘要后归档清空工作记忆 conn memory.conn # 1. 取出最近keep_rounds轮之前的全部历史 old_history conn.execute( SELECT role, content FROM working_memory WHERE session_id ? ORDER BY created_at DESC LIMIT -1 OFFSET ?, (session_id, keep_rounds * 2) ).fetchall() # 2. 用模型生成摘要 old_text \n.join([f{r}: {c} for r, c in reversed(old_history)]) summary call_llm(f请用300字以内总结这段对话的关键信息\n{old_text}) # 3. 摘要存入长期记忆 memory.save_long_term_memory( user_id, f会话历史摘要{summary}, memory_typesession_summary, importance6 ) # 4. 清空旧历史 conn.execute( DELETE FROM working_memory WHERE session_id ? AND created_at (SELECT created_at FROM working_memory ORDER BY created_at DESC LIMIT 1 OFFSET ?), (session_id, keep_rounds * 2) ) conn.commit()5.2 记忆检索出来一堆重复脏数据如果长期记忆写入逻辑没有做去重同一个事实可能会被存五六次。比如用户三次提到“我在做跨境电商”模型三次提炼出同一句话向量库里就有三条几乎重复的内容。检索时这三条被同时召回白白浪费token。解决思路是写入前先做一次相似度查重用新内容去向量库里检索一次如果和现有记忆的距离小于某个阈值就不重复写入而是更新原条目的最后访问时间和重要性。这个逻辑在save_long_term_memory入口处加一个判断即可。5.3 模型把历史记忆当成了事实依据回答被带偏这是个比较隐蔽的问题。当用户问一个超过当前时间的事件时模型可能会把记忆库里“用户上个月说过项目月底上线”当成有效信息直接回答“你的项目将在月底上线”完全没考虑现在已经过了期限或者用户后续有没有变更计划。要给记忆加上时间维度的敏感度。在Prompt组装时对每条记忆标注时间[2025-06-12 的记忆] 用户提到项目计划在月底上线并且在系统提示词里加入原则- 历史记忆仅作为参考如果当前时间和记忆中的时间不符需提示用户确认是否仍然有效。这样模型在引用历史记忆时会更自然地产生“这是当时的情况现在可能变了”的认知大幅减少“用过期记忆答当前问题”的尴尬。5.4 可复用的记忆质量评估模板最后分享一个我自己在项目里用来评估记忆系统的内部清单每项按1-5打分总分25分。每次改动记忆策略后跑一遍能快速发现退化的维度。评估项考察点评分写入准确性存入的记忆是否符合用户真实表达的语义无过度推测1-5写入去重率同一事实是否有重复条目1-5检索相关性召回的记忆是否与当前用户问题语义相关1-5抗噪声能力无效记忆被召回的频率1-5过期清理效果过期、矛盾记忆是否被及时遗忘或降权1-5这个表不用搞得很正式隔一两周让团队里不同的人拿一批测试对话跑一轮打分对比趋势就够了。记忆系统是个持续调优的过程不是一次性上线就完事的。你会发现随着用户使用时间的拉长记忆质量对产品体验的影响会越来越明显值得持续投入。我自己这几年做Agent项目下来最深的体会是记忆系统最后拼的不是算法多前沿而是产品设计的细节。什么该记、什么该忘、什么时候引用、什么时候怀疑这些决策直接决定用户觉得“这个AI真的懂我”还是“这个AI在瞎猜”。现在MCP协议和一些Agent框架也在把记忆能力往服务化方向推进以后做记忆的门槛会越来越低但理解和设计记忆的能力永远是做Agent应用的核心竞争力之一。