一、檢索結果重復
1. 問題分析
在構建向量數據庫時,對文檔分割會存在重復塊(chunk_overlap:指兩個塊之間共享的字符數量,用于保持上下文的連貫性,避免分割丟失上下文信息),如下圖所示:
因此,在進行向量檢索是容易出現檢索到重復結果的情況,如下:
docs[0]
page_content='第?回:Matplotlib 初相識\n?、認識matplotlib\nMatplotlib 是?個 Python 2D 繪圖庫,能夠以多種硬拷?格式和跨平臺的交互式環境?成出版物質量的圖形,?來繪制各種靜態,動態,\n交互式的圖表。\nMatplotlib 可?于 Python 腳本, Python 和 IPython Shell 、 Jupyter notebook , Web 應?程序服務器和各種圖形?戶界??具包等。\nMatplotlib 是 Python 數據可視化庫中的泰?,它已經成為 python 中公認的數據可視化?具,我們所熟知的 pandas 和 seaborn 的繪圖接?\n其實也是基于 matplotlib 所作的?級封裝。\n為了對matplotlib 有更好的理解,讓我們從?些最基本的概念開始認識它,再逐漸過渡到?些?級技巧中。\n?、?個最簡單的繪圖例?\nMatplotlib 的圖像是畫在 figure (如 windows , jupyter 窗體)上的,每?個 figure ?包含了?個或多個 axes (?個可以指定坐標系的?區\n域)。最簡單的創建 figure 以及 axes 的?式是通過 pyplot.subplots命令,創建 axes 以后,可以使? Axes.plot繪制最簡易的折線圖。\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\nimport numpy as np\nfig, ax = plt.subplots() # 創建?個包含?個 axes 的 figure\nax.plot([1, 2, 3, 4], [1, 4, 2, 3]); # 繪制圖像\nTrick: 在jupyter notebook 中使? matplotlib 時會發現,代碼運?后?動打印出類似 <matplotlib.lines.Line2D at 0x23155916dc0>\n這樣?段話,這是因為 matplotlib 的繪圖代碼默認打印出最后?個對象。如果不想顯示這句話,有以下三種?法,在本章節的代碼示例\n中你能找到這三種?法的使?。\n\x00. 在代碼塊最后加?個分號 ;\n\x00. 在代碼塊最后加?句 plt.show()\n\x00. 在繪圖時將繪圖對象顯式賦值給?個變量,如將 plt.plot([1, 2, 3, 4]) 改成 line =plt.plot([1, 2, 3, 4])\n和MATLAB 命令類似,你還可以通過?種更簡單的?式繪制圖像, matplotlib.pyplot?法能夠直接在當前 axes 上繪制圖像,如果?戶\n未指定axes , matplotlib 會幫你?動創建?個。所以上?的例?也可以簡化為以下這??代碼。\nline =plt.plot([1, 2, 3, 4], [1, 4, 2, 3]) \n三、Figure 的組成\n現在我們來深?看?下 figure 的組成。通過?張 figure 解剖圖,我們可以看到?個完整的 matplotlib 圖像通常會包括以下四個層級,這些\n層級也被稱為容器( container ),下?節會詳細介紹。在 matplotlib 的世界中,我們將通過各種命令?法來操縱圖像中的每?個部分,\n從?達到數據可視化的最終效果,?副完整的圖像實際上是各類?元素的集合。\nFigure:頂層級,?來容納所有繪圖元素' metadata={'source': 'docs/matplotlib/第一回:Matplotlib初相識.pdf', 'page': 0}
docs[1]
page_content='第?回:Matplotlib 初相識\n?、認識matplotlib\nMatplotlib 是?個 Python 2D 繪圖庫,能夠以多種硬拷?格式和跨平臺的交互式環境?成出版物質量的圖形,?來繪制各種靜態,動態,\n交互式的圖表。\nMatplotlib 可?于 Python 腳本, Python 和 IPython Shell 、 Jupyter notebook , Web 應?程序服務器和各種圖形?戶界??具包等。\nMatplotlib 是 Python 數據可視化庫中的泰?,它已經成為 python 中公認的數據可視化?具,我們所熟知的 pandas 和 seaborn 的繪圖接?\n其實也是基于 matplotlib 所作的?級封裝。\n為了對matplotlib 有更好的理解,讓我們從?些最基本的概念開始認識它,再逐漸過渡到?些?級技巧中。\n?、?個最簡單的繪圖例?\nMatplotlib 的圖像是畫在 figure (如 windows , jupyter 窗體)上的,每?個 figure ?包含了?個或多個 axes (?個可以指定坐標系的?區\n域)。最簡單的創建 figure 以及 axes 的?式是通過 pyplot.subplots命令,創建 axes 以后,可以使? Axes.plot繪制最簡易的折線圖。\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\nimport numpy as np\nfig, ax = plt.subplots() # 創建?個包含?個 axes 的 figure\nax.plot([1, 2, 3, 4], [1, 4, 2, 3]); # 繪制圖像\nTrick: 在jupyter notebook 中使? matplotlib 時會發現,代碼運?后?動打印出類似 <matplotlib.lines.Line2D at 0x23155916dc0>\n這樣?段話,這是因為 matplotlib 的繪圖代碼默認打印出最后?個對象。如果不想顯示這句話,有以下三種?法,在本章節的代碼示例\n中你能找到這三種?法的使?。\n\x00. 在代碼塊最后加?個分號 ;\n\x00. 在代碼塊最后加?句 plt.show()\n\x00. 在繪圖時將繪圖對象顯式賦值給?個變量,如將 plt.plot([1, 2, 3, 4]) 改成 line =plt.plot([1, 2, 3, 4])\n和MATLAB 命令類似,你還可以通過?種更簡單的?式繪制圖像, matplotlib.pyplot?法能夠直接在當前 axes 上繪制圖像,如果?戶\n未指定axes , matplotlib 會幫你?動創建?個。所以上?的例?也可以簡化為以下這??代碼。\nline =plt.plot([1, 2, 3, 4], [1, 4, 2, 3]) \n三、Figure 的組成\n現在我們來深?看?下 figure 的組成。通過?張 figure 解剖圖,我們可以看到?個完整的 matplotlib 圖像通常會包括以下四個層級,這些\n層級也被稱為容器( container ),下?節會詳細介紹。在 matplotlib 的世界中,我們將通過各種命令?法來操縱圖像中的每?個部分,\n從?達到數據可視化的最終效果,?副完整的圖像實際上是各類?元素的集合。\nFigure:頂層級,?來容納所有繪圖元素' metadata={'source': 'docs/matplotlib/第一回:Matplotlib初相識.pdf', 'page': 0}
2.解決方法
最大邊際相關性((MMR,Maximal Marginal Relevance):基本思想是同時考量查詢與文檔的相關度,以及文檔之間的相似度。它把搜索結果中相似度很高的文檔做了過濾,所以它保留了結果的相關性又同時兼顧了結果的多樣性。
smalldb_chinese.max_marginal_relevance_search(question_chinese,k=2, fetch_k=3)
#k=3 ,獲取 3 個文檔,k=2 表示返回最不同的 2 個文檔。
二、檢索錯誤答案
1.問題分析
當詢問關于文檔中某一講的問題,但得到的結果中也包括了來自其他講的結果,如下所示:
# 提問:
question_chinese = "他們在第二講中對Figure說了些什么?"
docs_chinese = vectordb_chinese.similarity_search(question_chinese,k=5)
for doc_chinese in docs_chinese:print(doc_chinese.metadata)# 檢索結果:
{'source': 'docs/matplotlib/第一回:Matplotlib初相識.pdf', 'page': 0}
{'source': 'docs/matplotlib/第一回:Matplotlib初相識.pdf', 'page': 0}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 9}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 10}
{'source': 'docs/matplotlib/第一回:Matplotlib初相識.pdf', 'page': 1}
2.解決辦法
(1)使用元數據進行過濾
#提問:
question_chinese = "他們在第二講中對Figure說了些什么?"
docs_chinese = vectordb_chinese.similarity_search(question_chinese,k=3,filter={"source":"docs/matplotlib/第二回:藝術畫筆見乾坤.pdf"}
)
for d in docs_chinese:print(d.metadata)#檢索結果:
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 9}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 10}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 0}
(2)LLM輔助檢索
LangChain提供了SelfQueryRetriever模塊,它可以通過語言模型從問題語句中分析出:
a:向量搜索的查詢字符串(search term)
b:過濾文檔的元數據條件(Filter)
# 提問:
# 使用 SelfQueryRetriever 構建一個支持自然語言查詢的向量檢索器
retriever_chinese = SelfQueryRetriever.from_llm(llm, # 語言模型,用于將用戶的自然語言查詢轉換為結構化檢索指令(如過濾條件、關鍵詞等)vectordb_chinese, # 向量數據庫,用于實際的向量相似度檢索(比如 Chroma、FAISS、Milvus 等)document_content_description_chinese, # 字符串,描述文檔的主要內容類型,例如“這是關于中國古代文學的文檔”metadata_field_info_chinese, # 元信息字段描述列表,定義哪些元數據字段可以被過濾,并提供字段的說明。例如: # [AttributeInfo(name="作者", description="文章的作者", type="string"), ...]verbose=True # 是否打印詳細的解釋和調試信息(例如構造出的查詢表達式等),便于理解 LLM 是如何生成查詢的
)
question_chinese = "他們在第二講中對Figure做了些什么?"
docs_chinese = retriever_chinese.get_relevant_documents(question_chinese)
for d in docs_chinese:print(d.metadata)#檢索結果:
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 9}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 10}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 0}
{'source': 'docs/matplotlib/第二回:藝術畫筆見乾坤.pdf', 'page': 6}
三、檢索結果冗余
1.問題分析
在使用向量檢索獲取相關文檔時,直接返回整個文檔片段可能帶來資源浪費,因為實際相關的只是文檔的一小部分。為改進這一點,LangChain提供了一種“壓縮”檢索機制。其工作原理是,先使用標準向量檢索獲得候選文檔,然后基于查詢語句的語義,使用語言模型壓縮這些文檔,只保留與問題相關的部分。例如,對“蘑菇的營養價值”這個查詢,檢索可能返回整篇有關蘑菇的長文檔。經壓縮后,只提取文檔中與“營養價值”相關的句子。
2.解決辦法
#提問:
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractordef 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)]))llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm) # 壓縮器compression_retriever_chinese = ContextualCompressionRetriever(base_compressor=compressor,base_retriever=vectordb_chinese.as_retriever()
)
# 對源文檔進行壓縮question_chinese = "Matplotlib是什么?"
compressed_docs_chinese = compression_retriever_chinese.get_relevant_documents(question_chinese)
pretty_print_docs(compressed_docs_chinese)#輸出結果:
Document 1:Matplotlib 是?個 Python 2D 繪圖庫,能夠以多種硬拷?格式和跨平臺的交互式環境?成出版物質量的圖形,?來繪制各種靜態,動態,交互式的圖表。
----------------------------------------------------------------------------------------------------
Document 2:Matplotlib 是?個 Python 2D 繪圖庫,能夠以多種硬拷?格式和跨平臺的交互式環境?成出版物質量的圖形,?來繪制各種靜態,動態,交互式的圖表。