LangChain 通义模型调用 (建立一个语义搜索引擎)
文章目录
介绍
本文将建立一个语义搜索引擎,说人话,就是做个文本的相似度查询。本文的目的也主要是为RAG技术打基础。RAG技术的本质就是检索出与用户问题相关信息,将信息传递给prompt,最后由模型进行生成。本文只是进行第一步,仅检索出相关信息。对于进行过自然语言处理任务的读者,文章很容易理解。如果对文章中embeddings不理解,可参考LangChain官网原文,当然我会对文中涉及的概念做简单解释。
文档(Documents)
LangChain 实现了一个 Document 抽象,它旨在表示一个文本单元和关联的元数据。它有三个属性:
- page_content:表示内容的字符串;
- metadata:包含任意元数据的 dict;
- id:(可选)文档的字符串标识符。
该属性可以捕获有关文档来源、文档与其他文档的关系以及其他信息的信息。
PS:单个对象通常表示较大文档的一个块。
文档加载(Document Loaders)
本文所用文档:示例文档
LangChain生态系统实现了与数百个常见数据源集成的文档加载器。这使得将这些来源的数据整合到你的AI应用中变得容易。这里我使用PyPDFLoader来加载文档。LangChain
from langchain_community.document_loaders import PyPDFLoaderfile_path = \"./example1.pdf\"loader = PyPDFLoader(file_path)docs = loader.load()print(len(docs))#输出#2
切分
对于信息检索和下游问答目的,页面的表示形式可能过于粗糙,模型输入token有限·。我们最终的目标是检索回答输入查询的对象,进一步拆分我们的 PDF 将有助于确保文档相关部分的含义不会被周围的文本“冲淡”和输入符合模型token限制;我们使用 text splitters 来实现这一目的。
Text splitters有四种,分别为基于长度的、基于文本结构的、基于文档结构的、基于语义的。这里我使用基于长度的RecursiveCharacterTextSplitter来进行切分,将文档切分为300个字符一个块,每个块重叠50个字符,通过add_start_index=True来设置字符索引,字符索引将保存到metadata中。
from langchain_text_splitters import RecursiveCharacterTextSplittertext_splitter = RecursiveCharacterTextSplitter( chunk_size=300,chunk_overlap=50,add_start_index=True)all_splits = text_splitter.split_documents(docs)len(all_splits)# 输出# 6
embedding
向量搜索是搜索和存储非结构化数据的常用方法,这个方法可以存储与文本相关的值(其实就是词向量)。给定一个查询,我们可以将其嵌入为相同维度的向量,并使用向量相似度指标(例如余弦相似度)来识别相关文本。
百炼大模型服务平台有多个支持词向量生成的模型,具体可参考官网
这里我生成两个词向量的示例。PS: 如果出现SSLError,多半是网络质量不太好。
import osfrom dotenv import load_dotenv, find_dotenvfrom langchain_community.embeddings import DashScopeEmbeddings# 加载环境变量_ = load_dotenv(find_dotenv())# 定义词向量模型embeddings = DashScopeEmbeddings( model=\"text-embedding-v1\", ) vector_1 = embeddings.embed_query(all_splits[0].page_content)vector_2 = embeddings.embed_query(all_splits[1].page_content)assert len(vector_1) == len(vector_2)print(f\"Generated vectors of length {len(vector_1)}\\n\")print(vector_1[:10])# 输出#Generated vectors of length 1536#[-6.747089385986328, -0.8481644988059998, -2.8858211040496826, 2.345369338989258, -1.3757027387619019, 3.001307249069214, -4.674506187438965, 1.7152488231658936, 4.980186939239502, -2.8219637870788574]
LangChain VectorStore 对象包含用于将文本和对象添加到存储区以及使用各种相似性指标查询它们的方法。Document通常使用嵌入模型进行初始化,嵌入模型确定如何将文本数据转换为数字向量。
向量存储
LangChain包括一系列不同的向量存储技术,某些向量由供应商(例如云供应商)提供,需要使用特定的凭证才能使用。有些数据库(如Postgres)运行在可以本地部署或通过第三方提供的独立基础设施上;还有可以在内存中运行的,以支持轻量级工作负载。这里我选择使用FAISS(简便起见,其实可以用InMemoryVectorStore),
大多数 vector store 实现都已实现可连接到现有的 vector store,笔者这里的网络质量就不是很好,所以我使用FAISS的目的是为了将词向量保存,再离线加载,减少SSLError(PS:其实最后相似度分数计算还是要调用embedding模型,并没有减少)。
接下来使用FAISS实例化向量存储。
import faissfrom langchain_community.docstore.in_memory import InMemoryDocstorefrom langchain_community.vectorstores import FAISS#定义索引index = faiss.IndexFlatL2(len(vector_1))#由于FAISS仅能存储向量值,所以还需InMemoryDocstore()来存储原始文档# index_to_docstore_id用来维护向量索引到文档 ID 的映射, 使得每个#存储的向量可以关联到其原始文本。vector_store = FAISS( embedding_function=embeddings, index=index, docstore=InMemoryDocstore(), index_to_docstore_id={}, )# 向向量存储 (vector_store) 添加文档,并返回其唯一 ID 列表。ids = vector_store.add_documents(documents=all_splits)# 保存词向量vector_store.save_local(\"faiss_index\")
一旦我们实例化一个包含文档的VectorStore,我们就可以查询它,VectorStore用于查询的方法包括:
- 同步和异步;
- 按字符串查询和按向量;
- 返回和不返回相似性分数;
- 通过相似性和最大边际相关性(以平衡相似性与查询与检索结果中的多样性)。
这些方法的输出中通常包括 Document 对象的列表。
用法
首先离线加载上步保存的向量。
new_vector_store = FAISS.load_local( \"faiss_index\", embeddings, allow_dangerous_deserialization=True)
嵌入向量通常将文本表示为“密集”向量,以便具有相似含义的文本在几何上接近。这样,我们只需传入一个问题即可检索相关信息,而无需了解文档中使用的任何特定关键术语。
- 根据与字符串查询的相似性返回文档:
results = new_vector_store.similarity_search( \"SM3是什么?\")print(results[0])# 输出\'\'\'page_content=\'SM3(Chinese National SM3 Hash Algorithm) • 位数:256-bit(32 字节) • 简介:SM3 是中国国家密码管理局(SCA)发布的哈希算法,作为中国商用密码标准(GBT.32905-2016) 。它基于 SHA-256 的设计,但对部分结构进行了改进,以增强安全性。 • 用途: 电子认证(如数字签名、身份认证)、区块链应用、安全协议(如 TLS/SSL)、数据完整性保护 • 安全性:SM3 的安全性比 SHA-1 更强,抗碰撞能力更优,目前没有有效的攻击方法。SM3 在中国市场中被广泛用于金融、政府、企业等安全应用,并被强\' metadata={\'producer\': \'Adobe PDF Library 22.1.117\', \'creator\': \'Acrobat PDFMaker 22 Word 版\', \'creationdate\': \'2025-03-03T15:20:09+08:00\', \'author\': \'政安 孟\', \'comments\': \'\', \'company\': \'\', \'keywords\': \'\', \'moddate\': \'2025-03-03T15:20:10+08:00\', \'sourcemodified\': \'D:20250303071839\', \'subject\': \'\', \'title\': \'\', \'source\': \'./example1.pdf\', \'total_pages\': 2, \'page\': 0, \'page_label\': \'1\', \'start_index\': 0}\'\'\'
- 异步查询
results = await new_vector_store.asimilarity_search(\"SM2 是什么?\")print(results[0])# 输出\'\'\'page_content=\'• 位数:256-bit(非对称加密,基于椭圆曲线) • 简介:SM2 是基于**椭圆曲线密码学(ECC)**的非对称加密算法,由中国国家密码管理局(SCA)制定,并成为国家标准(GBT.32918-2016) 。与国际上的 ECC(如 ECDSA、ECDH)类似,但具有更高效的优化。 • 用途: 数字签名:SM2 可用于电子签名、电子合同,确保数据的不可否认性和完整性。密钥交换:SM2 可用于安全通信协议(如 SSL/TLS) ,保障密钥传输安全。公钥加密:SM2 可用于电子邮件加密、云端存储数据加密等。金融与\' metadata={\'producer\': \'Adobe PDF Library 22.1.117\', \'creator\': \'Acrobat PDFMaker 22 Word 版\', \'creationdate\': \'2025-03-03T15:20:09+08:00\', \'author\': \'政安 孟\', \'comments\': \'\', \'company\': \'\', \'keywords\': \'\', \'moddate\': \'2025-03-03T15:20:10+08:00\', \'sourcemodified\': \'D:20250303071839\', \'subject\': \'\', \'title\': \'\', \'source\': \'./example1.pdf\', \'total_pages\': 2, \'page\': 0, \'page_label\': \'1\', \'start_index\': 790}\'\'\'
- 返回分数
#注意,不同供应商的计算方式不同,#分数和相似度可能是正相关,也可能是负相关。这里是正相关results = new_vector_store.similarity_search_with_score(\"What was Nike\'s revenue in 2023?\")doc, score = results[0]print(f\"Score: {score}\\n\")print(doc)# 输出\'\'\'Score: 7899.66552734375page_content=\'击方法。SM3 在中国市场中被广泛用于金融、政府、企业等安全应用,并被强制要求在部分领域替代 SHA-256。 SM4(Chinese National SM4 Block Cipher Algorithm) • 位数:128-bit 密钥(对称加密) • 简介:SM4 是中国国家密码管理局(SCA)发布的分组加密算法,作为国家商用密码标准(GBT.32907-2016) 。它是基于 Feistel 结构的对称加密算法,类似于 AES,但经过优化以适应轻量级设备和国产安全要求。 • 用途: 无线局域网(WLAN)安全:SM4 是中国 WAPI(无线局域网安全协\' metadata={\'producer\': \'Adobe PDF Library 22.1.117\', \'creator\': \'Acrobat PDFMaker 22 Word 版\', \'creationdate\': \'2025-03-03T15:20:09+08:00\', \'author\': \'政安 孟\', \'comments\': \'\', \'company\': \'\', \'keywords\': \'\', \'moddate\': \'2025-03-03T15:20:10+08:00\', \'sourcemodified\': \'D:20250303071839\', \'subject\': \'\', \'title\': \'\', \'source\': \'./example1.pdf\', \'total_pages\': 2, \'page\': 0, \'page_label\': \'1\', \'start_index\': 259}\'\'\'
- 根据与嵌入式查询的相似性返回文档:
embedding = embeddings.embed_query(\"SM3密码有什么用途?\")results = new_vector_store.similarity_search_by_vector(embedding)print(results[0])# 输出\"\"\"page_content=\'SM3(Chinese National SM3 Hash Algorithm) • 位数:256-bit(32 字节) • 简介:SM3 是中国国家密码管理局(SCA)发布的哈希算法,作为中国商用密码标准(GBT.32905-2016) 。它基于 SHA-256 的设计,但对部分结构进行了改进,以增强安全性。 • 用途: 电子认证(如数字签名、身份认证)、区块链应用、安全协议(如 TLS/SSL)、数据完整性保护 • 安全性:SM3 的安全性比 SHA-1 更强,抗碰撞能力更优,目前没有有效的攻击方法。SM3 在中国市场中被广泛用于金融、政府、企业等安全应用,并被强\' metadata={\'producer\': \'Adobe PDF Library 22.1.117\', \'creator\': \'Acrobat PDFMaker 22 Word 版\', \'creationdate\': \'2025-03-03T15:20:09+08:00\', \'author\': \'政安 孟\', \'comments\': \'\', \'company\': \'\', \'keywords\': \'\', \'moddate\': \'2025-03-03T15:20:10+08:00\', \'sourcemodified\': \'D:20250303071839\', \'subject\': \'\', \'title\': \'\', \'source\': \'./example1.pdf\', \'total_pages\': 2, \'page\': 0, \'page_label\': \'1\', \'start_index\': 0}\"\"\"
Retrievers
Retriever是一个根据查询从知识库返回相关信息的组件。
LangChain的VectorStore不是 Runnable的子类,而 Runnable是LangChain中各组件使用LCEL连接的基础,所以LangChain提供了 Retriever,来实现相关检索。 Retriever是 Runnable的子类,因此,它们实现了一组标准的方法(例如,同步和异步以及作)。尽管我们可以从向量存储构建检索器,但检索器也可以与非向量存储数据源(例如外部 API)交互。invokebatch
我们可以自己创建一个简单版本,无需子类化 。如果我们选择希望使用什么方法来检索文档,我们可以轻松地创建一个 runnable。下面我们将围绕该方法构建一个:
from typing import Listfrom langchain_core.documents import Documentfrom langchain_core.runnables import chain#@chain 装饰器是 LangChain 框架中的一个装饰器,#它的作用是将一个普通的 Python 函数转换为 可组合的可运行对象(runnable object),#从而可以与 LangChain 的其他组件(如 Agents、Chains、Pipelines 等)无缝集成。@chaindef retriever(query: str) -> List[Document]: return new_vector_store.similarity_search(query, k=1)retriever.batch( [ \"SM3是什么?\", \"SM4是什么?\", ],)
Vectorstores 实现了一个生成 Retriever 的方法,特别是 VectorStoreRetriever。这些检索器包括 specific 和 attributes,用于标识要调用的基础向量存储的方法以及如何参数化它们。例如,我们可以用下面的代码来复制上面的内容:
retriever = new_vector_store.as_retriever( search_type=\"similarity\", search_kwargs={\"k\": 1},)retriever.batch( [ \"SM3是什么?\", \"SM4是什么?\", ],)
VectorStoreRetriever
支持以下几种搜索类型:(默认的)“similarity”(相似度)、“mmr”(最大边际相关性,如上所述)以及通过相似度分数来设定文档输出阈值的 “similarity_score_threshold”。我们可以使用后者根据相似度分数对检索器输出的文档进行筛选。
检索器可以轻松地集成到更复杂的应用程序中,例如检索增强生成(RAG)应用程序,该应用将给定的问题与检索到的上下文结合起来,形成一个大型语言模型(LLM)的提示。
参考
[1]Build a semantic search engine
[2]Faiss