LangChain教程 | Retrival之Retrievers详解 | 检索器教程

1、前提准备 

        本文主要讲解 Retrievers 检索器 的内容,需要大家对 Retrival 中的其他内容有所了解,例如:

1、文档加载器

LangChain教程 | langchain 文件加载器使用教程 | Document Loaders全集_python unstructuredworddocumentloader用法-CSDN博客

2、文本分割

 LangChain教程 | langchain 文本拆分器 | Text Splitters全集_langchain.text splitter-CSDN博客

3、文本嵌入模型和向量存储,这两个在上面的文章都略有讲解,暂时不必太花心思在这里。 

        回到本文,一旦数据进入数据库,您仍然需要对它进行检索。LangChain支持许多不同的检索算法,是我们增加价值最多的地方之一。LangChain支持容易上手的基本方法——即简单的语义搜索。然而,我们还在此基础上添加了一组算法来提高性能。其中包括:

  • 父文档检索器:这允许您为每个父文档创建多个嵌入,允许您查找较小的块,但返回较大的上下文。
  • 子查询检索器:用户问题通常包含对一些东西的引用,这些东西不仅仅是语义上的,而是表达了一些逻辑,这些逻辑最好用元数据过滤器来表示。自查询允许您解析出语义的来自其他的查询的一部分元数据过滤器出现在查询中。
  • 集合检索器:有时您可能希望从多个不同的来源或使用多个不同的算法来检索文档。集合检索器让你很容易做到这一点。
  • 还有更多!

2、概述

Retrievers 检索器

        检索器是给定非结构化查询返回文档的接口。它比矢量存储器更通用。检索者不需要能够存储文档,只需要能够返回(或检索)它们。向量存储可以用作检索器的主干,但也有其他类型的检索器。

        检索器接受字符串查询作为输入,并返回 Documents 作为输出。

高级检索类型 

LangChain提供了几种高级检索类型。下面是完整的列表以及以下信息:

        名字:检索算法的名称。

        索引类型:这依赖于哪个索引类型(如果有)。

        使用LLM:此检索方法是否使用LLM。

        何时使用:我们关于何时应该考虑使用这种检索方法的讨论。

        描述:对该检索算法所做工作的描述。

名字变址类型使用LLM何时使用描述
VectorstoreVectorstore如果你刚刚起步,正在寻找快速简单的东西。这是最简单的方法,也是最容易上手的方法。它包括为每段文本创建嵌入。
ParentDocumentVectorstore + Document Store如果你的页面有很多小块的不同信息,最好单独索引,但最好一起检索。这涉及到为每个文档索引多个块。然后找到嵌入空间中最相似的块,但是检索整个父文档并返回它(而不是单个块)。
Multi VectorVectorstore + Document Store有时在索引期间如果您能够从文档中提取您认为比文本本身更与索引相关的信息。这涉及到为每个文档创建多个向量。每个向量可以通过多种方式创建——例如文本摘要和假设性问题。
Self QueryVectorstore如果用户提出的问题通过基于元数据而不是与文本的相似性来获取文档会得到更好的回答。它使用LLM将用户输入转换成两种东西:(1)语义查询字符串,(2)元数据过滤器。这很有用,因为问题通常是关于文档的元数据(而不是内容本身)。

Contextual Compression

任何的有时如果您发现检索到的文档包含太多无关信息,分散了LLM的注意力。这将一个后处理步骤置于另一个检索器之上,并且只从检索到的文档中提取最相关的信息。这可以通过嵌入或LLM来实现。

Time-Weighted

Vectorstore

Vectorstore如果您有与文档相关联的时间戳,并且希望检索最新的时间戳这基于语义相似性(如在正常向量检索中)和新近性(查看索引文档的时间戳)的组合来获取文档

Multi-Query

Retriever

任何的如果用户问的问题很复杂,需要多条不同的信息来回答这使用LLM从原始查询生成多个查询。当原始查询需要关于多个主题的信息才能正确回答时,这很有用。通过生成多个查询,我们可以为每个查询获取文档。
Ensemble任何的如果您有多种检索方法,并想尝试组合它们。这将从多个检索器中获取文档,然后将它们组合在一起。

Long-Context

Reorder

任何的如果您正在使用长上下文模型,并注意到它没有关注检索到的文档中间的信息。这将从底层检索器中获取文档,然后对它们进行重新排序,使最相似的文档位于开头和结尾附近。这很有用,因为已经证明对于较长的上下文模型,它们有时不会注意上下文窗口中间的信息。

在LCEL中使用检索器的简单示例:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

template = """Answer the question based only on the following context:

{context}

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()


def format_docs(docs):
    return "\n\n".join([d.page_content for d in docs])


chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

chain.invoke("What did the president say about technology?")

3、检索类型:向量存储检索器 Vector store-backed retriever

        向量存储检索器是使用向量存储来检索文档的检索器。它是向量存储类的轻量级包装器,使其符合检索器接口。它使用由向量存储实现的搜索方法(如相似性搜索和MMR)来查询向量存储中的文本。

        一旦构建了向量存储,构建检索器就非常容易了。让我们看一个例子。

        ①引入文本

from langchain_community.document_loaders import TextLoader

loader = TextLoader("../../state_of_the_union.txt")

        ②拆分文本,并存入向量存储FAISS中

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
db = FAISS.from_documents(texts, embeddings)

        ③使用向量存储生成检索器

retriever = db.as_retriever()

        ④开始通过检索器检索相关内容块

docs = retriever.get_relevant_documents("what did he say about ketanji brown jackson")

3.1 最大边际相关性检索

        默认情况下,矢量存储检索器使用相似性搜索。如果基础向量存储支持最大边际相关性搜索,您可以将其指定为搜索类型。

retriever = db.as_retriever(search_type="mmr")

3.2 相似性得分阈值检索

        您还可以设置一种检索方法,该方法设置相似性得分阈值,并且只返回得分高于该阈值的文档。设置属性 search_type 为 similarity_score_threshold

retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)

3.3 额外参数指定

        您还可以指定搜索关键字,例如 进行检索时使用。具体能用的参数大概有以下几种:

  •  k : 要返回的文档数量(默认值:4)
  •  score_threshold :最小相关性阈值,相似性_得分_阈值(默认值:0.5)
  •  fetch_k :传递给MMR算法的文档数量(默认值:20)
  •  lambda_mult :MMR返回结果的多样性;1表示最小分集,0表示最大分集。(默认值:0.5)
  •  filter :按文档元数据进行筛选
retriever = db.as_retriever(search_kwargs={"k": 1})
retriever = db.as_retriever(search_kwargs={'filter': {'paper_title':'GPT-4 Technical Report'}})

4、检索类型:多查询检索器 MultiQueryRetriever

        基于距离的向量数据库检索将查询嵌入(表示)在高维空间中,基于“距离”寻找相似的嵌入文档。但是,如果查询措辞有细微的变化,或者如果嵌入没有很好地捕获数据的语义,检索可能会产生不同的结果。有时需要进行即时工程/调整来手动解决这些问题,但这可能会很繁琐。

        MultiQueryRetriever 通过使用LLM从不同的角度为给定的用户输入查询生成多个查询,自动化提示调整的过程。对于每个查询,它检索一组相关的文档,并对所有查询进行惟一的联合,以获得一个更大的潜在相关文档集。通过对同一个问题生成多个视角。

        MultiQueryRetriever 可能能够克服基于距离的检索的一些限制,并获得更丰富的结果集。

# Build a sample vectorDB
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Load blog post
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data = loader.load()

# Split
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
splits = text_splitter.split_documents(data)

# VectorDB
embedding = OpenAIEmbeddings()
vectordb = Chroma.from_documents(documents=splits, embedding=embedding)

简单用法:

指定用于查询生成的LLM,检索器将完成剩下的工作。

from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_openai import ChatOpenAI

question = "What are the approaches to Task Decomposition?"
llm = ChatOpenAI(temperature=0)
retriever_from_llm = MultiQueryRetriever.from_llm(
    retriever=vectordb.as_retriever(), llm=llm
)
# Set logging for the queries
import logging

logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)
unique_docs = retriever_from_llm.get_relevant_documents(query=question)
len(unique_docs)

 增加自己的提示

您还可以提供一个提示和一个输出解析器,将结果分成一系列查询。

from typing import List

from langchain.chains import LLMChain
from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field


# Output parser will split the LLM result into a list of queries
class LineList(BaseModel):
    # "lines" is the key (attribute name) of the parsed output
    lines: List[str] = Field(description="Lines of text")


class LineListOutputParser(PydanticOutputParser):
    def __init__(self) -> None:
        super().__init__(pydantic_object=LineList)

    def parse(self, text: str) -> LineList:
        lines = text.strip().split("\n")
        return LineList(lines=lines)


output_parser = LineListOutputParser()

QUERY_PROMPT = PromptTemplate(
    input_variables=["question"],
    template="""You are an AI language model assistant. Your task is to generate five 
    different versions of the given user question to retrieve relevant documents from a vector 
    database. By generating multiple perspectives on the user question, your goal is to help
    the user overcome some of the limitations of the distance-based similarity search. 
    Provide these alternative questions separated by newlines.
    Original question: {question}""",
)
llm = ChatOpenAI(temperature=0)

# Chain
llm_chain = LLMChain(llm=llm, prompt=QUERY_PROMPT, output_parser=output_parser)

# Other inputs
question = "What are the approaches to Task Decomposition?"
# Run
retriever = MultiQueryRetriever(
    retriever=vectordb.as_retriever(), llm_chain=llm_chain, parser_key="lines"
)  # "lines" is the key (attribute name) of the parsed output

# Results
unique_docs = retriever.get_relevant_documents(
    query="What does the course say about regression?"
)
len(unique_docs)

5、检索类型:集合检索器 Ensemble Retriever

 EnsembleRetriever 

  将检索器列表作为输入,并集成其get_relevant_documents()方法并根据倒数等级融合算法。

        通过利用不同算法的优势 EnsembleRetriever 可以实现比任何单一算法更好的性能。

        最常见的模式是结合稀疏检索器(如BM25)和密集检索器(如嵌入相似性),因为它们的优势是互补的。它也被称为“混合搜索”。稀疏检索器擅长基于关键词查找相关文档,而密集检索器擅长基于语义相似性查找相关文档。

pip install --upgrade --quiet  rank-bm25
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
doc_list_1 = [
    "I like apples",
    "I like oranges",
    "Apples and oranges are fruits",
]

# initialize the bm25 retriever and faiss retriever
bm25_retriever = BM25Retriever.from_texts(
    doc_list_1, metadatas=[{"source": 1}] * len(doc_list_1)
)
bm25_retriever.k = 2

doc_list_2 = [
    "You like apples",
    "You like oranges",
]

embedding = OpenAIEmbeddings()
faiss_vectorstore = FAISS.from_texts(
    doc_list_2, embedding, metadatas=[{"source": 2}] * len(doc_list_2)
)
faiss_retriever = faiss_vectorstore.as_retriever(search_kwargs={"k": 2})

# initialize the ensemble retriever
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5]
)
docs = ensemble_retriever.invoke("apples")
print(docs)

就能看到能从doc_list_1和doc_list_2中分别取出相关的数据。

5.1 运行时配置

from langchain_core.runnables import ConfigurableField
faiss_retriever = faiss_vectorstore.as_retriever(
    search_kwargs={"k": 2}
).configurable_fields(
    search_kwargs=ConfigurableField(
        id="search_kwargs_faiss",
        name="Search Kwargs",
        description="The search kwargs to use",
    )
)
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5]
)
config = {"configurable": {"search_kwargs_faiss": {"k": 1}}}
docs = ensemble_retriever.invoke("apples", config=config)
print(docs)

请注意,这只从FAISS检索器返回一个源,因为我们在运行时传递相关的配置

6、检索类型:长上下文重排序 Long-Context Reorder

        无论模型的架构如何,当包含10+个检索到的文档时,性能都会大幅下降。简而言之:当模型必须在长上下文中访问相关信息时,它们往往会忽略所提供的文档。 参见:https://arxiv.org/abs/2307.03172 

        为避免此问题,您可以在检索后重新排序文档以避免性能下降。

pip install --upgrade --quiet  sentence-transformers langchain-chroma langchain langchain-openai > /dev/null
import os
from langchain.chains import LLMChain, StuffDocumentsChain
from langchain.prompts import PromptTemplate
from langchain_chroma import Chroma
from langchain_openai import OpenAI, OpenAIEmbeddings, ChatOpenAI
from langchain_community.document_transformers import (
    LongContextReorder,
)
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import OpenAI

# Get embeddings.
embeddings = OpenAIEmbeddings(
    openai_api_key=os.getenv("OPENAPI_API_KEY"),
    openai_api_base=os.getenv("OPENAI_API_BASE"))

texts = [
    "Basquetball is a great sport.",
    "Fly me to the moon is one of my favourite songs.",
    "The Celtics are my favourite team.",
    "This is a document about the Boston Celtics",
    "I simply love going to the movies",
    "The Boston Celtics won the game by 20 points",
    "This is just a random text.",
    "Elden Ring is one of the best games in the last 15 years.",
    "L. Kornet is one of the best Celtics players.",
    "Larry Bird was an iconic NBA player.",
]

# Create a retriever
retriever = Chroma.from_texts(texts, embedding=embeddings).as_retriever(
    search_kwargs={"k": 10}
)
query = "What can you tell me about the Celtics?"

# Get relevant documents ordered by relevance score
docs = retriever.get_relevant_documents(query)
print(docs)

参考结果:

[Document(page_content='This is a document about the Boston Celtics'),
 Document(page_content='The Celtics are my favourite team.'),
 Document(page_content='L. Kornet is one of the best Celtics players.'),
 Document(page_content='The Boston Celtics won the game by 20 points'),
 Document(page_content='Larry Bird was an iconic NBA player.'),
 Document(page_content='Elden Ring is one of the best games in the last 15 years.'),
 Document(page_content='Basquetball is a great sport.'),
 Document(page_content='I simply love going to the movies'),
 Document(page_content='Fly me to the moon is one of my favourite songs.'),
 Document(page_content='This is just a random text.')]

上面展示的是简单的检索器,下面我们使用 LongContextReorder 

# Reorder the documents:
# Less relevant document will be at the middle of the list and more
# relevant elements at beginning / end.
reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)

# Confirm that the 4 relevant documents are at beginning and end.
print(reordered_docs)

通过   LongContextReorder  将首次检索到的内容进行重排序,这时语义上就更好了。

[Document(page_content='The Celtics are my favourite team.'),
 Document(page_content='The Boston Celtics won the game by 20 points'),
 Document(page_content='Elden Ring is one of the best games in the last 15 years.'),
 Document(page_content='I simply love going to the movies'),
 Document(page_content='This is just a random text.'),
 Document(page_content='Fly me to the moon is one of my favourite songs.'),
 Document(page_content='Basquetball is a great sport.'),
 Document(page_content='Larry Bird was an iconic NBA player.'),
 Document(page_content='L. Kornet is one of the best Celtics players.'),
 Document(page_content='This is a document about the Boston Celtics')]

我们准备并运行一个自定义的Stuff链,将重新排序的文档作为上下文。

# Override prompts
document_prompt = PromptTemplate(
    input_variables=["page_content"], template="{page_content}"
)
document_variable_name = "context"
llm = OpenAI()
stuff_prompt_override = """Given this text extracts:
-----
{context}
-----
Please answer the following question:
{query}"""
prompt = PromptTemplate(
    template=stuff_prompt_override, input_variables=["context", "query"]
)

# Instantiate the chain
llm_chain = LLMChain(llm=llm, prompt=prompt)
chain = StuffDocumentsChain(
    llm_chain=llm_chain,
    document_prompt=document_prompt,
    document_variable_name=document_variable_name,
)
chain.run(input_documents=reordered_docs, query=query)

7、多向量检索器 MultiVector Retriever

        每个文档存储多个向量通常是有益的。这在许多用例中都是有益的。LangChain有一个基地 MultiVectorRetriever 这使得查询这种类型的设置很容易。很多复杂性在于如何为每个文档创建多个向量。本笔记涵盖了创建这些矢量和使用 MultiVectorRetriever .

        为每个文档创建多个向量的方法包括:

  • 较小的块:将文档分割成较小的块,并嵌入这些块(这是 ParentDocumentRetriever ,后面会讲)。
  • 摘要:为每个文档创建一个摘要,与文档一起(或代替文档)嵌入。
  • 假设性问题:创建每个文档都适合回答的假设性问题,将这些问题嵌入文档中。

        请注意,这也启用了另一种添加嵌入的方法-手动。这很好,因为您可以显式地添加问题或查询,这些问题或查询将导致文档被恢复,从而给您更多的控制。

from langchain.retrievers.multi_vector import MultiVectorRetriever
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
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)

较小的块

        通常情况下,检索较大的信息块是有用的,但嵌入较小的信息块也是有用的。这允许嵌入尽可能接近地捕捉语义含义,但尽可能多地将上下文传递到下游。请注意, ParentDocumentRetriever 确实如此。这里我们展示了引擎盖下发生的事情。

# The vectorstore to use to index the child chunks
vectorstore = Chroma(
    collection_name="full_documents", embedding_function=OpenAIEmbeddings()
)
# The storage layer for the parent documents
store = InMemoryByteStore()
id_key = "doc_id"
# The retriever (empty to start)
retriever = MultiVectorRetriever(
    vectorstore=vectorstore,
    byte_store=store,
    id_key=id_key,
)
import uuid

doc_ids = [str(uuid.uuid4()) for _ in docs]
# The splitter to use to create smaller chunks
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)))

         检索器对向量数据库执行的默认搜索类型是相似性搜索。LangChain矢量商店还支持通过以下方式进行搜索最大边际相关性因此,如果您想这样做,您可以只设置search_type属性如下:

from langchain.retrievers.multi_vector import SearchType

retriever.search_type = SearchType.mmr

len(retriever.get_relevant_documents("justice breyer")[0].page_content)

        通常情况下,摘要能够更准确地提取一个组块的内容,从而更好地进行检索。这里我们展示了如何创建摘要,然后嵌入它们。

import uuid

from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
chain = (
    {"doc": lambda x: x.page_content}
    | ChatPromptTemplate.from_template("Summarize the following document:\n\n{doc}")
    | ChatOpenAI(max_retries=0)
    | StrOutputParser()
)
summaries = chain.batch(docs, {"max_concurrency": 5})
# The vectorstore to use to index the child chunks
vectorstore = Chroma(collection_name="summaries", embedding_function=OpenAIEmbeddings())
# The storage layer for the parent documents
store = InMemoryByteStore()
id_key = "doc_id"
# The retriever (empty to start)
retriever = MultiVectorRetriever(
    vectorstore=vectorstore,
    byte_store=store,
    id_key=id_key,
)
doc_ids = [str(uuid.uuid4()) for _ in docs]
summary_docs = [
    Document(page_content=s, metadata={id_key: doc_ids[i]})
    for i, s in enumerate(summaries)
]
retriever.vectorstore.add_documents(summary_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
sub_docs = vectorstore.similarity_search("justice breyer")
print(sub_docs[0])
retrieved_docs = retriever.get_relevant_documents("justice breyer")
print(len(retrieved_docs[0].page_content))

8、父文档检索器 Parent Document Retriever

当分割文档以进行检索时,通常会有相互冲突的需求:

  1. 您可能希望拥有小文档,以便它们的嵌入能够最准确地反映它们的含义。如果太长,那么嵌入可能会失去意义。
  2. 您希望有足够长的文档来保留每个块的上下文。

ParentDocumentRetriever 通过拆分和存储小块数据来实现这种平衡。在检索过程中,它首先获取小的块,然后查找这些块的父id并返回那些较大的文档。

请注意,“父文档“是指一小块文档所源自的文档。这可以是整个原始文档,也可以是一个更大的块。

from langchain.retrievers import ParentDocumentRetriever

from langchain.storage import InMemoryStore
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter


loaders = [
    TextLoader("../../paul_graham_essay.txt"),
    TextLoader("../../state_of_the_union.txt"),
]
docs = []
for loader in loaders:
    docs.extend(loader.load())

 检索全部文档

在这种模式下,我们希望检索完整的文档。因此,我们只指定一个子拆分器。

# 此文本拆分器用于创建子文档
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# The vectorstore to use to index the child chunks
vectorstore = Chroma(
    collection_name="full_documents", embedding_function=OpenAIEmbeddings()
)
# 父文档的存储层
store = InMemoryStore()
retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=store,
    child_splitter=child_splitter,
)

retriever.add_documents(docs, ids=None)

这应该产生两个密钥,因为我们添加了两个文档。

print(list(store.yield_keys()))

现在让我们调用向量存储搜索功能-我们应该看到它返回小块(因为我们存储的是小块)。

# 向量检索内容
sub_docs = vectorstore.similarity_search("justice breyer")
# 打印结果
print(sub_docs[0].page_content)

 现在让我们从整个检索器中检索。这应该会返回大型文档——因为它会返回较小块所在的文档。

retrieved_docs = retriever.invoke("justice breyer")

print(len(retrieved_docs[0].page_content))

 检索较大的块

有时,完整的文档可能太大而不想按原样检索它们。在这种情况下,我们真正想做的是首先将原始文档分割成较大的块,然后再分割成较小的块。然后我们索引较小的块,但是在检索时我们检索较大的块(但仍然不是完整的文档)。

# 此文本拆分器用于创建父文档
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# 此文本拆分器用于创建子文档
# 它应该创建比父级更小的文档
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 用于索引子块的向量存储
vectorstore = Chroma(
    collection_name="split_parents", embedding_function=OpenAIEmbeddings()
)
# 父文档的存储层
store = InMemoryStore()

retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=store,
    child_splitter=child_splitter,
    parent_splitter=parent_splitter,
)

retriever.add_documents(docs)

我们可以看到现在有两个以上的文档-这些是较大的块。

len(list(store.yield_keys()))

让我们确保底层向量存储仍然检索小块。

sub_docs = vectorstore.similarity_search("justice breyer")

print(sub_docs[0].page_content)
retrieved_docs = retriever.invoke("justice breyer")

print(len(retrieved_docs[0].page_content))

print(retrieved_docs[0].page_content)

  创作不易,来个三连(点赞、收藏、关注),同学们的满意是我(H-大叔)的动力。

 代码运行有问题或其他建议,请在留言区评论,看到就会回复,不用私聊。

专栏人工智能 | 大模型 | 实战与教程里面还有其他人工智能|大数据方面的文章,可继续食用,持续更新。

 

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值