LLM本地知识库问答系统(一):使用LangChain和LlamaIndex从零构建PDF聊天机器人指南

       随着大型语言模型(LLM)(如ChatGPT和GPT-4)的兴起,现在比以往任何时候都更容易构建比普通熊更智能的智能聊天机器人,并且可以浏览堆积如山的文档,为您的输入提供准确的响应。

       在本系列中,我们将探索如何使用pre-trained的LLM创建一个聊天机器人,该聊天机器人可以分析、总结PDF文档并回答问题,使其成为企业和个人都非常有用的工具。无论您是想构建个人助理、定制聊天机器人还是自动文档分析系统,本系列都将为您提供构建自己的LLM聊天机器人所需的知识。所以,让我们用LangChain和LlamaIndex深入LLM和聊天机器人的世界吧!

创建什么?

       使用ChatGPT作为助手来帮助用户基于多个文档进行问答系统搭建的想法是非常酷。起初,我们的想法是用特定的数据对模型进行微调,以实现这一目标,但这可能成本高昂,并且需要庞大的数据集。此外,对模型进行微调只能教会它一项新技能,而不能提供有关文档的完整信息。

      另一种方法是使用提示工程在(多)文档QA的提示中提供上下文。然而,GPT模型的注意力范围有限,将上下文传递给API也可能很昂贵,尤其是在处理大量客户反馈电子邮件和产品文档时。

那么如何创建呢?

以下是实现这些目标的具体步骤:

  1. 首先加载文档(PDF、HTML、文本、数据库等);
  2. 然后将数据分割成块,并对这些块建立embedding索引,这样方便使用向量检索工具进行语义搜索;
  3. 对于每个问题,通过搜索索引和embedding数据来获取与问题相关的信息;
  4. 将问题和相关数据输入到LLM模型中。在这个系列中使用OpenAI的LLM;

       实现上述过程主要的两个框架,分别是:Langchain(https://python.langchain.com/en/latest/)和LLamaIndex(https://gpt-index.readthedocs.io/en/latest/)

我们如何开始

下面是使用Langchain和ChatGPT实现PDF问答系统的大致框架:

      在本文,不会详细介绍Langchain或LLamaIndex具体原理和实现细节,后面会专门介绍。本文主要介绍如下内容:

  • 基于Langchain的生成式问答
  • LLamaIndex生成式问答
  • 奖金部分。

准备工作

     首先我们需要在OPenAI官网获取API秘钥,具体步骤是:转到https://platform.openai.com,登录或注册新帐户→ 点击您的个人资料→ 查看API密钥并创建新的密钥,如下图所示:

Note:实际上,我们可以使用其他LLM模型。

       下面准备安装相关的python包,需要保证Python>=3.7来进行操作,然后创建一个虚拟环境并安装以下Python库:

## to create virtual environment$ python3 -m venv llm_app_env## on MacOS or Linux$ source llm_app_env/bin/activate## on Window$ llm_app_env\Scripts\activate.bat## then install the following libraries.openai[embeddings]==0.27.6langchain==0.0.155pypdf==3.8.1tiktoken==0.3.3faiss-cpu==1.7.4unstructured==0.6.2chromadb==0.3.21llama-index==0.6.1jupyterlab

Langchain介绍

       LangChain是一个强大的开源工具,可以轻松地与大型语言模型交互并构建应用程序。将其视为一个中间人,将您的应用程序连接到广泛的LLM提供商,如OpenAI、Cohere、Huggingface、Azure OpenAI等。

       然而,LangChain不仅仅是一个访问预训练语言模型的工具,它还提供了许多有用的特性和功能,允许您构建自定义应用程序和工具。例如:

  • 使用自己的文档进行问答和文本摘要
  • 处理内存和具有有限令牌问题的长文档。
  • 与OpenAI ChatGPT Retriever插件的出色集成
  • 多个链来处理您定义的问题,或者使用Agent将其提升。
  • 还有更多。

       Langchain是一个伟大的框架,它使人工智能应用程序的创建能力现在掌握在您手中。更令人惊讶的是,它是开源的,所以你知道它掌握在优秀社区的手中。

下面使用Langchain来搭建一个问答系统:

设置OpenAI API密钥

import loggingimport sysimport osos.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"

加载并拆分数据

## load the PDF using pypdffrom langchain.document_loaders import PyPDFLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitter# load the dataloader = PyPDFLoader('../notebooks/documents/Apple-Financial-Report-Q1-2022.pdf')# the 10k financial report are huge, we will need to split the doc into multiple chunk.# This text splitter is the recommended one for generic text. It is parameterized by a list of characters. # It tries to split on them in order until the chunks are small enough.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)data = loader.load()texts = text_splitter.split_documents(data)# view the first chunktexts[0]

简单问答

       我们将使用OpenAI作为LLM提供者,因此使用OpenAI Embedding,但请注意,OpenAI Embedding API使用的是“text-davinci-003”模型(定价参考:https://openai.com/pricing)

       接下来,我们将导入Chroma,Chroma是嵌入数据库,不像传统的SQL数据库,也不像你通常使用的NoSQL数据库。它嵌入了数据库,使构建LLM应用程序变得容易。

通过Chroma官方网站

       我们的文档以文本的形式表示,因此很难根据问题找到相关信息。假设你需要在1000页中找到苹果上一季度的收入,并将收入与前几年进行比较。这可能需要多大的挑战性和耗时?因此,为了让我们的搜索更容易,我们首先需要以数字格式转换或表示单词或短语,这些单词或短语可以用作机器学习模型的输入。换句话说,帮助机器理解文本。embeddings将每个单词或短语映射到实数向量,通常具有数百个维度,使得相似的单词或短语被映射到嵌入空间中的相似向量。

       使用embeddings的主要优点之一是,它们可以捕捉单词或短语之间的语义和句法关系。例如,在嵌入空间中,“国王”和“王后”的向量比“苹果”的向量更接近,因为它们在语义上与王室头衔相关。

       因此,嵌入数据库正是这样做的。它将把所有embeddings数据存储在数据库中,然后给我们提供非常多的索引,使我们能够执行类似数据检索的操作,并以可扩展的风格进行操作。如果你需要得到之前关于寻找苹果上季度收入的问题的答案,我们首先需要在嵌入Chroma等数据库的基础上进行相似性搜索或语义搜索,以提取相关信息,并将这些信息提供给LLM模型来获得答案。

       听起来太复杂了!这就是Langchain拯救我们的地方,所有的艰苦工作都将在后台完成。Just do it!

# import Chroma and OpenAIEmbeddingsfrom langchain.vectorstores import Chromafrom langchain.embeddings.openai import OpenAIEmbeddings# initialize OpenAIEmbeddingembeddings = OpenAIEmbeddings(model='text-embedding-ada-002')# use Chroma to create in-memory embedding database from the docdocsearch = Chroma.from_documents(texts, embeddings,  metadatas=[{"source": str(i)} for i in range(len(texts))])## perform search based on the questionquery = "What is the operating income?"docs = docsearch.similarity_search(query)

       您可以看到,我们能够执行相似性搜索,从嵌入数据库中获取相关信息。

       现在,我们将使用Langchain的主要组件之一Chain将LLM提供程序合并到我们的代码中。请记住,本文的目的是建立问答机器人。因此,只需按照步骤进行操作,如果你很好奇,迫不及待地想了解更多细节,请随时访问Langchain的官方网站。瓦尔哈拉在等着你!!!!

Langchain提供了四种预先构建的问答Chain,具体如下:

  • 问答:load_qa_chain
  • 有来源问答:load_qa_with_sources_chain
  • 检索问题答案:RetrievalQA
  • 资源检索问答:RetrievalQAWithSourcesChain

      它们非常相似,RetrievalQA和RetrievalQAWithSourcesChain分别使用load_qa_chain和load_qa_with_sources_chain,唯一的区别是前两者将把所有嵌入都馈送到LLM中,而后两者只向LLM提供相关信息。我们可以使用前两个来首先提取相关信息,并仅将该信息提供给LLM。此外,前两个比后两个给了我们更多的灵活性。

下面的代码将演示我们是如何做到这一点的。

## importing necessary frameworkfrom langchain.chains.question_answering import load_qa_chainfrom langchain.chains.qa_with_sources import load_qa_with_sources_chainfrom langchain.chains import RetrievalQAfrom langchain.chains import RetrievalQAWithSourcesChainfrom langchain.chat_models import ChatOpenAI

现在我们将尝试4种不同的问答链

1.load_qa_chain

## use LLM to get answeringchain = load_qa_chain(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'),                       chain_type="stuff")query = "What is the operating income?"chain.run(input_documents=docs, question=query)

2.load_qa_with_sources_chain

chain = load_qa_with_sources_chain(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'),                                    chain_type="stuff")query = "What is the operating income?"chain({"input_documents": docs, "question": query}, return_only_outputs=True)

3.RetrievalQA

qa=RetrievalQA.from_chain_type(llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'), chain_type="stuff",                                                 retriever=docsearch.as_retriever())query = "What is the operating income?"qa.run(query)

4.RetrievalQAWithSourcesChain

chain=RetrievalQAWithSourcesChain.from_chain_type(ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'), chain_type="stuff",                                                     retriever=docsearch.as_retriever())chain({"question": "What is the operating income?"}, return_only_outputs=True)

       上面的大部分代码都是非常基本的。我们只想在深入研究框架能提供什么之前完成这项工作。在此之前,让我们转到另一个可以与Langchain结合使用的框架,它将为您提供更多的能力来创建更好的LLM应用程序。

LLamaIndex介绍

       我首先介绍了Langchain,如果你花一些时间浏览它的官方文件,你可能会想“哇,没有什么好的东西可以超越这一点”。

       好吧,我的朋友们,有一个完整的SaaS行业建立在AWS之上,只是为了让您更好、更容易地使用AWS服务。其他LLM框架或LLM模型之间的竞争也是如此。我们生活在一个今天好的东西明天可能会过时的世界里。我个人认为Langchain将有一个非常光明的未来,并将成为用于构建LLM应用程序的核心技术。LLamIndex甚至让我们的工作变得更容易,它还通过处理一些痛苦的众所周知的问题和现有方法的局限性引入了自己的优势,这些问题和局限性将花费您的时间和手动操作,例如:

  • 文本块缺少全局上下文。通常,这个问题需要特定区块中索引之外的上下文。
  • 仔细调整前k/相似性得分阈值。如果值太小,就会错过上下文。让价值变得太大,成本/延迟可能会随着不相关的上下文而增加。
  • Embeddings并不总是为问题选择最相关的上下文。Embeddings本质上是在文本和上下文之间分别确定的。

       LLamaIndex(GPT索引)有自己的机制来处理这些限制。同样,这个博客的目的是完成这项工作。我不会详细介绍LLamaIndex是如何工作的(可以在官方文件上找到)。

那么LLM是什么

       一张由Jerry Liu抄写的羊皮纸,他在羊皮纸上公布了LlamaIndex,这是一个利用GPT的力量,利用询问者提供的知识,形成对询问的回复的门户网站。

       简而言之,LlamaIndex是通过以下步骤将LLM连接到用户来响应查询的另一种方式(类似于Langchain的方式):

  1. 加载文档(手动或通过数据加载程序)

  2. 将文档解析为节点

  3. 构造索引(从节点或文档)

  4. [可选,高级]在其他指数之上构建指数

  5. 查询索引

        简单来说,LlamaIndex将数据加载到文档对象中,并将其转换为索引。当您输入查询时,索引会将其发送到GPT提示符以生成响应,默认情况下使用OpenAI的text-davinci-003模型。尽管这个过程看起来很复杂,但只要几行代码就可以执行,您很快就会了解到这一点。

       您很快就会看到LLamaIndex是多么容易使用,因为它已经完成了所有的艰苦工作。你的工作只是阅读它的官方文件,学习不同类型的索引,然后分析你的应用程序需求,看看什么最适合你。当然,你的应用程序中可能需要越来越多复杂的东西,LLamaIndex的高级API可能不足以处理此类情况。这就是LLamaIndex可以与Langchain等其他工具集成以加快开发过程的便利之处。

让我们从设置简单索引和加载文档开始。

import loggingimport sys## setup your OpenAI Keyimport osos.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"# enable logs to see what happen underneathlogging.basicConfig(stream=sys.stdout, level=logging.DEBUG)logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

LlamaIndex的核心是指数,有多种类型的指数。

  • 列表索引
  • 矢量存储索引
  • 树索引
  • 关键字表索引
  • 图形索引
  • SQL索引。

       每个索引都有其独特的用途,具有不同的用途。好处是,您可以将索引堆叠在其他索引之上,这样做将使您的应用程序更强大,能够理解您的文档上下文和应用程序需求。

第一步是加载文档

from llama_index import GPTVectorStoreIndexfrom llama_index import download_loader# we will use this UnstructuredReader to read PDF fileUnstructuredReader = download_loader('UnstructuredReader', refresh_cache=True)loader = UnstructuredReader()# load the datadata = loader.load_data(f'../notebooks/documents/_10-Q-Q1-2022-(As-Filed).pdf', split_documents=False)

Document表示数据源的轻量级容器。可以选择下面两步骤之一:

  1. 将Document对象直接输入索引

  2. 首先,将文档转换为Node对象

       同样,本系列的目的是帮助您尽快构建第一个应用程序,因此我将直接讨论索引构建。我将在未来的一篇文章中介绍LLamaIndex的所有方面。

索引构建与查询

       我们现在可以在这些Document对象上建立一个索引。最简单的高级抽象是在索引初始化期间加载Document对象。

index = GPTVectorStoreIndex.from_documents(data)query_engine = index.as_query_engine()response = query_engine.query("What is the operating income?")print(response)

      根据您使用的索引,LlamaIndex可能会进行LLM调用以构建索引。GPTVvectorStoreIndex不会调用LLM,但GPTTreeStoreIndex会调用。

自定义LLM

      默认情况下,LlamaIndex使用OpenAI的text-davinci-003模型。在构造索引时,您可以选择使用另一个LLM。

from llama_index import LLMPredictor, PromptHelper, ServiceContextfrom langchain.chat_models import ChatOpenAI# define LLMllm_predictor = LLMPredictor(llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo'))# define prompt helper# set maximum input sizemax_input_size = 4096# set number of output tokensnum_output = 256# set maximum chunk overlapmax_chunk_overlap = 20prompt_helper = PromptHelper(max_input_size, num_output, max_chunk_overlap)service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, prompt_helper=prompt_helper)index = GPTVectorStoreIndex.from_documents(    documents,     service_context=service_context)query_engine = index.as_query_engine()response = query_engine.query("What is the operating income?")print(response)

       在短短几行代码中,我们就能够构建一个LLM应用程序,可以进行基本的问答。

       对于具有机器学习工程师或数据科学背景的人来说,这是相当简单明了的,但我相信对于一些新手来说,有时会觉得很困惑。我理解这一点,但很难在一篇帖子中解释所有内容。这篇文章的目的只是让你体验一下现在构建这样一个令人惊叹的LLM应用程序是多么容易。你现在可能有很多问题,甚至可能几行代码都不懂,但这没关系。

       您将很快收集组件的所有知识和方面,以构建自己的LLM应用程序。你可以等到我的下一篇文章,因为我将在下一篇中介绍LlamaIndex,或者如果你足够好奇,请通过阅读官方文件来做好准备。

       在此之前,我希望这篇文章能够帮助您扩展编码知识,并为LLM提供有价值的见解。记得保持好奇心,继续探索人工智能的广阔世界。

       祝贺你走到这一步!作为对您努力的奖励,这里有一段代码,您可以使用它与文档聊天

# do importsfrom langchain.agents import Toolfrom langchain.chains.conversation.memory import ConversationBufferMemoryfrom langchain.chat_models import ChatOpenAIfrom langchain.agents import initialize_agentfrom llama_index.langchain_helpers.agents import LlamaToolkit, create_llama_chat_agent, IndexToolConfigquery_engine = index.as_query_engine()tool_config = IndexToolConfig(    query_engine=query_engine,     name=f"Financial Report",    description=f"useful for when you want to answer queries about the Apple financial report",    tool_kwargs={"return_direct": True})toolkit = LlamaToolkit(    index_configs=[tool_config])memory = ConversationBufferMemory(memory_key="chat_history")llm=ChatOpenAI(temperature=0.2,model_name='gpt-3.5-turbo')agent_chain = create_llama_chat_agent(    toolkit,    llm,    memory=memory,    verbose=True)while True:    text_input = input("User: ")    response = agent_chain.run(input=text_input)    print(f'Agent: {response}')

YouTube视频结果展示地址:https://youtu.be/FuKFjNNbSVM

参考文献:

[1] https://langchain.readthedocs.io/en/latest/index.html(LangChain docs)

[2] https://langchain.readthedocs.io/en/latest/modules/memory.html#memory(LangChain Prompt Memory module)

[3] https://github.com/hwchase17/langchain(LangChain Repo)

[4] https://gpt-index.readthedocs.io/en/latest/index.html(LlamaIndex docs)

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

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

相关文章

java八股文面试[数据库]——数据库三范式

什么是范式&#xff1f; 范式是数据库设计时遵循的一种规范&#xff0c;不同的规范要求遵循不同的范式。 最常用的三大范式 第一范式(1NF)&#xff1a;属性不可分割&#xff0c;即每个属性都是不可分割的原子项。(实体的属性即表中的列) 理解&#xff1a;一个列不能包含两个数…

【GO】LGTM_Grafana_Tempo(2)_官方用例改后实操

最近在尝试用 LGTM 来实现 Go 微服务的可观测性&#xff0c;就顺便整理一下文档。 Tempo 会分为 4 篇文章&#xff1a; Tempo 的架构官网测试实操跑通gin 框架发送 trace 数据到 tempogo-zero 微服务框架使用发送数据到 tempo 根据官方文档实操跑起来 tempo&#xff0c;中间根…

网络编程 day 4

1、多进程并发服务器根据流程图重新编写 #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr, "__%d__:", __LINE__); \perror(msg);\ }while(0)#define PORT 8888 //端口号&#xff0c;范围1024~49151 #define IP "192.168.11…

渲染如何做到超强渲染?MAX插件CG MAGIC中的渲染功能!

渲染工作应该算是设计师的日常工作流程中最重要的环节之一了。如果渲染速度加快&#xff0c;可能是要看渲染技巧掌握的有多少了。 大家熟悉的3d Max本地渲染通道&#xff0c;对于CG MAGIC渲染功能你也一定不能错过&#xff0c;要知道操作简单易使用&#xff0c;就完全拿捏了效率…

微信小程序echart导出图片

echarts版本5.1.0 用到的echarts组件是uni插件市场的echart组件 <div style"overflow: hidden;"><dCanvas class"uni-ec-canvass" id"uni-ec-canvas" ref"canvas" canvas-id"mychart-gauge" :ec"ec"&g…

基于Thinkphp6框架全新UI的AI网址导航系统源码

2023全新UI的AI网址导航系统源码&#xff0c;基于thinkphp6框架开发的 AI 网址导航是一个非常实用的工具&#xff0c;它能够帮助用户方便地浏览和管理自己喜欢的网站。 相比于其他的 AI 网址导航&#xff0c;这个项目使用了更加友好和易用的 ThinkPHP 框架进行搭建&#xff0c;…

1. Spatial Intelligence of a Self-driving Car and Rule-Based Decision Making

主要内容 本文主要介绍了一些基于规则的方法&#xff0c;以实现自动驾驶规划技术在复杂车流中取得人类驾驶效果。因此此类场景更适合城市NOA。 当然本文在城市道路&#xff0c;封闭区域道路以及城际高速都适宜。主要技术点 &#xff08;1&#xff09;本文把自车周围车辆的决策…

webpack loader和plugins的区别

在Webpack中&#xff0c;Loader和Plugin是两个不同的概念&#xff0c;用于不同的目的。 Loader是用于处理非JavaScript模块的文件的转换工具。它们将文件作为输入&#xff0c;并将其转换为Webpack可以处理的模块。例如&#xff0c;当您在Webpack配置中使用Babel Loader时&…

【STM32】学习笔记(OLED)-江科大

调试方式 OLED简介 硬件电路 驱动函数 OLED.H #ifndef __OLED_H #define __OLED_Hvoid OLED_Init(void); void OLED_Clear(void); void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char); void OLED_ShowString(uint8_t Line, uint8_t Column, char *String); void OL…

qt设计界面

widget.h #ifndef WIDGET_H #define WIDGET_H //防止文件重复包含#include <QWidget> //QWidget类所在的头文件&#xff0c;父类头文件 #include<QIcon> #include<QPushButton> …

理论转换实践之keepalived+nginx实现HA

背景&#xff1a; keepalivednginx实现ha是网站和应用服务器常用的方法&#xff0c;之前项目中单独用nginx实现过负载均衡和服务转发&#xff0c;keepalived一直停留在理论节点&#xff0c;加之最近工作编写的一个技术文档用到keepalived&#xff0c;于是便有了下文。 服务组件…

使用Gitea自建仓库 并配置git上传

使用Gitea自建仓库 并配置git上传 使用 Docker 安装 | Gitea Documentation 1. 安装Docker 2. 使用Docker Compose快速安装 在安装目录下创建config 和 data两个文件夹 以下是我的配置&#xff0c;和官网提供的大差不差 version: "3"networks:gitea:external: …

论文阅读_医疗知识图谱_GraphCare

英文名称: GraphCare: Enhancing Healthcare Predictions with Open-World Personalized Knowledge Graphs 中文名称: GraphCare&#xff1a;通过开放世界的个性化知识图增强医疗保健预测 文章: http://arxiv.org/abs/2305.12788 代码: https://github.com/pat-jj/GraphCare 作…

uniapp项目实战系列(2):新建项目,项目搭建,微信开发工具的配置

目录 系列文章目录uniapp项目实战系列(1)&#xff1a;导入数据库&#xff0c;启动后端服务&#xff0c;开启代码托管&#xff08;点击跳转&#xff09;1.新建项目2.托管项目的操作&#xff1a;&#xff08;无勾选托管项目可无视&#xff09;3.项目编译预览3.1游览器编译3.2微信…

以GitFlow分支模型为基准的Git版本分支管理流程

以GitFlow分支模型为基准的Git版本分支管理流程 文章目录 以GitFlow分支模型为基准的Git版本分支管理流程GitFlow分支模型中的主要概念GitFlow的分支管理流程图版本号说明借助插件Git Flow Integration Plus实现分支模型管理其他模型TBD模型阿里AoneFlow模型 GitFlow分支模型中…

2023-8-30 八数码(BFS)

题目链接&#xff1a;八数码 #include <iostream> #include <algorithm> #include <unordered_map> #include <queue>using namespace std;int bfs(string start) {string end "12345678x";queue<string> q;unordered_map<strin…

ATKck靶场系列二

信息收集 nmap -sP 192.168.111.0/24 nmap -sS -T4 -A -v -p- 192.168.111.80─# nmap -sS -T4 -A -v -p- 192.168.111.80 Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-29 01:46 EDT NSE: Loaded 155 scripts for scanning. NSE: Script Pre-scanning. Initiating NS…

el-date-picker 等 点击无反应不回显问题解决

如上图&#xff0c;编辑回显正常&#xff0c;但是时间控件在拖动过程中时间不会跟随改变。 解决办法&#xff1a; <el-date-picker input"onInput()" ...><el-input input"onInput()" ...>js中onInput() {this.$forceUpdate();},

弹窗、抽屉、页面跳转区别 | web交互入门

当用户点击或触发浏览页面的某个操作&#xff0c;有很多web交互方式&#xff0c;可以大致分为弹窗、抽屉、跳转新页面三种web交互方式。虽然这三种web交互方式看起来没什么不同&#xff0c;但实际上弹窗、抽屉、跳转新页面对交互体验有蛮大的影响。 这需要UI\UX设计师针对不同…

Redis与Mysql区别

一、关系型数据库 mysql&#xff0c;pgsql,oracle ,sqlserver 支持连表关联查询&#xff08;会有一些特定的语法特特性&#xff09; 二、非关系型数据库 redis,mongodb,memcache &#xff08;key-value&#xff09; 三、关系型数据库与非关系型数据库的区别&#xff1a; 1&am…