第一阶段:LLM 的「失忆症」

🧠 一个类比帮你秒懂

想象你去健身房请了一个私教叫 Ray。

没有 Mem0 的 Ray(普通 LLM):

每次你走进健身房,Ray 都像第一次见你一样:「你好!你的健身目标是什么?」——即使你上周刚告诉他你要跑马拉松。

有 Mem0 的 Ray:

你走进健身房,Ray 说:「上周你跑了 15 公里,膝盖有点不舒服,今天我们做低强度的交叉训练吧。」

这就是核心问题:LLM 是无状态的(stateless)。每次 API 调用,对 GPT 来说都是一个全新的对话。

❌ 笨办法:把所有历史塞进 Context Window

messages = [
    # 3个月前的对话...
    # 2个月前的对话...
    # 上周的对话...
    # 昨天的对话...
    # 今天的问题
]

问题:

  • — 几万条消息,每次都要发送
  • — Token 数量爆炸,按量计费
  • 有上限 — Context Window 再大也有边界(GPT-4o 是 128K tokens)

✅ 聪明办法:Mem0

Mem0 的做法是:从对话中抽取关键事实,存起来,需要时检索相关的几条

就像人的大脑:你不会记住和朋友说过的每一个字,但你记得「他要结婚了」「他对花生过敏」这些关键信息。


第二阶段:核心循环 — 取→用→存

🔄 三步走,这是所有记忆系统的骨架

用户说话 → ① 从 Mem0 取相关记忆
         → ② 把记忆 + 用户消息一起发给 LLM
         → ③ 把这轮对话存回 Mem0

代码实现

from openai import OpenAI
from mem0 import MemoryClient
 
openai_client = OpenAI(api_key="your-openai-key")
mem0_client = MemoryClient(api_key="your-mem0-key")
 
def chat(user_input, user_id):
    # ① 取:用用户的输入做语义搜索,找到最相关的5条记忆
    memories = mem0_client.search(user_input, user_id=user_id, limit=5)
    context = "\n".join(m["memory"] for m in memories["results"])
 
    # ② 用:把记忆塞进 system prompt,让 LLM 知道这个用户的背景
    response = openai_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": f"You're Ray, a running coach. Memories:\n{context}"},
            {"role": "user", "content": user_input}
        ]
    ).choices[0].message.content
 
    # ③ 存:把这轮对话存进 Mem0,Mem0 会自动提取关键信息
    mem0_client.add([
        {"role": "user", "content": user_input},
        {"role": "assistant", "content": response}
    ], user_id=user_id)
 
    return response

🎯 关键理解:Mem0 的 add 不是存原文

很多人以为 mem0_client.add(...) 是把整段对话原封不动存进去。不是的。

Mem0 内部会用 AI 做信息抽取

用户说的原话Mem0 实际存储的
”嘿,我想在4小时内跑完马拉松,你觉得我能行吗?""Max 想跑进4小时完成马拉松"
"哈哈好的谢谢”❌ 不存(无信息量)
“我右膝盖下坡时会疼""Max 右膝盖下坡时会疼”

🧪 验证跨会话记忆

# 第一天
chat("I want to run a marathon in under 4 hours", user_id="max")
# Ray: "That's a solid goal. What's your current weekly mileage?"
 
# ——— 关闭 app,第二天重新打开 ———
 
# 第二天
chat("What should I focus on today?", user_id="max")
# Ray: "Based on your sub-4 marathon goal, let's work on building your aerobic base..."

App 重启了,但 Ray 还记得。 因为记忆在 Mem0 的云端,不在你的 app 进程里。


第三阶段:记忆分类与噪音过滤

问题1:记忆太杂

跑一周不过滤的结果:

memories = mem0_client.get_all(filters={"AND": [{"user_id": "max"}]})
# ["Max wants to run marathon under 4 hours", "hey", "lol ok", "cool thanks", "gtg bye"]

「hey」和「lol ok」混在里面,检索时会干扰真正有用的记忆。

解决方案:Custom Instructions(自定义指令)

告诉 Mem0「什么值得记,什么不值得」:

mem0_client.project.update(custom_instructions="""
Extract from running coach conversations:
- Training goals and race targets
- Physical constraints or injuries
- Training preferences (time of day, surfaces, weather)
- Progress milestones
 
Exclude:
- Greetings and filler
- Casual chatter
- Hypotheticals unless planning related
""")

这相当于给 Mem0 的信息抽取 AI 一套工作手册

效果验证:

chat("hey how's it going", user_id="max")      # ❌ 不会被存储
chat("I prefer trail running over roads", user_id="max")  # ✅ 存储
 
# 结果只有有意义的记忆:
# ["Max wants to run marathon under 4 hours", "Max prefers trail running over roads"]

问题2:不同类型的记忆需要分开管理

Max 的膝盖疼(临时伤病) vs 马拉松目标(长期目标),性质完全不同。

解决方案:Categories(分类)

mem0_client.project.update(custom_categories=[
    {"goals": "Race targets and training objectives"},
    {"constraints": "Injuries, limitations, recovery needs"},
    {"preferences": "Training style, surfaces, schedules"}
])

⚠️ 重要区分:Categories vs Metadata

Categories(分类)Metadata(元数据)
谁来打标签?Mem0 AI 自动打你手动指定
能强制吗?❌ 不能,AI 自己判断✅ 完全由你控制
适合什么场景?语义分类(目标、伤病、偏好)结构化标签(训练类型、强度等级)
类比让图书管理员帮你分类你自己贴标签
# Categories:AI 自动判断这是 "constraints"
mem0_client.add(
    [{"role": "user", "content": "My right knee flares up on downhills"}],
    user_id="max"
)
 
# Metadata:你手动强制打标签
mem0_client.add(
    [{"role": "user", "content": "10x400m intervals"}],
    user_id="max",
    metadata={"workout_type": "speed", "intensity": "high"}
)

按分类检索

制定训练计划时,只查伤病信息:

constraints = mem0_client.search(
    query="injury concerns",
    filters={
        "AND": [
            {"user_id": "max"},
            {"categories": {"in": ["constraints"]}}
        ]
    },
    threshold=0.0  # 降低阈值,确保短文本也能被召回
)
# Output: ["Max's right knee flares up on downhills"]

Ray 做训练计划时只拿到伤病信息,不会被马拉松目标等无关记忆干扰。


第四阶段:Agent 人格记忆

用户记忆 ≠ Agent 记忆

到目前为止,我们存的都是关于用户的信息(Max 的目标、伤病)。但 Ray 作为教练,也有自己的「性格」需要记住。

记忆类型存什么用什么 ID
用户记忆Max 的目标、偏好、历史user_id="max"
Agent 记忆Ray 的沟通风格、教练原则agent_id="ray_coach"
# 存储 Agent 人格
mem0_client.add(
    [{"role": "system", "content": "Max wants direct, data-driven feedback. Skip motivational language."}],
    agent_id="ray_coach"
)

使用时两者结合

# 取用户记忆
user_memories = mem0_client.search("training plan", user_id="max")
 
# 取 Agent 人格
agent_memories = mem0_client.search("coaching style", agent_id="ray_coach")
 
# 两者都传给 LLM
response = openai_client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": f"""
You're Ray, a running coach.
Your style: {agent_memories}
About the user: {user_memories}
"""},
        {"role": "user", "content": "How'd my run look today?"}
    ]
)

效果对比:

没有 Agent 记忆有 Agent 记忆
”Great job! You’re doing amazing! Keep it up! 🎉""Pace: 8:15/mile. Heart rate 152, zone 2. On track.”

Max 喜欢直接的数据反馈,Ray 记住了这一点。


第五阶段:时间维度管理

短期 vs 长期记忆策略

┌─────────────────────────────────────┐
│         你的 App 内存               │
│   最近 10 条消息(短期上下文)        │  ← 快、免费、重启就没了
└──────────────┬──────────────────────┘
               │ 只把有意义的存下去
               ▼
┌─────────────────────────────────────┐
│         Mem0 云端存储                │
│   关键事实(长期记忆)               │  ← 持久、跨会话、有成本
└─────────────────────────────────────┘

原则:不是每句话都要存进 Mem0。 最近几轮对话保持在 app 内存即可,只让 Mem0 处理值得长期保存的信息。配合 custom_instructions,大部分过滤是自动的。

会过期的记忆

Max 扭了脚踝,两周后会好。这条记忆也应该在两周后自动消失:

from datetime import datetime, timedelta
 
expiration = (datetime.now() + timedelta(days=14)).strftime("%Y-%m-%d")
 
mem0_client.add(
    [{"role": "user", "content": "Rolled my left ankle, needs rest"}],
    user_id="max",
    expiration_date=expiration  # 14天后自动删除
)

效果: 两周内 Ray 会说「注意脚踝,做低强度训练」。两周后记忆消失,Ray 不再提起。就像现实中教练知道你已经恢复了。


第六阶段:生产环境实战模式

🏷️ 用 run_id 隔离不同训练周期

# 备战波士顿马拉松的记忆
mem0_client.add(messages, user_id="max", run_id="boston-2025")
 
# 备战纽约马拉松的记忆
mem0_client.add(messages, user_id="max", run_id="nyc-2025")
 
# 只检索波士顿相关记忆,不会混入纽约的
boston = mem0_client.search("training plan", user_id="max", run_id="boston-2025")

run_id 就像文件夹——同一个用户的记忆可以按「情节」分开。

🔄 处理目标变更(矛盾检测)

Max 把目标从 sub-4 改成了 sub-3:45:

# 找到旧记忆
memories = mem0_client.get_all(filters={"AND": [{"user_id": "max"}]})
goal_memory = [m for m in memories["results"] if "sub-4" in m["memory"]][0]
 
# 更新它(而不是新建一条,避免矛盾)
mem0_client.update(goal_memory["id"], "Max wants to run sub-3:45 marathon")

关键:用 update 而不是再 add 一条。 否则 Mem0 里会同时存在 “sub-4” 和 “sub-3:45”,LLM 会困惑。

👥 多 Agent 协作

# Ray 管跑步
chat("easy run today", user_id="max", agent_id="ray")
 
# Jordan 管力量训练
chat("leg day workout", user_id="max", agent_id="jordan")

每个 Agent 有独立的人格记忆,但共享同一个用户的基本信息。

🗑️ 清理旧数据

# 删除单条记忆
mem0_client.delete(memory_id="mem_xyz")
 
# 删除整个训练周期
mem0_client.delete_all(user_id="max", run_id="old-training-cycle")

🎯 完整配置一览

把所有知识串起来的初始化代码:

from mem0 import MemoryClient
from datetime import datetime, timedelta
 
mem0_client = MemoryClient(api_key="your-mem0-key")
 
# 1. 告诉 Mem0 什么值得记
mem0_client.project.update(
    custom_instructions="""
    Extract: goals, constraints, preferences, progress
    Exclude: greetings, filler, casual chat
    """,
    custom_categories=[
        {"name": "goals", "description": "Training targets"},
        {"name": "constraints", "description": "Injuries and limitations"},
        {"name": "preferences", "description": "Training style"}
    ]
)
 
# 2. 存长期目标
mem0_client.add([
    {"role": "user", "content": "I want to run a sub-4 marathon"}
], user_id="max", agent_id="ray")
 
# 3. 存会过期的伤病
mem0_client.add(
    [{"role": "user", "content": "Rolled ankle, need light workouts"}],
    user_id="max",
    expiration_date=(datetime.now() + timedelta(days=14)).strftime("%Y-%m-%d")
)
 
# 4. 存 Agent 人格
mem0_client.add(
    [{"role": "system", "content": "Direct, data-driven feedback. No fluff."}],
    agent_id="ray"
)
 
# 5. 检索时按需过滤
memories = mem0_client.search("training plan", user_id="max", limit=5)

✅ 上线前 Checklist

#检查项为什么
1设置 custom_instructions过滤噪音,只存有价值的信息
2定义 2-3 个 categories太多会稀释标签准确度
3制定过期策略临时信息不应该永久存在
4API 调用加错误处理Mem0 挂了不能让主流程崩溃
5在 Mem0 Dashboard 监控记忆质量确认存的都是有用的
6清除测试数据别让「test123」出现在生产环境