ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AI Agent记忆系统实战:从短期上下文到长期知识库

2026/9/10 5:35:16 拓冰建站 浏览量
AI Agent记忆系统实战:从短期上下文到长期知识库 最近在折腾AI Agent时我越来越确定一件事一个真正好用的Agent核心差别不在模型、不在提示词而在记忆。这句话不是随便说的。断断续续做了几个Agent项目之后我最直观的感受是之前搭的Agent很“聪明”但也很“健忘”。它每次和你对话都是“初次见面”上一轮聊过的东西下一轮就忘得干干净净。用户稍微换一种说法它就一脸茫然。这种体验严重拉低了Agent的实际使用价值——你不可能每次和它对话都把前因后果重新讲一遍那还不如自己搜资料。前两篇我分别聊了Agent的整体架构和工具调用的实现细节这篇专门把“记忆”这件事拆开揉碎讲透。包括Agent为什么需要记忆、短期记忆和长期记忆有什么区别、双网络记忆模型到底是怎么回事、在工程上怎么落地一套可用的记忆系统以及我在实践中踩过的坑。如果你正在做Agent开发或者准备把Agent接入自己的产品这篇应该能帮你少走不少弯路。1. Agent记忆的本质从“金鱼”到“懂你”先想一个问题为什么Agent需要记忆大模型本身不是很有知识吗模型的知识和Agent的记忆是两码事。模型的知识是训练阶段固化进去的像一个博闻强识却得了失忆症的人——你说什么他都能接上但他记不住你刚才说过什么。这种“有知识、无记忆”的状态在真实场景里非常致命。1.1 无状态架构的窘境我最早做对话机器人时踩过一个很典型的坑用户问“帮我查一下上周的销售数据”Agent查到了用户接着问“那环比增长了多少”。结果Agent愣住了——它根本不知道“那”指的是什么。原因很简单每次调用大模型接口模型都只看到当前这一轮输入之前的对话历史如果没被显式传回去它一无所知。这就是所谓的“无状态”架构。解决这个问题的直观做法是把对话历史拼接到下一次请求里但拼接不是目的记忆的核心在于筛选关键信息、管理上下文、跨会话复用。我后来发现真正的记忆系统要解决的远不只是“别忘掉刚才的话”这么简单。1.2 记忆解决的真实问题具体来说记忆系统为Agent带来三个层次的能力第一个层次是连贯对话。这是最基础的要求用户说“刚才那个方案再细化一下”Agent能准确知道“那个方案”是哪一个。第二个层次是个性化服务。记忆让Agent逐渐了解用户的偏好、习惯、背景信息。比如用户之前提到过自己是做跨境电商的之后问“帮我写一封英文邮件”Agent能自动带上电商场景的术语和语气。第三个层次是持续学习与自主演进。这是最有想象力的部分。当Agent积累了足够多的用户反馈和历史决策它可以在新任务中主动调用过去成功的经验。这种能力让Agent从“工具”逐渐变成“协作伙伴”。无论是哪个层次背后都需要一套完整的记忆机制来支撑。业界目前最认可的思路就是下面要讲的“双网络记忆模型”。2. 记忆的骨架短期记忆与长期记忆的双网络模型“短期记忆长期记忆”这个提法在热词里反复出现确实也是当前Agent记忆架构的主流范式。它借鉴了人类大脑的记忆机制——我们记东西也不是全部塞进脑子里而是有一个“工作台”和一个“仓库”。2.1 短期记忆工作台上的临时信息短期记忆对应的是Agent当前任务执行期间需要实时存取的信息。在工程上它通常表现为当前会话的对话历史Agent正在处理的中间计算结果与当前任务相关的临时状态比如“正在等用户确认”我习惯把短期记忆看成Agent的“便签纸”。它有两个硬约束容量有限、用完即走。容量有限很好理解模型的上下文窗口再大也是有上限的。当对话历史超出窗口就必须做取舍。用完即走则是说任务一结束这些临时信息就不应该再占用资源否则会越堆越多把整个系统拖死。早期我做记忆系统时在这方面吃过大亏——把所有对话历史都攒着不清理不压缩结果token费用暴涨响应速度还特别慢。后来才明白短期记忆的关键在于“管理”而非“保存”。2.2 长期记忆仓库里的结构化知识长期记忆是用来存放那些需要跨会话、跨任务复用的信息。它的典型表现有用户的基础画像职业、偏好、习惯、表达风格历史任务中的关键结论和用户反馈领域相关的知识沉淀比如用户上传的资料、过往处理过的文档摘要长期记忆不能“一锅烩”地乱存否则取出来也是垃圾。我在项目中走了不少弯路最后形成的习惯是每条长期记忆都带上类型标签和时间戳——这是哪类信息事实/偏好/结论、什么时候写入的、来源是哪次对话。这样后面做检索和更新时才不会翻车。长期记忆的存储载体目前主流方案是向量数据库比如Milvus、Qdrant、Chroma、pgvector因为Agent需要按“语义相似度”来找记忆而不是按关键词匹配。2.3 双网络模型的协同方式把短期和长期记忆分开设计最大的价值是职责清晰互不干扰。一次典型的Agent任务运转流程是这样的Agent接收到用户请求后先把它理解为一个任务放上“工作台”短期记忆。开始执行前从长期记忆库中检索与该任务相关的历史信息——比如用户之前提到过的偏好或者过去类似任务的处理方式。执行过程中短期记忆实时记录每一步的中间状态长期记忆只负责在需要的时候提供背景知识。任务完成时系统提取有价值的结论、事实和偏好写入长期记忆库短期记忆则清理或做摘要归档。这种“双网协同”的设计在工程上更多体现为一种分层结构短期记忆负责“当前进行时”长期记忆负责“过去完成时”。理解这一点后面所有代码写起来都顺理成章。3. 实操第一步给Agent装上短期记忆理论说了这么半天现在开始动手。这一节先解决“会话内记忆”——也就是短期记忆最核心的部分。3.1 最简单也最有效的做法维护对话历史列表短期记忆的基础实现非常简单在Agent的每次调用里把之前的对话消息按时间顺序拼接进请求。用OpenAI的Chat API举例其实就是一个不断追加消息的过程。from openai import OpenAI client OpenAI(api_keyyour-api-key) # 维护一个会话消息列表 conversation_history [ {role: system, content: 你是一个严谨的技术助手。} ] def chat_with_memory(user_input: str) - str: # 把用户的新消息加入历史 conversation_history.append({role: user, content: user_input}) # 调用模型 response client.chat.completions.create( modelgpt-4o-mini, messagesconversation_history, temperature0.7 ) # 把模型回复加入历史 assistant_reply response.choices[0].message.content conversation_history.append({role: assistant, content: assistant_reply}) return assistant_reply这个方法零门槛效果立竿见影——Agent终于能接住“那”“刚才说的”这类指代了。但不要高兴太早。这个方案有个致命问题token会无限膨胀。用户聊十轮之后每次请求都要把前九轮的所有内容发给模型成本和时间都在涨而且模型对太长上下文的早期内容会“注意力衰减”。3.2 解决上下文超长的常用策略针对上下文膨胀我在项目中测试了几种收敛办法策略一滑动窗口只保留最近N轮对话更早的直接丢弃。适合对早期信息不敏感的场景。实现起来就一行MAX_HISTORY_ROUNDS 10 conversation_history conversation_history[-(MAX_HISTORY_ROUNDS * 2):]策略二摘要压缩当对话超过一定轮次后把早期对话用自己的话总结成一个浓缩的“摘要”塞进System Message只保留最近几轮完整对话。def summarize_old_messages(history, summary_model_client): oldest_messages history[:-6] # 保留最近6条不压缩 summary_prompt 请把以下对话压缩成一句话摘要保留关键事实和决定\n str(oldest_messages) summary summary_model_client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: summary_prompt}] ) return summary.choices[0].message.content策略三关键信息抽取不保留原文而是从对话里抽取结构化关键信息存起来。比如用户说“我叫小A做Java后端开发”就存成{name: 小A, role: Java后端开发}。我用的最多的是这个策略因为它天然就是后来长期记忆的雏形。这里想特别提醒一点滑动窗口和摘要压缩都不适合需要精确记忆的场景。比如用户给你发了一个订单号摘要压缩很可能把订单号“模糊化”后面Agent就查不到正确信息了。而关键信息抽取需要维护一个“什么信息值得抽”的规则不能什么都存。3.3 会话结束时的信息交接中期记忆要想长出去短期记忆结束时必须做好“交接”。我的做法是每段会话结束时触发一次“记忆沉淀”。抽取这次对话中用户明确表达的事实、偏好、要求把它们格式化成标准结构写入长期记忆库对整段对话生成一个简短摘要作为会话级可回溯记录这个“交接”动作是整套记忆系统的关键。不做这一层长期记忆就只能靠外部手动喂Agent的自主性会被严重限制。4. 落地核心长期记忆的写入、存储与召回短期记忆解决“别忘事”长期记忆才是让Agent真正“懂你”的引擎。下面我把长期记忆的完整链路拆成四个部分写入策略、存储方案、召回逻辑、更新与遗忘。4.1 写入策略什么值得记长期记忆不是垃圾桶垃圾进垃圾出存进去的每一份数据都会影响Agent后续的判断。我总结了一套筛选原则事实型偏好值得记用户的职业、行业、工作习惯、明确表达过的喜恶。这类信息生命周期长复用价值高。任务结论值得记比如用户和Agent一起敲定了一个方案或者完成了某一个具体任务核心结论可以沉淀。一次性请求不值得记比如“帮我查一下明天的天气”这种查完就可以扔记录只会制造噪音。隐私敏感信息要谨慎涉及到密码、密钥、身份信息默认不写入长期记忆除非用户明确要求。写入流程我一般是让Agent自己判断人工规则兜底def memory_extraction(chat_summary: str) - list: 输入最近的会话摘要返回值得存储的记忆条目列表 extract_prompt f 请从以下对话摘要中抽取值得长期记忆的信息。 只抽取事实、用户偏好、任务结论三类忽略寒暄和一次性请求。 按JSON数组返回每个元素包含 - type: fact | preference | conclusion - content: 具体内容用第三人称表述 - related_entity: 关联到的用户或项目名称如果明确 对话摘要 {chat_summary} # 调用模型抽取解析JSON返回 ...这样抽取出来的记忆条目还需要经过一个去重步骤——对比已有记忆如果内容高度重复就合并或更新而不是无脑新增。4.2 存储方案向量数据库的选型与建Collection长期记忆的载体我推荐向量数据库因为在真实场景里“回忆”往往不是精确匹配而是语义联想。比如用户问“我之前提过那个技术选型方案”Agent需要从一堆记忆里找到语义相近的那条向量相似度检索是最好的实现方式。选哪家向量库我的实测感受数据库适合场景部署复杂度备注Chroma个人项目、本地开发低轻量纯Python库Qdrant小规模生产中性能稳Rust写的Milvus大规模生产高分布式存储量大pgvector已有PostgreSQL的团队中不引入新组件直接复用数据库我的建议是个人项目和Demo直接用Chroma公司项目优先看团队已有的技术栈。如果不清楚选哪种可以从Chroma起步后面再迁移也不难——毕竟存入向量库的数据本质上还是结构化文本逻辑上的迁移成本不高。建Collection时我习惯带上metadata过滤字段方便召回时做精确筛选import chromadb from chromadb.utils import embedding_functions client chromadb.PersistentClient(path./agent_memory_db) collection client.create_collection( namelong_term_memory, embedding_functionembedding_functions.DefaultEmbeddingFunction(), metadata{hnsw:space: cosine} ) # 写入一条记忆 collection.upsert( ids[mem_001], embeddingsNone, # 自动用embedding_function编码content documents[用户是跨境电商从业者主营北美市场偏好简洁直接的邮件风格。], metadatas[{ type: preference, timestamp: 2025-01-12T10:00:00, related_entity: user_A }] )这里的metadatas字段后期有大用按时间范围过滤、按实体过滤、按类型过滤都要靠它。4.3 召回逻辑怎么把“对”的记忆拿出来存储只是第一步召回的质量直接决定记忆系统的价值。如果存了一堆东西却取不出来或者取出来的都是不相关的这个系统就是“有记忆但记不住重点”。我先说一个很多初学者的误区以为召回就是把查询向量跟库里所有向量算cosine相似度取TopK就完事了。实际做下来发现相似度排序之后还差一个用武之地——时效性加权。两年前的偏好和昨天才确认的新偏好即使相似度一样后者显然更值得优先参考。所以我在召回时会给每条记忆打一个综合分数def hybrid_score(similarity: float, timestamp: float, now: float, alpha: float 0.7) - float: 综合相关性分数语义相似度与时效性加权。 alpha越大越看重语义相似度反之越看重时效性。 recency 1.0 / (1.0 (now - timestamp) / 86400) # 按天衰减 return alpha * similarity (1 - alpha) * recency实际跑下来这个混合打分方案比纯相似度检索好用得多——尤其是涉及“用户偏好变化”的业务场景比如用户上个月还在用某个框架这个月换了新框架纯粹按相似度检索很容易召回旧信息加上时效性衰减之后新偏好会被优先命中。另一个让召回质量飙升的做法是分层召回先用精确条件过滤metadata比如限定related_entityuser_Atypepreference。在过滤结果上再做向量相似度检索。最后用规则或小模型对TopK做一次rerank剔除明显不相关的结果。这样召回的顺序是有业务逻辑的先把范围收窄到“跟谁相关、哪类信息”再用语义匹配找最相关的具体条目。比直接在全库向量检索更精准也快很多。4.4 更新的时机与遗忘策略记忆系统的最后一个关键环节是“更新”和“遗忘”这块最容易被忽略但恰恰是让长期记忆保持干净的核心保障。用户今天说“我喜欢喝美式”下周说“最近改喝拿铁了”。如果没有更新机制库里的两条记忆会打架Agent在不同时候召回出矛盾信息用户会觉得这个Agent“精神分裂”。我的处理方案是写入新记忆前先按related_entity和content做一次相似度比对如果已经存在高度相似的旧记忆就执行upsert覆盖掉旧条目并保留旧版本的时间戳记录方便追责。遗忘策略就是定期给记忆做“瘦身”超过一定期限且从未被召回过的记忆标记为低置信度可以归档。明确知道已失效的信息比如“用户已经换了一个城市”直接删除或更新。无实际价值的一次性记忆比如“今天问了天气”定期批量清理。我把这条经验写出来是因为早期我就是不清理结果库越来越大检索延迟越来越长最后不得不花钱重构。不要等到系统变慢了才想起清理从设计的第一天就把遗忘策略纳入考虑长期记忆才不会被垃圾占满。5. 本地记忆迁移与知识库整合把数据变成Agent的“长期资产”记忆不只在Agent的数据库里活着很多时候我们还需要它跟已有的工具、知识库打通。这部分我拿两个典型场景展开——本地历史对话迁移和Obsidian知识库整合。5.1 让记忆跨设备、跨系统迁徙开发过程中我经常遇到这样的需求Agent在测试环境积累了一些有价值的对话记录和偏好数据需要迁到生产环境或者一位用户迁移到新的AI系统里能不能把他之前的记忆一起带过去这种“本地记忆迁移”说起来简单做起来细节不少。我采用的是“导出-转换-重建”三步方案导出把旧系统的对话历史、偏好记录、摘要按统一JSON格式导出。{ user_id: user_A, exported_at: 2025-01-12T10:00:00, conversations: [ { session_id: s_1001, messages: [ {role: user, content: 我喜欢用Python写数据处理脚本}, {role: assistant, content: 好的我会优先用Python为你提供数据处理方案。} ] } ], preferences: [ {type: language, content: Python} ] }转换用模型对导出内容做标准化处理生成新系统可识别的实体、语义向量和metadata。重建把转换后的数据批量写入新系统的长期记忆库。这套流程最耗时的环节是“数据清洗”。旧系统的对话记录往往质量参差不齐、噪音多如果直接导入垃圾进垃圾出新系统继承了旧系统的脏数据反而影响Agent判断。我专门写了一个清洗规则把无效寒暄、重复内容、过时信息都过滤掉只保留有长期价值的部分。好在现在团队里很多前辈推荐的做法也是这个思路。5.2 用知识库给Agent外接“世界记忆”除了对话记忆Agent要更好地服务用户还需要领域知识。这类知识不适合一条条塞进长期记忆更适合以知识库的形式组织起来Need时检索。我最常搭的组合是“Obsidian本地知识库 向量化 Agent检索调用”。因为Obsidian基于Markdown文件天然适合清洗和向量化处理。第一步把Markdown文件批量切分、嵌入、写入向量库from langchain_community.document_loaders import DirectoryLoader from langchain.text_splitter import MarkdownHeaderTextSplitter from langchain_community.embeddings import OpenAIEmbeddings # 读取Obsidian仓库里的所有md文件 loader DirectoryLoader(/path/to/obsidian/vault, glob**/*.md) docs loader.load() # 按标题切分成长度适中的片段 splitter MarkdownHeaderTextSplitter(headers_to_split_on[(#, H1), (##, H2)]) chunks [] for doc in docs: chunks.extend(splitter.split_text(doc.page_content))第二步为Agent配置一个“知识检索工具”让它能在回答前先查询知识库def search_knowledge_base(query: str, top_k: int 5) - list: results collection.query(query_texts[query], n_resultstop_k) return results[documents]做了这个整合之后Agent回答问题的深度完全不一样——它能引用你积累的笔记和文档而不是只靠大模型自己的训练数据硬答。这种“第二大脑”式的架构也是我个人最推荐的知识管理方式。5.3 让记忆支持代码场景如何召回“上次改了什么”热词里提到“opencode如何通过记忆召回代码修改情况”这个方向非常接地气——很多做代码Agent或IDE插件的朋友最痛的就是Agent“忘记”了自己上次对代码做了什么改动。我实践下来的可行方案是每次Agent修改完代码把改动摘要改了什么文件、什么函数、为什么改、commit信息结构化写入长期记忆库。下次Agent启动并开始新任务时自动查询最近的代码改动记忆作为工作上下文。甚至在继续之前未完成的任务时直接检索对应的代码改动记忆让Agent“无缝衔接”上一次的工作。# 保存一次代码改动记忆 def record_code_change(file_path, change_summary, commit_msg): collection.upsert( ids[fcode_{int(time.time())}], documents[f文件 {file_path} 的改动{change_summary}commit: {commit_msg}], metadatas[{type: code_change, timestamp: datetime.now().isoformat()}] )这套方案我真实跑过一个多星期Agent接续工作时的状态一致性提高了很多——至少它再也没有把用户已经改过的函数又“改回原样”的尴尬情况。6. 常见问题与避坑指南记忆系统做多了坑也踩多了。下面这几个问题是我被问得最多的也是我自己实际反复遇到过的整理成一个速查表方便大家参考。问题表现我常用的解决方案上下文无限膨胀响应变慢、费用飙升滑动窗口摘要压缩关键信息抽取三层配合检索到无关记忆回答不相关内容或记忆互相矛盾先metadata过滤再向量检索再加rerank记忆污染旧信息覆盖新信息或用户偏好改变但库没更新写新记忆前做相似度比对用upsert覆盖旧条目嵌入效果差相似度检索不准确召回的同义词不匹配换更强的embedding模型比如text-embedding-3-large而不是只手搓simple模型隐私与安全长期记忆存了不该存的敏感信息写入前过一层敏感信息检测默认不存隐私字段多用户混乱记忆交叉污染A用户的记忆出现在B的上下文中所有记忆条目强制带user_id召回时严格过滤6.1 不要过度依赖记忆还有一个容易被忽视但极其重要的原则记忆不是万能的。有些信息不该靠记忆存比如十万字的文档、复杂的表格、需要实时计算的数据——这些应该走知识库检索或工具调用而不是硬塞进长期记忆。记忆系统要存储的是“经过提炼的信息”而不是“原始数据”。如果什么东西都往记忆里塞Agent会被大量的无用信息淹没召回准确率和响应速度都会下降。我个人的经验法则是一条记忆如果不能在未来的对话中反复使用就不要存。6.2 记忆系统的调试方法记忆系统是“黑盒”程度很高的模块出了问题不好定位。我常用的调试手段有两个一是建立记忆可见面板。开发阶段把所有记忆条目的写入/召回记录打出来用户每次提问后Agent召回的是哪些记忆、为什么选择这些一目了然。这个面板帮我发现了不少召回异常比如“Filter条件写错导致什么都召回不到”“相似度阈值设置太高导致召回为空”。二是单独测试记忆召回。写一个独立脚本输入不同的查询query打印检索结果和得分检查召回质量。我一般用十来个模拟查询覆盖不同类型的问题确保改一次召回逻辑能立刻看到全局影响。这套调试思路可能看起来简单但真的能省下大量“瞎调prompt”的时间——先确认记忆链路是通的再谈优化模型行为。7. 从记忆到真正的“个人Agent”最后再补充一点我自己的实践体会。做AI Agent的记忆系统最需要清醒认识的是这是一套组合工程不是一个单一功能。短期记忆管会话长期记忆管跨会话知识库管领域知识每次对话结束还要有信息交接。只有把这些部分有机组合起来Agent才真正具备了持续积累、持续进化的能力。我在实际项目中最大的体会是Agent能不能“记住你”最终决定了一个工具型产品和一个“个人助手”之间的分水岭。今天的模型能力已经足够强大工具调用已经足够灵活而谁能先把记忆系统做好谁就能先打造出那个真正“懂你”的Agent。如果你正准备开始做Agent记忆我的建议是不要急于追求复杂的架构。先把短期记忆的上下文管理做好再搭一个长期的向量记忆库把写入和召回的链路跑通然后一步步往里加东西。这条路线走下来一切都会水到渠成。后续如果大家感兴趣我可以继续写一讲怎么给记忆系统加“主动回忆”机制——让Agent不只在用户提问时才想起历史而是在合适的时机主动用历史经验辅助决策。这个功能做出来之后Agent的智能水平会上一个大台阶。