15-Mem0详解
Mem0 为大型语言模型提供了一个智能、自我改进的记忆层,使得跨应用程序的个性化 AI 体验成为可能。
官网:https://docs.mem0.ai/overview
开源地址:https://github.com/mem0ai/mem0
核心功能
- 用户、会话与智能体记忆: 跨用户会话、交互及智能体保留信息,确保连贯性与上下文一致性。
- 自适应个性化: 基于用户互动和反馈持续优化个性化体验。
- 开发者友好的API: 提供简洁明了的 API,实现与各类应用程序的无缝集成。
- 平台一致性: 确保不同平台和设备上的行为与数据保持一致。
- 托管服务: 提供易于部署和维护的托管解决方案。
快速开始
Jupyter Notebook Viewer (nbviewer.org)
原理分析
部分源码
def add(
self,
data,
user_id=None,
agent_id=None,
run_id=None,
metadata=None,
filters=None,
prompt=None,
):
"""
Create a new memory.
Args:
data (str): Data to store in the memory.
user_id (str, optional): ID of the user creating the memory. Defaults to None.
agent_id (str, optional): ID of the agent creating the memory. Defaults to None.
run_id (str, optional): ID of the run creating the memory. Defaults to None.
metadata (dict, optional): Metadata to store with the memory. Defaults to None.
filters (dict, optional): Filters to apply to the search. Defaults to None.
Returns:
str: ID of the created memory.
"""
if metadata is None:
metadata = {}
embeddings = self.embedding_model.embed(data)
filters = filters or {}
if user_id:
filters["user_id"] = metadata["user_id"] = user_id
if agent_id:
filters["agent_id"] = metadata["agent_id"] = agent_id
if run_id:
filters["run_id"] = metadata["run_id"] = run_id
if not prompt:
prompt = MEMORY_DEDUCTION_PROMPT.format(user_input=data, metadata=metadata)
extracted_memories = self.llm.generate_response(
messages=[
{
"role": "system",
"content": "You are an expert at deducing facts, preferences and memories from unstructured text.",
},
{"role": "user", "content": prompt},
]
)
existing_memories = self.vector_store.search(
name=self.collection_name,
query=embeddings,
limit=5,
filters=filters,
)
existing_memories = [
MemoryItem(
id=mem.id,
score=mem.score,
metadata=mem.payload,
text=mem.payload["data"],
)
for mem in existing_memories
]
serialized_existing_memories = [
item.model_dump(include={"id", "text", "score"})
for item in existing_memories
]
logging.info(f"Total existing memories: {len(existing_memories)}")
messages = get_update_memory_messages(
serialized_existing_memories, extracted_memories
)
# Add tools for noop, add, update, delete memory.
tools = [ADD_MEMORY_TOOL, UPDATE_MEMORY_TOOL, DELETE_MEMORY_TOOL]
response = self.llm.generate_response(messages=messages, tools=tools)
tool_calls = response["tool_calls"]
response = []
if tool_calls:
# Create a new memory
available_functions = {
"add_memory": self._create_memory_tool,
"update_memory": self._update_memory_tool,
"delete_memory": self._delete_memory_tool,
}
for tool_call in tool_calls:
function_name = tool_call["name"]
function_to_call = available_functions[function_name]
function_args = tool_call["arguments"]
logging.info(
f"[openai_func] func: {function_name}, args: {function_args}"
)
# Pass metadata to the function if it requires it
if function_name in ["add_memory", "update_memory"]:
function_args["metadata"] = metadata
function_result = function_to_call(**function_args)
# Fetch the memory_id from the response
response.append(
{
"id": function_result,
"event": function_name.replace("_memory", ""),
"data": function_args.get("data"),
}
)
capture_event(
"mem0.add.function_call",
self,
{"memory_id": function_result, "function_name": function_name},
)
capture_event("mem0.add", self)
return response
MEMORY_DEDUCTION_PROMPT 如下:
MEMORY_DEDUCTION_PROMPT = """
Deduce the facts, preferences, and memories from the provided text.
Just return the facts, preferences, and memories in bullet points:
Natural language text: {user_input}
User/Agent details: {metadata}
Constraint for deducing facts, preferences, and memories:
- The facts, preferences, and memories should be concise and informative.
- Don't start by "The person likes Pizza". Instead, start with "Likes Pizza".
- Don't remember the user/agent details provided. Only remember the facts, preferences, and memories.
Deduced facts, preferences, and memories:
"""
# 翻译
"""
从提供的文本中推断出事实、偏好和记忆。
只需在要点中返回事实、偏好和记忆:
自然语言文本:{user_input}
用户/代理详细信息:{元数据}
推断事实、偏好和记忆的约束:
-事实、偏好和记忆应该简明扼要。
-不要从“这个人喜欢披萨”开始。相反,从“喜欢披萨”开始。
-不要记住提供的用户/代理详细信息。只记住事实、偏好和记忆。
推断的事实、偏好和记忆:
“”
逻辑
这里的逻辑比较简单
- 通过 MEMORY_DEDUCTION_PROMPT 结合用户的数据,拼接成提示词
- 利用大模型提取抽取记忆数据,得到新的记忆项
- 从历史记忆中提取5条最相关的记忆
- 然后将新的记忆项、历史记忆 拼接到一起,交予大模型,让大模型调用合适的tool来更新记忆,tools :
[ADD_MEMORY_TOOL, UPDATE_MEMORY_TOOL, DELETE_MEMORY_TOOL]
- 根据function call的结果,调用tool_calls更新记忆
分析
本质上全部委托给大模型,通过prompt做了一定的约束:
- 通过LLM(大型语言模型)和特定的元数据来抽取记忆信息,类似于进行知识图谱抽取。
- 相关记忆信息通过向量化存储,因此可以支持记忆信息检索
- 记忆会根据输入的内容不断更新,但是同所有长期记忆一样,也会逐渐遗忘东西
问题
- 不支持中文,即使添加了中文记忆,也会自动翻译为英文
- 经多次测试,并不是每次都会翻译成英文,所以肯定不是刻意为之
- 阅读源码后发现,记忆提取使用的是llm,system prompt是用英文写的,所以assistant大概率会返回英文也就不奇怪了
- 解决方法也很简单,因为是开源项目,自己下载源代码,优化system prompt,让他返回中文即可
总结
mem0于RAG无任何关系,更像是之前智能体中常用的“长期记忆”功能的升级,升级了以下几点:
- 更精准的记忆提取,而非简单摘要
- 更方便的调用方式(其实就是把功能封装,可以向管理Memory一样管理记忆)
- 支持跨应用记忆