这期聊一个我最近在 Agent 项目里真正用起来、并且觉得值得写进“必装清单”的开源项目——ai-memory。项目在 GitHub 上已经攒了 7.9K Stars,定位是跨 Agent 的“记忆层”。我最早看到这个项目名的时候以为又是一个简单的对话缓存工具,实际读完源码、跑了几个场景之后才发现,它解决的恰恰是 Agent 开发里最容易被忽略、但到了多 Agent 协作阶段一定会卡壳的问题:记忆到底放在哪里、怎么共享、怎么保持干净。这篇文章我想把 ai-memory 的核心设计、接入方式、配置选型、还有我实测踩过的坑一次性讲清楚,给正在做 Agent 开发、尤其是已经在搞多 Agent 编排的朋友一个可以直接“抄作业”的参考。
1. 为什么 Agent 需要一套独立的记忆层
1.1 没有记忆的 Agent 就是一条金鱼
做过 ChatBot 或者 Agent 的朋友应该都有这种体验:模型本身是“无状态”的,你跟它对话,它每一次都是重新读一遍上下文。短期记忆还能靠把历史消息塞进 Prompt 来凑合,但一旦你的 Agent 要跨会话、跨任务、跨用户工作,这种“每次现算”的方案就完全行不通了。用户今天告诉 Agent 自己偏好简洁回复、喜欢某种代码风格,明天再开一个新会话,Agent 完全不记得,又得重新问一遍。这不是模型能力不够,而是架构里缺了“记忆”这一层。
更麻烦的是多 Agent 场景。假设你有三个 Agent:一个负责收集用户需求,一个负责写代码,一个负责做 Review。每个 Agent 独立启动、独立上下文,如果它们之间的记忆不共享,那这个协作流程就变成了一场接力赛——每个选手都从零开始,前面选手跑过的信息全部丢失。这也是多 Agent 协作效率上不去的核心原因之一。很多团队做到后面发现 Agent 之间靠 Prompt 互相传话,传着传着就丢了、偏了、重复了,本质就是没有一个统一的记忆中枢。
1.2 记忆层到底“层”在哪里
很多人会把记忆层理解成“给 Agent 加一个 Redis”或者“搞一个向量数据库存 embedding”。这些是存储手段,不是记忆层。记忆层的核心是:在 Agent 和存储之间,加一层能够理解、筛选、整理、检索记忆的管理逻辑。它要做的事不仅仅是“把数据存下来”,而是决定什么值得记、什么时候想起来、怎么把过时的记忆清掉。
举个例子,用户说了一句“我喜欢用 Python 写脚本”,这值得存;用户又说了一句“今天天气不错”,这就没必要进长期记忆。判断这两句话的差别,如果靠人工写规则,一个规则引擎根本扛不住真实对话的复杂性;如果靠模型来判断,那这个判断逻辑放在哪里?放在记忆层里,就是 ai-memory 这种组件的定位。它把“哪些对话值得记住”这个决策交给 LLM 来做,而不是靠开发者手工维护一套关键词或规则。
1.3 ai-memory 用一句话概括
ai-memory 是一个给 LLM Agent 提供长期记忆能力的开源库,核心思路是:把 Agent 的记忆从“会话上下文”里剥离出来,单独存储、单独管理,并且通过 LLM 自动决定记忆的写入、抽取、修剪和关联。项目同时支持单用户多 Agent 共享记忆、多用户隔离记忆,还能直接以 MCP 服务器的形式接入到现有 Agent 工具链里。我用下来最直观的感受是:它把“记忆”从一种靠提示词硬撑的临时方案,变成了一个可配置、可扩展、可观测的基础设施组件。
2. 核心架构拆解:LLM 接入、存储后端与记忆作用域
2.1 三个必须理解的抽象层
ai-memory 的架构不复杂,核心其实就三块:LLM 接入层、存储层、作用域层。先记住这三个概念,后面所有配置和代码都围绕它们展开。
LLM 接入层负责所有“需要模型理解”的操作,比如判断一段对话是否值得进入长期记忆、对记忆做修剪、检索时计算关联度。项目内置了多个运行时适配,包括 OpenAI、Anthropic、Google Gemini、Mistral、Ollama。选择 Ollama 这一点对本地部署党很友好——你完全可以用本地模型跑记忆层,数据不出内网。我用的是 OpenAI 的 gpt-4o-mini 做默认记忆判断,实测效果不错,成本也低。
存储层提供 SQLite 和 DynamoDB 两种后端。单机、小规模场景直接用 SQLite,零配置、文件即数据库,迁移方便;分布式或者需要高并发读写时再上 DynamoDB。项目默认使用 SQLite,初始化的时候会在本地自动建库建表,不需要你手工执行任何 DDL,这一点相当省心。
作用域层是这个项目最值得关注的设计。它有 ScopedMemory 和 MultiUserMemory 两种模式,决定了记忆的隔离粒度。这个细节后面我会单独展开讲,因为它直接关系到一个核心决策:你的多个 Agent 之间,到底是共享记忆还是各记各的。
2.2 为什么默认存 SQLite 而不是向量数据库
我第一次看到项目只支持 SQLite 和 DynamoDB 时,第一反应是“没有向量数据库做语义检索,效果会不会打折?”实际用下来发现这个设计其实是刻意为之。ai-memory 的检索策略不是纯向量相似度,而是结合了“LLM 判断 + 时间差 + 关键字”的综合打分。对于绝大多数对话记忆场景,这种方案在效果和成本之间取得了很好的平衡。向量库适合海量知识库的召回,但 Agent 的对话记忆通常不会积累到那个量级,而且向量召回结果还需要二次重排,反而增加架构复杂度。
项目也预留了接口,如果你确实需要向量检索,可以通过自定义存储或在查询后接 rerank 来实现。但我要说的是,如果不是特别极端的场景,SQLite 起步完全够用,先在功能上跑通,再根据瓶颈决定要不要升级,这个路径更稳健。
2.3 ScopedMemory 和 MultiUserMemory 怎么选
这两个概念直接决定了你的 Agent 记忆是“一人多 Agent 共享”还是“一人一 Agent 隔离”。我画个场景你就明白了。
ScopedMemory 模式下,记忆按照 Agent 维度组织,同一个 user 只要 agent_id 相同,就能读到自己的历史记忆。比如一个用户在小程序里用了你开发的写作助手和翻译助手,这两个助手如果共用一个 agent_id,用户对写作风格的偏好也会被翻译助手感知到;如果你希望每个助手记忆独立,就给不同 agent_id。在 ScopedMemory 下,记忆的 key 是 agent_id + user_id。
MultiUserMemory 则多了一个 user_id 的隔离维度。同一个 Agent 服务多个用户时,每个用户的记忆互不可见。这在 SaaS 场景下是刚需,否则用户 A 说过的偏好在对话里被用户 B 触发出来,就属于严重的数据事故了。我给一个客户做企业内部知识助手时,几个部门共用一个 Agent 入口,但每个员工必须有独立的记忆空间,用的就是 MultiUserMemory。
这两种模式在代码里只是初始化参数的差别,但架构上决定了你的系统能不能多租户化。我的建议是:除非你明确只需要单用户,否则一上来就用 MultiUserMemory。因为从 ScopedMemory 迁移到 MultiUserMemory 不是改一行配置那么简单,已有数据需要重新组织,越早定越好。
3. SPADE 记忆管理方法:从存储到自动整理
3.1 拆解 SPADE 四个字母
项目最核心的方法论叫 SPADE,四个字母分别代表 Store、Prune、Arrange、Detect。我在读源码之前一直以为这只是个营销概念,看完之后意识到,这其实是 Agent 记忆管理里非常少见的“处理闭环”。
Store 负责把值得记住的信息写入长期记忆。它不会盲存所有对话,而是让 LLM 分析当前消息,判断哪些内容有长期价值。这个判断结果会带一个 relevancy 分数,默认阈值是 30%,也就是说模型认为某条信息与用户长期偏好、任务上下文关联度高于 30% 才会写入。这个阈值可以根据你的业务调整,比如客服场景可以调低一点,多记一些用户线索;工具类场景可以调高,避免记忆库被噪声填满。
Prune 负责清理。记忆不是越多越好,堆积了大量过时信息之后,检索效率下降,成本上升,还容易把无关内容带进上下文。Prune 会定期扫描记忆库,让 LLM 判断哪些记忆已经不再相关或已经被新记忆覆盖,然后删除。它还支持做记忆压缩,把多条相关的旧记忆合并成一条摘要式记忆,这个能力在长周期对话里特别有用。
Arrange 负责整理。它会根据时间、主题、实体关联对记忆做归类排序,并且会尝试建立记忆之间的关联关系。比如用户说过“我喜欢测试驱动开发”,后来又说“我在用 pytest”,Arrange 会把这两条记忆关联起来,检索时如果命中其中一条,另一条也会被提升权重。这种关联能力让记忆不再是孤立条目,而是一张可以交叉检索的知识网。
Detect 是检索时最关键的一步。当 Agent 收到新消息需要调用记忆时,Detect 会根据当前上下文,从记忆库中找到最相关的若干条记忆,并附加到系统提示词里。它不是简单的向量 top-k 召回,而是结合 LLM 判断和时间衰减的综合检索。我记得项目里有个参数可以手动指定 top_k 数量,默认是 5,也就是说每次最多往上下文里注入 5 条记忆片段。这个设计很务实,控制了 token 成本,也防止记忆噪声污染主对话。
3.2 自动判断写入价值的逻辑
这里有个细节值得单独说说:为什么 ai-memory 用 LLM 做“是否值得写入”的判断,而不是直接全部存下来然后靠检索过滤?我一开始也倾向于后者——反正有检索,存得越多召回越全。但实际测试发现,全量存储有两个致命问题。
第一是成本。每条消息都向量化、都入库,token 消耗高,存储膨胀快。第二是信噪比。记忆库里充满了“我今天吃了午饭”“天气不错”这类无效条目,检索引擎会把真正重要的用户偏好淹没。现在用 LLM 做前置过滤,虽然每次对话多了一两次模型调用,但入库的都是高价值信息,后续每次检索都在省 token。算总账还是划算的。
而且 ai-memory 在这里做了一个很聪明的设计:它在检测写入价值时,可以同时判断该记忆的“可访问性”——即这条记忆是否应该对所有关联 Agent 可见,还是仅当前 Agent 可见。这个粒度比单纯的“存/不存”要细得多,等于在记忆写入阶段就做了权限控制,而不是事后补救。
3.3 衍生记忆与记忆导出
SPADE 之外,项目还支持“衍生记忆”的生成。比如你积累了一周的用户对话记忆,可以让 LLM 自动生成一份“周总结”,把零散信息浓缩成结构化摘要。我在一个偏数据分析的 Agent 场景里用到这个功能,效果相当于给 Agent 配了个每周自动写的“用户画像报告”,内容比人工整理还要全。记忆导出和导入也有现成接口,可以 JSON 格式导出某个用户的所有记忆,方便做迁移、备份,或者在不同环境间复制用户画像。这套能力对生产环境的价值很大,很多对话系统迁移时最难处理的就是历史记忆,有了标准导出格式,整个迁移链路会顺畅很多。
4. 实操接入:从零开始跑通第一个带记忆的 Agent
4.1 五分钟快速体验版
先说最快速的上手路径,两条命令加几行 Python 代码就能跑通。安装指令是 pip install ai-memory,如果你要用附加的 MCP 或者 DynamoDB 支持,就装 ai-memory[mcp] 或 ai-memory[dynamodb] 版本。我建议直接装 ai-memory[mcp],后面接工具链会方便很多。
初始化最简单的实例只需要一行代码:
from ai_memory import AgentMemory agent_memory = AgentMemory()如果这样直接跑,项目会尝试使用 OpenAI 的默认模型作为 LLM 运行时,存储用本地 SQLite。也就是说你甚至不需要任何额外配置就能得到一个可用的记忆层。对于想先看效果再深入配置的人来说,这个体验非常友好。
发一条消息进去试试:
res = agent_memory.send_message( agent_id="my-agent-id", user_id="user-1", message="Hello! I like reading sci-fi books.", metadata={"session_id": "session-1"}, ) print(res)这一步会触发写入判断。模型分析完这句话后,如果认为“喜欢读科幻小说”属于值得长期记忆的信息,就会存入记忆库。之后你换一个会话,再问它“what do I like?”,它能从记忆库里检索到答案:
memory = agent_memory.search_memory( agent_id="my-agent-id", user_id="user-1", query="What do I like?", ) print(memory)我实测用 gpt-4o-mini 跑这个流程,从发消息到记忆写入大概 1 秒左右,检索响应也是亚秒级。这个速度在真实对话场景里完全可以接受,因为记忆写入可以做成异步任务,检索则放在真正需要调用记忆的时候才触发。
4.2 指定 OpenAI 模型的完整接入
如果你不想用默认配置,或者需要控制模型、调整参数,初始化方式是这样的:
from ai_memory import AgentMemory, OpenAILLM, OpenAIConfig config = OpenAIConfig(model_name="gpt-4o-mini") llm = OpenAILLM(config) agent_memory = AgentMemory(llm=llm)如果你和我一样偏好本地或私有化部署,把 OpenAIConfig 换成 OllamaConfig,模型名换成本地模型名称即可。两套接口的使用方式几乎一致,切换成本很低。这也是项目做得好的地方:它把“不同模型提供商的差异”隔离在了接入层,业务代码不需要跟着改。从项目角度看,你真正要关心的不是用哪家模型,而是记忆层的存储策略和检索策略是否满足业务需求。
4.3 用 DynamoDB 做生产级存储
当你的 Agent 不止一个实例、需要横向扩展时,本地 SQLite 会成为瓶颈。项目提供了 DynamoDB 后端,通过 PynamoDB 操作。初始化方式如下:
from ai_memory import AgentMemory, OpenAILLM, OpenAIConfig, DynamoDBConfig, DynamoDBStorage config = OpenAIConfig(model_name="gpt-4o-mini") llm = OpenAILLM(config) storage_config = DynamoDBConfig( aws_access_key_id="your-access-key", aws_secret_access_key="your-secret-key", aws_region_name="us-east-1", ) storage = DynamoDBStorage(storage_config) agent_memory = AgentMemory(llm=llm, storage=storage)换成 DynamoDB 之后,同一份记忆库可以被多个 Agent 实例并发读写,适合部署在云上的容器化服务。不过要注意的是,DynamoDB 会产生实际费用,读写吞吐量需要提前规划。如果只是内部工具,SQLite 其实能扛很久,我自己的经验是单库几千条记忆以内,SQLite 完全没压力。
4.4 直接以 MCP 服务器方式接入
MCP 是现在 Agent 工具链很流行的集成协议,ai-memory 也支持直接作为 MCP 服务器运行。装好 ai-memory[mcp] 后,跑一句命令就能把记忆能力暴露成标准 MCP 工具:
ai-memory-mcp --transport stdio --storage sqlite --db-path my-memory.dbtransport 参数支持 stdio 和 sse 两种模式,stdio 适合本地进程内调用,sse 适合远程服务。启动后,任何支持 MCP 的 Agent 客户端都能直接调用记忆的写入与检索方法。我在一个基于 Claude 的桌面工具里通过 MCP 方式接入了记忆层,配置过程不超过十分钟,Agent 从“无记忆状态”变成了能跨会话记住用户偏好的状态。这种标准协议的集成方式,很大程度上降低了记忆层的接入门槛,你不需要懂 ai-memory 的内部实现,只要遵循 MCP 规范就能用。
5. 关键配置与选型建议
5.1 这组配置参数建议你反复调试
AI-Memory 有几个参数值得在项目初期就确定下来,而不是等数据积累多了再改。我把常用的几个整理成了参考表:
| 参数 | 作用 | 我的建议 |
|---|---|---|
| relevancy_percentage | 写入长期记忆的相关度阈值,默认 30 | 客服/用户画像场景设 20 左右,工具类场景设 40 左右 |
| top_k | 检索时注入上下文的最大记忆条数,默认 5 | 按 token 预算调整,一般不超过 10 |
| enable_buffer_memory | 短期对话缓冲记忆开关 | 建议开启,保留会话内短期上下文 |
| agent_id | 记忆作用域的 Agent 标识 | 按业务域划分,避免混用 |
| user_id | 记忆所属用户标识 | 多用户场景必须显式传入 |
有些朋友接入之后发现“怎么我的 Agent 还是什么都不记得”,大概率就是 relevancy_percentage 设置过高,信息被过滤掉了;反过来,如果记忆库里全是垃圾信息,那就是阈值太低或者 prune 没跑起来。这个阈值直接影响记忆库质量,需要观察实际对话数据来调。
5.2 本地部署和云部署怎么权衡
如果你跑的是企业内部应用、数据敏感度高,那 SQLite + Ollama 的组合就是最稳的。所有数据留在本机,模型调用不经过外部 API,记忆内容也不出内网。唯一要注意的是本地模型做记忆判断的效果参差不齐,我当时测试时发现 7B 级别的本地模型在“是否值得写入”这个判断上会有一定误判率,建议实测评估后再定。
如果追求效果和低维护成本,OpenAI API + DynamoDB 是更省事的组合。模型判断质量稳定,存储免运维,但费用上需要心里有数。尤其是 DynamoDB,读写吞吐量设置不合理的话,账单可能出乎意料。我的做法是先用 SQLite 跑通逻辑,确认业务稳定了再迁移到 DynamoDB,避免一开始就交云学费。
5.3 单 Agent 到多 Agent 的迁移要注意什么
单 Agent 接入之后,很多人下一个需求就是“再开一个 Agent,让它们共享记忆”。这时候最容易出的问题就是 agent_id 的混乱。假如你第一个 Agent 用的 agent_id 是 “assistant”,第二个 Agent 也想共享记忆,用了另一个 id,那就完全读不到之前的记忆了。正确的做法是:如果是同一用户下要共享记忆,保持 agent_id 一致;如果是同一个 Agent 服务不同用户,靠 user_id 区分。
我帮一个团队排查过一次“Agent 互相不认账”的问题,最后定位到就是三个 Agent 分别用了三个不同的 agent_id,改成一个公共 id 之后共享立刻生效。这个细节看起来简单,但在实际编排多 Agent 时特别容易被忽略。
6. 踩坑实录与常见问题排查技巧
6.1 “记忆写入失败”先检查模型配置
我遇到过的最常见问题,是 send_message 后没有报错,但记忆库里什么都查不到。排查路径很固定:第一步看 LLM 配置是否成功初始化,第二步看 relevancy_percentage 是否设得过高,第三步看 send_message 返回的 response 里有没有 memory_id。如果没有拿到 memory_id,说明这条消息被模型判定为“不值得记忆”,并不是系统故障。这种情况我建议先用一条明显值得记忆的消息测试,比如“I prefer concise responses”,确认链路通不通,再回到真实业务数据上调整阈值。
6.2 多用户场景记忆串号
如果你用的是 ScopedMemory 模式,同时服务多用户,很容易出现用户 A 触发用户 B 记忆的情况。这不是项目 bug,是作用域模式选错了。解决方案是把初始化切到 MultiUserMemory,确保每个 user_id 有自己的记忆空间。我曾经在社区看到有人反馈“用户的记忆跑到别人那里”,最后分析下来,几乎都是因为没有在 send_message 里传 user_id,或者作用域配置不对。
6.3 MCP 模式下工具调用超时
用 ai-memory-mcp 作为 MCP 服务器时,如果 Agent 客户端对每次工具调用的超时时间设置很短,记忆写入这种需要等模型判断的操作可能超时。我当时遇到的情况是客户端设了 10 秒超时,写入判断偶尔会超过这个时间。解决方案有两个方向:一是把记忆写入做成异步消息队列,二是调大客户端超时时间。如果对实时性要求不高,我推荐前者,异步写入还可以减少用户消息链路的延迟。
6.4 记忆库越用越“脏”怎么办
很多项目刚开始跑的时候记忆质量很好,一个月之后检索结果越来越差,根本原因是缺少 prune 和 arrange 的管理。检查一下你是不是在初始化时关闭了 prune 功能,或者长时间没有调用整理接口。项目虽然设计了大循环和小循环的自愈式记忆整理机制,但没有 cron 驱动的话,它只能在你调用相关接口时被动执行。我的建议是做一个定时任务,定期做一次 prune 和 arrange,保证记忆库长期保持干净。
还有一个容易被忽视的问题:如果 Agent 的行为逻辑改版了,历史记忆里会有大量基于旧逻辑写入的无效条目。这种时候不要指望 prune 能自动识别“过时业务逻辑”,直接按 agent_id 批量清理,重新积累更靠谱。
6.5 调试记忆内容的小技巧
项目支持把记忆导出为 JSON,这是个非常实用的调试手段。我建议在开发和联调阶段,设置一个调试接口,随时导出某个用户或某个 Agent 的全部记忆,直观地看一眼当前系统都记住了什么。很多检索效果不佳的问题,导出来一看就明白了——要么是记忆条目太碎,要么是阈值太低存了太多噪声。用导出的数据做分析,远比在黑盒里猜效果要好。
7. 我对这个项目的整体评价与扩展思路
如果要用一句话总结我对 ai-memory 的评价:它是我目前见过的最接近“Agent 记忆基础设施”定义的开源方案。项目不追求把记忆做得多复杂,而是用一套清晰的分层架构和 SPADE 方法论,把记忆管理中最难的部分——判断什么值得记、如何检索相关记忆、如何保持记忆库长期干净——交给了 LLM 和清晰的管理闭环。这种思路对正在做 Agent 开发的人很有参考价值,哪怕你不直接用这个库,也可以把它的记忆分层思路借鉴到自己的系统里。
以一个 7.9K Stars 的项目来说,ai-memory 的功能完整度和代码可读性都相当在线。它在“简单上手”和“生产可用”之间找到了一个还不错的平衡点:本地一条命令能跑,云端也有标准方案;单 Agent 能用,多 Agent 协作也能撑得住。个人项目用它来给 ChatBot 加记忆,或者公司内部想快速搭一套带用户画像的服务,都是很合适的选择;如果是超大规模的高并发场景,或者需要复杂的记忆权限矩阵,那可能还需要在它之上做更多定制。
最后再分享一个我在实际项目中验证过的扩展思路:把 ai-memory 作为一个记忆微服务来部署,开放 HTTP 接口给多个 Agent 调用,而不是在每个 Agent 进程内部各初始化一个 AgentMemory 实例。这样记忆层真正做到全局唯一、多 Agent 共享,也方便后续做记忆质量的监控和统计。等你的 Agent 数量多起来之后,你会感谢这个早期架构决策的。