LangChain实践10-检索

发布于:2025-02-16 ⋅ 阅读:(147) ⋅ 点赞:(0)

使用基本的相似性搜索大概能解决你80%的相关检索工作,但对于那些相似性搜索失败的边缘情况该如 何解决呢?这一章节我们将介绍几种检索方法,以及解决检索边缘情况的技巧,让我们一起开始学习 吧!

向量数据库检索

pip install -Uq lark

相似性检索(Similarity Search)

from langchain.vectorstores import Chroma

from langchain.embeddings.openai import OpenAIEmbeddings

persist_directory_chinese = 'docs/chroma/matplotlib/'

embedding = OpenAIEmbeddings()

vectordb_chinese = Chroma(

persist_directory=persist_directory_chinese,

embedding_function=embedding

)

print(vectordb_chinese._collection.count())

texts_chinese = [

 """毒鹅膏菌(Amanita phalloides)具有大型且引人注目的地上(epigeous)子实体

(basidiocarp)""",

 """一种具有大型子实体的蘑菇是毒鹅膏菌(Amanita phalloides)。某些品种全白。""",

 """A. phalloides,又名死亡帽,是已知所有蘑菇中最有毒的一种。""",

 ]

smalldb_chinese = Chroma.from_texts(texts_chinese, embedding=embedding)

question_chinese = "告诉我关于具有大型子实体的全白色蘑菇的信息"

相似性搜索,设置 k=2 ,只返回两个最相关的文档。

smalldb_chinese.similarity_search(question_chinese, k=2)

解决多样性:最大边际相关性(MMR)

MMR 的基本思想是同时考量查询与文档的相关度,以及文档之间的相似度。相关度确保返回结果对查询 高度相关,相似度则鼓励不同语义的文档被包含进结果集。具体来说,它计算每个候选文档与查询的相 关度,并减去与已经选入结果集的文档的相似度。这样更不相似的文档会有更高的得分。 总之,MMR 是解决检索冗余问题、提供多样性结果的一种简单高效的算法。它平衡了相关性和多样性, 适用于对多样信息需求较强的应用场景

 

smalldb_chinese.max_marginal_relevance_search(question_chinese,k=2, fetch_k=3)

还记得在上一节中我们介绍了两种向量数据在查询时的失败场景吗?当向量数据库中存在相同的文档 时,而用户的问题又与这些重复的文档高度相关时,向量数据库会出现返回重复文档的情况。现在我们 就可以运用Langchain的 max_marginal_relevance_search 来解决这个问题: 我们首先看看前两个文档,只看前几个字符,可以看到它们是相同的。

这里如果我们使用相似查询,会得到两个重复的结果,读者可以自己尝试一下,这里不再展示。我们可 以使用 MMR 得到不一样的结果。

 

docs_mmr_chinese = vectordb_chinese.max_marginal_relevance_search(question_chinese,k=3)

print(docs_mmr_chinese[0].page_content[:100])

print(docs_mmr_chinese[1].page_content[:100])

解决特殊性:使用元数据

在上一节课中,关于失败的应用场景我们还提出了一个问题,是询问了关于文档中某一讲的问题,但得 到的结果中也包括了来自其他讲的结果。这是我们所不希望看到的结果,之所以产生这样的结果是因为 当我们向向量数据库提出问题时,数据库并没有很好的理解问题的语义,所以返回的结果不如预期。要 解决这个问题,我们可以通过过滤元数据的方式来实现精准搜索,当前很多向量数据库都支持对 (metadata)的操作:

 

question_chinese = "他们在第二讲中对SeaTunnel说了些什么?"

# 我们以手动的方式来解决这个问题,我们会指定一个元数据过滤器filter

docs_chinese = vectordb_chinese.similarity_search(

question_chinese,

k=3,

filter={"source":"./data/seatunnel.pdf"}

)

for d in docs_chinese:

 print(d.metadata)

 

解决特殊性:在元数据中使用自查询检索器(LLM辅助检索)

在上例中,我们手动设置了过滤参数 filter 来过滤指定文档。但这种方式不够智能,需要人工指定过滤条 件。如何自动从用户问题中提取过滤信息呢? LangChain提供了SelfQueryRetriever模块,它可以通过语言模型从问题语句中分析出:

  1. 向量搜索的查询字符串(search term)
  2.  过滤文档的元数据条件(Filter)

以“除了维基百科,还有哪些健康网站”为例,SelfQueryRetriever可以推断出“除了维基百科”表示需要过滤的 条件,即排除维基百科的文档。

它使用语言模型自动解析语句语义,提取过滤信息,无需手动设置。这种基于理解的元数据过滤更加智能方 便,可以自动处理更复杂的过滤逻辑。 掌握利用语言模型实现自动化过滤的技巧,可以大幅降低构建针对性问答系统的难度。这种自抽取查询的 方法使检索更加智能和动态。

 

from langchain.retrievers.self_query.base import SelfQueryRetriever

from langchain.chains.query_constructor.base import AttributeInfo

metadata_field_info_chinese = [

 AttributeInfo(

 name="source",

 description="The lecture the chunk is from, should be one of `./data/seatunnel.pdf`, `./data/seatunnel1.pdf`",

 type="string",

 ),

 AttributeInfo(

 name="page",

 description="The page from the lecture",

 type="integer",

 ),

 ]

document_content_description_chinese = "Matplotlib 课堂讲义"

retriever_chinese = SelfQueryRetriever.from_llm(

 llm,

 vectordb_chinese,

 document_content_description_chinese,

 metadata_field_info_chinese,

 verbose=True

 )

question_chinese = "他们在第二讲中对seatunnel做了些什么?"   

 

for d in docs_chinese:

 print(d.metadata)

其他技巧:压缩

在使用向量检索获取相关文档时,直接返回整个文档片段可能带来资源浪费,因为实际相关的只是文档 的一小部分。为改进这一点,LangChain提供了一种“ 压缩”检索机制。其工作原理是,先使用标准向量 检索获得候选文档,然后基于查询语句的语义,使用语言模型压缩这些文档,只保留与问题相关的部分。 例如,对“蘑菇的营养价值”这个查询,检索可能返回整篇有关蘑菇的长文档。经压缩后,只提取文档中与 “营养价值”相关的句子。

 

from langchain.retrievers import ContextualCompressionRetriever

from langchain.retrievers.document_compressors import LLMChainExtractor

def pretty_print_docs(docs):

 print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]))

compressor = LLMChainExtractor.from_llm(llm)  # 压缩器

compression_retriever_chinese = ContextualCompressionRetriever(

base_compressor=compressor,

base_retriever=vectordb_chinese.as_retriever()

)

# 对源文档进行压缩

question_chinese = "seatunnel是什么?"

compressed_docs_chinese = compression_retriever_chinese.get_relevant_documents(question_chinese)

pretty_print_docs(compressed_docs_chinese)

 

结合各种技术

为了去掉结果中的重复文档,我们在从向量数据库创建检索器时,可以将搜索类型设置为 MMR 。

compression_retriever_chinese = ContextualCompressionRetriever(

 base_compressor=compressor,

 base_retriever=vectordb_chinese.as_retriever(search_type = "mmr")

 )

question_chinese = "seatunnel是什么?"

compressed_docs_chinese = compression_retriever_chinese.get_relevant_documents(question_chinese)

pretty_print_docs(compressed_docs_chinese)

其他类型的检索

值得注意的是,vetordb 并不是唯一一种检索文档的工具。 LangChain 还提供了其他检索文档的方式, 例如: TF-IDF 或 SVM 。

 

from langchain.retrievers import SVMRetriever

from langchain.retrievers import TFIDFRetriever

from langchain.document_loaders import PyPDFLoader

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载PDF

loader_chinese = PyPDFLoader("./data/seatunnel.pdf")

pages_chinese = loader_chinese.load()

all_page_text_chinese = [p.page_content for p in pages_chinese]

joined_page_text_chinese = " ".join(all_page_text_chinese)

# 分割文本

text_splitter_chinese = RecursiveCharacterTextSplitter(chunk_size = 1500,chunk_overlap = 150)

splits_chinese = text_splitter_chinese.split_text(joined_page_text_chinese)

# 检索

svm_retriever = SVMRetriever.from_texts(splits_chinese, embedding)

tfidf_retriever = TFIDFRetriever.from_texts(splits_chinese)

 

question_chinese = "这门课的主要主题是什么?"

docs_svm_chinese = svm_retriever.get_relevant_documents(question_chinese)

print(docs_svm_chinese[0])

 

 

question_chinese = "seatunnel是什么?"

docs_tfidf_chinese = tfidf_retriever.get_relevant_documents(question_chinese)

print(docs_tfidf_chinese[0])

 


网站公告

今日签到

点亮在社区的每一天
去签到