智能体面试准备(六):Agent 记忆系统设计——短期、长期与向量记忆的架构与实现 智能体面试准备六Agent 记忆系统设计——短期、长期与向量记忆的架构与实现你的 Agent 怎么记住用户上周说过的话——这个问题看似简单实际是 Agent 面试里区分度最高的题目之一。因为它考的不是单点知识而是架构设计能力上下文窗口管理、信息压缩、检索召回、遗忘策略每一层都有取舍。LLM 本身是无状态的所谓记忆全部是工程构造出来的。这篇把 Agent 记忆系统的分层设计、每层的实现方案与代码、以及面试高频追问一次讲清。一、为什么记忆是 Agent 的核心组件LLM 的两个先天限制决定了记忆系统的必要性无状态每次调用都是独立的模型不会记得上一次对话所有历史必须显式塞进 prompt上下文窗口有限且注意力会稀释即使是 128k 窗口塞满后不仅贵还会出现 lost-in-the-middle中间信息被忽略——窗口大小不等于有效记忆容量。所以记忆系统要解决的核心矛盾是信息无限增长 vs 上下文有限且昂贵。解法就是分层热数据放上下文短期记忆温数据压缩摘要冷数据外部存储按需检索长期记忆。这个思路与操作系统的内存分层寄存器→内存→磁盘完全同构面试时用这个类比会非常出彩——MemGPT 论文正是把 OS 虚拟内存思想搬进了 Agent 记忆管理。二、记忆分类一张表说清记忆类型存储位置生命周期典型内容实现手段短期/工作记忆上下文窗口内单次会话当前对话、工具结果消息列表 滑动窗口摘要记忆上下文内压缩态单次会话溢出历史的摘要LLM 递归摘要长期-情景记忆外部存储跨会话历史对话事件向量库 检索长期-语义记忆外部存储跨会话用户偏好、事实画像结构化 KV/知识图谱长期-程序记忆外部存储跨会话工作流、技能、经验教训文档/代码片段库这套分类借用了认知科学的术语episodic/semantic/procedural面试报出这三个词并各给一个 Agent 场景的例子情景上周聊过的需求细节语义用户偏好 Python 且讨厌啰嗦程序部署项目的标准操作流程立刻建立专业印象。高频追问语义记忆为什么不用向量库而用结构化存储因为用户画像类信息要求精确、可更新、可覆盖——用户从北京搬到上海必须覆盖旧值向量检索可能同时召回新旧两条产生矛盾结构化 KV 天然支持 upsert。向量库适合模糊语义匹配的情景记忆两者是互补关系。三、短期记忆管理窗口溢出的三种策略滑动窗口只保留最近 N 轮。实现最简单但会突然失忆丢掉早期关键信息比如任务最初的目标。递归摘要历史超过阈值时把最老的一段压缩成摘要插到头部反复进行。信息有损但保留主干LangChain 的 ConversationSummaryBufferMemory 就是这个方案。工程细节摘要要用独立的小模型调用做避免打断主流程摘要 prompt 要求保留实体、数字、承诺、未完成事项。重要性分级保留给每条消息打重要性标签用户目标 关键结论 工具原始输出溢出时先丢低级别内容。Claude Code、Manus 这类长程 Agent 普遍采用工具输出可丢弃/可截断用户指令永不丢的策略。实践中三者叠加滑动窗口保最近、摘要保主干、分级保关键。四、可运行代码三层记忆系统的完整实现下面用纯 Python无第三方依赖实现一个可运行的记忆系统短期滑动窗口 溢出摘要 基于 TF-IDF 余弦相似度的长期向量检索真实项目换成 embedding 模型 向量库接口不变。import math import re import time from collections import Counter # ---------- 长期记忆向量化存储 余弦检索 ---------- class VectorMemory: 用 TF-IDF 模拟 embedding 检索真实场景替换为 embedding API 向量库。 def __init__(self): self.items [] # [(text, meta, term_freq)] def _tokenize(self, text): return re.findall(r[\u4e00-\u9fff]|[a-zA-Z], text.lower()) def add(self, text, metaNone): tf Counter(self._tokenize(text)) self.items.append((text, meta or {}, tf)) def _cosine(self, tf1, tf2): common set(tf1) set(tf2) dot sum(tf1[t] * tf2[t] for t in common) n1 math.sqrt(sum(v*v for v in tf1.values())) n2 math.sqrt(sum(v*v for v in tf2.values())) return dot / (n1 * n2) if n1 and n2 else 0.0 def search(self, query, top_k2, min_score0.05): q_tf Counter(self._tokenize(query)) scored [(self._cosine(q_tf, tf), text, meta) for text, meta, tf in self.items] scored.sort(reverseTrue) return [(s, t, m) for s, t, m in scored[:top_k] if s min_score] # ---------- 记忆管理器短期窗口 溢出摘要 长期沉淀 ---------- class MemoryManager: def __init__(self, window_size4): self.window_size window_size self.short_term [] # 最近消息 self.summary # 递归摘要模拟 self.long_term VectorMemory() # 跨会话记忆 self.profile {} # 语义记忆结构化用户画像 def add_message(self, role, content): self.short_term.append({role: role, content: content, ts: time.time()}) if len(self.short_term) self.window_size: evicted self.short_term.pop(0) # 溢出消息进摘要模拟 LLM 摘要 沉淀到长期记忆 self.summary f[{evicted[role]}]{evicted[content][:30]}; self.long_term.add(evicted[content], {role: evicted[role]}) def update_profile(self, key, value): 语义记忆用 upsert 覆盖保证精确性。 self.profile[key] value def build_context(self, query): 组装最终 prompt 上下文画像 摘要 长期检索 短期窗口。 recalled self.long_term.search(query) parts [] if self.profile: parts.append(f## 用户画像\n{self.profile}) if self.summary: parts.append(f## 历史摘要\n{self.summary}) if recalled: lines [f- (相关度{s:.2f}) {t} for s, t, _ in recalled] parts.append(## 相关历史记忆\n \n.join(lines)) recent \n.join(f{m[role]}: {m[content]} for m in self.short_term) parts.append(f## 当前对话\n{recent}) return \n\n.join(parts) if __name__ __main__: mm MemoryManager(window_size3) mm.update_profile(语言偏好, Python) mm.add_message(user, 帮我调研 vLLM 的部署方案重点看显存占用) mm.add_message(assistant, vLLM 用 PagedAttention 管理显存建议预留 20% 余量) mm.add_message(user, 我们的显卡是 409024G 显存) mm.add_message(user, 另外记得数据库连接串要用环境变量) mm.add_message(assistant, 好的已记录环境变量规范) mm.add_message(user, 对了之前说的显卡是什么型号来着) print(mm.build_context(显卡 显存 型号))运行后可以看到窗口只留最近 3 条早期的4090、24G 显存已被挤出窗口但通过长期记忆检索被召回进上下文——这正是Agent 记住了窗口外信息的完整机制。这段代码覆盖手撕记忆模块的所有得分点窗口驱逐、摘要沉淀、向量召回、结构化画像、上下文组装。五、长期记忆的三个工程难题写什么记忆生成不能把所有对话原文都存进去噪声会淹没检索。主流做法是反思式写入会话结束或阶段完成时让 LLM 提取值得长期保留的事实/偏好/教训再写入Generative Agents 的 reflection 机制、Mem0 的事实提取都是这个思路。追问点什么时候写——事件驱动用户明确说记住 周期驱动会话结束批量反思结合。怎么取检索策略单纯向量相似度不够。Generative Agents 的经典打分公式是相关性 时近性 重要性三分量加权相关性用余弦相似度时近性用指数衰减decay^Δt重要性在写入时由 LLM 打 1~10 分。这条公式是面试的高频得分点务必能默写并解释每个分量的作用。怎么忘遗忘与冲突记忆会过时和矛盾。手段包括TTL 过期、访问频率淘汰LRU 思想、写入时冲突检测新记忆与已有记忆语义冲突时触发合并或覆盖——Mem0 的 ADD/UPDATE/DELETE 决策。记忆冲突怎么办是压轴追问答写入时让 LLM 判断新旧关系并选择增/改/删即可。六、面试答题框架与高频题设计一个有跨会话记忆的个人助理 Agent推荐结构分层短期窗口摘要、语义结构化画像upsert、情景向量库、程序技能文档写路径会话结束触发反思式提取LLM 判断增/改/删读路径每轮用当前 query 检索 top-k按相关性时近性重要性排序注入 system prompt治理PII 脱敏、用户可查看/删除自己的记忆合规卖点、记忆条数上限与淘汰。其他高频题速答长上下文模型1M token会让记忆系统过时吗——不会。成本与延迟随上下文线性增长、注意力稀释依旧存在、跨会话持久化仍需外部存储长窗口只是把短期记忆变大了。RAG 和记忆系统什么关系——机制同源外部存储检索注入对象不同RAG 检索的是外部知识库记忆检索的是 Agent 自身经历记忆还多了写入、更新、遗忘的生命周期管理。多 Agent 场景记忆怎么共享——公共黑板共享记忆池 私有记忆隔离写入公共池需要格式约定与权限控制。自检清单能报出情景/语义/程序三分类并各举一例吗能默写相关性时近性重要性检索公式吗能说清语义记忆为什么用结构化存储吗能手写一个带驱逐和召回的记忆管理器吗四问皆通记忆环节稳拿高分。下一篇讲多智能体协作角色分工、辩论机制与流水线编排的实现。

本月热点