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

背景介绍

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

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

向量数据库执行语义检索相对方便,但是包含语义内容的文本管理比较困难,特别是涉及到相关内容的更新和替换。在这种背景下,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"] = metadatafunction_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.mzph.cn/diannao/53304.shtml

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

相关文章

electron 客户端 windows linux(麒麟V10)多系统离线打包 最新版 <一>

electron客户端下载、构建、打包在国内网络情况下,绝对不是什么易事。更不要说离线干活,更是难上加难。 这一篇主要讲下windows离线环境下,如何完成electron的下载打包。咱废话不多说,直接上干货。注意,我的大前提是完…

软考真题之软件设计师的程序语言设计题型(上午题)

目录 编程程序和解释程序 相关习题 函数 编译,解释和翻译阶段 符号表 ​编辑 相关习题 ​编辑 词法分析 语法分析 语义分析 目标代码生成 相关习题 中间代码生成 正规式 相关习题 有限自动机 相关习题 上下文无关文法 相关习题 比较偏的真题 编程程序和解…

艾体宝洞察丨透过语义缓存,实现更快、更智能的LLM应用程序

传统的缓存只存储数据而不考虑上下文,语义缓存则不同,它能理解用户查询背后的含义。它使数据访问更快,系统响应更智能,对 GenAI 应用程序至关重要。 什么是语义缓存? 语义缓存解释并存储用户查询的语义,使…

【计算机网络】描述TCP建立连接与断开的过程

一、TCP连接的建立与断开 1、建立连接——三次握手 1、A的TCP向B发出连接请求报文段 其首部中的同步位SYN 1,并选择序号seq x,表明传送数据时的第一个数据字节的序号是 x 2、B的TCP收到连接请求报文段后,如同意,则发回确认。 B …

JavaScript( 简介)

目录 含义 实例 js代码位置 1 外部引入js文件 2 在 HTML 中,JavaScript 代码必须位于 标签之间。 小结 含义 js是一门脚本语言,能够改变HTML内容 实例 getElementById() 是多个 JavaScript HTML 方法之一。 本例使用该方法来“查找” id"d…

Android Launcher3

一、定义与功能 Android Launcher是Android操作系统中的一个重要组件,它负责管理和呈现用户界面,包括桌面、应用程序抽屉和部件。Launcher不仅为用户提供了一个启动应用程序的入口,还允许用户自定义手机的主屏幕、图标、小部件布局以及一些基…

【2024国赛B题】高教杯全国大学生数学建模国赛建模过程+完整代码论文全解全析

你是否在寻找数学建模比赛的突破点?数学建模进阶思路! 作为经验丰富的数学建模团队,我们将为你带来2024国赛数学建模竞赛(B题)的全面解析。这个解决方案包不仅包括完整的代码实现,还有详尽的建模过程和解析…

ARM32开发——DMA内存到内存

🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 需求数据交互流程开发流程依赖引入DMA初始DMA传输请求完整代码 关心的内容DMA初始化DMA初始化DMA数据传输请求完整代码 DMA中断开启…

.NET 8月份红队武器库和资源集合

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

MacBook真的不能打游戏吗?Mac打游戏会损坏电脑吗?苹果电脑怎么玩游戏

MacBook从来都是高端的代名词,超强的性能搭配顶尖的系统,不光处理大型文件时举重若轻,长期使用也不会有明显卡顿。但很多人在需要MacBook一流的生产力同时,也希望能在空闲时体验游戏的乐趣。在大多人的印象里,Mac电脑对…

【MIT 6.5840/6.824】In Search of an Understandable Consensus Algorithm 学习笔记

In Search of an Understandable Consensus Algorithm 1 Introduction2 Replicated state machines3 What’s wrong with Paxos?4 Designing for understandability5 The Raft consensus algorithm5.1 Raft basics5.2 Leader election5.3 Log replication5.4 Safety5.4.1 Elec…

服务器数据恢复—Raid磁盘阵列故障类型和常见故障原因

出于尽可能避免数据灾难的设计初衷,RAID解决了3个问题:容量问题、IO性能问题、存储安全(冗余)问题。从数据恢复的角度讨论RAID的存储安全问题。 常见的起到存储安全作用的RAID方案有RAID1、RAID5及其变形。基本设计思路是相似的:当部分数据异…

PyTorch 创建数据集

图片数据和标签数据准备 1.本文所用图片数据在同级文件夹中 ,文件路径为train/’ 2.标签数据在同级文件,文件路径为train.csv 3。将标签数据提取 train_csvpd.read_csv(train.csv)创建继承类 第一步,首先创建数据类对象 此时可以想象为单个数据单元的…

信创实践(3):基于x2openEuler将CentOS升级成openEuler,享受其带来的创新和安全特性

引言: 在当前的 IT 行业中,创新和安全性是两大关键趋势。随着 CentOS 停止维护,许多用户正在寻找替代方案,以保持其系统的更新和安全。openEuler 作为一个强大的开源操作系统,成为了理想的迁移目标。本教程将指导您如…

LiveQing视频点播流媒体RTMP推流服务功能-支持大疆等无人机RTMP推流支持OBS推流一步一步搭建RTMP视频流媒体服务示例

LiveQing支持大疆等无人机RTMP推流支持OBS推流一步一步搭建RTMP视频流媒体服务示例 1、流媒体服务搭建2、推流工具准备3、创建鉴权直播间4、获取推流地址5、配置OBS推流6、推流及播放7、获取播放地址7.1 页面查看视频源地址7.2 接口查询 8、相关问题8.1、大疆无人机推流花屏 9、…

感知机模型

一、概述 感知机模型(Perceptron Model)也叫做神经元模型,设计灵感即来自于生物神经元的运行机制,依次完成信息接收、处理、输出的过程。当前大放异彩的各种人工神经网络模型即由一个个人工神经元构成,因此,本文介绍的感知机模型&…

详解 MQ 消息队列

谈起消息队列,内心还是会有些波澜。 消息队列,缓存,分库分表是高并发解决方案三剑客,而消息队列是我最喜欢,也是思考最多的技术。 我想按照下面的四个阶段分享我与消息队列的故事,同时也是对我技术成长经…

0成本实现.NET Web API 8.0项目内网映射

1.背景 最近在学习CICD,里面会有用到内网映射的使用场景。为了加深对内网映射实操的记忆。我实操了下基于.Net 8.0的内网映射,并支持互联网访问。本文主要介绍了在win11下安装路由侠,并将.net 8.0发布到win11,项目运行、路由侠配…

【学习笔记】5G-A时代物联网应用及策略研究

摘要 海量物联网通信是5G典型应用场景之一,为了实现蜂窝网的全场景物联能力,需要更多的场景化技术,5G-A引入了RedCap(5G Reduced Capability)和Passive IoT。其中,RedCap降低了设备复杂性及成本&#xff0…

weblogic漏洞——CVE-2020-14882

一、基本信息 靶机:IP:192.168.100.40 二、攻击过程 进入 vulhub 靶场相关目录,并启动环境 cd master/weblogic/CVE-2020-14882 docker-compose up -d 绕过登录验证 http://192.168.100.40:7001/console/css/%252e%252e%252fconsole.por…