给大模型加上“记忆”,深入探索 Mem0 项目

news2024/11/24 19:06:30

背景介绍

在之前的软件应用中,我们总会在应用中保留大量的用户历史操作记录,方便用户下次使用时可以快速查看和复用,甚至基于这些用户记录可以为用户提供个性化的服务。而这些记录往往都保存在传统的结构化或非结构化数据库中。

在大模型的应用,特别是助手类的大模型应用中,我们往往需要处理大量语义化的文本或多模态的信息,方便后续快速匹配,从而提供个性化的服务。为了支持这种语义检索的需求,往往会将数据保存至向量数据库中。

向量数据库执行语义检索相对方便,但是包含语义内容的文本管理比较困难,特别是涉及到相关内容的更新和替换。在这种背景下,Mem0 应运而生。Mem0 可以给大模型应用提供智能记忆层,可以记住用户偏好,适应个人需求,并随着时间的推移不断改进,从而方便应用提供更加个性化的体验和服务。
请添加图片描述

从开源依赖,Github 上的 star 数量一路飙升,截止目前已经 20.6k 的 star 了。

上手体验

Mem0 提供了两种接入方案,一种是使用官方的托管平台,另一种是使用官方提供的 python 依赖包 mem0ai执行。下面以本地依赖包形式了解下 Mem0 的使用。

依赖配置

首先需要通过 pip 安装所需的依赖包:

pip install mem0ai

Mem0 需要依赖大模型才能执行,默认使用的 gpt-4o, 因此需要配置对应的 ChatGPT API 秘钥,需要将秘钥配置至环境变量中:

import os
os.environ["OPENAI_API_KEY"] = "sk-xxx"
动手实践

Mem0 实际使用在大模型应用中,我们主要关注下面的能力:

  1. 针对特定用户添加非结构化的文本信息;
  2. 特定用户根据查询问题进行文本检索;

针对上面的关注的能力,使用 Mem0 可以简单满足需求:

from mem0 import Memory

# 初始化记忆对象

m = Memory()

# 1. 针对特定用户添加非结构化的文本信息

result = m.add("I am working on improving my tennis skills. Suggest some online courses.", user_id="alice", metadata={"category": "hobbies"})

# 2. 特定用户根据查询问题进行文本检索

related_memories = m.search(query="What are Alice's hobbies?", user_id="alice")

可以看到常规的功能使用比较简单,看起来几行代码就可以快速为大模型应用加上“记忆”。

实现方案

官方方案概述

首先可以从官方描述中大致了解 Mem0 的实现方案,从官方文档了解到下面的信息:

Mem0 利用混合数据库方法来管理和检索人工智能代理和助手的长期记忆。每个内存都与一个唯一的标识符相关联,例如用户 ID 或代理 ID,允许 Mem0 组织和访问特定于个人或上下文的内存。

当使用 add() 方法将消息添加到 Mem0 时,系统会提取相关事实和偏好并将其存储在数据存储中:矢量数据库、键值数据库和图形数据库。这种混合方法可确保以最有效的方式存储不同类型的信息,从而使后续搜索快速有效。

当人工智能代理或LLM需要回忆记忆时,它会使用 search() 方法。然后 Mem0 在这些数据存储中执行搜索,从每个源检索相关信息。然后,该信息通过评分层,根据相关性、重要性和新近度评估其重要性。这确保了只显示最个性化和最有用的上下文。

可以大致了解到 Mem0 是基于混合数据库来管理,同时支持向量、键值和图形数据库,但是具体的实现细节依旧不清楚,这种情况下 Talk is cheap, show me the code.,下面我们通过源码来了解 Mem0 的实现方案。

方案深入解析

文本检索

Mem0 中的文本检索是通过调用 Memory.search() 方法实现,文本检索的主要流程如下所示:

  1. 检索文本向量化,构造过滤条件,方便基于用户 id 等进行过滤;
  2. 基于向量数据库进行检索和过滤,默认使用的向量数据库为 Qdrant;
  3. 可选基于知识图谱的检索(从 v1.1 开始支持);

从上面的流程来看,主流程就是简单的向量检索,同时利用向量数据库提供的元信息过滤的能力进行用户区分的封装,实现如下所示:

filters = filters or {}
if user_id:
    filters["user_id"] = user_id
if agent_id:
    filters["agent_id"] = agent_id
if run_id:
    filters["run_id"] = run_id

# 查询向量化

embeddings = self.embedding_model.embed(query)

# 向量检索

memories = self.vector_store.search(
    query=embeddings, limit=limit, filters=filters
)

# 可选的知识图谱检索

if self.version == "v1.1":
    if self.enable_graph:
        graph_entities = self.graph.search(query)

文本添加

Mem0 中非结构化文本添加是通过调用 Memory.add() 方法实现,文本添加的主要流程如下所示:

  1. 将添加的文本进行向量化,从向量数据库中检索相关内容;
  2. 将检索的相关内容与新增的文本提供给大模型,大模型判断所需采取的动作列表,可选动作包含 新增文本, 更新原有的文本, 删除原有文本
  3. 根据大模型给出的动作列表依次执行;

可以看到上面的流程中主要基于大模型提供的能力处理历史信息的更新问题,对应的实现如下所示:

# 从向量库中检索已有的文本内容

embeddings = self.embedding_model.embed(data)
existing_memories = self.vector_store.search(
    query=embeddings,
    limit=5,
    filters=filters,
)

# 转换检索到的文本内容,构造成特定格式,方便调用大模型

existing_memories = [
    MemoryItem(
        id=mem.id,
        score=mem.score,
        metadata=mem.payload,
        memory=mem.payload["data"],
    )
    for mem in existing_memories
]
serialized_existing_memories = [
    item.model_dump(include={"id", "memory", "score"})
    for item in existing_memories
]
messages = get_update_memory_messages(
    serialized_existing_memories, extracted_memories
)

# 调用大模型,确定需要采取的动作列表

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:
    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"]

        if function_name in ["add_memory", "update_memory"]:
            function_args["metadata"] = metadata

        function_result = function_to_call(**function_args)
        response.append(
            {
                "id": function_result,
                "event": function_name.replace("_memory", ""),
                "data": function_args.get("data"),
            }
        )

可以看到此流程主要依赖大模型的判断能力,利用常规向量库的 CRUD 作为基础操作,实现一个有语义的文本向量更新。

为什么需要做这么复杂,而不是直接插入向量数据库呢?可以参考官网提供的一个简单的例子:

用户 Alice 先插入了两条记录 I like going to hikesI love to play badminton,此时的关系如下所示:

请添加图片描述

当 Alice 隔一段时间不再喜欢羽毛球,新增了 I do not like badminton any more, 此时预期的关系应该更新为如下所示:

请添加图片描述

此时更符合语义的新增方案是更新原有的文本,如果直接插入,则 Alice 可能会关联到两条互相冲突的记录,最终导致可能出现判断问题。

总结

本文是对 Mem0 的实现方案的探索,可以看到 Mem0 提供了一套比较友好的记忆管理接口,可以方便地检索和更新相关的信息,比较适配于大模型应用的场景。

从实现原理上来看,Mem0 主要基于向量数据库实现了相关内容的检索,在文本的更新上,基于大模型进行历史信息的判断,通过组合向量数据库的基础 CRUD 能力,实现了一个有语义的文本更新能力流程。

当然新版本的 Mem0 开始引入了知识图谱进行文本的管理,有兴趣也可以详细了解下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高校为什么需要AIGC大数据实验室?

AIGC大数据实验室是一个专注于人工智能生成内容(AIGC)和大数据相关技术研究、开发与应用的创新实验平台。 AIGC主要研究方向包括:AIGC技术创新、大数据处理与分析、AIGC 与大数据融合应用。 AIGC 技术创新:探索如何利用人工…

企业微信hook协议接口,聚合群聊客户管理工具开发

服务提供了丰富的API和SDK,可以在企微的功能之上进行应用开发和功能扩展 自建应用可以调用企微hook或协议提供的接口来实现数据交互,可以直接调用hook或协议接口提供的功能来进行消息的发送与接收、用户管理、应用管理等操作,通过接口可以实…

线性代数教材书籍推荐

INTRODUCTION TO LINEAR ALGEBRA, 线性代数导论,GILBERT STRANG ,有第六版中译本,网上也有第五版英文电子版,个人认为讲理论最好的教材 Practical Linear Algebra for Data Science,From Core Concepts to Applicatio…

相机常见名词详解

本文主要参考超人视觉课程做的笔记,有讲解不太懂的,又做了详细的解释 1、物距:物体到镜片的距离; 2、像距:像到镜片的距离; 3、焦距:镜片到焦点的距离; (1)二倍焦距以外&#xff…

LLM指令微调实践与分析

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

毛辊清洗机的优势:

毛辊清洗机作为一种高效的清洗设备,在食品加工、农产品处理等多个领域得到了广泛应用。其主要优点可以归纳如下: 一、清洗效率高 有效容积大:毛辊清洗机设计有足够大的清洗空间,能够一次性处理大量的物料,如土豆、胡…

智能视频监控平台LntonAIServer安防监控视频平台视频质量诊断功能使用说明

LntonAIServer视频质量诊断功能是一种先进的技术,旨在通过智能分析来评估和优化视频流的质量。这种功能通常集成在视频传输和管理平台中,以提供对视频内容的实时监控和质量控制。以下是关于LntonAIServer视频质量诊断功能的使用说明阐述: 首…

【C++从练气到飞升】19---哈希:哈希冲突 | 哈希函数 | 闭散列 | 开散列

🎈个人主页:库库的里昂 ✨收录专栏:C从练气到飞升 🎉鸟欲高飞先振翅,人求上进先读书🎉 目录 ⛳️推荐 一、unordered 系列关联式容器 二、unordered_map 1.1 unordered_map 介绍 1.2 unordered_map 的…

COD论文笔记 BiRefNet

本质还是一个 U 型编码器解码器结构的分割模型。 我可以考虑将©和(d)结合,即对解码器的输入不进行 patchify,同时在各个阶段引入梯度参考信息 最近的相关工作,中间监督、额外先验(频率,梯度,边缘等)取得不错效果 作者观察到…

Elasticsearch简单介绍

1、 Elasticsearch简介 Elasticsearch 是一个分布式的、基于 RESTful API 的搜索和分析引擎,广泛用于大规模的数据存储和快速检索。它最初由 Shay Banon 于 2010 年开发,是开源的,并且是 Elastic Stack(通常称为 ELK Stack&#…

ERP系统与WMS仓储管理系统在库存管理中的不同作用

在当今复杂多变的企业环境中,大型企业对于信息系统的依赖日益加深,特别是在库存管理与供应链优化方面。企业资源规划ERP系统与WMS仓储管理系统作为两大核心系统,各自扮演着不可或缺的角色,并通过紧密协作,共同推动企业…

MuseTalk模型构建指南

一、介绍 MuseTalk 是由腾讯团队开发的先进技术,它是一个实时的音频驱动唇部同步模型。该模型能够根据输入的音频信号,自动调整数字人物的面部图像,使其唇形与音频内容高度同步。 二、特点 多语言支持:该模型支持多种语言&…

为何我建议你学会Queue集合

先赞后看,南哥助你Java进阶一大半 PriorityQueue的底层数据结构就如andrewlock.net网站提供的图一样,虽然PriorityQueue是一个平衡二叉堆,但JDK底层的实现却是:一个普普通通的二维数组!! 我是南哥&#xff…

计算机网络 数据链路层2

ALOHA:想发就发 CSMA 载波监听多路访问协议 CS:载波监听,在发送数据之前检测总线上是否有其他计算机在发送数据 1-坚持CSMA:主机想发送消息,需要监听信道; 信道空闲则直接传输信息; 信道忙碌则一直监听,直…

半路出家程序员感受:非科班出身如何转行程序员? 答案在这

🤟 基于入门网络安全打造的:👉黑客&网络安全入门&进阶学习资源包 非科班出身是指那些大学专业为非计算机相关专业的人群,多数人对于计算机基础了解比较少,甚至零基础。这部分人群中有相当多一部分处于对于编程…

dinput8.dll错误应该如何修复呢?五种快速修复dinput8.dll错误的问题

dinput8.dll文件是DirectInput库的一部分,主要负责处理游戏控制器的输入,如键盘、鼠标和游戏手柄等。这个文件通常位于Windows系统的System32文件夹中,是许多游戏和应用程序正常运行所必需的组件。它通过提供一个统一的接口来管理不同类型的输…

软媒市场-为企业提供了高效便捷的软文发布渠道和提升品牌曝光度

软媒市场是软文媒体自助发布平台,作为数字营销领域的一股重要力量,正日益受到企业与个人的青睐。这些平台通过整合海量媒体资源,提供从内容创作到多渠道发布的一站式解决方案,极大地提升了品牌曝光度和市场影响力。 一、平台优势 ‌资源丰富‌:软媒市场汇聚了包括门户网站、行业…

打造主播美颜工具:视频美颜SDK与直播美颜API的集成与优化详解

本篇文章,小编将深入讲解视频美颜SDK与直播美颜API的集成与优化策略,帮助开发者构建出色的主播美颜工具。 一、视频美颜SDK与直播美颜API的核心功能 直播美颜API则提供了实时美颜处理的能力,确保美颜效果在直播过程中流畅呈现,不…

【蔡英丽医生】颈动脉斑块:隐形杀手?揭秘症状与治疗新策略!

在繁忙的生活节奏中,你是否曾关注过隐藏在身体深处的健康隐患——颈动脉斑块?这个看似不起眼的“小东西”,实则可能成为引发中风、记忆力衰退等严重疾病的幕后黑手。今天,就让我们一起揭开颈动脉斑块的神秘面纱,了解它…

c++--智能指针(RAII)

智能指针可以帮助我们管理动态空间,即自动释放动态空间。 --------------------------------------------------------------------------------------------------------------------------------- 简单原理 事实上,智能指针的原理就是将指向动态空间…