【AIGC】基于检索增强技术(RAG)构建大语言模型(LLM)应用程序

本文涉及的产品
交互式建模 PAI-DSW,5000CU*H 3个月
简介: 【5月更文挑战第7天】基于检索增强技术(RAG)构建大语言模型(LLM)应用程序实践

2000元阿里云代金券免费领取,2核4G云服务器仅664元/3年,新老用户都有优惠,立即抢购>>>


阿里云采购季(云主机223元/3年)活动入口:请点击进入>>>,


阿里云学生服务器(9.5元/月)购买入口:请点击进入>>>,

[toc]


在之前的博客文章中,我们已经描述了嵌入是如何工作的,以及RAG技术是什么。本节我们我们将使用 LangChain 库以及 RAG 和嵌入技术在 Python 中构建一个简单的 LLM 应用程序。

我们将使用 LangChain 库在 Python 中构建一个简单的 LLM 应用程序。LangChain是一个流行的库,它使构建这样的应用程序变得非常容易。

我们的 RAG 应用程序将使用私有数据扩展 LLM 的知识。在这种情况下,它将是一个包含一些文本的 PDF 文件。

也可以通过使用 OpenAI 代理并通过将特定文件上传到 OpenAI 的服务器来扩展其知识库来实现类似的目标。但是,这种方法需要将我们的机密数据存储在 OpenAI 的服务器上,这可能并不总是符合我们的隐私偏好。

1.安装条件

在一开始,我们必须安装应用程序将使用的所有必需模块。让我们在终端的项目目录中编写此命令

pip install langchain-community==0.0.11 pypdf==3.17.4 langchain==0.1.0 python-dotenv==1.0.0 langchain-openai==0.0.2.post1 faiss-cpu==1.7.4 tiktoken==0.5.2 langchainhub==0.1.14

让我们创建一个“data”目录并将 PDF 文件放入其中。 我们还必须在项目目录中创建一个 main.py 文件,我们将在其中存储应用程序的整个代码。

在 main.py 文件中,我们将创建用于存储逻辑的 main() 函数。该文件将如下所示:

def main():
  print("Hello World!")

if __name__ == "__main__": 
  main()

2.导入PDF文件

我们将使用LangChain提供的名为PyPDFLoader的文档加载器。

from langchain_community.document_loaders import PyPDFLoader

pdf_path = "./data/2210.03629.pdf"

def main():
  loader = PyPDFLoader(file_path=pdf_path)
  documents = loader.load()
  print(documents) 

if __name__ == "__main__": 
  main()

首先,我们应该创建一个 PyPDFLoader 对象的实例,我们将路径传递给文件。下一步是简单地调用此对象的 load 函数,并将加载的文件保存在 documents 变量中。它将是一个由 Document 对象组成的数组,其中每个对象都是我们文件的一页的表示形式。

print() 函数应该输出一个类似于这样的数组:

[Document(page_content='[...]', metadata={'source': pdf_path, page: 1}), Document(page_content='[...]', metadata={'source': pdf_path, page: 2}), ...]

3.切割文件

我们不想将整个文档作为上下文发送到 LLM。为什么?在关于RAG的文章中对此进行了更详细的描述。为了拆分文档,我们将使用 LangChain 提供的一个名为 CharacterTextSplitter 的类,我们可以从 langchain 库中导入它:

from langchain.text_splitter import CharacterTextSplitter

然后我们可以创建一个实例并调用 split_documents() 函数,将加载的文档作为参数传递。

def main():
  loader = PyPDFLoader(file_path=pdf_path) 
  documents = loader.load() 
  text_splitter = CharacterTextSplitter( chunk_size=1000, chunk_overlap=50, separator="\n" ) 
  docs = text_splitter.split_documents(documents)

让我们简要描述一下这里发生了什么。

首先,我们将创建一个 CharacterTextSplitter 对象,该对象采用多个参数:

  • chunk_size - 定义以令牌为单位的单个块的最大大小。
  • chunk_overlap - 定义块之间的重叠大小。这有助于通过确保块不会以扭曲其含义的方式拆分来保留拆分文本的含义。
  • separator - 定义将用于描述块的分隔符。

在 docs 变量中,我们将得到一个 Document 对象数组 - 与 PyPDFLoader 类的 load() 函数相同。

4.准备环境变量

下一步是将这些块转换为数字向量,并将它们存储在向量数据库中。这个过程叫做嵌入,也有一篇关于它的博文,所以我们现在不会详细介绍它。

对于嵌入过程,我们需要一个外部嵌入模型。为此,我们将使用 OpenAI 嵌入。为此,我们必须生成一个 OpenAI API 密钥。
但在此之前,我们必须创建一个 .env 文件,用于存储此密钥。

现在,我们需要在 platform.openai.com/docs/overview 页面上创建一个帐户。 之后,我们应该通过创建一个新的密钥在 platform.openai.com/api-keys 页面上生成一个 API 密钥。

复制密钥并将其粘贴到 .env 文件中,如下所示:

OPENAI_API_KEY=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

让我们通过导入 load_dotenv 函数将环境变量加载到我们的项目中:

from dotenv import load_dotenv

并在 main 函数的开头调用它:

def main(): 
    load_dotenv()
    loader = PyPDFLoader(file_path=pdf_path) 
    documents = loader.load() 
    text_splitter = CharacterTextSplitter( chunk_size=1000, chunk_overlap=50, separator="\n" ) 
    docs = text_splitter.split_documents(documents)

5.直线嵌入功能

首先,我们必须导入 OpenAIEmbeddings 类:

from langchain_openai import OpenAIEmbeddings

我们应该创建这个类的实例。让我们将其分配给 'embeddings' 变量,如下所示:

embeddings = OpenAIEmbeddings()

6.设置向量数据库

我们已经加载并准备了我们的文件,我们还为嵌入模型创建了一个对象实例。我们现在已准备好将块转换为数字向量并将它们保存在向量数据库中。我们将使用 FAISS 矢量数据库将所有数据保存在本地。Facebook AI 相似度搜索 (Faiss) 是 Facebook AI 设计的一款工具,用于对密集向量进行有效的相似性搜索和聚类。

首先,我们需要导入 FAISS 实例:

from langchain_community.vectorstores.faiss import FAISS

并实现转换和保存嵌入的过程:

def main(): 
    load_dotenv() 
    loader = PyPDFLoader(file_path=pdf_path) 
    documents = loader.load() 
    text_splitter = CharacterTextSplitter( chunk_size=1000, chunk_overlap=50, separator="\n" ) 
    docs = text_splitter.split_documents(documents) 
    embeddings = OpenAIEmbeddings() 
    vectorstore = FAISS.from_documents(docs, embeddings)    
    vectorstore.save_local("vector_db")

我们在代码中添加了两行。第一行采用我们的拆分块 (docs) 和嵌入模型将块从文本转换为数字向量。之后,我们将转换后的数据保存在本地的“vector_db”目录中。

7.创建提示

为了准备提示,我们将使用“langchain”中心。我们将从那里提取一个名为“langchain-ai/retrieval-qa-chat”的提示。这个提示是专门为我们的案例设计的,允许我们从提供的上下文中向模型询问事物。在引擎盖下,提示如下所示:

Answer any use questions based solely on the context below:
<context> 
{context}
</context>

让我们从“langchain”库导入一个"hub":

from langchain import hub

然后,只需使用“pull()”函数从中心检索此提示并将其存储在变量中:

retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat")

8.设置大语言模型

接下来我们需要的是一个大型语言模型——在我们的例子中,它将是 OpenAI 模型之一。同样,我们需要一个 OpenAI 密钥,但我们已经将它与嵌入一起设置,因此我们不需要再做一次。

让我们继续导入模型:

from langchain_openai import ChatOpenAI, OpenAIEmbeddings

并将其分配给我们 main 函数中的一个变量:

llm = ChatOpenAI()

9.从数据库检索上下文数据

我们已经完成了向量数据库、嵌入和 LLM(大型语言模型)的准备工作。现在,我们需要使用链条连接所有东西。为此,我们需要“langchain”提供的两种类型的链。

第一个是 'create_stuff_documents_chain',我们需要从 'langchain' 库中导入它:

from langchain.chains.combine_documents import create_stuff_documents_chain

接下来,传递我们的大型语言模型 (LLM) 并提示它。

combine_docs_chain = create_stuff_documents_chain(llm, retrieval_qa_chat_prompt)

此函数返回一个 LCEL Runnable 对象,该对象需要上下文参数。运行它将如下所示:

combine_docs_chain.invoke({
   "context": docs, "input": "What is REACT in machine learning meaning?"})

10.仅检索相关数据作为上下文

它会起作用,但在这种情况下,我们将传递所有块 - 整个文档 - 作为上下文。在我们的例子中,文件有 33 页,这个上下文太大,我们可能会遇到这样的错误:

openai.BadRequestError: Error code: 400 - {'error': {'message': "This model's maximum context length is 4097 tokens. However, your messages resulted in 33846 tokens. Please reduce the length of the messages.", 'type': 'invalid_request_error', 'param': 'messages', 'code': 'context_length_exceeded'}}

为了解决这个问题,我们只需要将与查询相关的信息作为上下文传递。我们将通过将此链与另一条链相结合来实现这一点,该链将仅从数据库中检索对我们重要的块,并自动将它们作为上下文添加到提示中。

让我们从“langchain”库中导入该链:

from langchain.chains import create_retrieval_chain

首先,我们需要将数据库准备为检索器,这将启用对与查询相关的块的语义搜索。

retriever = FAISS.load_local("vector_db", embeddings).as_retriever()

因此,我们加载我们的目录,其中存储转换为向量的块并将其传递给嵌入函数。最后,我们将其作为检索器返回。

现在,我们可以组合我们的链:

retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain)

在后台,它将从数据库中检索相关块,并将它们作为上下文添加到我们的提示中。我们现在要做的就是使用我们的查询作为输入参数调用此链:

response = retrieval_chain.invoke({
   "input": "What is REACT in machine learning meaning?"})

作为响应,我们将收到一个包含三个变量的对象:

  • input - 我们的查询;

  • context - 我们作为上下文传递给提示的文档(块)数组;

  • answer - 由大型语言模型 (LLM) 生成的查询的答案。

10.LLM app 全部代码

我们用 .pdf 文件中的数据扩展了 LLM 模型的知识库。该模型现在能够根据我们在提示中提供的上下文来回答我们的问题。

from dotenv import load_dotenv
from langchain import hub
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores.faiss import FAISS

pdf_path = "./data/2210.03629.pdf"


def main():
    load_dotenv()

    loader = PyPDFLoader(file_path=pdf_path)
    documents = loader.load()

    text_splitter = CharacterTextSplitter(
        chunk_size=1000, chunk_overlap=50, separator="\n"
    )
    docs = text_splitter.split_documents(documents)

    embeddings = OpenAIEmbeddings()

    vectorstore = FAISS.from_documents(docs, embeddings)
    vectorstore.save_local("vector_db")

    retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat")

    llm = ChatOpenAI()

    combine_docs_chain = create_stuff_documents_chain(llm, retrieval_qa_chat_prompt)

    retriever = FAISS.load_local("vector_db", embeddings).as_retriever()
    retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain)

    response = retrieval_chain.invoke(
        {"input": "What is REACT in machine learning meaning?"}
    )

    print(response["answer"])


if __name__ == "__main__":
    main()

小结

遵守前面博客中的约定,输出一节基于RAG进行大语言模型构建的内容,我们共划分了10个小节分别进行了详细介绍,希望对初学者有很好的指导作用。

小编是一名热爱人工智能的专栏作者,致力于分享人工智能领域的最新知识、技术和趋势。这里,你将能够了解到人工智能的最新应用和创新,探讨人工智能对未来社会的影响,以及探索人工智能背后的科学原理和技术实现。欢迎大家点赞,评论,收藏,让我们一起探索人工智能的奥秘,共同见证科技的进步!

相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
4天前
|
弹性计算 自然语言处理 开发工具
基于阿里云向量检索 Milvus 版和 LangChain 快速构建 LLM 问答系统
本文介绍如何通过整合阿里云Milvus、阿里云DashScope Embedding模型与阿里云PAI(EAS)模型服务,构建一个由LLM(大型语言模型)驱动的问题解答应用,并着重演示了如何搭建基于这些技术的RAG对话系统。
|
4天前
|
存储 安全 机器人
【LLM】智能学生顾问构建技术学习(Lyrz SDK + OpenAI API )
【5月更文挑战第13天】智能学生顾问构建技术学习(Lyrz SDK + OpenAI API )
|
4天前
|
人工智能 测试技术 API
【AIGC】LangChain Agent(代理)技术分析与实践
【5月更文挑战第12天】 LangChain代理是利用大语言模型和推理引擎执行一系列操作以完成任务的工具,适用于从简单响应到复杂交互的各种场景。它能整合多种服务,如Google搜索、Wikipedia和LLM。代理通过选择合适的工具按顺序执行任务,不同于链的固定路径。代理的优势在于可以根据上下文动态选择工具和执行策略。适用场景包括网络搜索、嵌入式搜索和API集成。代理由工具组成,每个工具负责单一任务,如Web搜索或数据库查询。工具包则包含预定义的工具集合。创建代理需要定义工具、初始化执行器和设置提示词。LangChain提供了一个从简单到复杂的AI解决方案框架。
|
4天前
|
人工智能 自然语言处理
【AIGC】英语小助手Lingo:基于大语言模型的学习英语小帮手
【5月更文挑战第11天】英语小助手Lingo:基于大语言模型的学习英语小帮手
|
4天前
|
机器学习/深度学习 人工智能 NoSQL
【AIGC】深入浅出理解检索增强技术(RAG)
【5月更文挑战第10天】本文介绍了检索增强生成(RAG)技术,这是一种将AI模型与内部数据结合,提升处理和理解能力的方法。通过实时从大型文档库检索信息,扩展预训练语言模型的知识。文章通过示例说明了当模型需要回答未公开来源的内容时,RAG如何通过添加上下文信息来增强模型的回答能力。讨论了实际应用中令牌限制和文本分块的问题,以及使用文本嵌入技术解决相关性匹配的挑战。最后,概述了实现RAG的步骤,并预告后续将分享构建检索增强服务的详情。
|
4天前
|
机器学习/深度学习 人工智能 自然语言处理
【AIGC】基于大语言模型构建多语种聊天机器人(基于Bloom大语言模型)
【5月更文挑战第8天】基于大语言模型Bloom构建多语种聊天机器人
|
4天前
|
机器学习/深度学习 人工智能 自然语言处理
【大模型】使用哪些资源来了解 LLM 的最新进展?
【5月更文挑战第9天】【大模型】使用哪些资源来了解 LLM 的最新进展?
|
4天前
|
机器学习/深度学习 人工智能 自然语言处理
LLM 大模型学习必知必会系列(一):大模型基础知识篇
LLM 大模型学习必知必会系列(一):大模型基础知识篇
LLM 大模型学习必知必会系列(一):大模型基础知识篇
|
4天前
|
自然语言处理 搜索推荐 知识图谱
【大模型】描述与 LLM 相关的个人项目或感兴趣的领域
【5月更文挑战第9天】【大模型】描述与 LLM 相关的个人项目或感兴趣的领域
|
4天前
|
存储 安全 数据安全/隐私保护
【大模型】如何确保负责任地开发和部署 LLM?
【5月更文挑战第7天】【大模型】如何确保负责任地开发和部署 LLM?
http://www.vxiaotou.com