资讯动态

Agent记忆系统设计:从工作记忆到长期记忆的工程实践

发布时间:2026/10/14 23:40:57 来源:尧图企业网站定制
面试里一提到“Agent 的记忆系统怎么设计”很多候选人第一反应就是向量数据库、embedding、相似度检索这几个词一出来面试官基本就知道你背过八股。可只要多追问两句——记忆存什么什么时候写入什么时候遗忘取出来之后怎么塞进Prompt——大多数人就卡住了。这篇文章不是来帮你背答案的而是把记忆系统当成一个真实工程问题来拆解面试官到底期待你展示什么设计能力以及一套能直接迁移到实际项目里的落地思路。全文按我这些年评审候选人、复盘自己项目的视角写适合做LLM应用、Agent框架、对话系统的开发者参考。1. 面试官问“记忆系统”时到底在考察什么1.1 别急着背概念先理解为什么Agent需要记忆大模型本身没有持续状态。你把一段Prompt发过去它返回一段文本下一次调用时它对上一次对话毫无概念。Agent和普通聊天机器人的最大区别就是要在一个复杂任务里连续做决策先拆步骤再逐步执行中间可能还要回头修正。没有记忆Agent就没有“昨天”也就没法积累用户偏好和任务经验。面试官问这个问题第一个想确认的就是你有没有看透这一层。如果你上来就讲向量数据库说明你只看到存储层。我习惯把Agent记忆拆成两层来看。工作记忆解决的是“当前这轮任务里我现在要做什么”长期记忆解决的是“这个用户、这个领域、这类问题我积累了什么经验”。对应到人身上工作记忆是你写代码时脑中的当前栈帧长期记忆是你的代码仓库和wiki。你在调试一个函数时不会把整个代码仓库读进内存Agent也一样。这个类比面试现场可以直接用它能帮面试官理解你有抽象建模能力。1.2 从“对话历史缓存”到“自主记忆”面试官的评分点拆解这些年我面过很多候选人也被人面过发现大家对“记忆系统”的理解大概有三个层次。第一层是及格线知道要保存对话历史按轮次截断超过N轮就把前面的丢掉。这是缓存不是记忆系统只能证明你写过业务代码。第二层是进阶线知道要区分短期和长期长期记忆要做向量化检索写入时做摘要。到了这一层候选人基本能画出一张“写入-存储-检索”的链路图。第三层是高分线能讲清楚记忆的写入时机、内容结构化、遗忘策略、冲突处理、来源追溯以及记忆到底如何影响模型决策。面试官想听到的是你在一个具体场景里的权衡而不是一堆名词。举个例子候选人说“我用向量数据库存记忆”我通常会追问为什么要向量什么时候向量检索反而不如关键词如果你能说出“用户自然语言适合语义召回但ID、日期、人名这类精确信息向量召回容易出错必须结合结构化过滤”这就能证明你真的调过系统。评分点不在用了什么技术而在你能不能解释为什么是这个技术。2. Agent记忆系统的整体架构三个不能省的核心模块2.1 工作记忆上下文窗口内的短期状态管理工作记忆是Agent在单次任务执行过程中必须暂存的信息包括用户当前输入、任务目标、已经计划的步骤、每个步骤执行后的结果、临时变量。它不是对话历史本身而是对话历史里“对当前决策有用”的状态。比如用户说“查一下明天A城市的天气顺便提醒我带伞”Agent需要记住的是目标、城市、明天、输出格式而不是把两句话原封不动地反复放在后续每一步里。工程实现上工作记忆通常在内存里维护一个任务状态对象而不是一个字符串列表。我常用的结构是当前目标、已执行步骤、关键上下文、最近一次模型输出。每执行一步就往steps里追加一条同时限制最大步数。为什么要限步因为模型对超长上下文的注意力会被中间噪声稀释步骤越多越容易忘记最初的目标。限制步数之后老步骤可以压缩成“已确认结果登录成功”摘要。class WorkingMemory: def __init__(self, max_steps10): self.current_task self.key_context {} self.steps [] self.max_steps max_steps def reset(self): self.current_task self.key_context {} self.steps [] def add_step(self, action: str, result: str): self.steps.append({ action: action, result: result, summary: f{action}: {result[:80]} }) if len(self.steps) self.max_steps: self.steps.pop(0)代码很简单但面试里能说出这两点就足够了工作记忆有容量上限老信息要被压缩而不是直接扔工作记忆的数据要能被快照和恢复方便Agent在任务中断后继续执行。2.2 长期记忆从结构化存储到向量检索长期记忆负责保存跨会话、跨任务的信息。很多方案把长期记忆做成一个“向量库”所有聊天记录都往里扔这其实是偷懒。真正要落地长期记忆至少应该分成事实记忆、事件记忆和知识记忆三类。记忆类型典型内容适合存储常用检索方式事实记忆用户偏好、个人属性、业务规则结构化表精确匹配、键值查询事件记忆某次任务的执行过程、结果文档/日志向量索引时间倒序、语义检索知识记忆领域术语、常用流程、踩坑经验文档向量索引语义检索、关键词混合事实记忆用结构化存储是因为“用户A的昵称是xx”这类信息需要精确查询。你不需要相似度你需要等值。事件记忆和知识记忆适合向量检索因为它们的问题是开放式的比如“这个用户之前遇到过什么问题”“这个流程上次怎么失败的”。如果只用一个向量库精确信息和语义信息互相干扰召回质量会明显下降。每条长期记忆都要带上时间戳、来源、重要度、访问次数。没有时间戳的记忆系统时间一长就开始混乱。后面伪代码部分我会给出一个具体的数据模型面试时能写出这类结构比只描述一张表更有说服力。2.3 记忆的写入、遗忘与回滚生命周期管理记忆不是多多益善。写得太多检索噪声变大提示词膨胀模型更容易被无关信息带偏。我总结出来的写入时机有三类信息发生变化时比如用户修改了偏好任务完成或失败时比如一个多步骤流程执行完生成一条结构化摘要用户明确纠正时比如用户说“不对你记错了”。其他时候的闲聊内容最多进入短期缓存不进长期记忆。遗忘和回滚是记忆系统和普通存储最不一样的地方。事件记忆会过时比如用户三个月前的出差安排今天不应该成为高权重信息事实记忆也可能失效比如用户换了工作城市。设计时要给记忆一个衰减机制访问越少、时间越久、重要度越低检索时排名越靠后。回滚能力更重要一旦发现某条记忆导致Agent做出错误决策应该能把它标记为无效或直接删除而不是等着模型自己纠错。面试中能主动提到遗忘策略通常比只讲检索加分。3. 从零实现一套可用的Agent记忆系统伪代码/原理解析3.1 数据模型设计给记忆打标签、带时间戳、设优先级先定义一个最基础的数据模型。我不建议面试时把设计图画得很复杂但你需要能写出下面这种结构并且能说明每个字段为什么存在。from dataclasses import dataclass, field import time dataclass class MemoryItem: memory_id: str content: str memory_type: str # fact / event / knowledge importance: float 0.5 # 0~1用于排序 created_at: float time.time() last_access_at: float time.time() access_count: int 0 source_message_id: str vector: list field(default_factorylist) metadata: dict field(default_factorydict)memory_id是全局唯一标识content是记忆内容memory_type决定了后续检索策略importance是权重created_at和last_access_at用于时间衰减access_count用于热度加权避免高价值记忆被冷数据挤掉source_message_id是溯源的关键metadata可以塞会话ID、用户ID、业务标签。为什么要单独维护importance因为向量相似度只回答“像不像”不能回答“重不重要”。一个用户三个月前说过的偏好和一个用户今天反复强调的规则向量上可能都很像但后者优先级要高得多。重要性可以在写入时用规则初始化用户明确纠正为0.9任务结果为0.6闲聊为0.3。这个数值不是绝对的但它让后续检索排序有抓手。3.2 记忆检索混合检索与相关性重排检索是记忆系统最容易被高估的部分。很多人以为有一个好向量模型就够了实际上生产环境里我见到的问题大多数出在检索策略太单一。正确做法是混合召回先按条件过滤再做向量/关键词排序最后加权重排。def hybrid_search(query, memory_items, top_k5): query_vec embed(query) candidates filter_by_metadata(memory_items, query) scored [] for item in candidates: sim cosine_similarity(query_vec, item.vector) keyword_score keyword_overlap(query, item.content) # 时间衰减越久没访问系数越低 elapsed time.time() - item.last_access_at recency math.exp(-elapsed / decay_span) # 综合评分语义、关键词、新鲜度、重要度 score 0.5 * sim 0.2 * keyword_score 0.1 * recency 0.2 * item.importance scored.append((score, item)) scored.sort(reverseTrue) return [item for _, item in scored[:top_k]]这组权重只是示例关键是让几类信号组合起来。语义相似度解决“说法不同但意思相同”关键词匹配解决专有名词、ID、精确数字时间衰减解决旧记忆干扰重要度解决核心规则不被长尾内容淹没。伪代码里遍历了所有记忆生产环境可别这么写要用倒排索引或向量索引先召回候选集再做重排。还有一个很容易漏的点每次读取记忆之后要更新last_access_at和access_count。否则时间衰减会一直按写入时间计算导致频繁使用的记忆也慢慢被降权这和你想要的“活跃记忆保持高优先级”正好相反。3.3 上下文组装把记忆塞进Prompt的正确姿势检索到记忆只是开始怎么放进Prompt直接决定模型能不能用对。我的习惯是分区块组装固定顺序系统规则、用户画像、相关记忆、对话历史、当前问题。不同类型的内容放在不同位置。def build_prompt(system_rules, user_profile, memory_hits, history, user_query): memory_block \n.join( f- {m.content}来源{m.source_message_id} for m in memory_hits ) history_block \n.join(history) # 最近N轮 return f [系统规则] {system_rules} [用户画像] {user_profile} [相关记忆] {memory_block} [对话历史] {history_block} [用户] {user_query} 每条记忆后面带source_message_id能让模型在不确定时参考来源也让你在调试时能沿着ID找到原始对话。不要小看这个细节很多Agent幻觉问题就是因为记忆被当成“模型自己的知识”而不是“引用的外部资料”。组装还有一个预算问题。上下文窗口是有限的我给自己的约束是系统规则和当前指令占20%到30%对话历史占40%到50%长期记忆检索结果不要超过30%。如果记忆太多优先保留重要度高的然后用摘要压缩事件记忆而不是盲目截断。如果检索到的记忆已经出现在最近对话里就不需要重复拼进相关记忆区否则模型会觉得冗余。4. 面试现场的高频追问与避坑实录4.1 被问“记忆冲突怎么办”时的回答套路冲突是记忆系统里一定会遇到的问题。用户昨天说喜欢简洁回答今天说今天要详细一点用户先说自己住在A城市后来又搬到B城市。怎么处理简单粗暴的办法是后者覆盖前者可实际业务里经常不是这么回事。我通常会分三步回答。第一步给记忆加版本和来源。不是删除旧记忆而是新增一条带新时间戳的记忆旧记忆降权但不消失。第二步用场景判断冲突是否真实存在。用户说“今天要详细一点”是对当前任务的要求不是新偏好不应该写入长期记忆所以不存在冲突。第三步当两条记忆确实互斥时以更明确的指令为准同时把冲突提示给用户确认而不是自己默默改掉。这套回答的好处是把问题从“数据怎么存”提升到“语义怎么理解”。面试官想听的其实就是你有判断力知道哪些冲突需要程序化解哪些需要靠模型理解力。4.2 被问“Token超限怎么处理”时的工程化方案上下文窗口是硬约束再好的记忆系统也要面临Token预算。有些方案会直接截断对话历史这是最没有技术含量的做法。更工程化的思路是给Prompt内容分级优先保证不能动的再压缩可以动的。内容类型默认优先级超限处理方式系统身份、安全规则最高不允许压缩放在最前面当前任务指令高不允许丢失可精简措辞最近对话历史中先保留最近N轮更早的做摘要长期记忆检索结果中低按重要度截断只保留TopK可丢弃的中间步骤低压缩成一行状态面试里可以用一句口诀总结先保指令再保现状最后保历史。模型如果连当前任务都不知道那给它多少记忆都没有意义。记忆系统设计要和Prompt工程联动它不是一个独立存储模块。4.3 被问“多轮对话翻车”时的排查思路排查记忆问题我有一套固定的顺序。按这套顺序来几分钟就能定位问题。查写入这条信息当时有没有被写进记忆看日志里有没有对应的memory_id。查召回如果写入了检索时有没有排进TopK把query和检索结果打印出来。查组装进入Prompt之后有没有被截断排序是否正确查生成模型有没有忽略记忆或者编造出记忆里不存在的内容一个很常见的翻车案例用户在对话里多次提到自己住在某城市Agent后来却给出另一个城市的出行建议。排查结果是向量检索召回到一条更早的错误记忆错误记忆的向量相似度更高而正确记忆因为信息量少、向量稀疏排到了后面。修复方式就是我在检索代码里加时间衰减同时把“当前城市”这类事实记忆提升重要度。如果这类信息放在结构化表里用精确查询覆盖根本不会走到向量检索这一步。5. 我亲测有效的一页纸设计清单可直接背5.1 从项目到面试的表述顺序如果你在面试里被问到记忆系统不要上来就讲方案细节。我建议用一条问题链来组织表达场景问题是什么整体架构怎么分层关键链路怎么打通效果和坑在哪里举个例子你可以说我之前做的某跨平台助手用户在多轮对话中反复表达同一组偏好模型每次都要重新问一遍体验很差。所以我设计了工作记忆加长期记忆两层结构写入时做类型识别和摘要检索时做混合召回最后把带来源的记忆拼进Prompt。上线后多轮任务成功率有了明显提升也踩了不少坑比如记忆串会话、旧记忆覆盖新规则。这个顺序本身就能让面试官觉得你有全局观而不是只盯着某一个算法点。5.2 三个容易忽略但很加分的细节第一个细节是记忆溯源。每条长期记忆都要保存来源消息ID这样你才能回答“这条记忆是哪句话来的”模型也能在生成时引用来源。没有溯源的记忆出了问题只能靠猜。第二个细节是异步写入。记忆写入不应该阻塞主流程。用户已经得到回复了再慢慢把今天的对话摘要写入长期记忆就行。如果同步写入一次请求的延迟会增加几十甚至上百毫秒成本也会上涨。第三个细节是会话隔离。所有记忆都必须带上user_id和conversation_id检索时先过滤。很多实际事故都来自记忆串线A用户的信息跑到了B用户的上下文里。不要觉得这是小事面试官抛出一个多用户场景时你如果能主动说隔离已经表现出生产意识。面试官真正想听的不是你有没有一个完美系统而是你知道哪里会坏、坏了怎么查、上线后怎么调。记忆系统不是数据库设计题它是决策上下文治理题。能把这层想清楚你的答案就超过了大部分候选人。最后说一点我个人体会。这几年我在面试中被问记忆系统也在面试别人时问记忆系统最大的变化是我不再把记忆当成“存和取”而是把它当成Agent整个决策链条里的一环。你写什么进记忆决定了模型下一次决策的信息基础你怎么组织记忆决定了模型能不能在噪声里抓住关键线索。面试前如果你时间紧别急着背论文先把一条记忆从产生、写入、检索到拼进Prompt完整走一遍每一步都问自己为什么。这个为什么能答上来其实比记住多少名词都重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑