深入 Mem0 源码:揭秘 `add` 方法如何将记忆存入 Neo4j

在构建能够学习和演化的智能体(Agent)时,一个强大而灵活的记忆系统是核心。Mem0 项目旨在提供这样一个智能记忆层。许多开发者对信息如何被处理并最终存储到图数据库(如 Neo4j)中感到好奇。

今天,我们将深入探讨 Mem0 的核心功能之一:add 方法。我们将通过时序图和源码分析,一步步揭示从一个简单的文本输入,到最终在 Neo4j 数据库中形成知识图谱的全过程。

整体流程概览

在高层次上,当调用 mem0.add(text) 时,系统会执行以下操作:

  1. 接收数据: Mem0 主类接收到文本数据。
  2. 图谱抽取: 内部的 GraphMemory 模块会调用一个大型语言模型(LLM)来分析文本,从中抽取出关键的实体(Nodes)和它们之间的关系(Edges)。
  3. 数据持久化: 抽取出的图谱结构(三元组)被传递给专门的数据库接口(例如 MemgraphMemory)。
  4. 写入数据库: 该接口将图谱结构转换成 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 是一个强大的命令。如果一个标签为 Entityname 属性匹配的节点已存在,它会匹配该节点;如果不存在,则会创建它。这确保了我们不会重复创建同一个实体。
    • MERGE (source)-[:RELATION_TYPE]->(target): 同样,这条命令会在 sourcetarget 节点之间创建一条指定类型的关系,前提是这条关系尚不存在。
  • _format_relationship_type 方法确保了关系名称符合 Cypher 的语法要求(例如,将 “is capital of” 转换为 “IS_CAPITAL_OF”)。
  • self.db.execute(query) 将最终的 Cypher 查询发送到数据库执行,从而完成了记忆的存储。
结论

通过这次深入分析,我们清晰地看到了 Mem0 如何巧妙地结合了大型语言模型和图数据库:

  • LLM 负责从非结构化文本中理解和抽取出结构化的知识。
  • 图数据库 (Neo4j/Memgraph) 负责以一种高度关联和可查询的方式持久化这些知识。
  • MERGE 命令是图构建的关键,它以一种幂等的方式创建节点和关系,确保了知识图谱的一致性和无冗余。

这个流程不仅高效,而且极具扩展性,为构建真正拥有长期记忆和推理能力的 AI 系统奠定了坚实的基础。希望这篇教程能帮助你更好地理解 Mem0 的内部工作原理!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值