凌晨两点被报警电话叫醒,说是AI助教"失忆"了------半小时前学生刚告诉它自己的实验进度,结果追问一句"那我下一步该做什么",它直接反问"请问你的进度是什么?"用户心态当场炸裂。我爬起来翻日志,发现Mem0里记忆确实写入了,add接口返回了200,但后续检索却查不出来。就这一个bug,让我整整排查了3天。
问题拆解
我们的AI Agent用Mem0管理长期记忆:对话历史、用户偏好、任务状态全存在里面。用户连续对话时,Agent会先调用search()捞出相关记忆,拼进提示词再回答。按理说写入后立刻能搜到,可现实是:
- 写入成功但检索为空:api调用返回memory_id,紧接着用相同query搜索,结果列表里没它。
- 局部可见,全局消失:单个用户维度可以搜到,但跨会话或者换一个user_id去查公共记忆时丢了。
- 时好时坏:本地跑测试全绿,推上CI就红,一查又是记忆丢失。
根因指向三个可能:异步索引延迟 、搜索参数与写入内容不匹配 、默认清理策略。常规手工测试完全覆盖不到这些时序敏感场景------你没法每次部署都手动发几十条消息再去验证。需要一套自动化回归测试,专门盯着记忆的"写入-检索"闭环。
方案设计
我们直接用 pytest + Mem0 Python SDK 搭了一套记忆存储验证体系。选型时有过权衡:
- unittest 不够灵活,fixture管理麻烦,参数化支持弱。
- Mock掉Mem0 API 测不出真实索引行为,等于白测。
- 直接用curl/bash 可维护性差,断言简陋。
最终方案:docker-compose起一个Mem0服务(带Qdrant向量库),pytest的conftest里封装client fixture并做数据隔离,测试用例覆盖单条写入/批量写入/更新后检索/并发写入等场景。这样每次push代码后,CI都会把整个记忆链路跑一遍,漏网的异步问题提前暴露。
核心实现
这段代码解决测试环境的基础设施:启动Mem0 client,并为每个测试创建独立的user_id/app_id,避免数据串扰。
python
# conftest.py
import pytest
from mem0 import Memory
@pytest.fixture(scope="session")
def mem0_client():
"""连接本地Mem0服务,配置写入同步模式"""
return Memory.from_config({
"version": "v1.1",
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
},
"vector_store": {
"provider": "qdrant",
"config": {"host": "localhost", "port": 6333}
}
})
@pytest.fixture
def fresh_agent(mem0_client: Memory):
"""
每个测试拿全新agent_id,测试结束清理数据,
保证测试间完全隔离。
"""
agent_id = f"test_agent_{pytest.uid}"
yield mem0_client, agent_id
# 清理:删除该agent所有记忆
mem0_client.delete_all(user_id=agent_id)
接下来是核心验证:写入后必须能搜到。我们加入重试逻辑,因为Mem0的向量索引默认是异步的------这是血泪教训换来的。
python
# test_memory_basic.py
import time
from mem0 import Memory
def test_add_and_search_must_find(fresh_agent):
"""基本闭环:写入一条记忆,立刻检索必须出现"""
client, agent_id = fresh_agent
# 写入:记录用户偏好
payload = f"用户{agent_id}喜欢用黑暗模式阅读代码"
client.add(payload, user_id=agent_id)
# 坑点:索引异步,立即search可能为空,需要retry
deadline = time.time() + 5 # 5秒超时
found = False
while time.time() < deadline:
results = client.search("喜欢什么模式", user_id=agent_id)
# 至少命中一条且内容包含关键词
if any("黑暗模式" in r["memory"] for r in results):
found = True
break
time.sleep(0.5)
assert found, f"5秒内未检索到写入的记忆,search返回: {results}"
上面这个test是整篇文章的核心,一句话就是**"不信API,只信重试后的结果"**。下面再补一个并发写入不丢数据的压测用例:
python
# test_concurrent_write.py
from concurrent.futures import ThreadPoolExecutor
from mem0 import Memory
def test_concurrent_add_never_lost(fresh_agent):
"""10个线程同时写入不同偏好,最终都应能搜到"""
client, agent_id = fresh_agent
preferences = [
f"{agent_id}偏好亮色主题",
f"{agent_id}习惯用2空格缩进",
f"{agent_id}喜欢在代码里加emoji注释",
# ... 共10条
] * 10 # 批量复制到10条
with ThreadPoolExecutor(max_workers=10) as pool:
pool.map(lambda p: client.add(p, user_id=agent_id), preferences)
# 等待索引完成
time.sleep(3)
results = client.search("主题", user_id=agent_id)
# 验证至少能搜到亮色主题那条
assert any("亮色主题" in r["memory"] for r in results), "并发写入后记忆丢失"
通过参数化,我们还可以验证更新记忆后新旧内容共存情况、跨用户隔离等,十几条case把记忆的核心链路打了个封条。
踩坑记录
坑1:官方文档说add是同步,实际是异步
现象:单步调试时test全过,不加断点直接跑就挂。日志显示add返回成功,紧接着search返回空数组。
原因 :读源码发现,默认配置下Mem0的add只是把消息丢给队列,真正的嵌入和入库是后台worker做的。文档里那句"instantly available"指的是API响应,不是检索可用。
解决 :要么开启同步模式(version: v1.1之后配置async_mode: false),要么像我们写的那样加retry。但生产环境为了吞吐,多半还是异步,所以retry是验证的"标配"。
坑2:记忆被默默设了TTL,隔夜全丢
现象:CI nightly测试一大早就全员飘红,前一天写入的数据全搜不到。
原因 :Mem0默认配置没有显式设过期时间,但我们用的Qdrant底库有默认的ttl策略,超过多少秒自动清理。这个在Mem0的README里没有提示。
解决 :在vector_store配置里追加 "ttl": None 强制禁用自动清理。同时测试里增加对"24小时后数据仍可检索"的定时验证job。
效果验证
手工测试阶段,记忆丢失问题每月至少一次线上投诉。接入这套pytest套件后,CI pipeline会在任何改动后3分钟内跑完31条记忆验证case,覆盖了单用户、多并发的写入/更新/检索全链路。上线以来记忆丢失投诉降为0。具体数据:
| 指标 | 优化前 (手工) | 优化后 (自动化) |
|---|---|---|
| 回归耗时 | 25分钟/次 | 2.8分钟/次 |
| 缺陷发现率 | 仅集成测试阶段发现 | 90%在PR阶段拦截 |
| 记忆丢失投诉 | 3次/月 | 0次 |
可直接用的代码/工具
想马上在自己项目里用?把这个fixture丢到conftest,改一下embedder配置就能跑:
python
# 一行命令装依赖
# pip install pytest mem0ai openai
@pytest.fixture(scope="session")
def mem0_client():
return Memory.from_config({
"version": "v1.1",
"async_mode": False, # 测试用同步模式
"embedder": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
"vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333, "ttl": None}},
"history_db_path": ":memory:" # 测试用内存历史,避免磁盘残留
})
把上面那段add+search重试循环封装成函数,你的记忆存储就有了自动化守门员。
#Python #AI测试 #Mem0 #pytest #Agent调试
关于作者
一个给AI Agent做基础设施的实战派后端,专注于让LLM的"记忆"不再玄学。
GitHub: github.com/baofugege
Sponsor: github.com/sponsors/ba... --- 如果这篇文章帮你少熬了夜,请我喝杯咖啡。
提供服务:Python后端性能优化 / AI工具链定制 / 技术咨询,Telegram联系 @baofugege