mem0源码学习03

本文中的 Mem0 源码片段来自 Mem0 官方 GitHub 仓库,并使用了AI工具进行学习,仅用于个人学习与源码阅读记录。源码链接参见:mem0/mem0 at main · mem0ai/mem0 · GitHub

python 复制代码
# 定义一个实例方法,用于处理 metadata_filters(元数据过滤条件)
# self 表示当前类实例
# metadata_filters 的类型是 Dict[str, Any],也就是键为字符串、值可以是任意类型的字典
# 返回值也是一个 Dict[str, Any] 类型的字典
def _process_metadata_filters(
    self,
    metadata_filters: Dict[str, Any]
) -> Dict[str, Any]:

    # 方法的文档字符串,用于说明这个方法的功能
    """
    Process enhanced metadata filters and convert them to vector store compatible format.

    Args:
        metadata_filters: Enhanced metadata filters with operators

    Returns:
        Dict of processed filters compatible with vector store
    """

    # 创建一个空字典,用来保存最终处理完成的过滤条件
    processed_filters = {}

    # 定义内部函数 process_condition
    # key 表示元数据字段名,例如 "age"、"category"
    # condition 表示这个字段对应的过滤条件
    # 返回值是处理完成后的字典
    def process_condition(key: str, condition: Any) -> Dict[str, Any]:

        # 判断 condition 是否不是字典
        # 如果不是字典,说明这是最简单的"等值查询"
        # 例如:{"category": "python"}
        if not isinstance(condition, dict):

            # 如果条件值是星号 "*",则表示通配符
            if condition == "*":

                # 将该字段的过滤条件原样保存为 "*"
                # 具体 "*" 如何解释,要看后面的向量数据库实现
                return {key: "*"}

            # 普通情况下直接返回 key:value
            # 例如 key="category",condition="python"
            # 返回 {"category": "python"}
            return {key: condition}

        # 创建空字典,用来保存当前字段处理后的条件
        result = {}

        # 遍历 condition 字典中的每一个操作符以及对应的值
        # 例如:
        # condition = {"gte": 18, "lte": 30}
        # 第一次 operator="gte",value=18
        # 第二次 operator="lte",value=30
        for operator, value in condition.items():

            # 定义操作符映射表
            # 左侧是平台接收到的操作符
            # 右侧是内部统一使用的操作符
            operator_map = {

                # eq 表示 equal,即"等于"
                "eq": "eq",

                # ne 表示 not equal,即"不等于"
                "ne": "ne",

                # gt 表示 greater than,即"大于"
                "gt": "gt",

                # gte 表示 greater than or equal,即"大于等于"
                "gte": "gte",

                # lt 表示 less than,即"小于"
                "lt": "lt",

                # lte 表示 less than or equal,即"小于等于"
                "lte": "lte",

                # in 表示字段值位于某个集合中
                "in": "in",

                # nin 表示 not in,即字段值不在某个集合中
                "nin": "nin",

                # contains 表示"包含"
                "contains": "contains",

                # icontains 一般表示忽略大小写的"包含"
                "icontains": "icontains"
            }

            # 判断当前传入的操作符是否属于系统支持的操作符
            if operator in operator_map:

                # result.setdefault(key, {}):
                # 如果 result 中不存在 key,就创建一个空字典
                # 如果已经存在 key,就直接取得已有的字典
                #
                # [operator_map[operator]] = value:
                # 将转换后的操作符和值写入字段条件中
                #
                # 例如:
                # key = "age"
                # operator = "gte"
                # value = 18
                #
                # 最终可能得到:
                # {
                #     "age": {
                #         "gte": 18
                #     }
                # }
                result.setdefault(key, {})[operator_map[operator]] = value

            # 如果传入的操作符不在 operator_map 中
            else:

                # 主动抛出 ValueError 异常
                # 告诉调用者当前 metadata filter 使用了不支持的操作符
                raise ValueError(
                    f"Unsupported metadata filter operator: {operator}"
                )

        # 返回当前字段处理完成后的过滤条件
        return result

    # 定义一个内部函数 merge_filters
    # 用于把 source 中的过滤条件合并到 target 中
    # 函数本身不需要返回值,所以返回类型标记为 None
    def merge_filters(
        target: Dict[str, Any],
        source: Dict[str, Any]
    ) -> None:

        # 函数文档字符串
        # 说明对于同一个字段的操作符字典,需要进行深一层的合并
        """Merge source into target, deep-merging nested operator dicts for the same key."""

        # 遍历 source 字典中的所有键和值
        for key, value in source.items():

            # 判断是否满足三个条件:
            # 1. key 已经存在于 target 中
            # 2. target[key] 是字典
            # 3. 当前 source 中的 value 也是字典
            #
            # 如果三个条件同时成立,说明两个过滤条件属于同一个字段,
            # 并且都包含操作符,需要把操作符合并起来
            if (
                key in target
                and isinstance(target[key], dict)
                and isinstance(value, dict)
            ):

                # 使用 update 将新的操作符条件合并进原有字典
                #
                # 例如:
                # target = {"age": {"gte": 18}}
                # value = {"lte": 30}
                #
                # 合并后:
                # target = {
                #     "age": {
                #         "gte": 18,
                #         "lte": 30
                #     }
                # }
                target[key].update(value)

            # 如果不是同一个字段的嵌套字典
            else:

                # 直接把 key:value 写入 target
                target[key] = value

    # 遍历用户传入的所有 metadata_filters
    # key 可能是普通字段,例如 "age"
    # 也可能是逻辑操作符,例如 "AND"、"OR"、"NOT"
    for key, value in metadata_filters.items():

        # 判断当前 key 是否为逻辑 AND
        if key == "AND":

            # AND 后面应该跟一个条件列表
            # 如果 value 不是 list,则说明参数格式错误
            if not isinstance(value, list):

                # 抛出异常,告诉调用者 AND 必须接收一个条件列表
                raise ValueError(
                    "AND operator requires a list of conditions"
                )

            # 遍历 AND 中的每一个条件
            # 例如:
            # [
            #     {"age": {"gte": 18}},
            #     {"age": {"lte": 30}}
            # ]
            for condition in value:

                # 遍历当前条件中的字段名和字段条件
                for sub_key, sub_value in condition.items():

                    # 先调用 process_condition 对当前条件进行标准化处理
                    # 然后再调用 merge_filters 合并到 processed_filters 中
                    merge_filters(
                        processed_filters,
                        process_condition(sub_key, sub_value)
                    )

        # 如果当前 key 是 OR,表示逻辑"或"
        elif key == "OR":

            # 判断 value 是否是列表
            # 同时使用 not value 判断列表是否为空
            if not isinstance(value, list) or not value:

                # OR 必须传入一个非空条件列表,否则抛出异常
                raise ValueError(
                    "OR operator requires a non-empty list of conditions"
                )

            # 在 processed_filters 中创建 "$or"
            # 使用列表保存所有 OR 分支
            #
            # 最终结构类似:
            # {
            #     "$or": [
            #         {...},
            #         {...}
            #     ]
            # }
            processed_filters["$or"] = []

            # 遍历 OR 中的每一个条件分支
            for condition in value:

                # 为当前 OR 分支创建一个空字典
                or_condition = {}

                # 遍历当前 OR 条件中的字段及对应条件
                for sub_key, sub_value in condition.items():

                    # 先标准化当前字段条件
                    # 再将结果合并到当前 OR 分支中
                    merge_filters(
                        or_condition,
                        process_condition(sub_key, sub_value)
                    )

                # 当前 OR 分支处理完成后
                # 将它加入 "$or" 对应的列表中
                processed_filters["$or"].append(or_condition)

        # 如果当前 key 是 NOT,表示逻辑"非"
        elif key == "NOT":

            # NOT 后面同样必须是一个非空列表
            if not isinstance(value, list) or not value:

                # 如果不是列表或者列表为空,则抛出异常
                raise ValueError(
                    "NOT operator requires a non-empty list of conditions"
                )

            # 创建 "$not" 列表
            # 用于存放所有需要取反的条件
            processed_filters["$not"] = []

            # 遍历 NOT 中的每一个条件
            for condition in value:

                # 创建一个空字典
                # 用于保存当前 NOT 条件处理后的结果
                not_condition = {}

                # 遍历当前 NOT 条件中的字段和条件
                for sub_key, sub_value in condition.items():

                    # 对当前字段条件进行标准化
                    # 然后合并进当前 not_condition 中
                    merge_filters(
                        not_condition,
                        process_condition(sub_key, sub_value)
                    )

                # 将当前处理好的 NOT 条件加入 "$not" 列表
                processed_filters["$not"].append(not_condition)

        # 如果 key 既不是 AND,也不是 OR,更不是 NOT
        # 那么就认为它是普通的 metadata 字段
        else:

            # 调用 process_condition 处理普通字段条件
            # 再通过 merge_filters 合并到最终结果中
            merge_filters(
                processed_filters,
                process_condition(key, value)
            )

    # 所有过滤条件处理完成后,返回最终的过滤条件字典
    return processed_filters

可以把 _process_metadata_filters() 理解成一个 过滤条件翻译器 / 格式转换器
整个函数可以理解成下面这条流水线:

metadata_filters

判断是普通字段还是 AND / OR / NOT

process_condition()

处理单个字段的 eq、gte、contains 等操作符

merge_filters()

合并同一个字段的多个条件

processed_filters

返回给 Vector Store
这段代码实际上分成了 3 层职责

_process_metadata_filters()

├── process_condition()

│ └── 负责:处理"一个字段"的过滤条件

├── merge_filters()

│ └── 负责:合并重复字段的过滤条件

└── 主循环

└── 负责:处理 AND / OR / NOT / 普通字段

python 复制代码
# 定义一个私有方法,用来判断 filters 中是否包含"高级操作符"
# self 表示当前类实例
# filters: Dict[str, Any] 表示 filters 应该是一个字典:
#   key 通常是字符串,value 可以是任意类型
# -> bool 表示这个方法最终返回 True 或 False
def _has_advanced_operators(self, filters: Dict[str, Any]) -> bool:

    # 方法的文档字符串,用来说明这个方法的用途
    """
    Check if filters contain advanced operators that need special processing.

    Args:
        filters: Dictionary of filters to check

    Returns:
        bool: True if advanced operators are detected
    """

    # 先判断 filters 是不是字典类型
    # isinstance(对象, 类型) 用于判断某个对象是否属于指定类型
    if not isinstance(filters, dict):

        # 如果 filters 不是字典,就无法按照后面的过滤器规则进行解析
        # 因此直接返回 False,表示没有检测到高级操作符
        return False

    # 遍历 filters 字典中的每一个键值对
    # key 表示字段名或逻辑操作符
    # value 表示这个字段对应的过滤条件
    for key, value in filters.items():

        # 判断当前 key 是否是平台风格的逻辑操作符
        # AND 表示"并且"
        # OR 表示"或者"
        # NOT 表示"非"
        if key in ["AND", "OR", "NOT"]:

            # 只要发现 AND、OR、NOT 中的任意一个
            # 就说明当前 filters 使用了高级逻辑操作符
            return True

        # 判断当前 value 是否是一个字典
        # 例如:
        # {
        #     "age": {
        #         "gt": 18
        #     }
        # }
        #
        # 此时 value 就是 {"gt": 18}
        if isinstance(value, dict):

            # 遍历 value 这个字典中的所有 key
            # 这里的 op 是 operator(操作符)的缩写
            # 比如 gt、eq、in 等
            for op in value.keys():

                # 判断操作符是否属于系统支持的高级比较操作符
                #
                # eq        = equal,等于
                # ne        = not equal,不等于
                # gt        = greater than,大于
                # gte       = greater than or equal,大于等于
                # lt        = less than,小于
                # lte       = less than or equal,小于等于
                # in        = 在指定集合中
                # nin       = not in,不在指定集合中
                # contains  = 包含,通常区分大小写
                # icontains = 忽略大小写的包含判断
                if op in [
                    "eq",
                    "ne",
                    "gt",
                    "gte",
                    "lt",
                    "lte",
                    "in",
                    "nin",
                    "contains",
                    "icontains"
                ]:

                    # 如果检测到上面的任意一个比较操作符
                    # 就说明 filters 中存在高级操作符
                    return True

        # 判断 value 是否等于通配符 "*"
        # "*" 一般可以表示"任意值"或者"匹配全部"
        if value == "*":

            # 出现通配符,也认为当前过滤条件属于高级过滤
            return True

    # 如果整个 filters 都遍历完了
    # 既没有 AND / OR / NOT
    # 也没有高级比较操作符
    # 也没有 "*" 通配符
    # 那么返回 False
    return False

整个函数可以概括成下面这个流程:

收到 filters

是不是 dict?

不是 → False

遍历每一个 key/value

key 是 AND / OR / NOT?

↓ 是

True

↓ 否

value 是 dict?

↓ 是

检查里面有没有 gt / gte / in 等操作符

↓ 有

True

↓ 没有

value 是 "*"?

↓ 是

True

↓ 否

继续遍历

全部检查完都没有

False

相关推荐
zzj_2626101 小时前
Python数组定义及操作
python
溪语流沙1 小时前
【Python项目实战】部署上线:把博客发布到云服务器
服务器·开发语言·python
智码看视界1 小时前
神经网络基础——从感知机到多层网络的数学原理
python·深度学习·神经网络·感知机·激活函数·前向传播·权重初始化
oceanstonetree1 小时前
zgy地震数据体的显示
python·zgy
“AI国潮设计-小江”2 小时前
【Python/SDXL实战】潮汕国潮IP视觉落地:普宁美食猫IP & 创意甜品设计(附ComfyUI工作流与商用授权思路)
开发语言·人工智能·python·prompt·aigc
L@ncor2 小时前
第一章 初识智能体 · 学习笔记
人工智能·python
宁渡AI大模型2 小时前
河南宁渡科技有限公司|宁渡课堂 AI 全栈面试分享,大模型 API 开发与流式输出面试题
人工智能·python·ai·大模型
Java后端的Ai之路2 小时前
Git冲突完整排查与实战:本地修改覆盖报错到成功推送全流程复盘
开发语言·人工智能·git·python·pop