链载Ai

标题: RAG全解析,并使用LangChain进行代码实现 [打印本页]

作者: 链载Ai    时间: 2025-12-2 09:28
标题: RAG全解析,并使用LangChain进行代码实现

介绍 RAG 的相关概念,以及如何使用LangChain、OpenAI 语言模型和 Weaviate 向量数据库来实现简单的 RAG 流水线。

自从人们意识到可以用自有数据为大型语言模型(LLM)增效之后,就开始讨论如何最有效地弥合 LLM 的通用知识与专有数据之间的差距。围绕着微调还是检索增强生成(RAG)哪个更适合这一问题,人们展开了激烈的争论(两者都适合)。

本文重点介绍 RAG 的概念,并首先介绍其理论。然后,文章会继续展示如何使用LangChain进行编排、OpenAI 语言模型和 Weaviate 向量数据库来实现一个简单的 RAG 流水线。

1 检索增强生成(RAG)

检索增强生成(RAG)是一种技术概念,旨在为大型语言模型(LLM)提供来自外部知识源的附加信息。这样,它们能够生成更准确、更具上下文的答案,同时还能减少误导性答案或虚假信息的生成。

简而言之,RAG 是一个多步骤的流程,从检索开始,然后推进到生成。它能够加速模型的回答过程,同时确保模型能够访问最新、最及时的事实和相关信息。

1.1 问题

最先进的大型语言模型(LLM)是通过在大量数据上进行训练,以获得存储在神经网络权重(参数记忆)中的广泛通用知识。然而,当提示 LLM 生成一些不包含在其训练数据中的知识时,例如更新的、专有的或特定领域的信息,可能会导致生成的结果不准确或不符合实际情况,从而误导用户。如下图所示:

ChatGPT回答“总统对布雷耶法官说了什么”的问题

因此,弥合 LLM 的通用知识与附加上下文之间的差距非常重要,这样才能帮助 LLM 生成更准确、更符合语境的补全,减少误导。

1.2 解决方案

传统上,神经网络通过微调模型来适应特定领域或专有信息。虽然这种技术是有效的,但它需要大量计算资源,成本高昂,并且需要专业技术知识,因此在适应不断变化的信息方面不够灵活。

在2020年,Lewis等人在论文《 Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks 》中提出了一种更灵活的技术,称为检索增强生成(RAG)。在这篇论文中,研究人员将生成模型与检索模块相结合,从外部知识源提供更易于更新的附加信息。

1.3 RAG基本工作流程

简单来说,检索增强生成(RAG)对于语言模型(LLMs)就像人类的开卷考试一样。开卷考试背后的理念是,考试侧重于学生的推理能力,而不是记忆特定信息的能力。

类似地,RAG 将事实知识与LLM的推理能力分离,并将其存储在外部知识源中,这样可以更轻松地访问和更新:

下面是RAG的基本工作流程:

(1)检索(Retrieve):使用用户查询从外部知识源中检索相关上下文。为此,将用户查询与嵌入模型嵌入到向量数据库中的附加上下文相同的向量空间中。这允许进行相似性搜索,并返回向量数据库中与之最接近的前k个数据对象。

(2)增强(Augment):将用户查询和检索到的附加上下文填入提示模板。

(3)生成(Generate):最后,将检索增强的提示输入LLM。

这种方法使LLM能够从外部知识源中获取更丰富的信息,而不仅仅依赖于其内部参数化知识。

2 使用 LangChain 实现 Retrieval-Augmented Generation(RAG)流程

这里使用 OpenAI LLM 结合 Weaviate 向量数据库和 OpenAI 嵌入模型,在 Python 中实现一个 RAG 流程。LangChain 用于协调整个流程。

2.1 先决条件

请确保已安装所需的 Python 包:

#!pipinstalllangchainopenaiweaviate-client

还需要在根目录下的 .env 文件中定义相关环境变量。要获取 OpenAI API 密钥,需要拥有 OpenAI 账户,然后在 API 密钥下选择“创建新密钥”。

OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

接下来,运行以下命令以加载相关环境变量:

importdotenv
dotenv.load_dotenv()

2.2 准备工作

作为准备步骤,需要准备一个向量数据库作为外部知识源,其中包含所有附加信息。该向量数据库通过以下步骤进行填充:

  1. 收集并加载数据
  2. 将文档分块
  3. 嵌入和存储块

2.3 具体步骤

首先,需要收集和加载数据。在这个示例中,将使用拜登总统2022年的国情咨文作为附加上下文。原始文本文件可在 LangChain 的 GitHub 存储库(https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/docs/modules/state_of_the_union.txt)中找到。要加载数据,可以使用 LangChain 提供的多个内置 DocumentLoaders。一个 Document 是一个包含文本和元数据的字典。为了加载文本,将使用 LangChain 的 TextLoader

importrequests
fromlangchain.document_loadersimportTextLoader

url="https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/docs/modules/state_of_the_union.txt"
res=requests.get(url)
withopen("state_of_the_union.txt","w")asf:
f.write(res.text)

loader=TextLoader('./state_of_the_union.txt')
documents=loader.load()

接下来,对文档进行分块处理。由于原始文档过长,无法适应 LLM 的上下文窗口,需要将其分成较小的块。LangChain 提供了许多内置的文本分块工具。在这个简单的示例中,可以使用 CharacterTextSplitter,设置 chunk_size 约为 500,chunk_overlap 为 50,以保持文本在块之间的连续性。

fromlangchain.text_splitterimportCharacterTextSplitter
text_splitter=CharacterTextSplitter(chunk_size=500,chunk_overlap=50)
chunks=text_splitter.split_documents(documents)

最后,嵌入和存储这些文本块。为了实现对文本块的语义搜索,需要为每个块生成向量嵌入,并将它们与其嵌入一起存储。可以使用 OpenAI 嵌入模型生成向量嵌入,然后使用 Weaviate 向量数据库存储它们。通过调用 .from_documents(),向量数据库会自动填充这些块。

fromlangchain.embeddingsimportOpenAIEmbeddings
fromlangchain.vectorstoresimportWeaviate
importweaviate
fromweaviate.embeddedimportEmbeddedOptions

client=weaviate.Client(
embedded_options=EmbeddedOptions()
)

vectorstore=Weaviate.from_documents(
client=client,
documents=chunks,
embedding=OpenAIEmbeddings(),
by_text=False
)

步骤 1:检索(Retrieve)

向量数据库被填充后,可以将其定义为检索器组件,该组件根据用户查询与嵌入式块之间的语义相似性获取附加上下文。

retriever=vectorstore.as_retriever()

步骤 2:增强(Augment)

接下来,为了将附加上下文与提示一起使用,需要准备一个提示模板。如下所示,可以轻松地从提示模板自定义提示。

fromlangchain.promptsimportChatPromptTemplate

template="""Youareanassistantforquestion-answeringtasks.
Usethefollowingpiecesofretrievedcontexttoanswerthequestion.
Ifyoudon'tknowtheanswer,justsaythatyoudon'tknow.
Usethreesentencesmaximumandkeeptheanswerconcise.
Question:{question}
Context:{context}
Answer:
"""
prompt=ChatPromptTemplate.from_template(template)

print(prompt)

步骤 3:生成(Generate)

最后,可以构建一个RAG流水线的链,将检索器、提示模板和LLM连接在一起。一旦定义了RAG链,就可以调用它。

fromlangchain.chat_modelsimportChatOpenAI
fromlangchain.schema.runnableimportRunnablePassthrough
fromlangchain.schema.output_parserimportStrOutputParser

llm=ChatOpenAI(model_name="gpt-3.5-turbo",temperature=0)

rag_chain=(
{"context":retriever,"question":RunnablePassthrough()}
|prompt
|llm
|StrOutputParser()
)

query="WhatdidthepresidentsayaboutJusticeBreyer"
rag_chain.invoke(query)
"ThepresidentthankedJusticeBreyerforhisserviceandacknowledgedhisdedicationtoservingthecountry.
ThepresidentalsomentionedthathenominatedJudgeKetanjiBrownJacksonasasuccessortocontinueJusticeBreyer'slegacyofexcellence."








欢迎光临 链载Ai (http://www.lianzai.com/) Powered by Discuz! X3.5