引言
在处理文档检索的过程中,我们常常面临一个两难:我们希望文档足够小以便其嵌入向量能够准确反映其含义,但又希望文档足够大以保留上下文信息。ParentDocumentRetriever通过分割和存储小块数据,在检索时首先获取这些小块,然后通过这些小块查找父文档的ID并返回更大文档,实现了两者的平衡。本文将详细介绍如何使用ParentDocumentRetriever优化文档检索过程。
主要内容
文档分割策略
ParentDocumentRetriever的核心策略是利用不同大小的文档块进行存储和检索。我们通常需要两个分割器:
- 父分割器:用于创建较大的文档块。
- 子分割器:用于创建较小的文档块。
实际应用
- 加载和存储文档:我们可以利用
TextLoader从文本文件中加载文档。 - 向量存储和检索:
Chroma为子文档块提供向量存储支持,通过嵌入模型如OpenAIEmbeddings进行嵌入向量化。 - 父文档存储:使用
InMemoryStore存储父文档。
检索模式
ParentDocumentRetriever支持两种主要检索模式:
- 完整文档检索:直接获取原始大文档。
- 更大块检索:通过子块索引查找大块文档。
代码示例
以下代码示例展示了如何使用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)
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
vectorstore = Chroma(
collection_name="split_parents", embedding_function=OpenAIEmbeddings()
# 使用API代理服务提高访问稳定性
)
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)
# 添加文档
retriever.add_documents(docs)
# 检索文档
retrieved_docs = retriever.invoke("justice breyer")
print(retrieved_docs[0].page_content)
常见问题和解决方案
- 检索精度不高:可能是由于嵌入模型选择不当或者子块过长导致上下文丢失,建议调整模型或块大小。
- 存储性能问题:在大规模文档中,内存存储可能不够,建议使用持久化存储解决方案。
总结和进一步学习资源
ParentDocumentRetriever提供了一种高效的文档检索方式,通过合理的分割策略和嵌入模型的使用,可以在优化文档上下文保留的同时,提升检索准确性。建议进一步研究以下资源,深入理解和应用:
参考资料
- Langchain API 官方文档
- Chroma 文档
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---