Source code for langchain.retrievers.parent_document_retriever
import uuid
from typing import List, Optional, Sequence
from langchain_core.documents import Document
from langchain_text_splitters import TextSplitter
from langchain.retrievers import MultiVectorRetriever
[docs]class ParentDocumentRetriever(MultiVectorRetriever):
"""获取小块,然后检索它们的父文档。
在检索文档时,通常存在冲突的需求:
1. 您可能希望有小型文档,以便它们的嵌入可以最准确地反映它们的含义。如果太长,那么嵌入可能会失去意义。
2. 您希望有足够长的文档,以保留每个块的上下文。
ParentDocumentRetriever 通过拆分和存储小块数据来平衡这一点。在检索过程中,它首先获取小块,然后查找这些块的父ID,并返回那些更大的文档。
请注意,“父文档”指的是小块来源的文档。这既可以是整个原始文档,也可以是更大的块。
示例:
.. code-block:: python
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.storage import InMemoryStore
# 此文本拆分器用于创建父文档
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000, add_start_index=True)
# 此文本拆分器用于创建子文档
# 它应该创建比父文档更小的文档
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400, add_start_index=True)
# 用于索引子块的向量存储
vectorstore = Chroma(embedding_function=OpenAIEmbeddings())
# 用于父文档的存储层
store = InMemoryStore()
# 初始化检索器
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)""" # noqa: E501
child_splitter: TextSplitter
"""用于创建子文档的文本分割器。"""
"""用于跟踪父ID的关键。这将存储在子文档的元数据中。"""
parent_splitter: Optional[TextSplitter] = None
"""用于创建父文档的文本分割器。
如果没有指定,则父文档将是传入的原始文档。"""
child_metadata_fields: Optional[Sequence[str]] = None
"""要保留在子文档中的元数据字段。如果为None,则保留所有父文档的元数据。"""
[docs] def add_documents(
self,
documents: List[Document],
ids: Optional[List[str]] = None,
add_to_docstore: bool = True,
) -> None:
"""将文档添加到文档存储库和向量存储库中。
参数:
documents: 要添加的文档列表
ids: 文档的可选id列表。如果提供应与文档列表长度相同。
如果父文档已经在文档存储库中,且不想重新添加到文档存储库,则可以提供此参数。
如果未提供,则将使用随机UUID作为id。
add_to_docstore: 是否将文档添加到文档存储库的布尔值。
如果提供了`ids`,则此参数可以为false。如果文档已经在文档存储库中,且不想重新添加,则可能希望将其设置为False。
"""
if self.parent_splitter is not None:
documents = self.parent_splitter.split_documents(documents)
if ids is None:
doc_ids = [str(uuid.uuid4()) for _ in documents]
if not add_to_docstore:
raise ValueError(
"If ids are not passed in, `add_to_docstore` MUST be True"
)
else:
if len(documents) != len(ids):
raise ValueError(
"Got uneven list of documents and ids. "
"If `ids` is provided, should be same length as `documents`."
)
doc_ids = ids
docs = []
full_docs = []
for i, doc in enumerate(documents):
_id = doc_ids[i]
sub_docs = self.child_splitter.split_documents([doc])
if self.child_metadata_fields is not None:
for _doc in sub_docs:
_doc.metadata = {
k: _doc.metadata[k] for k in self.child_metadata_fields
}
for _doc in sub_docs:
_doc.metadata[self.id_key] = _id
docs.extend(sub_docs)
full_docs.append((_id, doc))
self.vectorstore.add_documents(docs)
if add_to_docstore:
self.docstore.mset(full_docs)