【LangChain 速递】Parent Document Retriever
很喜欢 LangChain 父文档检索(Parent Document Retriever)的理念,以及 @clusteredbytes 制作的很酷的手绘图。所以周末花了一些时间重构了一个序列图来处理内部工作流程,希望它能帮助大家更好的了解这个检索器~
原推:https://twitter.com/zhanghaili0610/status/1692887244745388125?s=20

Parent Document Retriever 的实现流程如下:
使用两个文本分割器将原始文本分割成较大的块(父块)和较小的块(子块)
在向量存储(Vector Store)中仅存储较小的子块,因为在嵌入后它们能更准确地反映语义含义
在内存存储(Memory Store)中存储较大的父块,键是每个父块的唯一 ID,值是对应的文本内容
对每个子块的向量嵌入,将其对应的父块的唯一 ID 存储为元数据(metadata)
创建 ParentDocumentRetriever,传入上面创建的向量存储、内存存储和两个分割器
调用 add_documents 方法将文档添加到检索器中时,执行以下操作:
使用父分割器拆分文档成父块,为每个父块生成唯一 ID
将父块及其 ID 存入内存存储
使用子分割器进一步拆分父块成子块
将所有子块存入向量存储,并将每个子块的父 ID 作为元数据
当调用 get_relevant_documents 方法时,首先从向量存储中获取相关的子块,然后取出其父 ID,再从内存存储中获取对应的父块并返回
这样我们使用子块进行相似性匹配,但返回包含更多上下文的父块给下游任务使用。
Parent Document Retriever 的核心价值在于它结合了小块和大块的优势:
小块在向量嵌入后可以更准确地反映语义含义,这有利于后续的相似性检索
而大块提供了更完整的上下文信息,这对下游的语言生成任务很重要,可以帮助模型更好地理解查询的语义
Parent Document Retriever 通过使用小块进行相似性匹配,但返回大块作为结果,巧妙地利用了两者的优势
这样可以避免仅使用小块时可能出现的断章取义的问题,也避免了仅使用大块时嵌入语义损失的问题
使得后续的检索增强生成模型可以基于语义相关且上下文完整的文档来产生更加连贯和符合逻辑的回复
通过调整两个分割器的粒度,可以平衡语义精确度和上下文完整度,灵活适应不同的任务需求
相比直接使用一种策略,Parent Document Retriever 提供了一种更聪明、更有效的文档存储和检索机制