
搞 Agent 开发的朋友应该都被同一个问题折磨过Agent 比谁都聪明就是没有记性。上一轮说好的事情换个 session 就忘得干干净净多个 Agent 协作时更是灾难A 调研到的信息B 完全不知道。今天想聊的 ai-memory 就是奔着这个问题来的——一个 7.9K stars 的跨 Agent 记忆层能把散落在各个会话里的记忆统一管起来让 Agent 真正拥有“长期记忆”。这篇文章会从项目定位、核心架构、实操接入、调参避坑几个角度把这个记忆层拆开来看。1. 项目概述Agent 的“外接大脑”到底解决什么问题1.1 从上下文窗口到记忆层中间差了什么先说个基本事实LLM 的上下文窗口是有上限的即便到了 128k、200k也不能无限塞历史。而且每一轮对话都要重新传一遍历史时间和钱都受不了。Agent 应用最常用的做法是维护一个 session 级别的临时缓冲把当前会话里的对话记录拼到 prompt 里。问题是 session 一关这个缓冲就没了Agent 对用户的了解也归零。这就是“上下文窗口”和“记忆”的差别窗口是临时工作台记忆是能跨会话取用的资料库。ai-memory 要解决的不是“多塞几轮对话”而是把对话和工具调用中产生的关键信息抽出来、存下去、能检索在需要时重新放回上下文窗口。为什么这件事不能指望 LLM 自己记住因为模型本身没有持久化存储。你可以在 System Prompt 里反复强调“记住用户偏好”但落地时记忆还是得放在模型外部。这也是记忆层这类中间件存在的原因。1.2 跨 Agent 记忆层的适用场景你可能想问每个 Agent 自己管理历史记录不就行了为什么非要跨 Agent因为信息孤岛的代价太大了。举个最常见的客服场景用户上午在 App 反馈过产品问题下午从网页端进来接的是另一个客服 Agent。如果两个 Agent 没有共享记忆用户得把问题重新描述一遍。有了 ai-memory 这类组件客服实例在同一个命名空间下读写直接调出上午的记录体验完全不一样。类似的场景还有团队内部知识库助手多个 Agent 共用同一个项目上下文避免重复调研。多 Agent 编排系统规划 Agent 拆解出的中间结论执行 Agent 不需要重新推导。个人助理跨应用共享用户画像日历、邮件、待办里的信息沉淀到同一个记忆池。企业级业务系统不同部门的工作流 Agent 共享工单状态、审批进度、客户偏好。只要你的业务里存在“多个智能体共享背景信息”的需求ai-memory 这类记忆层就是刚需。它的价值不在于“存得下”而在于“能按需拿回”。1.3 为什么不直接裸写向量数据库很多团队第一反应是记忆嘛把文本 embedding 后丢进向量库不就完了确实不少项目一开始就是这么干的但自己从零做的代价往往比想象中大得多。你需要处理的事至少包括embedding 模型的选择、切换和降级向量索引的构建和重排metadata 过滤TTL 和遗忘策略不同 Agent 之间的命名空间隔离还要自己封装一套稳定、可观测的 API。这些工作单独看都不难合在一起就是一套系统需要持续维护。ai-memory 把这一层抽象成标准产品能力底层可以接 Redis、Chroma、Qdrant、pgvector上层统一暴露 SDK。你可以把它理解为“记忆版 Redis”——不用关心底层怎么分片只关心写入什么、能查回来什么。2. 核心架构拆解记忆条目、接口与存储选型2.1 记忆不是缓存数据模型决定了它的定位记忆和缓存最大的区别是缓存可丢记忆需要可靠持久化并且带索引和检索能力。ai-memory 的数据模型也不是一个简单的 key-value。它至少需要覆盖三类记忆长期语义记忆用户画像、领域知识、情景记忆某次具体发生了什么、以及一部分状态记忆异步任务进行到哪一步。因此它保存的每个条目往往是一段带有结构化元数据的文本而不是一个冷冰冰的字符串。落到实际设计上你会看到记忆条目通常包含namespace、text、metadata、embedding 向量、创建时间、最近访问时间、访问次数、过期时间。这些字段合在一起决定了这个记忆系统能支持的玩法。2.2 一个记忆条目长什么样我习惯用 JSON 来理解这类项目的数据结构。一个典型的记忆条目大概是这样的{ id: mem_01J2AB..., namespace: tenant1:customer_service, text: 用户张先生希望每周五晚上收到产品周报, metadata: { user_id: zhang, channel: app, agent: crm-bot, priority: 1 }, created_at: 2025-01-10T09:30:00Z, last_access_at: 2025-01-11T10:00:00Z, access_count: 5, ttl: null }namespace 是隔离和共享的边界。一个租户、一个团队、一个 Agent 都可以作为 namespace。metadata 是结构化过滤条件后续检索时可以按用户、渠道、业务线精确过滤。access_count 和 last_access_at 用于热度衰减避免记忆池无限膨胀后检索质量下降。text 除了原文之外通常还会被 embedding 模型转成向量放在独立的向量索引里。向量负责语义相似度metadata 负责精确匹配两者结合才是完整的记忆查询。2.3 写入、检索、遗忘三类核心接口记忆层的核心操作其实就三件事写进去、查出来、忘掉不重要的。写入接口一般叫 remember 或 add。它接收文本、metadata 和 namespace异步生成 embedding把原始字段存在 KV 或关系库里向量存在向量索引里。有些实现还支持“按业务键幂等合并”比如同一 user_id 同一实体写入时自动覆盖旧条目避免记忆库膨胀。检索接口一般叫 search 或 recall。它接收 query、过滤条件、top_k。query 先转 embedding然后去向量库做相似度检索。这里的关键参数有 top_k、score_threshold、filter。很多人刚开始调不明白后面我会专门讲。遗忘接口是记忆层和普通缓存最不一样的地方。它支持按 id 删除、按 TTL 自动过期也支持根据访问频率把长期不用的记忆降权让相似度检索不再命中它们。对隐私合规模块来说这个接口是必需的。2.4 存储后端选型与权衡ai-memory 不会绑定一个存储不同场景有不同选择。我整理了一张对比表后端适合场景优点缺点内存模式本地测试、Demo零依赖启动快重启即丢不能跨进程Redis元数据、热数据缓存延迟低支持 TTL纯 KV语义检索弱Chroma / Qdrant / pgvector语义检索支持向量索引和过滤要额外维护一个服务MySQL / PostgreSQL结构化记忆、审计强一致好查询向量索引依赖扩展如 pgvector生产环境我建议组合方案Redis 存元数据和热缓存向量数据库做语义索引底层再落一份可靠持久化存储。先在内存模式跑通逻辑等数据量上来再迁移也不算晚。3. 五分钟接入安装、单 Agent 与多 Agent 配置3.1 安装与初始化我以 Python 生态为例因为 Agent 开发最常见的就是 Python。先装 SDKpip install ai-memory服务端通常可以用 Docker 直接拉起来。我最近一次试的配置是docker run -d --name ai-memory \ -p 8080:8080 \ -e MEMORY_REDIS_URLredis://redis-host:6379 \ -e MEMORY_VECTOR_BACKENDchroma \ -e MEMORY_EMBEDDING_MODELBAAI/bge-m3 \ ai-memory:latest客户端初始化from ai_memory import MemoryClient client MemoryClient( endpointhttp://localhost:8080, namespacemy-app )如果你只是本地联调甚至可以不指定 Redis 和向量库用内置的内存模式。不过一旦需要重启不丢数据就得及时切到持久化后端。3.2 在单个 Agent 中接入记忆层接入流程不复杂核心是在 Agent 主循环里增加三个动作生成回复前检索历史记忆拼进 prompt生成回复后把值得沉淀的信息写入记忆遇到长流程任务时更新任务状态。代码示例from ai_memory import MemoryClient memory MemoryClient(namespacecrm-assistant) def handle_message(user_id, user_text): # 1. 检索历史记忆 hits memory.search( queryuser_text, filter{user_id: user_id}, top_k5, score_threshold0.6 ) past \n.join(hit.text for hit in hits) # 2. 拼入提示词 prompt f用户历史情况\n{past}\n\n当前用户说{user_text} # 3. 调用 LLM 生成回复这里省略实际推理代码 reply llm_reply(prompt) # 4. 如果用户说了新的事实落一条记忆 if 偏好 in user_text or 希望 in user_text: memory.remember( textuser_text, metadata{user_id: user_id} ) return reply这里有个容易忽略的点记忆检索不是把用户历史全部塞给模型而是按语义相似度召回。用户说“之前说的报告还发吗”query 是这句话向量库里和它语义最接近的记忆会被捞出来。这比傻乎乎把最近 20 轮对话全部拼进 prompt 要省 token也更容易命中关键信息。3.3 让多个 Agent 共享一套记忆跨 Agent 共享的核心是 namespace 策略。最简单的模型一个私有 namespace 对应一个 Agent一个共享 namespace 对应一个团队或业务线。# Agent A 写入共享空间 a.remember( text项目 road map 已调整v2.0 优先做移动端, namespaceproject:nebula, metadata{owner: agent-A, topic: roadmap} ) # Agent B 从共享空间读取 hits b.search( query当前版本的优先事项是什么, namespaceproject:nebula )需要提醒的是共享不代表混乱。如果多 Agent 往同一个 namespace 写入metadata 里最好带上写入者身份和主题领域否则 A 的临时结论很容易污染 B 的检索结果。权限要求高的场景服务端还要做 namespace 级别的访问控制。3.4 关键参数与调优建议top_k 不是越大越好因为 Agent 的 prompt 有 token 预算。可以按这个思路估算假设 prompt 总预算 8000 token我最多分 2500 token 给记忆单条记忆平均 200 token那 top_k 最好不要超过 12稳妥起见取 5。score_threshold 和 embedding 模型强相关。不同的模型分数的语义分界线差别很大有的模型 0.7 已经很相关有的模型 0.7 还在十万八千里。建议先不做截断跑一批真实 query 看分数分布再找“不相关”和“勉强相关”之间的分界线。中文场景我用 BAAI/bge-m3 的经验是 0.6 起步英文用 OpenAI embedding 可以到 0.7但这不是定论。embedding 模型选择也要注意。本地化需求优先考虑 bge 系列、text2vec效果优先可以考虑 OpenAI 或 Cohere 的 embedding API。原则是写入和检索必须用同一个模型否则历史向量的语义空间不一致分数会失真。4. 常见问题与排查技巧实录4.1 记忆串台不同 Agent 读到了彼此的记忆这个问题的出现频率最高。症状很直接用户 A 的信息出现在用户 B 的上下文里或者客服 Agent 读到了营销 Agent 的笔记。排查方向先看 namespace。如果所有 Agent 都用默认 namespace记忆池就是一个大锅。解决办法是层级隔离租户、业务线、Agent、用户逐级收敛。在 SDK 层面我习惯统一封装def recall(user_id, query): return client.search( queryquery, namespaceftenant:default:agent:{agent_id}, filter{user_id: user_id} )这样即使同一个 Agent 面对不同用户也不会串数据。关键是 filter 里的 user_id 每次都要强制带上不能依赖上层调用方自觉。4.2 检索结果不准相似度阈值与 top_k 如何配置如果你发现召回的记忆不着调第一反应不应该是不停压阈值而是先看 embedding 和日志。我的经验是query 太短的时候比如“用户叫什么”“订单号是多少”embedding 往往捕捉不到实体信息。这时候更可靠的是走 metadata 精确过滤用户 ID、订单号、日期范围都是结构化字段filter 一筛一个准向量相似度只负责“找语义相关的模糊内容”。比较推荐的做法是混合检索先用 filter 做硬筛选再用向量检索做软排序。top_k 初始值取 510然后根据线上命中率调整。4.3 记忆污染与过期数据处理另一个高频问题是“写入太随便”。Agent 什么都往里写写了几千条寒暄和无关信息后检索结果质量会明显下降。原因是很多聊天内容本身不是“值得长期记忆的事实”。比如“今天天气不错”这种话存下来只会成为噪声。我建议在写入前加一道“记忆提取器”。我试过一个简单的抽取提示词核心就一句话“只提取对后续任务有长期价值的承诺、偏好、约束和进度用一句话陈述忽略寒暄。”实际跑下来记忆库存量降了大概四成检索质量反而明显变好。配合 TTL 一起用短期任务状态记忆设 24 小时过期用户画像类不设 TTL但按访问频率降权。长期没被命中的记忆隔一段时间清理一次避免记忆池变成垃圾场。4.4 性能瓶颈与高并发问题如果你在 Agent 主线程里同步调用记忆层写入并且等待 embedding 返回延迟会非常明显。embedding 模型一次请求几十毫秒到几百毫秒不等高频对话场景下会把整个 Agent 拖慢。我的建议是写入走异步。把写入请求丢进 Redis Stream 或消息队列由独立 worker 批量消费。检索可以走缓存同一个 namespace 和 query 的短时间查询直接返回缓存结果。向量库也要确认索引建好否则数据量上去后检索直接扫全表延迟会很夸张。4.5 排查速查表给一张现场排查用的速查表后面自己和团队排障时直接对着看症状可能原因解决方案Agent 重启后记忆全丢使用了内存模式存储切换到 Redis / 向量库持久化后端多 Agent 之间串记忆namespace 未隔离按租户/业务线/Agent 划分空间检索强制 filter召回内容不相关阈值太低 / embedding 模型不匹配查看分数分布换与写入一致的模型增加 filter记忆库无限增长没有 TTL / 没有清理策略配置过期时间清理低频条目写入太慢、阻塞主线程同步 embedding异步写入独立 worker 消费检索返回空阈值太高 / 向量索引不一致先降到 0.40.5 排查检查模型是否一致5. 进阶玩法多 Agent 协作黑板的实际经验5.1 把记忆层当成“共享黑板”多 Agent 系统里最怕的是每个 Agent 都在自己的上下文里闭门造车。我习惯把 ai-memory 当作团队的共享黑板规划 Agent 把任务拆解、当前进度和关键假设写入一个 shared namespace执行 Agent 开工时拉取相关信息完成后把结果更新回去。举个例子三个 Agent 协作处理一条复杂工单调度 Agent 写入用户的核心诉求质检 Agent 写入历史规则客服 Agent 最后生成回复。每一步都有留痕整个团队在一份共享记忆上工作。这种模式最大的好处是每个 Agent 的 prompt 不用塞下全局状态只需要检索和自己当前任务相关的碎片token 压力小协作效率高。5.2 记忆质量评估与持续改进记忆层不是接上就能一劳永逸它需要度量。我自己的做法是在 Agent 回复的 debug 日志里把本次检索命中的记忆 id 和文本打出来每周人工抽看统计“被回答实际采用的记忆”比例。比例低说明检索策略或记忆库质量有问题。还有一种可行的做法是定期导出记忆库按 tag 或 namespace 做聚类。如果发现大量语义重复条目说明去重策略没生效如果某个业务维度长期不被命中大概率是信息过期了该清理就清理。记忆系统的健康度和业务代码一样需要持续维护。5.3 我踩过的坑和最终推荐配置说说我这边实际跑的配置不一定适合所有人但可以参考。一开始我图省事所有 Agent 共用一个 namespace结果客服机器人和做策略推荐的 Agent 互相污染用户画像。后来改成“租户 业务线 Agent 用户”四级隔离总算稳定。embedding 模型也换过。最早用英文模型中文 recall 一塌糊涂换成 bge-m3 之后才有明显改善。当前我比较顺手的组合是服务端 Redis Qdrant bge-m3SDK 开启异步写入namespace 按业务线共享filter 强制带 user_idtop_k 初始 5score_threshold 0.6 起步。先跑两周再根据日志调阈值。最后补一句记忆层不是一个能“装上就忘”的组件它需要根据业务变化持续调策略。但至少当你的 Agent 再忘事的时候不用反复堆 prompt 喊“你记住你记住”。给它一个外接大脑比口头叮嘱靠谱得多。