优化LangChain父文档检索,仅使用向量数据库增强上下文,提升检索效率和动态性。
原文标题:独家 | LangChain的父文档检索器——再探仅使用向量数据库增强上下文检索
原文作者:数据派THU
冷月清谈:
该方法的核心思想是在每个数据块的元数据中保存其所属文档ID和在文档中的序列号。检索时,先进行常规相似度搜索,找到最相似的块后,根据其元数据中的文档ID和序列号,检索同一文档中序列号相邻的块,构成更完整的上下文。
文中分别给出了使用ChromaDB和Milvus实现该方法的代码示例,并讨论了如何适配Pinecone等其他向量数据库。此外,还分析了该方法的局限性,例如向量数据库并非为常规数据库操作而设计,以及需要额外的查询操作。
怜星夜思:
2、文章中提到向量数据库并非为常规数据库操作而设计,那么在实际应用中,如何选择合适的向量数据库和常规数据库的组合方案?
3、动态修改检索窗口大小在实际应用中有哪些优势?
原文内容
文:Omri Eliyahu Levy翻译:付雯欣校对:赵茹萱本文约3200字,建议阅读8分钟本文为你介绍LangChain的父文档检索器。
总结:我们通过利用元数据查询,实现了与LangChain的父文档检索器(链接:https://python.langchain.com/v0.1/docs/modules/data_connection/retrievers/parent_document_retriever/)相同的功能。
你可以在这里查看相关代码:
https://gist.github.com/omriel1/7243ce233eb2986ed2749de6ae79ecb7
RAG简介
如果你对这些概念完全是陌生的,我建议你看看Deeplearning.ai的一门很好的课程:https://www.deeplearning.ai/short-courses/langchain-chat-with-your-data/
什么是“父文档检索”?
https://python.langchain.com/v0.1/docs/modules/data_connection/retrievers/parent_document_retriever/
```
# This text splitter is used to create the parent documents
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# This text splitter is used to create the child documents
# It should create documents smaller than the parent
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# The vectorstore to use to index the child chunks
vectorstore = Chroma(
collection_name="split_parents", embedding_function=OpenAIEmbeddings()
)
# The storage layer for the parent documents
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)
retrieved_docs = retriever.invoke("justice breyer")
```
替代方案
一般概念
```
{document_id: "example.pdf", sequence_number: 20}
```
```
chromadb==0.4.24
langchain==0.2.8
pymilvus==2.4.4
langchain-community==0.2.7
langchain-milvus==0.1.2
```
```
from langchain_community.document_loaders import PyPDFLoader
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
document_id = "example.pdf"
def preprocess_file(file_path: str) -> list[Document]:
"""Load pdf file, chunk and build appropriate metadata"""
loader = PyPDFLoader(file_path=file_path)
pdf_docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=0,
)
docs = text_splitter.split_documents(documents=pdf_docs)
chunks_metadata = [
{"document_id": file_path, "sequence_number": i} for i, _ in enumerate(docs)
]
for chunk, metadata in zip(docs, chunks_metadata):
chunk.metadata = metadata
return docs
```
```
from langchain_community.vectorstores import Milvus, Chroma
from langchain_community.embeddings import DeterministicFakeEmbedding
embedding= DeterministicFakeEmbedding(size=384) # Just for the demo :)
def parent_document_retrieval(
query: str, client: Milvus | Chroma, window_size: int = 4
):
top_1 = client.similarity_search(query=query, k=1)[0]
doc_id = top_1.metadata["document_id"]
seq_num = top_1.metadata["sequence_number"]
ids_window = [seq_num + i for i in range(-window_size, window_size, 1)]
# ...
```
```
if isinstance(client, Milvus):
expr = f"document_id LIKE '{doc_id}' && sequence_number in {ids_window}"
res = client.col.query(
expr=expr, output_fields=["sequence_number", "text"], limit=len(ids_window)
) # This is Milvus specific
docs_to_return = [
Document(
page_content=d["text"],
metadata={
"sequence_number": d["sequence_number"],
"document_id": doc_id,
},
)
for d in res
]
# ...
```
```
elif isinstance(client, Chroma):
expr = {
"$and": [
{"document_id": {"$eq": doc_id}},
{"sequence_number": {"$gte": ids_window[0]}},
{"sequence_number": {"$lte": ids_window[-1]}},
]
}
res = client.get(where=expr) # This is Chroma specific
texts, metadatas = res["documents"], res["metadatas"]
docs_to_return = [
Document(
page_content=t,
metadata={
"sequence_number": m["sequence_number"],
"document_id": doc_id,
},
)
for t, m in zip(texts, metadatas)
]
```
```
docs_to_return.sort(key=lambda x: x.metadata["sequence_number"])
return docs_to_return
```
Pinecone(及其他)
限制
结论
参考资料:(按原文提到的顺序)
[1] ARAGOG: Advanced RAG Output Grading, https://arxiv.org/pdf/2404.01037, section 4.2.2
[2]https://python.langchain.com/v0.1/docs/modules/data_connection/retrievers/parent_document_retriever/
[3]关于父文档无法动态的讨论:
https://github.com/langchain-ai/langchain/issues/14267
https://github.com/langchain-ai/langchain/issues/20315
https://stackoverflow.com/questions/77385587/persist-parentdocumentretriever-of-langchain
[4] https://docs.llamaindex.ai/en/stable/api_reference/node_parsers/sentence_window/
作者主页:
https://towardsdatascience.com/author/omri-levy/
原文链接:
https://towardsdatascience.com/langchains-parent-document-retriever-revisited-1fca8791f5a0/
编辑:王菁
译者简介
翻译组招募信息
工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。
你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。
其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。
点击文末“阅读原文”加入数据派团队~
转载须知
如需转载,请在开篇显著位置注明作者和出处(转自:数据派ID:DatapiTHU),并在文章结尾放置数据派醒目二维码。有原创标识文章,请发送【文章名称-待授权公众号名称及ID】至联系邮箱,申请白名单授权并按要求编辑。
发布后请将链接反馈至联系邮箱(见下方)。未经许可的转载以及改编者,我们将依法追究其法律责任。