用网站内容构建RAG应用
本文是使用网络抓取、分块和向量嵌入构建RAG应用程序的全面指南。

最近,大型语言模型(LLMs)的进步为复杂的自然语言应用解锁了令人兴奋的可能性。这些模型,如ChatGPT、LLAMA和Mistral,正在革新我们与AI的互动方式,从生成类人文本到驱动个性化聊天机器人。然而,一个主要的限制仍然存在:这些模型受限于它们训练时的知识,并且无法更新新的信息。这种限制阻碍了它们应对时间敏感或领域特定查询的能力。
这就是检索增强生成(RAG)发挥作用的地方。RAG使我们能够将实时上下文信息输入到LLMs中,使它们能够提供更相关和精确的答案。一个有价值的上下文信息来源是网站内容。
在这篇指南中,我们将解释如何从网站提取内容并利用它来改进LLMs在RAG应用程序中的响应。我们将涵盖从网络抓取的基础知识到分块策略以及创建向量嵌入以实现高效检索的所有内容。让我们开始吧!
1、网络抓取基础
为了将网站内容集成到RAG系统中,第一步是从网站提取内容。这个过程被称为网络抓取。虽然一些网站提供了访问其数据的API,但许多没有。在这种情况下,网络抓取变得非常有价值。
有几个流行的Python库可以帮助提取网页数据。在这个例子中,我们将使用Beautiful Soup解析HTML内容和requests进行HTTP请求。还可以使用更高级的工具,如Selenium(用于动态内容)或Scrapy(用于大规模抓取)。
示例:抓取维基百科
让我们从使用BeautifulSoup抓取维基百科页面开始。
import requests
from bs4 import BeautifulSoup
# 向维基百科的数据科学页面发送请求
response = requests.get(
url="https://en.wikipedia.org/wiki/Data_science",
)
# 解析HTML内容
soup = BeautifulSoup(response.content, 'html.parser')
# 获取文章主体内的文本内容
content = soup.find(id="bodyContent")
print(content.text)
这段代码向维基百科发送请求,获取“数据科学”页面的内容,并提取主体文本以供进一步处理。
2、分块:将内容分解
成功抓取一些内容后,下一步是将其拆分为块。分块有几个重要原因:
- 粒度:将文本拆分为较小的部分使其更容易检索最相关的信息。
- 提高语义性:对整个文档使用单个嵌入会导致有意义的信息丢失。
- 效率:较小的文本块在嵌入过程中导致更高效的计算。
固定大小 vs. 上下文感知分块
最常见的分块方法是固定大小分块和上下文感知分块。固定大小分块在预定义的时间间隔内分割文本,而上下文感知分块根据句子或段落边界调整分块大小。
在这个指南中,我们将使用LangChain框架中的RecursiveCharacterTextSplitter
执行分块,确保在文本的逻辑点上进行拆分。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512, # 设置块大小为512字符
length_function=len
)
chunked_text = text_splitter.split_text(content.text)
这段代码将抓取的文本拆分为大约512个字符的块,并根据自然断点调整拆分。
3、从块到向量嵌入
一旦我们有了文本块,下一步就是将它们转换为向量嵌入。嵌入是文本的数值表示,捕捉其语义意义,允许高效的相似性比较。
嵌入类型
有两种主要类型的嵌入:
- 密集嵌入:由OpenAI或Sentence Transformers等深度学习模型生成。它们很好地编码语义相似性。
- 稀疏嵌入:由TF-IDF或BM25等经典方法生成。它们对于基于关键字的相似性有效。
对于我们的RAG应用程序,我们将使用Sentence Transformers生成的密集嵌入,即all-MiniLM-L6-v2
模型。
from langchain.embeddings import SentenceTransformerEmbeddings
# 加载用于生成嵌入的模型
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
# 为第三个文本块创建嵌入
chunk_embedding = embeddings.embed_documents([chunked_text[3]])
这段代码使用MiniLM-L6-v2模型将其中一个块转换为密集嵌入。实际上,您需要为所有块生成嵌入。
4、使用Milvus存储和检索嵌入
生成嵌入后,我们需要将它们存储在向量数据库中以实现高效检索。Milvus是一个开源向量数据库,专门用于存储和搜索嵌入。它与LangChain集成良好,是RAG应用程序的理想选择。
以下是将块嵌入存储到Milvus的方法:
from langchain.vectorstores.milvus import Milvus
# 将嵌入存储到Milvus
vector_db = Milvus.from_texts(texts=chunked_text, embedding=embeddings, collection_name="rag_milvus")
这段代码在Milvus中创建一个集合并存储所有块嵌入以便将来检索。
5、构建RAG管道
有了块存储和嵌入准备就绪,现在是时候构建我们的RAG管道了。该管道将根据用户查询检索最相关的嵌入,并将其传递给LLM以生成响应。
第一步:设置检索器
首先,我们需要设置一个检索器,该检索器根据用户的查询从向量数据库中检索最相关的嵌入。
retriever = vector_db.as_retriever()
第二步:初始化LLM
接下来,我们使用OpenAI的GPT-3.5-turbo初始化我们的语言模型:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo-0125")
第三步:定义自定义提示
我们需要创建一个提示模板,该模板将指导LLM根据检索到的内容生成适当的答案。
from langchain_core.prompts import PromptTemplate
template = """使用以下部分上下文回答问题。
如果你不知道答案,就说你不知道,不要试图编造答案。
最多使用三句话,并尽可能简洁地回答。
答案结尾总是说“感谢提问!”
{context}
问题:{question}
有用的答案:"""
custom_rag_prompt = PromptTemplate.from_template(template)
第四步:构建RAG链
最后,我们将创建RAG链,该链将检索最相关的块,将其传递给LLM,并输出生成的响应。
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| custom_rag_prompt
| llm
| StrOutputParser()
)
设置了RAG链后,你可以向管道发送查询并接收基于网站内容的答案。
for chunk in rag_chain.stream("什么是数据科学家?"):
print(chunk, end="", flush=True)
6、结束语
在这篇指南中,我们介绍了如何从网站提取内容并使用它来改进LLMs在RAG应用程序中的响应。我们讨论了网络抓取、文本分块、生成向量嵌入以及在向量数据库(如Milvus)中存储这些嵌入的过程。
通过使用这种方法,您可以开发出更知情和上下文感知的AI应用程序。无论是创建聊天机器人还是问答系统,RAG都能提升生成响应的相关性和准确性。
重要的是要记住,您的RAG管道的成功取决于数据的质量以及您如何组织块、嵌入和检索过程。尝试不同的模型、分块大小和检索方法以优化您的系统。
原文链接:Building RAG Applications with Website Content
汇智网翻译整理,转载请标明出处