CH3_02_langchain_faiss
1511 字
8 分钟
CH3_02_langchain_faiss
下面的代码演示了使用 LangChain 和 FAISS 完成一个完整的“创建 -> 保存 -> 加载 -> 查询”流程。
from langchain_community.vectorstores import FAISSfrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_core.documents import Document这里我们引入了 LangChain 的强大生态系统:
langchain_community.vectorstores&FAISS:这是由社区维护的第三方集成包,FAISS就是向量数据库的封装类。它将 FAISS 封装为了 LangChain 统一的向量存储接口,提供from_documents、similarity_search、save_local、load_local等方法,方便与 LangChain 的其他组件(Chain、Retriever 等)无缝配合。langchain_community.embeddings&HuggingFaceEmbeddings:这里是将 Hugging Face 上的 Embedding 模型(如我们接下来用的BAAI/bge-small-zh-v1.5)封装成 LangChain 统一接口。只需指定model_name,就能把文本转为向量,供 FAISS 存储和检索。langchain_core.documents&Document:这是 LangChain 的核心基础包,其中Document是文档数据类。它是 LangChain 中最基本的数据结构,包含两个字段:page_content(文本内容)和metadata(元数据字典)。整个 RAG 流程中的数据载体——文本加载、分块、向量化、检索,都以Document为单位传递。
# 1. 示例文本和嵌入模型texts = [ "张三是法外狂徒", "FAISS是一个用于高效相似性搜索和密集向量聚类的库。", "LangChain是一个用于开发由语言模型驱动的应用程序的框架。"]docs = [Document(page_content=t) for t in texts]embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")# 2. 创建向量存储并保存到本地vectorstore = FAISS.from_documents(docs, embeddings)
local_faiss_path = "./faiss_index_store"vectorstore.save_local(local_faiss_path)
print(f"FAISS index has been saved to {local_faiss_path}")from_documents:返回一个由documents和embedding初始化的 VectorStore 对象,接收三个参数:documents:要添加到 VectorStore 的文档列表;embedding:要使用的嵌入函数;kwargs:其他关键字参数。
save_local:将 FAISS 索引、文档库和index_to_docstore_id保存到磁盘,接收两个参数:folder_path:要保存的文件夹路径;index_name:用于指定保存的索引文件名。
索引创建细节
通过深入 LangChain 源码,可以发现索引创建是一个分层、解耦的过程,主要涉及以下几个方法的嵌套调用:
-
from_documents(封装层):- 这是我们直接调用的方法。它的职责很简单:从输入的
Document对象列表中提取出纯文本内容 (page_content) 和元数据 (metadata)。 - 然后,它将这些提取出的信息传递给核心的
from_texts方法。
- 这是我们直接调用的方法。它的职责很简单:从输入的
-
from_texts(向量化入口):- 这个方法是面向用户的入口。它接收文本列表,并执行关键的第一步:调用
embedding.embed_documents(texts),将所有文本批量转换为向量。 - 完成向量化后,它并不直接处理索引构建,而是将生成的向量和其他所有信息(文本、元数据等)传递给一个内部的辅助方法
__from。
- 这个方法是面向用户的入口。它接收文本列表,并执行关键的第一步:调用
-
__from(构建索引框架):- 一个内部方法,负责搭建 FAISS 向量存储的“空框架”。
- 它会根据指定的距离策略(默认为 L2 欧氏距离)初始化一个空的 FAISS 索引结构(如
faiss.IndexFlatL2)。 - 同时,它也准备好了用于存储文档原文的
docstore和用于连接 FAISS 索引与文档的index_to_docstore_id映射。 - 最后,它调用另一个内部方法
__add来完成数据的填充。
-
__add(填充数据):- 真正执行数据添加操作的核心。它接收到向量、文本和元数据后,执行以下关键操作:
- 添加向量: 将向量列表转换为 FAISS 需要的
numpy数组,并调用self.index.add(vector)将其批量添加到 FAISS 索引中。 - 存储文档: 将文本和元数据打包成
Document对象,存入docstore。 - 建立映射: 更新
index_to_docstore_id字典,建立起 FAISS 内部的整数 ID(如 0, 1, 2…)到我们文档唯一 ID 的映射关系。
- 添加向量: 将向量列表转换为 FAISS 需要的
- 真正执行数据添加操作的核心。它接收到向量、文本和元数据后,执行以下关键操作:
# 3. 加载索引并执行查询# 加载时需指定相同的嵌入模型,并允许反序列化loaded_vectorstore = FAISS.load_local( local_faiss_path, embeddings, allow_dangerous_deserialization=True)load_local:从磁盘加载 FAISS 索引、文档库和index_to_docstore_id,接收四个参数:folder_path:要从中加载索引、文档库和index_to_docstore_id的文件夹路径;embeddings:生成查询时使用的嵌入;index_name:用于保存的特定索引文件名;allow_dangerous_deserialization:是否允许反序列化数据,这涉及到加载 pickle 文件。恶意攻击者可以修改 pickle 文件,从而传递恶意载荷,导致在您的计算机上执行任意代码。
序列化与反序列化
序列化(Serialization):把内存中的对象(比如 Python 的列表、字典、自定义类实例)转换成可以存储或传输的格式(比如二进制文件、JSON 字符串)。
反序列化(Deserialization):反过来,把存储的二进制数据恢复成内存中的对象。
当我们调用 FAISS.load_local() 时,如上所述,它需要从磁盘恢复三样东西:
index.faiss:内容为 FAISS 向量索引,二进制格式,安全;index.pkl:文档库 +index_to_docstore_id,Pickle 格式,有风险。
之所以说 index.pkl 是危险的,是因为 Pickle 反序列化可以执行任意代码。考虑一个恶意构造的 pickle 文件:
# 攻击者构造的恶意 pickle 数据import pickleimport os
# 在反序列化时自动执行系统命令class Malicious: def __reduce__(self): return (os.system, ("rm -rf /重要数据",))
with open("index.pkl", "wb") as f: pickle.dump(Malicious(), f)可以看到,当我们用 pickle.load() 加载它时,这段恶意代码就会在我们的电脑上执行。相当于你捡了一个 U 盘插到自己电脑上,如果 U 盘里有病毒,你就中招了。
这里我们传入 allow_dangerous_deserialization=True,是因为这里的 .pkl 文件是我们刚刚自己保存的,来源可信,所以安全。
# 执行相似性搜索query = "FAISS是做什么的?"results = loaded_vectorstore.similarity_search(query, k=1)similarity_search:返回最符合 query 的 docs,接收四个参数:query:要查找相似 doc 的文本;k:要返回 docs 的数量,默认为 4;filter:可选,按元数据过滤,默认为 None;fetch_k:可选,过滤前要获取的 docs 数量,默认为 20.
print(f"\n查询: '{query}'")print("相似度最高的文档:")for doc in results: print(f"- {doc.page_content}")文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
CH3_02_langchain_faiss
https://datawhalechina.github.io/all-in-rag/#/chapter1/03_get_start_rag 相关文章 智能推荐
1
CH2_02_character_splitter
all-in-rag LangChain 中文本分割器的几种核心策略
2
CH3_04_multi_milvus
all-in-rag 使用 Milvus 进行多模态检索
3
CH3_03_llamaindex_vector
all-in-rag 实现对 LlamaIndex 存储数据的加载和相似性搜索
4
CH3_01_bge_visualized
all-in-rag 常用多模态嵌入模型,以 bge-visualized-m3 为例
5
CH2_01_unstructured_example
all-in-rag 如何直接使用 Unstructured 库的示例
随机文章 随机推荐