资讯动态

Agent Memory 架构设计与工程实践:双存储、MCP 协议与记忆分层

发布时间:2026/10/30 4:48:40 来源:尧图企业网站定制
1. 从零理解 Agent Memory 到底在解决什么问题1.1 为什么大模型 Agent 需要独立的记忆系统做过 Agent 项目的人都有一个共同体会模型本身很聪明但它的“记性”差得离谱。一次对话结束后上下文窗口一关之前聊过的用户偏好、任务进展、关键决策全部归零。下次用户再来Agent 表现得像个陌生人又得从头问一遍“你是谁”“你要干什么”。这种体验放在客服、编程助手、个人助理这类需要连续交互的场景里基本没法用。Agent Memory 要解决的核心矛盾就在这里模型的推理能力是瞬时的但真实任务往往需要跨会话、跨天甚至跨周的状态延续。你不能指望把几百轮对话全部塞进上下文窗口一来 token 成本扛不住二来超长上下文会导致注意力稀释模型反而抓不住重点。所以必须有一套外置的记忆架构把“什么该记、记多久、怎么取出来”这件事工程化。我自己的理解是Agent Memory 本质上是给 Agent 配了一套“人脑式”的记忆分层短期记忆负责当前对话的连贯性长期记忆负责跨会话的知识沉淀工作记忆负责当前任务的临时状态。这三者各司其职缺一不可。1.2 记忆系统的三个层次与对应需求把记忆拆开看其实对应三种完全不同的需求。短期记忆Short-term Memory解决的是“这一轮对话别断片”。用户说“把刚才那个函数改成异步的”Agent 得知道“刚才那个函数”指的是什么。这部分通常直接放在上下文窗口里或者用一个滑动窗口缓存最近 N 轮对话。它的特点是读写频繁、生命周期短、容量小。长期记忆Long-term Memory解决的是“记住用户是谁、偏好什么、做过什么”。比如用户三个月前说过“我不用 Java只用 Python”那这次生成代码时就应该默认 Python。这部分需要持久化存储通常用向量数据库做语义检索或者用结构化数据库存事实型信息。工作记忆Working Memory解决的是“当前这个任务进行到哪一步了”。比如一个多步骤的数据处理任务Agent 需要记住“已经清洗完数据、正在做特征工程、下一步要训练模型”。这部分生命周期介于前两者之间任务结束就可以清理。提示很多团队一开始只做短期记忆觉得上下文窗口够用。但一旦用户量上来、会话变长就会发现没有长期记忆的 Agent 根本留不住用户因为每次都要重新建立信任。1.3 双存储架构的由来与核心思路为什么热词里反复出现“双存储”因为单一存储方案根本覆盖不了上面三种记忆的差异。向量数据库擅长语义相似度检索但你让它存“用户 ID 对应的偏好列表”就很别扭关系型数据库擅长结构化查询但你让它做“找语义最相近的历史对话”又力不从心。双存储的核心思路就是分工一个存储负责“精确查找”一个存储负责“模糊召回”。常见的组合是关系型数据库如 PostgreSQL、MySQL 向量数据库如 Milvus、Qdrant、pgvector。前者存用户画像、任务状态、事实型记忆后者存对话片段、文档摘要、语义向量。这个架构的好处是当 Agent 需要回忆“用户上次提到的那个项目叫什么”时可以先在关系库里按用户 ID 和时间范围缩小候选集再在向量库里做语义匹配两级过滤既快又准。如果只用向量库全量检索的延迟和噪声都很难控制。2. 架构设计的核心决策与选型逻辑2.1 记忆写入策略什么时候该记什么时候不该记这是整个系统里最容易被低估的环节。很多团队的做法是“每轮对话都写一条记忆”结果向量库里堆了几百万条垃圾检索时全是噪声。我的经验是写入必须做过滤和压缩。过滤的第一层是重要性判断。不是每句话都值得记。用户说“你好”“谢谢”这种寒暄记了也没用。真正值得记的是用户的明确偏好“我喜欢简洁的回答”、关键事实“我的项目用的是 Vue3”、任务决策“我们决定用方案 B”。这部分可以用一个轻量的分类模型或者规则引擎来做成本很低但效果显著。第二层是压缩。一轮对话可能有十几条消息但真正有价值的信息可能就一两句。与其把整轮对话存进去不如让模型先做一次摘要把“用户询问了 XAgent 回答了 Y最终结论是 Z”压缩成一条结构化记忆。这样既省存储又提高检索精度。第三层是去重。用户可能反复提到同一个偏好如果每次都写一条检索时会返回一堆重复结果。可以在写入前做一次相似度检查超过阈值就更新而不是新增。2.2 记忆检索策略如何做到又快又准检索的核心挑战是在正确的时间把正确的记忆喂给模型。喂少了Agent 表现迟钝喂多了上下文被占满模型反而抓不住重点。我的做法是多路召回 重排序。第一路是向量检索拿当前 query 的 embedding 去向量库找 top-K 相似记忆。第二路是结构化检索按用户 ID、任务 ID、时间范围拉取相关记录。第三路是最近记忆直接把最近 N 条短期记忆带上。三路结果合并后用一个重排序模型或者简单的加权打分挑出最相关的几条。这里有个关键参数最终注入上下文的记忆条数。我实测下来5 到 8 条是比较舒服的区间。太少覆盖不全太多会挤占对话空间。如果记忆本身比较长还要再做一次摘要压缩。注意检索时一定要带上时间衰减因子。三个月前的记忆和昨天的记忆权重不应该一样。可以在打分公式里加一个exp(-λ * Δt)的衰减项λ 根据业务场景调一般 0.01 到 0.05 之间。2.3 双存储的具体分工与数据同步双存储不是简单地把数据存两份而是各存各的、按需关联。我的设计是这样的存储类型存什么查询方式典型延迟关系型数据库用户画像、任务状态、事实型记忆、记忆元数据SQL 精确查询毫秒级向量数据库对话摘要、文档片段、语义向量相似度检索十毫秒级关系库里存一条记忆的“骨架”记忆 ID、用户 ID、类型、创建时间、重要性分数、向量库中的对应 ID。向量库里存“血肉”具体的文本内容和 embedding。检索时先从关系库按条件筛出候选 ID 列表再去向量库做语义匹配最后合并打分。数据同步用异步写入。对话结束后先写关系库保证不丢然后发一条消息到队列由后台 worker 做摘要、生成 embedding、写向量库。这样主流程不会被 embedding 计算的延迟拖慢。2.4 MCP 协议在记忆系统里的角色MCPModel Context Protocol这两年被讨论得很多它的核心价值是把记忆系统标准化成一个可插拔的服务。以前每个 Agent 框架都要自己实现一套记忆读写逻辑换框架就得重写。有了 MCP记忆系统可以作为一个独立的 server 暴露工具接口任何支持 MCP 的客户端都能调用。具体到 Agent Memory可以暴露这几个工具memory_write写入记忆、memory_search检索记忆、memory_update更新记忆、memory_forget删除记忆。Agent 在需要的时候主动调用这些工具而不是把记忆逻辑硬编码在 prompt 里。这样做的好处是解耦。记忆系统的实现可以独立迭代换向量库、换摘要模型都不影响 Agent 本身。而且多个 Agent 可以共享同一套记忆服务用户在一个 Agent 里建立的偏好另一个 Agent 也能用上。3. 核心模块的实操实现细节3.1 记忆数据结构设计先看关系库的表结构。我一般会建三张表memory_meta、memory_content、user_profile。memory_meta存元数据CREATE TABLE memory_meta ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id VARCHAR(64) NOT NULL, session_id VARCHAR(64), memory_type TINYINT NOT NULL, -- 1短期 2长期 3工作 importance FLOAT DEFAULT 0.5, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, vector_id VARCHAR(64), is_deleted TINYINT DEFAULT 0, INDEX idx_user_type (user_id, memory_type), INDEX idx_created (created_at) );memory_content存具体内容和 meta 一对多CREATE TABLE memory_content ( id BIGINT PRIMARY KEY AUTO_INCREMENT, memory_id BIGINT NOT NULL, content TEXT NOT NULL, content_type VARCHAR(32), -- text / json / summary INDEX idx_memory (memory_id) );user_profile存结构化的用户画像方便快速读取CREATE TABLE user_profile ( user_id VARCHAR(64) PRIMARY KEY, preferences JSON, facts JSON, last_active TIMESTAMP, total_sessions INT DEFAULT 0 );向量库那边每条记忆存一个向量payload 里带上memory_id、user_id、memory_type、created_at方便做过滤。3.2 记忆写入的完整流程写入流程分同步和异步两段。同步段在对话结束时触发拿到本轮对话的完整消息列表。调用一个轻量模型做重要性打分0 到 1。如果分数低于阈值我一般设 0.3直接丢弃。如果通过做一次摘要压缩生成结构化记忆文本。写memory_meta和memory_content拿到memory_id。发消息到队列带上memory_id和文本内容。异步段由 worker 处理从队列取消息。调用 embedding 模型生成向量。写入向量库拿到vector_id。回写memory_meta.vector_id。这里有个细节embedding 模型的选择。如果追求效果用大模型如 text-embedding-3-large如果追求成本和速度用小的如 bge-small。我一般用 bge-base 这个量级效果和成本比较平衡。维度选 768 或 1024 都行看向量库的支持情况。实操心得写入队列一定要做幂等。网络抖动导致重复消费很常见如果不去重向量库里会出现重复记忆。可以在memory_meta上加一个content_hash字段写入前先查一下。3.3 记忆检索的完整流程检索发生在每轮对话开始前。输入是当前用户消息和用户 ID输出是一组记忆文本。第一步query 改写。用户的消息往往很短直接拿去做向量检索效果不好。我会先用一个小模型把 query 扩展成几个相关的检索词或者生成一个假设性回答HyDE 思路用这个回答的向量去检索召回率会高不少。第二步多路召回向量路用改写后的 query 向量去向量库检索 top-20带上user_id过滤。结构化路从user_profile直接读偏好和事实从memory_meta按user_id和memory_type3读工作记忆。最近路从短期记忆缓存里取最近 5 轮对话。第三步重排序。把三路结果合并用一个 cross-encoder 或者简单的加权公式打分。我的加权公式大概是score 0.5 * vector_similarity 0.2 * importance 0.2 * recency_decay 0.1 * type_weight其中recency_decay exp(-0.02 * days_since_created)type_weight里工作记忆给 1.0长期记忆给 0.8短期给 0.6。第四步截断与注入。取 top-6 条如果总 token 超过预算比如 800 token就再做一次摘要。最后拼成一段“背景信息”注入 system prompt。3.4 工作记忆的状态管理工作记忆是最容易被忽略但实际很关键的部分。它记录的是“当前任务进行到哪了”。我的做法是给每个任务建一个状态机状态存在关系库里结构大概是这样{ task_id: xxx, user_id: yyy, task_type: data_pipeline, current_step: feature_engineering, completed_steps: [data_cleaning], pending_steps: [model_training, evaluation], context: { dataset_path: /data/xxx.csv, target_column: label }, updated_at: 2025-01-15T10:30:00Z }每次 Agent 执行完一步就更新这个状态。下一轮对话开始时把当前任务状态注入上下文Agent 就知道自己该干什么了。任务完成后把关键结论沉淀成长期记忆然后清理工作记忆。注意工作记忆一定要设过期时间。用户可能中途放弃任务如果不清理状态会一直挂着下次对话时 Agent 会莫名其妙地继续一个已经废弃的任务。我一般设 24 小时过期。4. 常见问题与排查技巧实录4.1 记忆检索不准的排查思路检索不准是最常见的问题表现是“Agent 答非所问”或者“明明记过却想不起来”。排查按这个顺序来先看召回。把 query 和向量库里 top-20 的结果打出来人工看有没有相关记忆。如果 top-20 里都没有说明是召回问题可能是 embedding 模型不适合你的领域或者 query 改写没做好。可以试试换模型或者加关键词检索做补充。再看排序。如果 top-20 里有相关记忆但没进最终的 top-6说明是排序问题。检查打分公式的权重是否合理特别是时间衰减因子是不是太激进把有用的老记忆压下去了。最后看注入。如果检索结果没问题但 Agent 还是没用上那可能是 prompt 拼接的问题。检查记忆是不是被放在了 system prompt 的末尾或者被其他指令覆盖了。我一般把记忆放在 system prompt 靠前的位置并明确标注“以下是相关背景信息”。4.2 记忆膨胀与性能下降的应对系统跑几个月后向量库可能涨到几百万条检索延迟从几十毫秒涨到几百毫秒。这时候要做记忆归档。我的策略是分层最近 30 天的记忆留在热存储30 天到 1 年的放温存储可以用更便宜的向量库或者降低检索频率1 年以上的做冷归档只保留摘要原始向量删掉。检索时优先查热存储如果结果不够再查温存储。另外定期做记忆合并。同一个用户的相似记忆可以合并成一条更概括的记忆。比如用户多次提到“喜欢 Python”可以合并成一条“用户偏好 Python多次强调”。这样既减少数量又提高单条记忆的信息密度。4.3 多用户记忆隔离的坑多用户场景下记忆隔离是必须做好的。我踩过的坑是向量检索时忘了加user_id过滤结果 A 用户的记忆被检索给了 B 用户。这种 bug 很隐蔽因为大部分时候检索结果看起来是合理的只有偶尔出现“Agent 怎么知道我没说过的事”才会暴露。解决办法是在向量库的 payload 里强制带user_id并且检索时必须加过滤条件。可以在封装层做一个校验如果检索请求没带user_id直接抛异常。另外测试时要专门造一批跨用户的数据验证隔离是否生效。4.4 常见问题速查表问题现象可能原因排查方法解决方案Agent 记不住刚说的话短期记忆窗口太小检查上下文缓存配置扩大窗口或加摘要检索结果全是无关内容embedding 模型不匹配人工看 top-20 召回换模型或加关键词检索老记忆压过新记忆时间衰减太弱检查打分公式加大衰减系数向量库写入重复队列重复消费查 content_hash加幂等校验跨用户记忆串了检索没加 user_id 过滤造跨用户测试数据强制过滤 校验工作记忆不清理没设过期时间查任务状态表加 TTL 定时清理检索延迟越来越高向量库膨胀看库大小和延迟曲线分层归档 记忆合并4.5 几个我踩过的坑第一个坑是摘要模型和主模型不一致。我用小模型做摘要结果它把一些关键细节丢了导致记忆检索时召回不准。后来改成用同一个模型做摘要虽然成本高一点但质量稳定很多。第二个坑是embedding 维度不统一。中途换了一次 embedding 模型维度从 768 变成 1024结果新旧向量混在一起检索直接崩了。教训是换模型必须做全量重刷或者建新 collection 做双写迁移。第三个坑是记忆注入位置不对。一开始我把记忆放在 user message 里结果模型把它当成用户说的话产生了混淆。后来改成放在 system prompt 里并用明确的分隔符标出来问题就解决了。5. 记忆系统的扩展与演进方向5.1 从被动检索到主动记忆管理现在的记忆系统大多是被动检索对话来了才去查。更高级的做法是主动管理。Agent 在空闲时可以定期回顾自己的记忆做整理、合并、遗忘。比如每天晚上跑一个任务把当天的短期记忆摘要成长期记忆把过期的任务状态清理掉。这需要一个“记忆管理器”角色可以是一个独立的 Agent专门负责记忆的增删改查。它有自己的 prompt 和工具能判断哪些记忆该保留、哪些该合并、哪些该删除。这个思路在复杂场景下很有价值因为记忆质量会随时间自然衰减需要主动维护。5.2 记忆与 RAG 的边界很多人把 Agent Memory 和 RAG 混为一谈其实两者有明确边界。RAG 检索的是外部知识比如文档、手册、知识库这些内容不随对话变化。Agent Memory 检索的是交互历史是用户和 Agent 之间产生的私有数据。两者可以共用一套向量库但要用不同的 collection 或 namespace 隔离。检索时也要分开处理RAG 结果作为“参考资料”注入Memory 结果作为“背景信息”注入prompt 里的标注要区分开否则模型会混淆。5.3 记忆的可解释性与用户控制企业级场景下用户往往想知道“Agent 到底记住了我什么”。所以记忆系统需要提供可解释性能列出某个用户的所有记忆能追溯每条记忆的来源对话能手动删除或修改。我的做法是提供一个管理接口用户可以看到自己的记忆列表按类型和时间筛选支持单条删除和批量清理。这个功能看起来简单但在合规场景下是刚需。另外记忆的写入最好也做一次用户确认特别是涉及敏感信息时。5.4 性能优化的几个方向如果记忆量很大性能优化可以从这几个方向入手。索引优化向量库建 HNSW 索引关系库给常用查询字段建复合索引。缓存用户画像和最近记忆放 Redis减少数据库查询。批处理embedding 生成和向量写入做批量减少网络往返。异步化所有非关键路径的操作都丢到队列主流程只做必要的同步写入。我实测下来一个中等规模百万级记忆的系统检索 P99 延迟可以控制在 100 毫秒以内写入延迟异步在秒级。这个性能对大多数对话场景是够用的。5.5 我个人的一些体会做 Agent Memory 这两年最大的感受是记忆系统的难点不在技术而在产品判断。什么该记、什么该忘、什么时候该主动提醒、什么时候该装傻这些决策没有标准答案得根据具体场景反复调。我见过一些团队一上来就追求“记住一切”结果 Agent 变得啰嗦又迟钝用户反而更烦。也见过一些团队过于保守Agent 像个失忆症患者每次都要重新自我介绍。平衡点在哪里只能靠实际数据和用户反馈来找。另一个体会是记忆系统要可观测。上线后一定要有监控看写入量、检索命中率、记忆使用率这些指标。如果发现某类记忆从来没被检索过那可能就是白存了该考虑清理。如果发现检索命中率很低那可能是写入质量有问题得回头调摘要和过滤逻辑。最后分享一个小技巧给记忆加一个“置信度”字段。有些记忆是用户明确说的置信度高有些是模型推断的置信度低。检索时优先用高置信度的低置信度的作为补充。这样能避免模型把自己的推断当成事实减少幻觉。这个系统后续还可以往多模态方向扩展比如记住用户发过的图片、听过的音频。也可以往多 Agent 协作方向走让多个 Agent 共享一套记忆互相补充。路还很长但核心思路是不变的分层、过滤、检索、维护把这四件事做好记忆系统就成功了一大半。

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

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

免费获取报价 →
↑