这几年做大模型应用我踩得最多的坑不是模型能力不够而是 Agent 老把用户说过的话忘得精光。上个月用户还在抱怨“我血糖偏高推荐菜谱要少糖”这个月再问膳食建议它一脸无辜地给你推荐红烧肉。问题就出在大多数 agent 只有上下文窗口里的短期记忆关上对话就归零。所以我拿到agent-memory这个开源项目时第一反应是“这玩意终于有人认真做了”。它不是简单把聊天记录塞进向量库而是一个完整的记忆分层、写入、遗忘与检索框架让 AI 像人一样分得清哪些话是随口一说、哪些偏好要记一辈子。这篇文章我就把源码设计、接入步骤和我在生产环境里踩过的坑一次性讲透适合正在做 AI Agent、AI 助手或任何需要跨会话记忆的开发者。1. 为什么 AI 需要“不会忘”的长期记忆——问题拆解与核心价值1.1 对话 AI 的“金鱼记忆”困境现在的 LLM 本质上是一个“无状态函数”你给它一段提示词它吐回一段文字处理完就什么都不剩。所谓上下文窗口哪怕做到 200K token也只是这段对话内的临时纸片。一旦会话结束、服务重启、或者你换了新 session模型对你这个人一无所知。我在实际项目里吃过一次大亏做一个企业内部的文档助手用户每天在里面问各种制度问题头一天还教它“我是行政部的常用报销流程模板”第二天再问“我们部门的报销入口在哪”它回答成通用的财务入口结果被领导点名批评。问题不在模型而在设计——你没有一个“跨会话持久层”去承接这些用户画像和个性化信息。agent-memory解决的核心就是这个它把 agent 从“金鱼”变成“有大象记忆的助理”。它不只是记录还会判断哪些信息值得长期留下、哪些只是临时协同、哪些过了时效要清理然后通过检索引擎在最合适的时机把记忆“喂回”给 LLM。1.2 长期记忆到底解决什么场景问题需要长期记忆的场景远比你想象的多除了对话机器人还有:个性化推荐助手记住用户口味、过敏源、家庭成员、常去的地点推荐才有意义。AI 编程助手记住项目的技术栈偏好、代码规范、历史踩坑记录生成的代码才不会一遍遍重复同样的问题。智能客服工单用户历史报修记录、设备型号、之前的处理方案必须跨会话带上来。个人知识库助理用户收藏的文章、笔记、标签需要长期积累才能生成有价值的总结。这些场景共通的痛点是模型本身不记人你需要自己设计一套“记忆系统”而这恰恰是大部分团队最头疼的部分。你完全可以用 Redis 存 JSON但一旦数据量大了、查询复杂了、需要语义检索了你就得自己处理向量化、索引更新、过期策略、去重合并一大堆问题。1.3 agent-memory 在这个生态里的定位现在 AI 开源社区里关于记忆的碎片轮子很多有直接存 Redis 的有套 LangChain 的 VectorStore 跑语义检索的也有用 SQLite 简单落盘的。但它们大多只解决“存储”这一环从来不解决“什么时候该记”“怎么合并同类记忆”“怎么防止记忆互相矛盾”这些更关键的问题。agent-memory的定位是一个记忆编排层Memory Orchestration Layer。它介于 LLM 与底层存储之间帮你完成:决定一条信息要不要写进长期记忆判断新信息与旧记忆是冲突还是补充在需要时从海量记忆中精准捞出相关片段注入 prompt定期合并、压缩、遗忘保持记忆库干净。这个定位让它可以兼容任何 LLM无论是 OpenAI、Claude、本地部署的 Qwen还是国产开源模型只要你能往 prompt 里加文本就能接入。2. 记忆体系设计中短期、长期与永久记忆怎么分层2.1 三层记忆模型工作期、生命周期、永久agent-memory把所有记忆分成三层对应人类的记忆模式记忆层级生命周期典型内容底层存储短期记忆单次会话内当前任务上下文、临时状态、用户上一条指令内存 / 滑动窗口中期记忆数天到数周最近的偏好、进行中的项目背景、最近聊过的话题Redis / 关系库长期/永久记忆跨会话、永久用户身份信息、稳定偏好、知识积累、重大事实向量库 结构化存储为什么要分层因为成本和召回准确率差异太大。短期记忆必须快用 Redis 内存最好中期记忆需要能按时间衰减用带 TTL 的结构化数据长期记忆需要语义检索必须向量化。如果全塞向量库不仅查询慢而且向量检索对“精确事实”的记忆非常不友好——你问“用户叫什么名字”语义检索可能把“用户喜欢什么名字的品牌”也给召回来那就出大事了。2.2 记忆的写入、合并与遗忘策略记忆系统的核心难点不在存而在“记什么”和“忘什么”。agent-memory给了一条在我看来非常务实的策略重要性评分每条记忆候选都经过一个小模型的快速打分0-1低于阈值的直接不写。比如用户随口一句“今天天气还行”就不该占用永久记忆位置。冲突合并如果新信息与旧记忆指向同一个实体比如用户又强调了一遍“不吃辣”不是简单追加而是合并到同一条记忆记录里并且用新的事实覆盖旧的。时间衰减中期记忆每条带 last_access_time超过 N 天没有被命中就自动降级或删除。永久记忆不会自动删但允许用户或 Agent 显式遗忘。这个机制解决了我之前遇到的记忆污染问题早期做记忆功能时我简单粗暴地把所有聊天记录都存进向量库结果 Agent 被一堆闲聊噪音干扰反而把关键偏好淹没了。有了“重要性评分 冲突合并”记忆库才能长期保持干净。2.3 为什么用“向量结构化”而不是单纯 Redis只用一个 Redis 做长期记忆问题在于长尾查询。你问“有没有用户对 Linux 运维很熟悉的线索”Redis 只能做 key 匹配做不了语义召回。但只用一个向量库也不行因为精确记忆号码、名称、时间点用向量召回反而容易出错。agent-memory的实现是双路存储语义记忆描述性偏好、兴趣、经历→ 向量库嵌入用于模糊匹配事实记忆用户 ID、配置项、硬性限制→ 结构化存储Redis / SQLite用于精确查询。查询的时候两条腿走路向量检索召回 Top-K 个语义候选同时结构化查询精确命中若干事实片段再把两者合并去重后交给 LLM。这个设计我后来在自己的项目里也照搬了召回准确率比纯向量库高一大截。3. agent-memory 的核心实现逻辑一次“深读”源码的拆解3.1 整体架构概览agent-memory的代码结构非常工程化核心模块拆成四块Memory Manager对外统一接口负责调度写入、查询、遗忘Extractor从对话文本中抽取候选记忆用 LLM 输出 JSON 结构化字段实体、属性、价值、置信度Store存储抽象层适配 Redis / SQLite / Milvus / ChromaDB / QdrantRetriever检索编排包括向量召回、结构化精确查询、重排序。接入流程一句话总结对话流经过 Extract 抽取记忆候选 → 重要度评分 → 写入 Store → 下一次对话开始时调用 Retriever 拿相关记忆 → 注入 System Prompt。3.2 关键数据结构与存储方案选型看源码最惊喜的是它的记忆条目设计不是简单的“文本块 向量”而是一个包含多种字段的 MemoryUnitclass MemoryUnit(BaseModel): memory_id: str # 全局唯一 ID user_id: str # 归属用户隔离必需 session_id: str | None # 来源会话可为空 content: str # 记忆的文本描述 entities: list[str] # 实体列表如 [user, diet, sugar] memory_type: str # factual / preference / episodic / procedural importance: float # 0~1 的重要性评分 created_at: datetime updated_at: datetime last_access_at: datetime access_count: int # 被命中的次数用于热度感知这个结构的巧妙之处在于entities字段。向量检索解决“语义像不像”而entities解决“是否在说同一件事”。比如两条记忆都带[user, diet]哪怕文本完全不同也可以归并处理。我在用的时候发现这比纯文本 embedding 做聚类准确得多。存储方案选型上它默认提供了三种适配器适配器适用场景选型理由Redis中期记忆、高频读写快、支持 TTL、原生过期SQLite sqlite-vec单机、轻量部署零依赖、易落地ChromaDB / Milvus长期记忆、大容量语义检索向量召回性能好、可横向扩展我自己的建议是个人项目或小团队直接用 SQLite ChromaDB 组合生产环境用 Redis存事实 Milvus存向量后面会细说原因。3.3 记忆写入、更新与删除的核心流程写入流程是这种记忆系统最有技术含量的部分agent-memory的实现分三步走第一步候选提取。每轮对话结束后Extractor 把最近几条消息打包发给 LLM提示词大概是“从这段对话中提取值得长期记住的事实或偏好输出为 JSON 数组”。这一步非常关键它不是把所有消息都存下来而是只抽出用户显式表达的偏好、需求和身份事实。第二步重要性过滤。每个候选带上 LLM 给出的importance评分默认阈值 0.6 以下的直接丢弃。比如“今天下雨了”重要性只有 0.2“我喜欢用暗色主题的 IDE”重要性 0.9。第三步去重合并更新。对新记忆做 embedding与库中已有同类实体entities 相同的记忆做余弦相似度比对。如果相似度超过 0.85就认为是同一事实的重复表达不新增记录而是用新的内容更新旧记录并 refresh 时间戳如果相似度在 0.5~0.85 之间说明是同一主题的补充信息可以选择追加到同一条或不处理。只有完全新的事实才真正插入库中。这套流程我从源码里读完后最直接的感觉是“它把记忆写坏的可能性降到了最低”。实际跑起来库里的记忆密度很高几乎没有废话。3.4 检索与相关性排序记忆写入做得好检索才有意义。agent-memory的 Retriever 不是简单 top-k 返回它做了一件让我拍腿叫好的事动态注入 重排序。查询的时候用用户问题生成一个 query embedding从向量库召回 Top-50从结构化存储中查命中的实体事实如当前用户 ID、明确配置项用重排序模型Rerank对 50 条候选按“与当前问题的相关性”排序只取最相关的 5~10 条注入 prompt并附带时间戳。它还做了一个很实用的细节衰退加权。排序分 相关性分 × (1 access_count × 0.1) × 时间衰减因子。这意味着经常被用到的记忆会“越来越容易被想起”而很长时间没命中的记忆会自动沉底。这个设计非常适合知识库类的 Agent——热点内容自动浮上来冷门内容不占 prompt 空间。4. 实操从零接入 agent-memory三步让 Agent 记住用户偏好4.1 环境准备与最小可用配置我建议你在一个干净的 Python 3.11 环境里操作。先安装依赖pip install agent-memory chromadb sqlite-vec openai初始化一个最简记忆管理器from agent_memory import MemoryManager from agent_memory.stores.sqlite_store import SQLiteStore from agent_memory.embeddings.openai_embedding import OpenAIEmbedding store SQLiteStore(memory.db) embeddings OpenAIEmbedding() # 也可以用本地 embedding 模型 manager MemoryManager( storestore, embedderembeddings, importance_threshold0.6, default_top_k8, )这套配置不需要额外部署服务一条 SQLite 文件搞定所有记忆落盘嵌入用 OpenAI 的text-embedding-3-small十分钟就能跑通。如果你不想用外部 API可以换sentence-transformers本地嵌入效果略差但完全离线。4.2 核心操作让 Agent 记住“我不吃辣”假设你做一个 AI 饮食助手用户在第一轮对话里说“我最近在控糖晚饭别给我推荐高碳水的”。我们看这轮对话怎么被agent-memory捕获# 这是 agent 回答用户后需要调用的记忆写入钩子 messages [ {role: user, content: 我最近在控糖晚饭别给我推荐高碳水的荞麦面可以。}, {role: assistant, content: 好的那晚餐我会避开米饭、白面这类高碳水优先推荐荞麦面、藜麦这类选项。}, ] candidates manager.extract_candidates(messages) # 输出大概是: # [{content: 用户需要控糖晚餐避免高碳水荞麦面可以接受, # entities: [user, diet, sugar, dinner], # importance: 0.88, memory_type: preference}] manager.add_memory(candidates[0], user_iduser_123)下次用户再问“今天晚饭吃什么”检索时relevant_memories manager.search( query推荐今天的晚餐, user_iduser_123, top_k5, ) prompt build_prompt_with_memories(messages, relevant_memories) # prompt 里会带上类似: # [记忆] 用户需要控糖晚餐避免高碳水荞麦面可以接受时间昨天你不需要告诉 agent 怎么用这条记忆只要把它放进 system prompt模型自然会根据“控糖”约束去调整推荐结果。这才是记忆系统的正确姿势——记忆只做约束不参与生成逻辑。4.3 中期记忆的实战用 Redis 做会话滑窗长期记忆负责“记得住”中期记忆负责“接得上”。实际对话中用户上一句说“我刚说的那个文件你帮我看了没”模型需要记住上一轮的文件名。agent-memory把短期对话上下文放在 Memory Manager 的BufferedConversationMemory里conversation manager.get_conversation(user_iduser_123, session_idsess_99) conversation.add_user_message(我刚说的那个文件你帮我看了没) conversation.add_assistant_message(你是指《Q3 预算表》那份吗)底层是用 Redis 的 list 结构存滚动窗口默认保留最近 20 条消息。超过 20 条最老的消息会被移出滑动窗口但其中抽取出的长期记忆已经落库了所以不会丢重要信息。这就是“短期窗口 长期抽取”的双层配合。我把这个当成中间件的标准动作每次对话结束调用一次manager.process_session(user_id..., session_id...)它内部会自动完成滑窗消息入 Redis → 抽取候选记忆 → 重要性过滤 → 合并写入长期存储。接入方不用操心流程只管调用一个接口。4.4 永久记忆的场景化实践让 Agent 记住用户项目历史说一个我实际做过的场景给开源项目维护者做一个 AI 助手它需要永久记住每个 issue 会话里用户提过的环境信息。用户可能一个月前提过“我在 Windows 11 下跑 Python 3.10”这个信息如果不落到永久记忆下个月他再来报 bugAgent 又会问一遍“你用的什么系统”体验非常裂。用agent-memory实现这个场景额外一个小技巧在 extract 的提示词里强化提取规则让它更关注环境相关的实体。agent-memory支持自定义提取提示词模板manager MemoryManager( ..., extract_prompt从对话中提取关于用户环境、技术栈、项目偏好的事实。 只输出符合以下 JSON 格式的数组{{ entities: [...], content: ... }}, )我在生产环境用下来的体感是加入永久记忆后的 agent 有了“老友感”。用户一说“又出问题了”Agent 能主动回复“还是上次那台 CentOS 的机器吗”——这种体验不是任何 prompt 工程能堆出来的它就是记忆系统带来的质变。5. 常见问题与排查技巧实录5.1 记忆检索不到或召回率低这是最常遇到的现象记忆明明写进去了Agent 却想不起来。先别怪模型大概率是你查询方式的问题。我在项目里排查过三个原因第一embedding 模型不一致。如果你写入记忆时用的 OpenAI embedding查询时换成了本地模型向量空间完全不对齐召回率为零。agent-memory本身不会阻止这种误用所以要把 embedding 模型配置固定在同一个实例或配置文件里。第二top_k 太小。默认是 8 条但你的 Agent 一次决策可能需要更多背景线索。我在客服场景直接调到 20然后让 LLM 自己从候选中挑相关的继续追问效果比硬性缩小召回更稳。第三用户 ID 隔离不匹配。很多人测试时写入的 user_id 和查询时不一致导致一直查不到。检查一下你的 user_id 是否稳定生成不要每次会话都重新生成一个 ID。5.2 记忆污染与互斥冲突一个让我当时头大的问题是用户先说了“我爱喝美式”过了一周又说“最近戒咖啡了”。如果没有冲突处理机制Agent 会同时拿到两条矛盾记忆回复时精神分裂。agent-memory的思路是“新信息覆盖旧事实”但实际更新依赖相似度阈值跑得准不准。我建议你在写入接口里主动加一道校验如果新记忆与旧记忆entities重合度高且内容有点反义就直接把旧记录的content替换掉而不是追加。另外还要给用户一个显式的“忘记”入口比如对话里用户说“我以后不要这个偏好了”Agent 应该能调用manager.delete_memory(memory_id)。这个交互看起来简单但优雅程度远超自动覆盖。5.3 成本与性能控制长期记忆系统最容易被低估的是成本。每条对话都要调用 LLM 做候选抽取每轮查询都要做向量召回和重排序。如果做得太频繁光 embedding 费用就够你肉疼。在这一块我有三条实操建议一是批量抽取。不要每轮对话都提取记忆而是隔几轮或者隔一段时间批量处理一次。折算下来5~10 轮对话调用一次 extract效果没有明显差异成本却直接降到 1/5。二是降级重排序。小规模项目可以不接 Rerank 模型直接用向量余弦相似度排序也能凑合。我刚开始把 Rerank 当必选配置后来发现数据量在两三千条以内时差别不大于是只在数据量过万的项目里才开重排。三是设置查询缓存。同一用户短时间内反复问相似问题直接命中缓存省掉重复向量查询的钱。这个优化不大但胜在代码简单值得加。5.4 多用户隔离与权限设计做多租户应用时最忌讳记忆串号。agent-memory在接口层面把所有写入和查询都强制带user_id但只要上层调用不小心仍然可能把 A 用户的记忆注入 B 用户的 prompt。我的经验是三层防护第一层存储层按 user_id 分区第二层检索层强制过滤 user_id第三层prompt 组装层再检查一遍所有记忆块的user_id是否匹配当前请求。最后这层看着冗余但我正是在这个环节抓到过 Bug——某个共用 Redis 实例的测试环境因为user_id传成了空串导致所有记忆共享了。从那以后每层都查水表再没出过串号的故障。6. 长期记忆未来的演进方向读agent-memory的源码我最大的感受是它把“记忆”从一个伪需求变成了一个可落地的工程组件。但离真正的“类人记忆”还有几大步比如记忆的自动冲突检测目前还依赖 embedding 相似度对反义词、转折句的理解不够比如跨用户的社会性记忆“团队里谁是 DBA”还没有充分建模比如记忆的隐私清洗与脱敏也还是靠规则而非模型判断。我自己的路线图是接一层小型分类模型做记忆类型预判把episodic经历型、semantic事实型、procedural技能型分开管理再做一层知识图谱化把实体之间的关系比如“用户A 是项目B 的维护者”显式建边。这些能力agent-memory目前没有完全内置但它的插件式架构给二次开发留足了空间。如果你正被 agent 的“金鱼记忆”折磨我的建议是直接 clone 下来改代码量不大设计思路却非常完整。一个能记住用户的 Agent和你之前做的所有聊天机器人完全是两种产品形态。
