AI Agent记忆三层架构:短期、长期与工作记忆详解
很多同学学 AI Agent学到一半就卡在“记忆”这个词上。不是概念难而是网上说法太多有人说记忆就是上下文窗口有人说记忆就是向量数据库也有人说记忆等于 RAG还有人直接把它归到记忆引擎。都有道理但都不完整。记忆在 Agent 里不是一个单一模块而是至少三层结构的组合短期记忆管当前对话长期记忆管跨会话的事实和知识工作记忆管一次任务的执行状态。本文就用这套三层架构把 Agent 记忆一次讲清顺带给出可落地的代码框、接口设计和验证流程。为什么这个问题值得单独写一篇因为 Agent 和普通 LLM 应用最大的差别恰恰就在于它能不能“记住”和“使用”信息。没有记忆的 Agent 每次对话都从零开始用户画像会丢、业务规则会忘、多步任务做到一半就失忆。可以说记忆设计决定了 Agent 是“能对话的接口”还是“真正能办事的智能体”。本文先给三层架构的规格速览再从第一层讲到第三层最后给出接口示例、性能观察、排查清单和工程建议。内容偏工程向适合正在做 Agent 开发、准备 Agent 面试或者想给自己项目接入记忆能力的同学阅读。为了不让文章停在概念层面我会用 Python 和少量伪代码把每一层记忆的最小实现写出来。你不需要照抄代码理解每条记录的结构、归属和生命周期就能迁移到自己的项目里。技术选型可以换成 Redis、MySQL、Chroma、FAISS、Elasticsearch 或云厂商的向量库思路是一致的。如果你已经能把一个 LLM API 调通本文的内容就可以直接在此基础上扩展。1. 核心能力速览Agent 记忆的三层架构记忆层级核心职责存储形态生命周期典型实现短期记忆维持当前对话上下文模型输入中的消息序列会话期间有效messages 列表、system prompt长期记忆保存用户画像、偏好、事实、知识数据库 向量库/关键词索引跨会话长期保存MySQL、Redis、Chroma、FAISS、Elasticsearch工作记忆记录一次任务的计划、步骤、中间结果进程内对象 / 状态存储当前任务执行期间有效内存字典、Redis、任务状态表三层架构不是三条并列的内存而是职责分工。短期记忆服务模型输入长期记忆服务查询检索工作记忆服务任务执行。判断设计是否合理的标准很简单掉电之后哪些数据必须恢复哪些可以接受消失必须恢复的是长期记忆可以消失但任务还需要继续推进的是工作记忆只对当前回复有意义的短期记忆则可以随轮次丢弃。从材料里看很多 Agent 教程、面试题和开发项目都在讨论“记忆”这个概念但大多只停留在“给模型拼一段历史消息”的程度。真正面试或开发时面试官和业务方更关心的是记忆存在哪里、按什么粒度存、什么时候写入、什么时候读取、多用户之间怎么隔离。这正好是三层架构要回答的问题。2. 为什么 Agent 需要记忆从无状态到有状态先看三个典型场景。第一个场景是用户画像丢失。用户第一次告诉 Agent“我喜欢简洁的回答风格平时关注量化交易”第二次对话时 Agent 完全没记住又从头问一遍。这是因为很多初版 Agent 根本没有长期记忆每次请求都是独立调用模型只能看到当前这一轮输入。第二个场景是业务知识不落地。客服 Agent 需要知道“售后政策”、“退款规则”、“最近一次运营活动的时间”这些信息如果只放在 prompt 里会占大量 token而且更新困难。放进向量库或数据库按需检索才是最合理的做法。第三个场景是长任务中断。Agent 要完成“查一下近三天的销售数据生成一份周报再发给相关同事”。如果执行到第二步时进程重启或者模型调用超时前面的中间结果全部丢失任务得从头再来。工作记忆就是为这种情况设计的把计划、步骤、中间结果保存下来让任务可以断点续跑。无状态调用是简单的但也是脆弱的。给 Agent 加上记忆本质上就是把对话系统从“一问一答”升级成“带上下文、带知识、带执行状态”的完整系统。三层架构每一层解决一类问题互相不可替代。3. 第一层记忆短期记忆上下文窗口3.1 短期记忆的本质短期记忆最简单也最容易被误当成全部记忆。它的本质就是模型输入里的一段消息序列用户说了什么、助手之前回答了什么、系统设定是什么。模型本身没有状态所有“记忆”都体现在输入文本里。实现方式很直接把历史消息拼成 messages 列表连同当前问题一起发给模型。# 维护短期记忆的最小示例 messages [ {role: system, content: 你是面向电商客服的 AI 助手。}, {role: user, content: 我的订单一直显示配送中怎么办}, {role: assistant, content: 请提供订单号我帮您查询。}, {role: user, content: 订单号是 20250121001。}, ] def build_request(new_user_message): if new_user_message: messages.append({role: user, content: new_user_message}) # 实际项目中把 messages 发给模型即可 return messages3.2 上下文窗口的边界问题在于上下文窗口是有限的。窗口越大单次请求的 token 越多成本和延迟同步上涨。而且模型对一段超长上下文的理解能力并不是线性提升早期内容很可能被“淹没”在长文本里。实际项目里一般两种处理方案滑动窗口和总结压缩。滑动窗口就是只保留最近 N 条消息超出部分直接丢弃MAX_HISTORY 8 def trim_history(messages): # 保留 system再保留最近的 MAX_HISTORY 条消息 system_msgs [m for m in messages if m[role] system] history [m for m in messages if m[role] ! system][-MAX_HISTORY:] return system_msgs history这种方式实现简单能防止 token 超长但丢弃的早期信息无法找回。更进一步的方案是“总结压缩”先让模型把旧对话总结成要点再把要点放回 system prompt。这个方案在上下文要求较高的场景里很常见缺点是每次总结都多一次模型调用成本和延迟会上升。短期记忆的设计原则是只保留对当前回复有直接帮助的信息。不要试图把所有用户历史都塞进上下文那既不经济也不一定能提升回答质量。4. 第二层记忆长期记忆外部存储与检索4.1 长期记忆存什么长期记忆解决的是“跨会话记住事实”的问题。典型内容包括四类用户画像偏好、职业、年龄、使用习惯。业务事实用户在某系统里的角色、权限、历史订单。知识点产品文档、FAQ、技术手册。历史结论之前确认过的需求和决策记录。结构化程度高的事实可以放关系型数据库比如“用户 ID 1001 的偏好是简洁回答”。非结构化文本比如一段用户讨论、一篇文章摘要更适合向量化后放入向量库。4.2 长期记忆的写入与读取写入记忆时要考虑四个字段归属人、内容、时间、附加属性。下面是一个简化示例def save_long_term_memory(user_id, key, content, metadataNone): # 伪代码具体写入逻辑依赖数据库和向量库 # 1. 将 content 向量化 # 2. 写入向量库并附带 user_id 和 key # 3. 同步写入结构化数据库便于过滤和删除 record { user_id: user_id, key: key, content: content, metadata: metadata or {}, } vector_db.upsert(record) sql_db.insert(record) return record读取记忆时一般用语义检索def search_long_term_memory(user_id, query, top_k5): query_vector embed(query) candidates vector_db.search( query_vector, filter{user_id: user_id}, top_ktop_k ) return candidates注意检索条件里带了user_id过滤。这是最容易踩坑的地方如果不按用户隔离A 用户的偏好和事实会被检索出来给 B 用户用造成严重的记忆污染。4.3 长期记忆与 RAG 的关系很多人把长期记忆和 RAG 混为一谈。严格说RAG 是一种给模型补充外部知识的技术路径长期记忆是 Agent 架构里的一个能力模块。两者经常结合RAG 负责从文档、知识库中检索片段长期记忆负责统一管理这些片段以及用户相关的历史事实。从工程上看长期记忆比 RAG 多一层语义它不仅要能“查得到”还要负责“留下重要的、删掉没用的、避免存进去一堆噪音”。所以在长期记忆模块里写入策略比检索策略更关键。默认做法不是把所有对话都写进长期记忆而是先判断这段内容是否有复用价值、是否经过用户确认、是否属于稳定事实。否则记忆库会很快膨胀成大型垃圾场检索结果相关性直线下降。5. 第三层记忆工作记忆任务状态与情景记忆5.1 工作记忆是什么工作记忆是三层架构里最容易被忽略、但恰恰是让 Agent“能办事”的一层。它记录的是当前任务的执行状态定好的计划、进行到第几步、每一步产出的中间结果、工具调用的入参和返回、出错信息。它与短期记忆的区别在于短期记忆面向对话工作记忆面向任务执行。对话可以有很多轮但任务可能只执行几分钟任务可以跨多个工具调用但对话上下文未必需要保留所有工具细节。它与长期记忆的区别在于长期记忆跨会话保留工作记忆只在当前任务生命周期内有效。任务结束后有价值的结论沉淀到长期记忆中间过程可以清理。5.2 工作记忆的最小实现class WorkingMemory: def __init__(self, task_id, user_id): self.task_id task_id self.user_id user_id self.plan [] self.current_step 0 self.status running self.intermediate_results {} self.error None def update_step(self, index, result): self.intermediate_results[index] result self.current_step index 1 def finish(self): self.status done def fail(self, error): self.status failed self.error str(error)真实系统里工作记忆的结构化字段可以放在进程内或 Redis 里大块输出放对象存储方便任务中途被拉起后恢复。任务重启后从任务状态表里读取task_id对应的状态就能知道之前执行到哪一步、下一步该做什么。5.3 工作记忆的使用边界不是所有 Agent 都需要复杂的工作记忆。如果你的 Agent 只做问答、不做多步执行用短期记忆加长期记忆就够了。但一旦 Agent 涉及工具调用、多步骤流程、异步执行工作记忆就是必须的。它让你能回答三个问题现在做到哪了、之前得到了什么、下一步做什么。有些 Agent 还会用“情景记忆”这个概念记录“某次任务怎么执行的、效果如何”以便下次遇到类似任务时参考。情景记忆可以理解为工作记忆沉淀后的产物仍然归在长期记忆的体系里只是它的内容是关于经验的而不是关于用户的。这种双网络记忆模型的核心思想本质上就是短期记忆与长期记忆分离再加上任务执行状态的辅助。6. 三层记忆如何协同工作一次完整任务流程只看单层结构容易“不识庐山真面目”。把三层串起来跑一遍才看得出它们各自的价值。def run_agent(user_id, task_description): # 1. 读取长期记忆中的用户画像 profile search_long_term_memory(user_id, task_description, top_k3) # 2. 组装短期记忆上下文 messages assemble_short_term_context(profile, task_description) # 3. 创建工作记忆 wm WorkingMemory(task_iduuid4(), user_iduser_id) # 4. 规划并逐步执行 plan plan_tasks(task_description) wm.plan plan for idx, step in enumerate(plan): # 5. 每一步执行前先检索相关长期记忆 related search_long_term_memory(user_id, step, top_k3) result execute_step(step, contextrelated) # 6. 更新工作记忆 wm.update_step(idx, result) wm.finish() # 7. 抽取重要事实写回长期记忆 save_important_facts(user_id, wm) return wm这个流程里短期记忆负责把用户当前的问题和系统设定组装成模型输入长期记忆负责在规划前和执行中提供用户画像与业务知识工作记忆负责记录计划、步骤和中间结果保证任务可推进、可恢复、可复盘。最值得细看的是最后一步“抽取重要事实写回长期记忆”。为什么放在任务末尾因为任务执行过程中会产生大量噪声只有最终确认下来的结论才值得沉淀。比如客服 Agent 处理完订单后可以记录“用户偏好使用邮件接收回执”但不应该记录“用户问过配送时间”这种一次性信息。如果这一步不做长期记忆会快速膨胀检索质量下降是非常隐蔽的劣化过程。7. 记忆模块的接口设计与批量任务适配7.1 统一记忆接口无论底层用什么存储记忆模块对外接口应该保持稳定。推荐至少五个操作写入、读取、检索、更新、删除。class MemoryManager: def __init__(self): self.store {} def write(self, scope, key, value): scoped self.store.setdefault(scope, {}) scoped[key] value def read(self, scope, key): return self.store.get(scope, {}).get(key) def search(self, scope, query): # 简化演示关键词匹配 records self.store.get(scope, {}) return {k: v for k, v in records.items() if query in str(v)} def delete(self, scope, key): self.store.get(scope, {}).pop(key, None)这里的scope是记忆隔离的核心。推荐组合使用user_id和task_id多轮对话用user_id隔离批量任务用user_id:task_id隔离。这样 A 用户的任务不会读到 B 用户的任务状态同一用户的两个并发任务也不会互相覆盖。7.2 批量任务的记忆适配批量任务场景里记忆管理最容易出问题。常见错误是把所有任务的结果写进同一个全局记忆导致后一个任务被前一个任务的信息干扰。更稳妥的做法是每个任务一个独立 scope任务内共享短期记忆和工作记忆。任务执行结束后再把需要长期保留的结论按用户维度汇总写回。批量任务要加日志每条记忆的写入、读取、删除都有 trace方便排查串数据问题。配置文件可以这样约束默认行为{ memory: { scope_separator: :, short_term: {max_messages: 8, compression: true}, long_term: {store: vector_db, top_k: 5}, working: {store: redis, ttl: 3600} } }如果是 Java/Spring Boot 技术栈上面这套设计同样适用Redis 管理工作记忆和会话状态PostgreSQL/MySQL 存结构化长期记忆向量库存非结构化偏好MemoryManager 作为 Service 层封装向上游提供稳定接口。记忆存储的具体技术可以变职责边界不变。7.3 记忆接口的测试维度测试记忆模块时不要只验证“能写入”“能读出来”。建议覆盖以下用例写入后立即可读。不同 scope 之间检索不串数据。删除后不可读。检索结果按相关性排序。批量写入高并发时scope 隔离依然有效。任务中断后工作记忆能恢复。这些用例跑通记忆模块才算基本可用。8. 资源占用与性能观察记忆设计得好不好最终会反映在资源占用和响应速度上。不需要等到上线开发阶段就可以建立观察清单。观察项观察方法关注指标token 消耗调用统计单会话 token 总量、成本检索耗时打点平均检索延迟、P95检索质量人工评估/抽样命中率、改写后相关度记忆写入量日志统计每条会话沉淀条目数先说短期记忆。短期记忆越长单次请求 token 越多成本和延迟同步上升。如果开了总结压缩还要额外统计每次压缩产生的模型调用成本。一个常见优化是只在上下文接近上限时才压缩而不是每轮都压缩。再说长期记忆。向量检索的耗时与索引规模相关数据量小的时候毫秒级数据量大了之后如果没有做索引优化检索耗时和内存占用都会明显上涨。大规模数据场景一般用 HNSW 等近似最近邻索引用少量召回率换更快的查询速度和更低内存。长期记忆还有一个隐藏成本噪音导致的效果劣化。记忆越多检索出无关内容的概率越高。所以观察指标不能只看“检索耗时”还要抽样统计“检出的 5 条里真正对回答有帮助的有几条”。如果命中率一直在下降就要考虑做记忆整理、去重、过期清理甚至给记忆加重要度评分。工作记忆的优化重点是控制体积。不要在每次调用模型时都把工作记忆全量塞进上下文而是只把当前步骤需要的部分组装进 prompt。工作记忆本身的读写用 Redis 这类低延迟存储避免大块数据频繁序列化。9. 常见问题与排查方法问题现象可能原因排查方式解决方案Agent 忘记当前话题短期记忆被裁剪检查 messages 是否超长调大窗口或用总结压缩总是检索不到记忆未按 user_id 过滤 / 向量距离过远查看检索日志和 filter增加元数据过滤调整 top_k回答把无关记忆混进来检索召回噪音检查相似度阈值加相似度下限做重排多用户记忆互相污染scope 没隔离查看记录结构强制 user_id task_id 隔离任务中断后状态丢失工作记忆只存在内存检查进程重启策略把状态写入 Redis 或状态表上下文超长报错短期记忆无限增长查看实际 token 数限制轮数、启用压缩长期记忆写入后搜不到向量化模型不一致对比写入与检索的向量来源统一 embedding 模型记忆删除了还能读出来删除只删了一个存储副本检查双写逻辑统一删除向量库和数据库排查顺序要固定先看日志有没有报错再看请求体里是否包含预期记忆最后看检索条件是否正确。不要一上来就换模型、换向量库。多数“记忆没生效”的问题最后都出在数据没写入、写入主键不一致、或者检索条件带了错误过滤这三个点上。10. 最佳实践与使用建议第一从两层起步。短期记忆必备长期记忆按需引入工作记忆只在你真正需要跑多步任务时再加。不要在第一天就把三层全部搭起来那样排查成本会很高。第二给记忆定义完整生命周期。写入、读取、更新、清理都要有规则。短期记忆跟随会话过期长期记忆要做去重、过期、重要度评分工作记忆在任务结束后按结果决定沉淀还是清理。没有生命周期的记忆库三个月后就是负担。第三记忆必须和授权、隐私一起设计。用户数据写入长期记忆之前需要明确授权边界敏感信息要脱敏系统要提供“查看我记住了什么、删除我的记忆”的入口。如果涉及人脸、声音、肖像等素材的记忆更要以合法授权为前提不能默认采集。第四人工知识沉淀与 Agent 检索结合。用 Obsidian 这类工具维护一份人工审核过的知识库再通过检索接口接入 Agent能显著提高回答的可信度而不是只依赖模型自述。这类场景对“记忆引擎”的要求其实不高关键是知识源质量和检索策略。第五面试角度提前准备。Agent 记忆是高频考察点短期记忆、长期记忆、工作记忆如何区分为什么需要工作记忆如何处理上下文超长如何防止记忆污染批量任务怎么隔离这些都可以从本文找到答案。建议准备时尝试自己写一个最简 MemoryManager面试时直接讲实现取舍比背概念更有说服力。11. 总结与下一步三层记忆架构最值得先做的是最小两层系统短期记忆加长期记忆。先跑通“写入 - 检索 - 作为上下文参与生成”这个闭环再考虑工作记忆和复杂任务编排。最容易踩的坑是记忆污染。多用户之间没做 scope 隔离、长期记忆被写入大量噪音这两个问题在最开始就要防住。建议把user_id过滤和写入前判断作为默认规则而不是事后再补。后续可以扩展的方向包括长期记忆的时间衰减机制、记忆之间的冲突消解、记忆自动整理与摘要、以及用记忆评价指标量化 Agent 效果提升。从趋势看Agent 的发展会越来越依赖记忆能力但底层架构思路不会脱离三层框架。先把基础层做扎实后面的扩展才会顺。