Chainlit集成Mem0使用一个拥有个性化AI记忆的网页聊天应用

news2024/9/21 12:30:12

前言

Mem0 简介,可以看我上一篇文章《解决LLM的永久记忆的解决方案-Mem0实现个性化AI永久记忆功能》。本篇文章是对Mem0 实战使用的一个示例。通过Chainlit 快速实现ui界面和open ai的接入,通过使用Mem0 实现对聊天者的对话记录的记忆。

设计实现基本原理

  • 用户每次发送消息时,会通过ai的调用,总结用户发送的消息,并将总结后的消息进行向量化的存储
  • 在总结消息后到向量化存储的过程中,会先用总结后的消息,进行向量化检索,检查出最相关的前5条数据,并把用户最新总结的消息和从数据库查询出来最相关的5条数据交给ai,让AI根据相关度来判断是否选择调用 数据库增加方法、修改方法或者删除方法。
  • 用户记忆搜索的逻辑,就是从更具用户ID和用户问题,从向量数据库查询相关的记录(可以设置具体返回几条最相关)以提示词的方式交给 AI,作为AI的记忆。
  • Mem0 还支持neo4j图数据库,通过提取三元组的数据,和对三元组数据的查询作为AI的记忆,提示AI记忆检索的广度。

快速上手

创建一个文件,例如“chainlit_chat”

mkdir chainlit_chat

进入 chainlit_chat文件夹下,执行命令创建python 虚拟环境空间(需要提前安装好python sdkChainlit 需要python>=3.8。,具体操作,由于文章长度问题就不在叙述,自行百度),命令如下:

python -m venv .venv
  • 这一步是避免python第三方库冲突,省事版可以跳过
  • .venv是创建的虚拟空间文件夹可以自定义

接下来激活你创建虚拟空间,命令如下:

#linux or mac
source .venv/bin/activate
#windows
.venv\Scripts\activate

在项目根目录下创建requirements.txt,内容如下:

chainlit
mem0

在项目根目录下创建.env环境变量,配置如下:

OPENAI_BASE_URL="open地址或者代理地址"
OPENAI_API_KEY="your-api-key"
  • OPENAI_BASE_URL 是 openai的api 的base地址,如果你能直接使用openai,这可以不设置,也可以设置成openai兼容的代理地址,注意代理服务要能兼容openai向量化接口
  • OPENAI_API_KEY 替换成你自己的API 密匙

执行以下命令安装依赖:

pip install -r .\requirements.txt
  • 安装后,项目根目录下会多出.chainlit.files文件夹和chainlit.md文件

在项目根目录下创建app.py文件,代码如下:

import chainlit as cl

from personalai_tutor import PersonalAITutor


@cl.on_chat_start
async def on_chat_start():
    ai_tutor = PersonalAITutor()
    cl.user_session.set("ai_tutor", ai_tutor)


@cl.on_message
async def on_message(message: cl.Message):
    ai_tutor = cl.user_session.get("ai_tutor")
    msg = cl.Message(content="")
    response = await ai_tutor.ask(question=message.content, user_id="tarzan")
    async for part in response:
        if token := part.choices[0].delta.content or "":
            await msg.stream_token(token)
    await msg.update()

在项目根目录下创建personalai_tutor.py文件,代码如下:

import threading
from typing import Optional, List

from mem0 import Memory
from mem0.configs.prompts import MEMORY_ANSWER_PROMPT
from openai import AsyncOpenAI


MEMORY_DEDUCTION_PROMPT = """
从所提供的文本中推断出事实、偏好和记忆。
只需将事实、偏好和记忆以要点形式返回即可:
自然语言文本:{user_input}
User/Agent详细信息:{metadata}

推断:推断事实、偏好和记忆的限制:
-事实、偏好和记忆应该简明扼要。
-不要以“这个人喜欢披萨”开头。相反,从“喜欢披萨”开始。
-不记得提供的用户/代理详细信息。只记住事实、偏好和回忆。

推断出的事实、偏好和记忆:
"""


def _format_query_with_memories(messages, relevant_memories):
    memories_text = "\n".join(memory["memory"] for memory in relevant_memories)
    return f"- Relevant Memories/Facts: {memories_text}\n\n- User Question: {messages[-1]['content']}"


def _prepare_messages(messages: List[dict]) -> List[dict]:
    if not messages or messages[0]["role"] != "system":
        return [{"role": "system", "content": MEMORY_ANSWER_PROMPT}] + messages
    messages[0]["content"] = MEMORY_ANSWER_PROMPT
    return messages


class PersonalAITutor:
    def __init__(self):
        """
        Initialize the PersonalAITutor with memory configuration and OpenAI client.
        """
        config = {
            'llm': {
                'config': {
                    'openai_base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
                    'api_key': 'api_key',
                    'model': 'qwen-plus'
                }
            },
            'vector_store': {
                'provider': 'chroma',
                'config': {
                    'path': 'chroma',
                }
            },
            'embedder': {
                'config': {
                    'openai_base_url': 'https://open.bigmodel.cn/api/paas/v4',
                    'api_key': 'api_key',
                    'model': 'embedding-3'
                }
            }
        }
        self.memory = Memory.from_config(config)
        self.client = AsyncOpenAI(base_url='https://dashscope.aliyuncs.com/compatible-mode/v1',
                                  api_key='api_key')

    def ask(self, question: Optional[str] = None, agent_id: Optional[str] = None, run_id: Optional[str] = None,
            metadata: Optional[dict] = None,
            filters: Optional[dict] = None,
            user_id=None, limit: Optional[int] = 10):
        """
        Ask a question to the AI and store the relevant facts in memory

        :param run_id:
        :param limit:
        :param filters:
        :param metadata:
        :param agent_id:
        :param question: The question to ask the AI.
        :param user_id: Optional user ID to associate with the memory.
        """
        if not any([user_id]):
            raise ValueError("One of user_id must be provided")

        messages = [
            {"role": "system", "content": "你是一个私人的AI助理。"},
            {"role": "user", "content": question}
        ]
        prepared_messages = _prepare_messages(messages)

        if prepared_messages[-1]["role"] == "user":
            self._async_add_to_memory(
                question, user_id, agent_id, run_id, metadata, filters
            )
            relevant_memories = self._fetch_relevant_memories(
                question, user_id, agent_id, run_id, filters, limit
            )
            print('relevant_memories', relevant_memories)
            prepared_messages[-1]["content"] = _format_query_with_memories(
                messages, relevant_memories
            )
            print('prepared_messages', prepared_messages)
        stream = self.client.chat.completions.create(
            model="qwen-plus",
            stream=True,
            messages=prepared_messages
        )
        return stream

    def get_user_memories(self, user_id=None):
        """
        Retrieve all memories associated with the given user ID.

        :param user_id: Optional user ID to filter memories.
        :return: List of memories.
        """
        return self.memory.get_all(user_id=user_id)

    def get_knowledge(self, app_id=None):
        """
        Retrieve all memories associated with the given user ID.

        :param app_id: Optional user ID to filter memories.
        :return: List of memories.
        """
        return self.memory.get_all(app_id=app_id)

    def _async_add_to_memory(
            self, question, user_id, agent_id, run_id, metadata, filters
    ):
        # 如果用户发送的消息以英文为主,可以不传prompt,会使用自带的英文提示词
        prompt = MEMORY_DEDUCTION_PROMPT.format(user_input=question, metadata=metadata)

        def add_task():
            self.memory.add(
                data=question,
                user_id=user_id,
                agent_id=agent_id,
                run_id=run_id,
                metadata=metadata,
                filters=filters,
                prompt=prompt
            )

        threading.Thread(target=add_task, daemon=True).start()

    def _fetch_relevant_memories(
            self, question, user_id, agent_id, run_id, filters, limit
    ):
        # TODO: 过总结过去的谈话来做得更好
        return self.memory.search(
            query=question,
            user_id=user_id,
            agent_id=agent_id,
            run_id=run_id,
            filters=filters,
            limit=limit,
        )

  • 代码实现使用时国内通义千文和智谱清言的兼容open ai的接口
  • llm的接口是用的通义千文、向量化接口是用的智谱清言的接口
  • 因为通义千文没有提供和openai向量化处理兼容的接口,原本想只用智谱清言接口实现的,当时实践发现,智谱清言在tool函数识别选择调用的准确率太低,于是换成了阿里的qwen-plus,几乎准确率100%。
  • Mem0默认先从env环境变量里取base_urlapi_key的值,再从 config配置 的openai_base_urlapi_key的值。所以使用config文件设置openai_base_urlapi_key时,不要在设置环境变量了。

运行应用程序

要启动 Chainlit 应用程序,请打开终端并导航到包含的目录app.py。然后运行以下命令:

 chainlit run app.py -w   
  • -w标志告知 Chainlit 启用自动重新加载,因此您无需在每次更改应用程序时重新启动服务器。您的聊天机器人 UI 现在应该可以通过http://localhost:8000访问。
  • 自定义端口可以追加--port 80

启动后界面如下:

在这里插入图片描述

  • 里面关于我的记录,都是上周我和ai对话记录的信息,默认我设置只返回,和我提问最相关的十条记录。

相关文章推荐

《Chainlit快速实现AI对话应用的界面定制化教程》
《Chainlit接入FastGpt接口快速实现自定义用户聊天界面》
《使用 Xinference 部署本地模型》
《Fastgpt接入Whisper本地模型实现语音输入》
《Fastgpt部署和接入使用重排模型bge-reranker》
《Fastgpt部署接入 M3E和chatglm2-m3e文本向量模型》
《Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)》
《vllm推理服务兼容openai服务API》
《vLLM模型推理引擎参数大全》
《解决vllm推理框架内在开启多显卡时报错问题》
《Ollama 在本地快速部署大型语言模型,可进行定制并创建属于您自己的模型》

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

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

相关文章

网络空间信息安全实验

实验1 基础实验(加密与隐藏) 一、实验目的 提高对加密与解密原理的认识;提高对信息隐藏原理的认识;学会使用加密与隐藏软件。 二、实验环境 Pentiuum III、600 MHz以上CPU , 128M 以上内存,10G 以上硬盘&#xff0…

Hoverfly api/v2/simulation 任意文件读取漏洞复现(CVE-2024-45388)

0x01 产品简介 Hoverfly是一个为开发人员和测试人员提供的轻量级服务虚拟化/API模拟/API模拟工具。 0x02 漏洞概述 Hoverfly api/v2/simulation 接口存在任意文件读取漏洞,未经身份验证攻击者可通过该漏洞读取系统重要文件(如数据库配置文件、系统配置文件)、数据库配置文…

CSS学习13--学成网例子

CSS例子 学成网 需要使用的图片&#xff1a; 代码&#xff1a; <html><head><style>/*CSS初始化*/* { /*清除内外边框*/padding: 0;margin: 0;}ul {list-style: none; /*清除列表样式*/}.clearfix:before,.clearfix:after { /*清除浮动*/content: &qu…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 9月9日,星期一

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年9月9日 星期一 农历八月初七 1、 三部门&#xff1a;拟允许在北京、天津、上海、广州、深圳、南京等地设立外商独资医院。 2、 巴黎残奥会结束&#xff1a;中国体育代表团获得94金76银50铜&#xff0c;连续六届残奥会位列…

C语言第二周课

目录 引言: 一、数据类型大小及分类 (1)计算机中常用存储单位 (2)整体介绍一下C语言的数据类型分类。 (3)下面是我们本节课要学的基本内容----常用的数据类型 二、 数据类型的数值范围 三、打印输出类型 数据类型打印示例: 引言: 我们常常在写C语言程序时&#xff0c;总…

用AI操作电脑:使用PyQt和Python结合AI生成代码的实用指南

​ 在github上有这样一个名字叫做open-interpreter的项目&#xff0c;收获了52k个Star。该项目通过自然语言来控制电脑&#xff0c;极大简化了使用电脑的难度&#xff0c;提高了工作效率。受该项目启发&#xff0c;我们可以做一个中文版桌面AI助手。 分步思考&#xff1a; 自…

算法工程师转行大模型:时代的选择or个人的选择

算法工程师的黄金时代&#xff1a;大模型转行之路 随着人工智能技术的飞速发展&#xff0c;尤其是深度学习领域的大规模预训练模型&#xff08;大模型&#xff09;的兴起&#xff0c;算法工程师们正面临前所未有的机遇与挑战。本文旨在探讨算法工程师如何顺利过渡到大模型领域…

基于Matlab的图像去雾系统(四种方法)关于图像去雾的基本算法代码的集合,方法包括局部直方图均衡法、全部直方图均衡法、暗通道先验法、Retinex增强。

基于Matlab的图像去雾系统&#xff08;四种方法&#xff09; 关于图像去雾的基本算法代码的集合&#xff0c;方法包括局部直方图均衡法、全部直方图均衡法、暗通道先验法、Retinex增强。 所有代码整合到App designer编写的GUI界面中&#xff0c;包括导入图片&#xff0c;保存处…

吴恩达发布企业AI转型手册,AI Transformation Playbook,对公司高管、正在创业的AIer是不错的指南。

AI 时代&#xff0c;人人都在创业。 今天看到一篇吴恩达发布的 AI 转型指南&#xff0c;原文《 AI Transformation Playbook——How to lead your company into the AI era》。 对公司高管、正在创业的 AIer 是个不错的指南。 这本人工智能转型手册借鉴了领导谷歌大脑团队和百…

浅谈OLTP 与 OLAP 数据建模的差异

OLTP 与 OLAP&#xff1a;常见工作流 联机分析处理 (OLAP) 和联机事务处理 (OLTP) 是两种主要的数据处理系统。两者之间存在多种差异。 OLTP 系统旨在处理来自多个用户的多个事务&#xff0c;它们通常用于许多应用程序的后端。例如&#xff0c;在线商务网站将使用 OLTP 系统来…

chapter13-常用类——(Arrays)——day16

目录 481-Arrays排序源码解读 483-Arrays模拟排序 484-Arrays其他方法 485-Arrays课堂练习 481-Arrays排序源码解读 接口编程-compare 483-Arrays模拟排序 484-Arrays其他方法 二分搜索查找要求有序&#xff0c;如果数组中不存在这个元素&#xff0c;返回-&#xff08;low1&…

Sentence-BERT实现文本匹配【CoSENT损失】

引言 还是基于Sentence-BERT架构&#xff0c;或者说Bi-Encoder架构&#xff0c;但是本文使用的是苏神提出的CoSENT损失函数1。 点击来都是缘分&#xff0c;之前过时的方法可以不细看&#xff0c;别的文章可以不收藏&#xff0c;现在是最流行的方法&#xff0c;这篇文章建议收藏…

ASIO网络调试助手之一:简介

多年前&#xff0c;写过几篇《Boost.Asio C网络编程》的学习文章&#xff0c;一直没机会实践。最近项目中用到了Asio&#xff0c;于是抽空写了个网络调试助手。 开发环境&#xff1a; Win10 Qt5.12.6 Asio(standalone) spdlog 支持协议&#xff1a; UDP TCP Client TCP Ser…

利用高德+ArcGIS优雅获取任何感兴趣的矢量边界

荷花十里&#xff0c;清风鉴水&#xff0c;明月天衣。 四时之景不同&#xff0c;乐亦无穷尽也。今天呢&#xff0c;梧桐君给大家讲解一下&#xff0c;如何利用高德地图&#xff0c;随机所欲的获取shp边界数据。 文章主要分成以下几个步骤&#xff1a; 首先搜索你想获取的矢量…

深度学习中常见的损失函数

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 深度学习中常见的损失函数 损失函数的作用 损失函数是衡量神经网络输出与真实标签之间差距的指标。在训练过程中&#xff0c;神经网络的目标是最小化损失函数的值。常见的损失函数包括均方误差&#xff08;MS…

HCIA--实验十二:交换机TRUNK接口实验

两个交换机TRUNK接口实验 一、实验内容 1.需求/要求&#xff1a; 通过两台交换机和四台主机连通&#xff0c;划分两个VLAN&#xff0c;实现同VLAN间通信。理解access接口和trunk接口的区别&#xff0c;熟记access和trunk的配置命令。 二、实验过程 1.拓扑图&#xff1a; 2…

从0开始深入理解并发、线程与等待通知机制

1、 从0开始深入理解并发、线程与等待通知机制 从上面两大互联网公司的招聘需求可以看到&#xff0c;大厂的Java岗的并发编程能力属于标配。 而在非大厂的公司&#xff0c;并发编程能力也是面试的极大加分项&#xff0c;而工作时善用并发编程则可以极大提升程序员在公司的技术…

Gray_opening和gray_closing 你用吗?

一张图&#xff0c;来自南京信息工程大学的视频课程 #1&#xff0c;复习一下各种图像处理神器&#xff0c; 取最小值&#xff0c;取最大值&#xff0c;取中间值&#xff0c;取均值&#xff0c;取高斯值&#xff0c;还有双边滤波&#xff0c;丰富对图像的处理 #2&#xff0c;…

不正确使用 public、private、protected,导致封装性差或无法访问

在Java学习中&#xff0c;封装&#xff08;Encapsulation&#xff09;是面向对象编程的重要特性之一。它通过隐藏对象的内部细节&#xff0c;并通过公开的接口与外界交互&#xff0c;从而提高代码的安全性、可维护性和模块化。 然而&#xff0c;如果不正确地使用访问修饰符&am…

ROS2图形化方式新建功能包工具- Turtle Nest

提示&#xff1a;全文AI生成。 链接&#xff1a; https://github.com/Jannkar/turtle_nest 配置 Turtle Nest 使用说明&#xff08;中文版&#xff09; 一、Turtle Nest 简介 正如海龟巢是幼海龟的诞生地&#xff0c;ROS 2 Turtle Nest 是 ROS 2 包的诞生和成长之地。Turtl…