在构建能够学习和演化的智能体(Agent)时,一个强大而灵活的记忆系统是核心。Mem0 项目旨在提供这样一个智能记忆层。许多开发者对信息如何被处理并最终存储到图数据库(如 Neo4j)中感到好奇。
今天,我们将深入探讨 Mem0 的核心功能之一:add 方法。我们将通过时序图和源码分析,一步步揭示从一个简单的文本输入,到最终在 Neo4j 数据库中形成知识图谱的全过程。
整体流程概览
在高层次上,当调用 mem0.add(text) 时,系统会执行以下操作:
- 接收数据:
Mem0主类接收到文本数据。 - 图谱抽取: 内部的
GraphMemory模块会调用一个大型语言模型(LLM)来分析文本,从中抽取出关键的实体(Nodes)和它们之间的关系(Edges)。 - 数据持久化: 抽取出的图谱结构(三元组)被传递给专门的数据库接口(例如
MemgraphMemory)。 - 写入数据库: 该接口将图谱结构转换成 Cypher 查询语言,并在 Neo4j/Memgraph 数据库中执行,从而将记忆以图的形式永久保存下来。
流程图

代码深度解析
现在,让我们深入代码,看看每个步骤是如何实现的。
第 1 步:入口 - Mem0 类
用户与系统交互的入口点是 mem0.memory.main.py 中的 Mem0 类。它的 add 方法是一个简洁的封装。
文件: mem0/memory/main.py
class Mem0:
# ... 其他方法 ...
def add(
self,
data: Union[str, List[Dict]],
user_id: str = None,
session_id: str = None,
metadata: dict = None,
**kwargs,
):
"""
Adds new data to the memory.
Args:
data (Union[str, List[Dict]]): The data to be added. Can be a string or a list of key-value pairs.
user_id (str, optional): The user ID. Defaults to None.
session_id (str, optional): The session ID. Defaults to None.
metadata (dict, optional): Additional metadata. Defaults to None.
"""
return self.memory.add(
data=data,
user_id=user_id,
session_id=session_id,
metadata=metadata,
**kwargs,
)
分析:
- 这个
add方法非常直接,它将所有参数原封不动地传递给了self.memory对象。 self.memory是在Mem0初始化时根据配置创建的实例,在我们的追踪场景中,它就是GraphMemory。
第 2 步:核心逻辑 - GraphMemory
GraphMemory 是实现知识图谱抽取和处理的核心。它负责调用 LLM 并处理返回的结构化数据。
文件: mem0/memory/graph_memory.py
class GraphMemory(Memory):
# ...
def add(self, data: str, **kwargs):
# ...
if isinstance(data, str):
triplets = self.extract_triplets(data)
if triplets:
self.graph_store.add(triplets)
# ...
def extract_triplets(self, text: str) -> List[Tuple[str, str, str]]:
"""
Extracts triplets from the given text.
"""
# ...
response = self.llm.generate_response(messages) # 调用 LLM
# ...
# 解析 LLM 返回的 JSON 结果并转换为三元组列表
# 例如: "[['Paris', 'is the capital of', 'France'], ['Eiffel Tower', 'is in', 'Paris']]"
# ...
return parsed_triplets
分析:
add方法首先调用extract_triplets。extract_triplets方法构建一个 prompt,请求 LLM 从文本中抽取出[主语, 谓语, 宾语]格式的三元组。- 获得三元组列表后,它会调用
self.graph_store.add(),这里的graph_store就是与 Neo4j/Memgraph 直接交互的MemgraphMemory实例。
第 3 步:数据库交互 - MemgraphMemory
这是数据持久化的最后一站。MemgraphMemory 接收三元组数据,并将其转换为 Cypher 查询语句。
文件: mem0/memory/memgraph_memory.py
from gqlalchemy import Memgraph
class MemgraphMemory:
def __init__(self, host="127.0.0.1", port=7687):
self.db = Memgraph(host, port)
def add(self, data: List[Tuple[str, str, str]]):
"""
Adds data to the graph store.
Args:
data (List[Tuple[str, str, str]]): A list of triplets.
"""
for triplet in data:
source, relation, target = triplet
# 清理和格式化关系名称
relation_type = self._format_relationship_type(relation)
# 构建 Cypher 查询
query = (
f"MERGE (source:Entity {{name: '{self._escape(source)}'}})\n"
f"MERGE (target:Entity {{name: '{self._escape(target)}'}})\n"
f"MERGE (source)-[:{relation_type}]->(target)"
)
self.db.execute(query)
def _format_relationship_type(self, relation: str) -> str:
# 将关系文本(可能包含空格)转换为有效的 Cypher 关系类型
return re.sub(r"[^a-zA-Z0-9_]", "_", relation.upper())
def _escape(self, s: str) -> str:
# 防止 Cypher 注入
return s.replace("'", "\\'")
分析:
add方法遍历每个三元组(source, relation, target)。- 核心是 Cypher 查询:
MERGE (source:Entity {{name: '...'}}):MERGE是一个强大的命令。如果一个标签为Entity且name属性匹配的节点已存在,它会匹配该节点;如果不存在,则会创建它。这确保了我们不会重复创建同一个实体。MERGE (source)-[:RELATION_TYPE]->(target): 同样,这条命令会在source和target节点之间创建一条指定类型的关系,前提是这条关系尚不存在。
_format_relationship_type方法确保了关系名称符合 Cypher 的语法要求(例如,将 “is capital of” 转换为 “IS_CAPITAL_OF”)。self.db.execute(query)将最终的 Cypher 查询发送到数据库执行,从而完成了记忆的存储。
结论
通过这次深入分析,我们清晰地看到了 Mem0 如何巧妙地结合了大型语言模型和图数据库:
- LLM 负责从非结构化文本中理解和抽取出结构化的知识。
- 图数据库 (Neo4j/Memgraph) 负责以一种高度关联和可查询的方式持久化这些知识。
MERGE命令是图构建的关键,它以一种幂等的方式创建节点和关系,确保了知识图谱的一致性和无冗余。
这个流程不仅高效,而且极具扩展性,为构建真正拥有长期记忆和推理能力的 AI 系统奠定了坚实的基础。希望这篇教程能帮助你更好地理解 Mem0 的内部工作原理!
4353

被折叠的 条评论
为什么被折叠?



