
从标题聊起吧。这条标题其实涵盖了一个现在很典型的Agent开发路径先确定“扩展范式”再构建“记忆体系”最后通过“多轮记忆改造”把前两件事情贯通起来。说白了很多团队做到后面都发现Agent好不好用往往不是模型本身的问题而是“扩展方式是不是顺手、记忆是不是能留住、多轮对话是不是真的在上下文里存了该存的东西”。这篇内容就是围绕这条路径的一次系统性复盘。我把它拆成了几个层次来讲先说扩展范式为什么是记忆体系的“地基”然后讲Memory记忆体系到底在解决什么问题再落到多轮记忆改造里最关键的实操细节最后把容易踩的坑也一起列出来。适合正在搭Agent、想把交互做得更长久、更连续的开发者参考也适合第一次接触这套概念、想搞明白“记忆到底怎么设计”的入门者阅读。1. Agent扩展范式为什么先解决“怎么接入”再谈“记忆”1.1 扩展范式的本质是把模型的边界拓宽不管你用的是哪家主模型单靠模型本身能做的事情都非常有限。模型只擅长一件事根据输入生成合理的文本输出。但Agent要做的是“行动”查数据库、调用接口、操作工具、读取文件、发送通知。这些都不在模型的天然能力范围内所以必须通过“扩展”把能力接进去。这套“如何把外部能力接到模型上”的规范就是扩展范式。现在最主流的两种方式一种是Function Calling函数调用一种是MCPModel Context Protocol模型上下文协议这类统一接入协议。我最早做Agent项目的时候用的是最原始的Function Calling方案在系统提示词里把工具定义成JSON Schema模型根据用户需求自主决定调用哪个函数。这套方案的好处是灵活、直接缺点也明显——函数一多管理起来就非常混乱而且跨服务调用时要自己定义协议。后来MCP这类方案开始普及核心思路是把“工具”抽象成标准化的服务端点模型通过协议直接发现和调用远端能力不再需要为每一个新工具写胶水代码。你可以理解成Function Calling是给模型配了一本电话簿MCP是给模型配了一整套可发现、可动态接入的“服务体系”。对于多轮记忆改造这件事来说扩展范式的影响非常直接。因为“记忆”本身也是一项能力完全可以用同样的方式暴露给模型——比如提供一个“读记忆”“写记忆”“检索记忆”的工具集。这样记忆就变成了Agent体系里的一个标准扩展而不是散落在业务代码里的零散逻辑。1.2 Skill机制与范式组合让Agent知道“什么场景用什么方式”在扩展范式里面还有一个经常被讨论的概念叫Skill。它和普通工具的区别在于工具对应的是单一动作Skill对应的是一整套带流程、带约束的能力包。举个例子。你有一个“查询订单”的工具它能做的只是输入订单号返回订单状态。但如果你把这个工具升级成一个Skill它可能包含先获取用户身份、再按权限过滤、再做一次状态缓存检查、最后把结果格式化输出。整个过程有顺序、有条件分支、有异常处理。热词里反复出现“skill和agent的区别”“agent skills”其实就是在讨论这个分层Skill是Agent能力的“带流程版本”而Agent本身是负责调度这些Skill的决策主体。在多轮记忆改造中Skill还有一个特殊价值——可以设计一个“记忆Skill”来统一承担记忆的存取策略比如在新会话开始时自动加载上次对话摘要在对话过程中判断什么时候该更新长期记忆。这样记忆逻辑不会散落在各个业务分支里而是收敛到一个统一入口。扩展范式的核心判断依据就一条你的Agent要接入多少种外部能力以及这些能力需要多复杂的编排。能力少、逻辑简单直接用Function Calling就够。能力强、服务多、还要支持团队协作开发就必须考虑MCP这类标准化协议。而记忆体系的位置就处在范式选型确立之后的那一层——先确定了怎么接能力才有可能知道记忆应该以什么形态接入进来。2. Memory记忆体系Agent的记忆到底该存什么、怎么存2.1 工作记忆、情景记忆与语义记忆三类记忆的分工很多开发者第一次设计记忆体系时都会犯一个相同的错误试图把“历史聊天记录”原封不动地塞给模型。但真正好用的记忆体系是把记忆按照“用途”分成不同层级的而不是一股脑地按时间顺序堆在一起。比较稳定的分法有三层第一层是工作记忆Working Memory。它对应的是当前会话进行中的短期状态比如用户刚刚强调的几个偏好、当前任务进行到哪一步、刚才计算出来的中间结果。这一层记忆的特点是生命周期短会话结束就可以丢弃作用是支撑多轮对话的连续性和当前任务的推进。第二层是情景记忆Episodic Memory。它对应的是“发生过的事情”比如用户在上次会话里问过什么问题、做过什么操作、得到过什么结论。这一层记忆的价值在于让Agent能够跨会话延续服务体验比如用户两周前来咨询过某个产品的故障问题今天再次来访时Agent能主动提起“上次那个网络问题后来解决了没有”。第三层是语义记忆Semantic Memory。它对应的是“提炼出来的规则和知识”比如用户的行业背景、长期偏好、不希望被触碰的话题边界。这一层记忆不一定来自某一次具体对话而是Agent在长期交互中沉淀下来的结构化事实。如果拿人来打比方工作记忆是你正在对话时脑子里记着的那几句话情景记忆是“上周三我们聊过什么”语义记忆是你对这个人整体形成了什么印象。三层记忆的存储方式和管理策略都很不一样但好的Agent必须同时拥有这三层缺了任何一层都会显得“不通人性”。2.2 存储选型、生命周期与检索策略记忆体系的底层设计确认了记忆的分层之后要考虑实现层面的具体问题记忆数据放哪里、怎么写、什么时候过期、怎么查。我个人的做法是先用热词里频繁出现的那个组合Redis 向量数据库。Redis负责工作记忆和短期高频访问的会话状态特点是快、支持过期向量数据库负责情景记忆和语义记忆这类需要检索的内容特点是支持语义相似度匹配。沉淀后的长期记忆也可以考虑落到PostgreSQL这类关系型数据库里配合Redis做缓存加速。具体到一条记忆的生命周期至少要覆盖四个阶段写入、检索、更新、遗忘。写入发生在对话过程中Agent认为某条信息值得保存时触发检索发生在需要回忆相关记忆时通过向量相似度或关键词匹配召回更新发生在用户主动纠正或新信息覆盖旧信息时长期记忆不能只增不改否则越攒越乱遗忘则靠过期时间或记忆重要性衰减机制来实现。检索策略里有一个细节值得单独说不要每次都把全部记忆塞进上下文。上下文窗口就那么大正确做法是“按需检索优先级排序”——先把用户最近的意图和当前关键词拿出来据此召回Top-K条相关记忆再按“重要程度”和“时间新旧”做一次排序只把最关键的部分注入提示词。这也是为什么记忆体系离不开向量检索你不可能用字符串搜遍所有历史对话语义检索才能做到“用户上次提过一个模糊的抱怨这次系统能自己关联起来”。实操建议给每条记忆打一个复合索引结构大致为user_id session_id memory_type keyword embedding。检索时先按user_id做隔离过滤再按记忆类型和embedding相似度排序查询效率会明显提升。2.3 记忆框架选型从自研到成熟框架关于“agent记忆框架以及选型”这个热搜词很多新入行的朋友会纠结到底是用成熟框架还是自己写一套。我的判断标准很简单看你的记忆需求里有几个“非标准”的地方。如果只是把聊天摘要存起来、检索出来、再注入系统提示词用现成框架会省很多事因为框架里已经内置了记忆写入、检索、上下文组装这几段样板逻辑。但如果你要做细粒度的记忆权限控制、多用户记忆隔离、记忆合并去重、或者复杂的记忆过期策略现阶段还是要自己动手改造一层。自研这套东西时核心模块大概就四个记忆写入模块负责从对话里抽取值得记的东西、记忆存储模块负责把抽取结果持久化、记忆检索模块负责按需召回、记忆注入模块负责把检索结果拼到合适的提示词位置。这四个模块解耦之后后续无论换存储还是换模型改动面都控制得住。还有一个容易忽略的点记忆体系一定要有“版本概念”。因为你对记忆结构的定义会随业务变化而调整如果一张记忆表的schema写死了将来加字段会非常痛苦。建议一开始就在存储层预留一个version字段每次改造记忆结构时递增版本号老数据按版本迁移或兼容读取。3. 多轮记忆改造从“一问一答”到“带着记忆连续对话”3.1 改造前的基线先判断你现在的对话处于什么阶段我把没有记忆改造的Agent对话分成三个典型阶段你可以按图索骥判断自己处于哪个阶段。阶段一纯单轮对话。Agent对每一轮输入都独立处理不做任何跨轮引用。这是最原始的形态很多用API直连模型的Demo就是这样写的。阶段二上下文拼接。把最近几轮的用户消息和模型回复直接拼在prompt里传给模型模型看起来“记得”刚才说了什么但实际上所有历史都是无差别塞进去的。这个方案在session短的时候好用session一长就开始出问题——上下文窗口溢出、无关信息干扰决策、token费用飙升。阶段三带结构性记忆的对话。引入分层记忆机制有一块专门存放当前会话的摘要有一块存放抽取出来的长期事实系统只选择性地把最相关的部分放进下一轮prompt。这才是真正的“多轮记忆改造”。绝大多数需要改造的项目都卡在阶段二到阶段三的升级过程中。而改造的核心就是要回答三个问题哪些对话信息值得记、记成什么结构、下次怎么拿出来用。3.2 改造实操代码级别的完整改造流程下面我以一套Python代码为例完整演示一个最简单的多轮记忆改造流程。这个代码结构不是虚构的是我在多个项目里复用过的孵化版本你可以直接复制改成自己的业务需求。第一步定义记忆数据模型。用dataclass定义工作记忆和长期记忆两类数据结构方便后续持久化和检索。from dataclasses import dataclass, field from typing import Optional from datetime import datetime import uuid dataclass class WorkingMemoryItem: content: str created_at: datetime field(default_factorydatetime.now) expires_at: Optional[datetime] None dataclass class LongTermMemoryItem: memory_id: str field(default_factorylambda: str(uuid.uuid4())) user_id: str content: str memory_type: str semantic # episodic / semantic importance: float 0.5 created_at: datetime field(default_factorydatetime.now) updated_at: datetime field(default_factorydatetime.now)第二步实现一个记忆管理器。把工作记忆的读写和长期记忆的检索更新都封装在这个管理器里。import redis import json class MemoryManager: def __init__(self, redis_client: redis.Redis, vector_storeNone): self.redis redis_client self.vector_store vector_store # 可选用于语义检索 def write_working_memory(self, session_id: str, item: WorkingMemoryItem): key fworking:{session_id} items json.loads(self.redis.get(key) or []) items.append({ content: item.content, created_at: item.created_at.isoformat(), expires_at: item.expires_at.isoformat() if item.expires_at else None }) self.redis.setex(key, 3600, json.dumps(items)) # 1小时过期 def read_working_memory(self, session_id: str): key fworking:{session_id} return json.loads(self.redis.get(key) or []) def write_long_term_memory(self, item: LongTermMemoryItem): # 实际应用中这里会写入向量数据库 关系型数据库 # 这里简化为Redis哈希表存储 key fmemory:lt:{item.user_id} memories json.loads(self.redis.get(key) or []) memories.append(item.__dict__) self.redis.set(key, json.dumps(memories)) def retrieve_long_term_memory(self, user_id: str, query: str, top_k: int 5): # 实际项目中会调用向量检索 # 这里先用一个示例实现只做字符串过滤 key fmemory:lt:{user_id} memories json.loads(self.redis.get(key) or []) scored [] for mem in memories: if query in mem[content]: scored.append((mem[importance], mem)) scored.sort(keylambda x: x[0], reverseTrue) return [item[1] for item in scored[:top_k]]第三步在对话循环里组装带记忆的上下文。def build_context_with_memory(user_query: str, session_id: str, user_id: str, memory_manager: MemoryManager): # 1. 读取工作记忆当前会话的短期状态 working_memories memory_manager.read_working_memory(session_id) # 2. 检索长期记忆跨会话的关键事实 long_term_memories memory_manager.retrieve_long_term_memory(user_id, user_query, top_k3) # 3. 分层拼接prompt memory_context if long_term_memories: memory_context 【长期记忆】\n for idx, mem in enumerate(long_term_memories): memory_context f{idx1}. {mem[content]}\n if working_memories: memory_context 【本会话进行中】\n for item in working_memories: memory_context f- {item[content]}\n system_prompt f 你是一个带有连续记忆能力的Agent。 请先参考以下记忆内容再回答用户当前的问题。 如果记忆与当前问题无关请忽略记忆正常回答。 {memory_context} return system_prompt第四步在每轮对话结束后判断哪些信息值得写入记忆。def extract_and_store_memories(user_query: str, assistant_response: str, session_id: str, user_id: str, memory_manager: MemoryManager): # 提取工作记忆例如用户当前的操作目标 working_item WorkingMemoryItem( contentf用户本轮提及需求{user_query[:100]}, expires_atNone # 会话内有效 ) memory_manager.write_working_memory(session_id, working_item) # 提取长期记忆判断用户是否表达了明确偏好或事实 # 实际项目中可以用一个轻量分类模型或正则规则判断 preference_keywords [我喜欢, 我不喜欢, 我的职业, 我住在, 我常用的] for keyword in preference_keywords: if keyword in user_query: long_term_item LongTermMemoryItem( user_iduser_id, contentuser_query, memory_typesemantic, importance0.8 ) memory_manager.write_long_term_memory(long_term_item) break这套代码可以直接跑通但它的意义更多是展示“多轮记忆改造”的最小可行结构写入逻辑在哪里触发、记忆以什么结构存储、检索结果怎么合入最终prompt。你在实际项目中要替换的只是存储介质和抽取策略骨架不用动。3.3 多轮记忆改造中最关键的一个细节上下文拼装顺序改造之后很多同学会发现记忆内容确实存进去了也检索出来了但模型回答还是不够好。问题往往出在“拼装顺序”上。我的经验是长期记忆放在系统提示词的偏后段、紧挨着用户输入的位置效果最好。原因是长期记忆相当于“背景事实”它不应该抢掉模型对当前用户意图的注意力但必须在模型生成答案前被“看见”。如果把它放在很靠前的位置模型可能会把记忆里的旧信息当成当前的主任务如果放到最后又可能出现记忆没被充分引用的现象。推荐的拼装顺序是系统角色设定你是谁、有什么能力、遵循什么守则当前任务的说明这一轮要做什么用户当前输入长期记忆片段作为参考背景本会话上下文摘要或前几轮的要点模型的输出指令格式要求、不要重复已给过的建议等这套顺序的核心逻辑是让模型先聚焦当下再看背景最后结合全部信息做输出决策。顺序一旦搞反改造效果会大打折扣很多坑都出在这里。3.4 改造后必须做的回归测试与效果对比多轮记忆改造完成后别急着上线。这个类型的改动涉及对话逻辑的底层行为变化需要系统地做一次回归测试。我会准备一组固定测试用例覆盖以下几种场景单轮对话正常性不能因为加了记忆让简单问答变啰嗦、多轮连续任务让Agent在五轮以内完成一个需要多步骤的任务观察每一步是否沿用前面步骤的结论、跨会话记忆召回新开一个会话问一个只有上个会话出现过的信息观察是否能命中、记忆干扰规避在记忆里故意放一条与当前无关的旧信息观察模型是否会受干扰。对照测试也很重要。同一组问题分别用“改造前无记忆版本”和“改造后带记忆版本”跑一遍记录三类指标任务完成率用户目标是否达成而非仅“回答流不流畅”、平均轮数完成同一件事需要多少轮对话、用户显性重述次数用户是否反复表达同一个需求多轮记忆做得好坏直接体现在这个指标上。多轮记忆不是为了“显得聪明”而是为了减少用户被反复追问、反复解释的挫败感。这些数据会告诉你改造是否真的有价值。4. 多轮记忆改造里的常见问题与排查实录4.1 记忆内容太多导致上下文溢出或生成质量下降多轮记忆改造最常见的翻车现场就是这个记忆体系建好了存的也越来越多了某一天线上突然开始频繁报错或者模型输出质量明显下滑。排查方向有两个。第一检查注入prompt的记忆条数上限和单条长度限制。我这边给团队定的默认值是长期记忆最多5条每条不超过100字工作记忆摘要最多3条每条不超过50字。超过了就要做淘汰或者压缩。第二看存储层的过期策略是不是失效了。用Redis存工作记忆的场景经常出现redis key没有设置过期时间导致数据累积的情况。记一个死规矩所有工作记忆相关的key必须设置TTL长期记忆表必须设置更新时间和重要性衰减机制。4.2 多用户/多会话记忆串线另一个高频问题是记忆串线A用户的信息被当成了B用户的背景资料或者上个会话的中间状态被错误地带入了新会话。这个问题多发生在两个环节检索时没有按user_id过滤以及session_id的传递逻辑有漏洞。排查时先看日志里检索记忆时的过滤条件是不是真的带上了用户标识。很多早期实现只把user_id放在记忆内容里没有放在查询条件里等于所有用户在一个大池子里捞数据。隔离必须做在查询层面而不是靠事后过滤。更进一步如果同一个用户同时打开了多个会话比如网页端和手机端同时在线还要考虑session维度的隔离工作记忆按session_id隔离长期记忆按user_id聚合两者不要混。4.3 记忆更新不及时或旧记忆覆盖新记忆还有一类问题用户明确说了“之前的方案不用了换一种”但Agent还在反复引用旧方案。这是典型的重写逻辑缺失。处理方案是在写入长期记忆的流程里不止做append还要做一遍“冲突检测”。可以按记忆的语义相似度判断是否有旧条目与当前新信息描述同一主题如果有且新信息的imporrtance更高或时间更新就覆盖旧条目标记为新版本而不是新增一条并存。否则多条互相矛盾的旧记忆并存检索时按相似度排序会把新旧内容同时召回模型就懵了。4.4 本地部署场景的内存与存储问题热词里有一批关于out of memory的报错提示这在本地部署Agent的开发者群里非常常见。如果你是在本地或小规格机器上跑Agent加记忆体系通常会遇到两类内存问题一类是模型推理导致的内存溢出典型表现是加载模型时爆显存或系统内存不足。这类问题通常出现在加载超大模型或把模型和向量数据库同时塞进同一台机器时。解决思路有三条换更小的量化模型把向量检索独立到低资源消耗的进程里不要和模型推理进程抢内存如果用的是Docker容器部署还要检查容器内存限额是不是设置得比实际需要小。另一类是Redis这类缓存组件加载数据时的内存问题日志里常见的“Loading Redis is loading the dataset in memory”就属于这一类。排查时先执行内存检查命令看容器或系统剩余内存再看Redis的maxmemory配置是否合理以及是否有大key没设置过期。记住一句话记忆体系里所有的“持久化”都应该是分层的Redis只承载高频短生命周期的数据真正的长期记忆一定要落到磁盘型数据库里。4.5 记忆安全与权限控制最后提一个经常被忽视的话题记忆安全。记忆体系里会沉淀大量用户的隐私信息和业务事实这些数据一旦被错误地注入到别的场景或者被越权检索后果非常严重。热词里有“a-memguard: a proactive defense framework for llm-based agent memory”这样一个短语方向就是针对Agent记忆的防御框架。虽然这是学术概念但落地时的思路是通用的给记忆条目标注访问级别敏感记忆不回注入到低权限工具的调用场景在写入前做脱敏判断在系统提示词里明确告诉模型不得主动披露记忆中涉及第三方或高度隐私的信息。记忆不是越全越好记忆的安全边界应该从上线的第一天起就划清楚。5. 如果你的Agent还没记忆从今天开始的改造路线图可能你现在的Agent还是最原始的“一问一答”状态连上下文拼接都还没做。那么从现在开始我建议按这条路线走每走完一步都验证之后再进下一步。第一步先做“日志化”。把每一轮用户输入、模型输出、系统调用全部落日志。这一步不是为了立刻改造而是为了让你看清当前会话的真实形态——用户到底平均会在一个session里聊几轮最常追问的信息是什么哪些内容反复出现但Agent每次都要重新问。这些数据是后面所有设计的依据。第二步做“工作记忆”。把最近三轮对话摘要存起来在下一轮注入prompt。这只需要很小的改动就能让多轮体验上一个台阶。注意只做摘要不要全文拼接。第三步做“长期记忆抽取”。在对话过程中识别用户明确表达的偏好、事实、身份类信息写入长期记忆库并在新会话开始时做一次召回。第四步做“跨会话检索”。引入向量检索能力让长期记忆不依赖关键词也能被语义召回。这一步之后Agent才真正拥有了“记得你”的能力。第五步做“评估和迭代”。建立测试集、记录指标、持续调整写入阈值、检索条数、记忆过期策略。记忆体系是个持续调优的系统不是一次上线就完事的。这套路线最关键的指导思想是记忆改造永远不要追求一步到位。先让Agent“不忘记当下”再让Agent“记得住过往”最后才让Agent“用得上积累”。每一步都有独立的收益不需要等全链路做完才看到效果。在实际操作中我觉得最有成就感的一个瞬间是第一次看到Agent在两轮对话之后主动说出了用户在上一轮提及但当前问题里没有重复的信息。那一刻你才会真正意识到多轮记忆改造带来的不是技术指标的提升而是产品体验从“功能执行器”到“有连续感的服务体”的本质变化。如果你的Agent正在做扩展范式和记忆体系的整合希望这篇内容能帮你少踩几个我当年踩过的坑。