玛莎是某位大老板的资深私人助理,跟了老板整整十年。老板每天都会丢给她大量信息:会议日程、供应商电话、客户的古怪要求、老板自己的口味偏好。十年下来,玛莎练出了一套记忆心法。她的桌上永远放着一个巴掌大的记事本,本子只记“当下正在处理的事”;身后的房间里立着一排档案柜,柜子按主题分好类,整整齐齐码着十年的卷宗;每晚她还会花十分钟翻一翻旧卷宗,把过时的东西降级,把重要的东西往前排。这套心法,其实就是今天要讲的主题——Agent 记忆系统设计。AI Agent 要真正像玛莎一样贴心,也必须具备类似的记忆结构:一个容纳当前对话的短期记忆,一个跨会话持久化的长期记忆,外加一套编码、检索、遗忘的管理流程。本文就用玛莎的故事,带你从零看懂 Agent 记忆系统的短期与长期设计。
掌握 Agent 记忆系统,你就能让 AI 应用从“每次都像第一次见面”进化成“老友般的默契”——这是对话式 AI、智能客服、个人助手类产品最核心的竞争力。
可视化演示
对比观察者模式的发布-订阅通知机制与命令模式的请求封装-执行流程,动态展示主题状态变更触发观察者更新的完整链路。
本节配有交互动效(进入视口后自动加载,约 3KB)。
观察者模式 Subject状态变更触发notify() 遍历观察者列表调用update() S→O1/O2/O3 一对多依赖关系通知
记忆系统概览
玛莎接手新老板的第一天做了什么?她没有急着买新本子,而是先立规矩:“什么放桌上、什么进档案柜、什么只过脑不过夜。”她很清楚,记忆不是一口缸,而是一套分工。桌上是手边记事本,柜子里是十年卷宗,脑子里是正在盘算的事——三者各管一摊,互不越界。
Agent 也一样。业界把 Agent 的记忆分成三层:工作记忆是模型正在计算时产生的中间状态,一算完就释放;短期记忆是当前对话的上下文窗口,负责“这一轮在聊什么”;长期记忆是模型之外的持久化存储,负责回答“这个用户上次发生过什么”。三者组合,才构成了完整的记忆生命周期:编码 → 存储 → 检索 → 更新 → 遗忘。每一环设计得好不好,都直接决定 Agent 像个笨拙的新秘书,还是像玛莎那样游刃有余。
实际架构中,工作记忆由 Transformer 的注意力机制隐式完成,开发者一般不用管;短期记忆由上下文窗口决定,需要做预算和淘汰;长期记忆则外包给向量数据库等外部组件。接下来的章节,就按这三层分工逐一来拆解。
有了分工,玛莎才能不慌不忙:桌上的事做不完不乱,柜子里的文件找得到不慌,脑中始终清楚下一步干什么。Agent 的架构设计,同样始于这份“记忆分工图”。
- 记忆总量Agent 可以调用的全部记忆资源总和,是系统设计时的总预算。
- 工作记忆正在计算中、用完即释放的中间信息,类似玛莎脑中正在思考的内容。
- 短期记忆上下文窗口内、随对话结束而失效的消息。
- 长期记忆向量库等外部存储中、可以跨会话持久化的知识。
短期记忆设计
玛莎的记事本只有巴掌大,老板语速又快,常常十分钟就写满一页。她总结出三条铁律:只记当前任务、写满就翻页、最后三页永远留给老板正在说的话。这三条铁律,恰好映射了短期记忆设计的全部要点。
短期记忆在技术上就是 上下文窗口(context window)——大语言模型一次能“看”到的 Token 总数。Token 可以粗略理解成“文字碎片”,模型按碎片读入整个窗口的内容。窗口内消息按顺序排列,一旦超出上限,最早的碎片就会被挤掉或触发处理。所以短期记忆设计的第一课题是:窗口多大、怎么给内容排座次。
主流做法是滑动窗口:只保留最近 N 轮对话,更早的内容做摘要或转存。同时要算 Token 预算——系统提示词、工具定义、用户当前提问必须保底,历史消息只分剩余空间。这里的预算公式相当直观:总容量减去必须占用的保底量,才是历史消息能用的额度。一旦预算被历史消息塞满,短期记忆就会失效,如同记事本被扔进碎纸机,再也找不回来。因此窗口内留白,是对“当前问题”的最高尊重。
记事本写得越规矩,老板提问时玛莎就越从容。窗口里永远给“正在问的问题”留够位置,短期记忆才算设计到位。
- 窗口容量大模型上下文窗口的 Token 上限(如 128K),相当于记事本的完整页数。
- 保底预留系统提示词、工具定义、用户当前提问必须占用的 Token 数,相当于玛莎永远留出的最后三页。
- 可用空间扣掉保底后,历史消息可分得的 Token 预算;如果算出来是负数,说明必须压缩或转存历史。
长期记忆设计
档案柜房才是玛莎的镇宅之宝。十年卷宗没有一页混放:谈过咖啡的客户放在餐饮区,报过修的老客户放在售后区,每层架子都有编号。老板三年前说“那个酸酸的咖啡”,今天改口说“那份偏酸的豆子”,玛莎都能翻出同一份卷宗——因为她不是靠字面检索,而是靠“意思”归档。
Agent 的长期记忆要做的事情,就是把这间档案柜自动化。它必须解决两个核心问题:第一,知识跨会话持久化,对话一结束不蒸发;第二,按“语义”而不是按“标题”存取。主力工具就是向量数据库(vector database)。它的工作方式分三步:先把长文本切成小块(chunking),再用嵌入模型把每块文字变成高维向量,最后建立索引(常用 HNSW 这类近似最近邻结构)支持快速搜索。语义相近的文本,向量距离就近,于是“酸酸的咖啡”和“偏酸的豆子”能落到同一个角落。
工程上很少只用一种存储。成熟的 Agent 系统通常采用混合存储架构:Redis 存键值型事实(“用户:小明,等级:VIP”),关系数据库存结构化信息,向量库存自由文本片段——就像玛莎的档案柜房既有按主题分类的大柜,也有放小物件的抽屉,各司其职。
档案柜的价值在于“异时同义”:不管老板用什么词,玛莎总能找到那卷该找的档案。向量检索正是为了实现这份默契而存在的。
- 查询向量把用户当前问题编码成的语义向量。
- 文档向量长期记忆中某条候选文本的语义向量。
- 向量点积两向量对应元素相乘后求和,衡量“同向”程度。
- 模长归一除以各自的长度,消除文本长短带来的误差。
记忆编码方法
玛莎归档之前,总要先问自己三个问题:这条值不值得记?用什么词能最快找回来?原文太长要不要写摘要?把一堆杂乱信息“变成卷宗”的这道工序,就是记忆编码。
编码,是把原始对话转换为适合长期存储与检索的形式。这一步最容易犯的错是“全盘照抄”——所有聊天记录原样塞进数据库。结果档案柜被垃圾塞爆,真正要找的卷宗反而被埋在最底下。因此,标准流水线要经过四道工序。
第一道是信息筛选:去重、去噪、抽取实体与事实,判断“用户喜欢喝咖啡”比“今天天气不错”更值得记。第二道是向量化表示:用嵌入模型把筛选出的文本转成语义向量。第三道是元数据标注:给记忆贴上时间戳、说话人、主题标签、重要度,就像卷宗封面写清楚编号和日期。第四道是摘要压缩:长对话先总结成要点再入库存,控制体积。编码有多认真,检索就有多轻松——这和卷宗写得好不好决定找不找得到,是同一个道理。
写档案卡的功夫,决定未来找档案的速度。编码环节多花一分心思,检索环节就能省下十分力气。
- 原始文本待编码的消息或摘要片段,相当于玛莎决定“这份原始记录值得归档”。
- 嵌入模型预训练的文本编码器(如 text-embedding 系列),把文字映射到固定维度的空间。
- 语义向量文本在高维空间中的坐标,语义相近的文本坐标彼此靠近。
- 不可逆性向量无法还原成原文,所以原始文本要和向量一起存储,否则检索到坐标也读不回内容。
记忆检索机制
老板问“三年前那个供应商的电话”,玛莎绝不会把整间档案房倒出来,而是先想“供应商 + 三年前 + 合作过”,定位到两三个柜子,抽出最像的三份卷宗精读,最后把答案写在便签上递给老板。这套动作,就是记忆检索。
Agent 的检索也是这条路线:先把查询向量化,在向量库里做相似度搜索粗筛出 Top-K 候选;再用重排序精读这些候选,挑出真正有用的;最后把结果注入 Prompt,让模型基于它们生成回答。这条管线在业界叫检索增强生成(Retrieval-Augmented Generation,RAG),是目前让 Agent“带上记忆说话”的主流武器。
但只比语义相似度还不够。三年前的信息即便相似度略低,对“当前问题”也可能更关键。所以工程上常用混合打分:语义相似度占大头,加上记忆的重要度权重,再叠加一个新鲜度(时间越近分数越高)。这样既找得准,又不至于让陈芝麻烂谷子压过最近进展。
检索不是大海捞针,而是按索引拿针;针拿得多准,取决于编码和打分有多讲究。
- 语义相似度查询与某条记忆片段的余弦相近程度,是“像不像”的度量。
- 新鲜度因子衡量记忆“有多新”的衰减分数,越近越高。
- 权重调剂α 与 β 分别控制相似度和新鲜度的贡献,工程上由线下评测调参决定。
遗忘与优先级
每月最后一个周五,玛莎都会做“档案大扫除”:三年前的外卖单降级到地下室,客户合同升级到金柜,还有一批“好像有用又好像没用”的卷宗贴上复习标签,下月再翻一次。她把遗忘本身当作工作的一部分,而不是失误。
这个直觉,心理学上早有定论。艾宾浩斯遗忘曲线告诉我们:刚记完的内容衰减最快,之后越记越牢,遗忘速度整体呈现“先快后慢”的指数趋势。Agent 的长期记忆也需要主动执行类似的记忆衰减——否则档案柜里的陈年垃圾会无限膨胀,把高价值记忆挤到检索死角。
具体的做法是“降权而非删除”。每条记忆都带两个属性:年龄(记录多久没被访问了)和重要度(由规则或模型给出评分)。检索时,年龄大的记忆得分自动打折;被再次命中的记忆刷新年龄、权重回升。容量快满时,低重要度记忆被移到冷存储,高重要度记忆进“金柜”优先保留。这样,有限的存储永远在服务最重要的事情——会遗忘的档案柜,才是好档案柜。
玛莎的大扫除不是把卷宗扔掉,而是把力气留给重要的事。Agent 的遗忘策略,同样是为了让记忆系统“拿得起、放得下”。
- 留存率经过时间 t 之后,记忆仍然能被有效找回的概率。
- 时间变量从编码到当前检索所经过的时间。
- 记忆强度与内容重要度、复习次数正相关的常数,S 越大记忆保持越久。
- 指数衰减整个趋势是先快后慢——新记忆迅速模糊,核心轮廓留存更久,所以复习要在衰减最快的早期进行。
实用系统设计
玛莎把自己的记忆心法写成了一本工作守则:每天开工先扫一遍日程(读取);每收到一条信息先判断值不值得记(写入);回答老板任何问题之前先翻档案(检索)。这套守则,其实就是一套完整的记忆系统流程设计。
落到工程实现,核心问题只有三个:什么时候写、什么时候读、用什么工具和指标评估。读写流程是事件驱动的——对话中识别到关键信息(用户明确说“记住……”、出现订单号/型号等实体)立即触发编码写入;生成回答前,先从长期记忆检索相关片段注入上下文。写和读之间,还需要异步任务定期做去重、衰减和复习调度。
工具链选型上,想省力可以直接用托管方案 Mem0、Zep,它们把记忆的增删改查封装成了现成的 API;想自建,向量库选 Chroma、Pinecone 或 Qdrant,缓存用 Redis,再加上一个调度队列驱动遗忘与复习任务。上线之后,用检索命中率、召回相关性和端到端任务成功率持续评测,再根据结果调节衰减率和重要度规则——记忆系统不是搭好就完事,而是一个需要长期养护的活系统。
这一整套,其实就是把玛莎十年练成的心法自动化。当 Agent 也拥有了记事本、档案柜和大扫除的节奏,它就不再是“每次都像第一次见面”的陌生人,而是真正懂你的私人助手。
- 命中率衡量检索“找得到”能力的基础指标,是所有记忆评测的起点。
- 命中次数在评测查询中,确实召回相关记忆的查询数量。
- 查询总数评测期间发起的全部检索请求数。
- 组合评估命中率高不代表回答好,还要配合答案质量与任务成功率,避免“捡到新闻却答非所问”。
AI 应用场景
电商售后客服 Agent
阶段:记忆编码写入 + 检索增强生成
解决的问题:传统客服系统把每一轮对话当成全新会话,用户每次都要重新描述订单号、设备型号、上次处理到哪一步;信息反复追问,体验极差。
具体案例:用户一个月前报修过耳机充电故障,处理结果已经被编码写入长期记忆,并标注了“耳机/充电/已换新”标签。新会话里用户说“上次那个耳机还出问题”,客服 Agent 通过向量检索召回上次的订单与处理记录,直接接续上下文处理售后,无需用户复述。
跨会话编程助手 Agent
阶段:记忆编码 + 长期记忆检索
解决的问题:编程助手每次新开会话就忘记项目背景,架构决策、依赖选型、代码风格偏好全部丢失,开发者总要重复交代。
具体案例:把“项目使用 FastAPI + SQLAlchemy,端口固定 8000,所有接口返回 JSON”写入长期记忆并打好标签;下一次会话问“加一个登录接口”,Agent 自动检索到项目规范,直接按既有风格生成代码,新成员也能快速上手。
个性化教育 Agent
阶段:记忆优先级管理 + 遗忘曲线复习调度
解决的问题:学生水平、错题记录、学习兴趣无法跨课时记忆,每次都从零开始教学。
具体案例:教育 Agent 把学生每节课的错题点、易错概念、兴趣话题存入长期记忆,并按重要度排序;当遗忘曲线预测某个知识点即将淡忘时,系统自动安排复习提问,真正做到“因材施教、按时巩固”。
Python 演示
# -*- coding: utf-8 -*-"""简易 Agent 记忆系统演示: 短期记忆(滑动窗口) + 长期记忆(近似向量检索)"""import mathimport re class ShortTermMemory: """短期记忆: 容量有限的上下文窗口, 超出后逐出最旧消息""" def __init__(self, capacity=3): self.capacity = capacity self._messages = [] def add(self, role, content): """追加消息, 窗口满时返回被逐出的消息, 否则返回 None""" self._messages.append({"role": role, "content": content}) if len(self._messages) > self.capacity: return self._messages.pop(0) return None def to_prompt(self): """将窗口内消息拼成适合送进模型的上下文文本""" return "\n".join( f"{m['role']}: {m['content']}" for m in self._messages ) class LongTermMemory: """长期记忆: 用关键词重合度近似语义检索, 带年龄衰减""" def __init__(self): self._records = [] self._total = 0 self._decay_step = 0.2 def write(self, text, tags, importance): self._records.append({ "id": self._total, "text": text, "tags": tags, "importance": importance, "age": 0.0 }) self._total += 1 def age_all(self): for rec in self._records: rec["age"] += self._decay_step def search(self, query, top_k=2): q_terms = set(re.findall(r"[\w\u4e00-\u9fff]+", query)) scored = [] for rec in self._records: text_terms = set(re.findall( r"[\w\u4e00-\u9fff]+", rec["text"][:30])) terms = set(rec["tags"]) | text_terms word_sim = len(q_terms & terms) / max(len(q_terms | terms), 1) freshness = math.exp(-rec["age"]) score = 0.6 * word_sim + 0.3 * rec["importance"] + 0.1 * freshness scored.append((score, rec)) scored.sort(reverse=True) return [rec for _, rec in scored[:top_k]] def main(): stm = ShortTermMemory(capacity=3) ltm = LongTermMemory() conversation = [ ("user", "你好,我叫小明"), ("assistant", "你好小明,很高兴认识你"), ("user", "我最喜欢喝咖啡"), ("assistant", "我会记住你喜欢咖啡"), ("user", "帮我推荐一款咖啡豆"), ("assistant", "首选哥伦比亚浅烘焙"), ] for role, content in conversation: evicted = stm.add(role, content) if evicted: if "咖啡" in evicted["content"]: ltm.write(f"{evicted['role']}: {evicted['content']}", tags=["咖啡", "喜好"], importance=0.95) else: ltm.write(f"{evicted['role']}: {evicted['content']}", tags=["问候"], importance=0.5) ltm.age_all() recalled = ltm.search("小明有什么喜好", top_k=1) print("=== 当前短期记忆窗口 ===") print(stm.to_prompt()) print("\n=== 长期记忆检索结果 ===") for rec in recalled: print(rec["text"], "| 重要度:", rec["importance"], "| 年龄:", rec["age"]) if __name__ == "__main__": main()
evicted = stm.add(role, content)每条新消息先进入短期记忆窗口,容量 3 条满时返回被逐出的最旧消息。if evicted:只有“被窗口挤出来”的消息才值得沉淀,避免把每一句闲聊都写进长期记忆。ltm.write(f"{evicted['role']}: {evicted['content']}", tags=["咖啡", "喜好"], importance=0.95)将咖啡偏好编码进长期记忆,附带主题标签与高重要度。rec["age"] += self._decay_step每次调用让全部长期记忆统一“变老”一点,模拟时间流逝。freshness = math.exp(-rec["age"])用指数衰减计算新鲜度,正是艾宾浩斯遗忘曲线的简化实现。word_sim = len(q_terms & terms) / max(len(q_terms | terms), 1)用 Jaccard 重合度近似语义相似度;真实系统会替换成向量余弦相似度。scored.sort(reverse=True)按综合得分降序重排,相当于检索里的 Top-K 重排序。recalled = ltm.search("小明有什么喜好", top_k=1)生成回答前触发检索,把长期记忆结果带回上下文。
常见误区
误区“把上下文窗口开到 128K,Agent 就拥有超强记忆了,根本不用再折腾什么长期记忆。”
正解窗口再大也属于“当场易失”记忆:对话一结束或超出限制,内容就没了。短期记忆像手边记事本,换成更大的本子也终有翻到顶的一天;真正的长期记忆是把重要内容转抄进档案柜(向量库)。而且窗口变大,成本线性上升,无关噪音也会稀释模型注意力。
为何常见:因为“记忆 = 能记住多少信息”的直觉很自然,人们常把“容量大”和“持久”两件事混为一谈。
误区“每次把完整聊天记录贴进 Prompt,等于手动实现记忆系统,何必引入数据库和一堆组件。”
正解全量回填就像把所有档案堆在桌面上:成本高、噪音大、模型注意力被无关信息分散,真正关键的线索反而被淹没。正确做法是短期窗口只保留最近几轮,更早的细节交给向量检索按需召回。
为何常见:早期小 demo 里直接贴历史也能跑通,掩盖了记忆系统在真实长交互中的必要性。
误区“建一个向量数据库,把所有对话记录 embed 存进去,长期记忆就算大功告成了。”
正解只有向量远远不够。档案柜还需要标签、时间戳、重要度评分、定期清理;系统还需要短期窗口、混合存储、重排序等配套设计。向量只是“语义坐标”,管理记忆靠的是整套系统设计。
为何常见:因为向量检索是记忆系统里最炫、最常被教程单独拎出来讲的组件,容易让人误以为它就是全部。
误区“该忘的就删,把旧记忆从数据库里删掉,简单粗暴又省空间。”
正解真正的遗忘是“降权与归档”:把陈旧但可能相关的记忆调低权重、移入冷存储、在复习时重新激活。直接删除会丢失可恢复的线索,好比玛莎不会烧掉旧卷宗,只会把它们转入地下室冷柜。
为何常见:因为“删除”在工程上最简单,也吻合人类“想不起来 = 不存在”的直觉。
误区“记忆系统上线以后就不用管了,Agent 自己会越用越聪明。”
正解记忆系统需要持续维护:调整 Token 预算、调节衰减率、清理脏数据、定期评测命中率与回答质量。忘记维护的档案柜会积灰,检索质量会随数据膨胀逐渐下降。
为何常见:因为记忆常被当成“数据存储”一样的静态设施,忽略了它是需要调参与评测的活系统。
面试题
Q1. 在 Agent 架构中,短期记忆通常指什么?
- A.模型上下文窗口← 正确答案
- B.长期记忆向量库
- C.本地配置文件库
- D.外部日志文件集
答案:A。短期记忆就是当前对话的上下文窗口,像玛莎的手边记事本,随对话结束而失效。向量库是长期记忆,配置文件与日志都不属于记忆系统。
Q2. 长期记忆最常用的存储载体是什么?
- A.模型上下文窗口
- B.向量数据库仓库← 正确答案
- C.临时内存缓存池
- D.系统环境变量表
答案:B。向量数据库把文本变成语义坐标,可以跨会话持久找回,对应玛莎的档案柜房。上下文窗口是短期的,缓存和环境变量都不是长期记忆载体。
Q3. 设计短期记忆时预留 Token 预算的目的是?
- A.让输入窗口显示更满
- B.给系统提示与用户问题留空间← 正确答案
- C.降低模型推理运行速度
- D.让模型生成更短的答案
答案:B。窗口预算像记事本最后几页始终留给老板的当前问题,必须给系统提示词和用户提问预留名额,否则关键指令会被历史消息挤掉。
Q4. 向量记忆中衡量两条文本语义相近最常用的指标?
- A.余弦相似度算法← 正确答案
- B.曼哈顿距离公式
- C.编辑距离计算方法
- D.汉明距离比较法
答案:A。余弦相似度只看方向、不看长度,适合高维语义向量;编辑距离与汉明距离面向字符级差异,曼哈顿距离在语义空间效果较差。
Q5. 记忆编码阶段的正确操作包括?
- A.筛选值得保留的信息← 正确答案
- B.利用嵌入模型向量化← 正确答案
- C.原样保存所有聊天记录
- D.标注时间与标签元数据← 正确答案
答案:ABD。编码像写档案卡:先筛选、再向量化、加元数据。原样全存会导致噪声与成本失控,不是正确编码,故不选 C。
Q6. 遗忘曲线 R(t)=e^(−t/S),S 值越大表示?
- A.记忆保持时间越久← 正确答案
- B.记忆遗忘速度越快
- C.记忆被彻底永久删除
- D.记忆存储容量越小
答案:A。S 是记忆强度常数,S 越大曲线下降越缓,记忆保持越久——如同重要卷宗被放进金柜,不容易被清理掉。
Q7. 检索增强生成(RAG)的典型做法包括?
- A.从长期记忆中召回片段← 正确答案
- B.把召回片段注入提示词← 正确答案
- C.用随机噪声增强文本输出
- D.让模型依据片段组织回答← 正确答案
答案:ABD。RAG 的核心是召回 → 注入 → 生成三步。随机噪声属于对抗增强的范畴,不属于 RAG,故不选 C。
Q8. 短期记忆窗口已满时,最合理的处理方式?
- A.直接清空窗口全部消息
- B.转存关键信息进长期记忆← 正确答案
- C.抛出异常终止当前对话
- D.无限制扩大窗口容量
答案:B。窗口满时应像玛莎把记事本写满的旧页转抄进档案柜:筛选关键信息编码入长期库,窗口保持进退有序。
Q9. 长期记忆和短期记忆最本质的区别?
- A.长期记忆存储容量更小
- B.短期记忆永久保存数据
- C.长期记忆跨会话保持持久← 正确答案
- D.两者在工作中没有区别
答案:C。核心区别是持久性:短期记忆随上下文窗口失效,长期记忆可跨会话召回,对应记事本和档案柜的差别。
Q10. 记忆系统上线后应持续评测的指标包括?
- A.记忆检索命中率水平← 正确答案
- B.页面主题颜色偏好
- C.任务成功率与回答质量← 正确答案
- D.机房环境温度湿度
答案:AC。评测要针对记忆性能与最终任务效果,命中率和回答质量是核心指标;界面颜色与机房温度不在记忆系统范畴内。