PDR 父文档检索 引见一种适用的RAG技术

作为一种能在先进的RAG(Retrieval-Augmented Generation,检索增强生成)模型中成功的方法,父文档检索(Parent Document Retrieval,PDR)旨在复原那些可以从中提取到相关子段落(或片段)的完整父文档。此类文档经过将丰盛的高低文,传递给RAG 模型,以便对复杂或纤细的疑问,做出更片面且外延丰盛的回答。通常,在RAG 模型中检索出父文档的关键步骤包括:

墨守成规的实施

依据上图,咱们可以将成功父文档检索的步骤便捷分为如下四个不同的阶段:

1. 预备数据

咱们首先应为自己的RAG 系统创立环境并预处置数据,以便对后续的父文档展开文档检索。

(1)导入必要的模块

咱们将从已装置的库中导入所需的模块,以设置咱们的PDR 系统:

from langchain.schema import Documentfrom langchain.vectorstores import Chromafrom langchain.retrievers import ParentDocumentRetrieverfrom langchain.chains import RetrievalQAfrom langchain_openai import OpenAIfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain.storage import InMemoryStorefrom langchain.document_loaders import TextLoaderfrom langchain.embeddings.openai import OpenAIEmbeddings

上述这些库和模块正是造成整个环节步骤的关键局部。

(2)设置OpenAI API 密钥

接着,咱们经常使用OpenAI LLM来生成照应,为此咱们须要一个OpenAI 的API 密钥。该密钥可被用来设置环境变量:OPENAI_API_KEY。

OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] = ""# Add your OpenAI API keyif OPENAI_API_KEY == "":raise ValueError("Please set the OPENAI_API_KEY environment variable")

(3)定义文本嵌入函数

经过如下形式,咱们应用OpenAI 的嵌入来示意文本数据:

embeddings = OpenAIEmbeddings()

(4)加载文本数据

为了读取想要检索的文本文档,你可以应用类TextLoader来读取文本文件:

loaders = [TextLoader('/path/to/your/document1.txt'),TextLoader('/path/to/your/document2.txt'),]docs = []for l in loaders:docs.extend(l.load())

2. 检索完整的文档

上方,咱们将经过设置系统,来检索与子段落相关的完整父文档。

(1)完整文档的拆分

咱们经常使用RecursiveCharacterTextSplitter将加载的文档宰割成比所需大小更小的文本块。这些子文档将使咱们能够有效地搜查相关段落:

child_splitter = RecursiveCharacterTextSplitter(chunk_size=)

(2)矢量存储和存储设置

上方,咱们将经常使用Chroma向量存储来嵌入各个子文档,并应用InMemoryStore来跟踪与子文档关联的完整父文档:

vectorstore = Chroma(collection_name="full_documents",embedding_function=OpenAIEmbeddings())store = InMemoryStore()

(3)父文档检索器

接着,让咱们从类ParentDocumentRetriever中实例化一个对象。该类关键担任完整父文档与基于子文档相似性检索相关的外围逻辑。

full_doc_retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter)

(4)参与文档

而后,这些加载的文档将经常使用add_documents方法被馈入ParentDocumentRetriever中,如下代码所示:

full_doc_retriever.add_documents(docs)print(list(store.yield_keys()))# List document IDs in the store

(5)相似性搜查和检索

至此,检索器已基本成功,你可以在给定查问的状况下,去检索相关的子文档,并失掉相关的完整父文档:

sub_docs = vectorstore.similarity_search("What is LangSmith?", k=2)print(len(sub_docs))print(sub_docs[0].page_content)retrieved_docs = full_doc_retriever.invoke("What is LangSmith?")print(len(retrieved_docs[0].page_content))print(retrieved_docs[0].page_content)

3. 检索更大的数据块

有时,在文档十分大的状况下,咱们或者不可失掉完整的父文档。对此,可参考如下从父文档失掉较大片段的方法:

块和父级文本宰割:

矢量存储和存储设置(相似完整的文档检索):

(1)父文档检索器

该检索器可处置RAG 中的一个基本疑问:因为被检索的整个文档过大,而不可蕴含足够的高低文。为此,RAG需将文档切成小块启动检索,进而对这些小块启动索引。不过,在查问之后,它不会去检索这些文档片段,而是检索整个父文档,从而为后续的生成提供更为丰盛的高低文。

parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)child_splitter = RecursiveCharacterTextSplitter(chunk_size=)vectorstore = Chroma(collection_name="split_parents",embedding_function=OpenAIEmbeddings())store = InMemoryStore()big_chunks_retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter,parent_splitter=parent_splitter)# Adding documentsbig_chunks_retriever.add_documents(docs)print(len(list(store.yield_keys())))# List document IDs in the store

(2)相似性搜查和检索

该环节依然与完整的文档检索相似,咱们须要查找相关的子文档,而后从父文档中失掉相应的更大文档块。

sub_docs = vectorstore.similarity_search("What is LangSmith?", k=2)print(len(sub_docs))print(sub_docs[0].page_content)retrieved_docs = big_chunks_retriever.invoke("What is LangSmith?")print(len(retrieved_docs))print(len(retrieved_docs[0].page_content))print(retrieved_docs[0].page_content)

4. 与 RetrievalQA 集成

至此,咱们曾经成功了一个父文档检索器,你可以将其与RetrievalQA链集成,以经常使用检索到的父文档启动各种问答:

qa = RetrievalQA.from_chain_type(llm=OpenAI(),chain_type="stuff",retriever=big_chunks_retriever)query = "What is LangSmith?"response = qa.invoke(query)print(response)

小结

综上所述,PDR 大幅提高了RAG 模型输入照应的准确性,而且这些照应都带有丰盛的高低文。而经过对父文档的全文检索,咱们可以深化准确地回答各种复杂疑问,这也是复杂人工智能的基本要求。

译者引见

陈峻(Julian Chen),社区编辑,具备十多年的IT名目实施阅历,擅长对内外部资源与危险实施管控,专一流传网络与消息安保常识与阅历。

原文题目:Parent Document Retrieval (PDR): Useful Technique in RAG,作者:Intiaz Shaik

链接:。

您可能还会对下面的文章感兴趣: