LangChain快速入门-05RAG(向量数据库)
文章目录
5.向量存储和检索
5.1 向量数据库的理解
假设你是一名摄影师,拍了大量的照片。为了方便管理和查找,你决定将这些照片存储到一个数据库中。传统的关系型数据库(如 MySQL、PostgreSQL 等)可以帮助你存储照片的元数据,比如拍摄时间、地点、相机型号等。
但是,当你想要根据照片的内容(如颜色、纹理、物体等)进行搜索时,传统数据库可能无法满足你的需求,因为它们通常以数据表的形式存储数据,并使用查询语句进行精确搜索。那么此时,向量数据库就可以派上用场。
我们可以构建一个多维的空间使得每张照片特征都存在于这个空间内,并用已有的维度进行表示,比如时间、地点、相机型号、颜色….此照片的信息将作为一个点,存储于其中。以此类推,即可在该空间中构建出无数的点,而后我们将这些点与空间坐标轴的原点相连接,就成为了一条条向量,当这些点变为向量之后,即可利用向量的计算进一步获取更多的信息。当要进行照片的检索时,也会变得更容易更快捷。
注意,在向量数据库中进行检索时,并不是检索唯一的匹配结果,而是查询和目标向量最为相似的一些向量,具有模糊性。
延伸思考一下,只要对图片、视频、商品等素材进行向量化,就可以实现以图搜图、视频相关推荐、相似商品推荐等功能。
5.2 常用的向量数据库
LangChain提供了众多向量存储的集成,包括开源的本地向量存储与云托管的私有向量存储。并公开了一个标准接口,可以轻松地在向量存储之间进行交换。
常用向量数据库:
| 向量数据库 | 描述 |
|---|---|
| FAISS | 一个用于高效相似性搜索和密集向量聚类的库 |
| Chroma | 开源的轻量级向量数据库,有极简的 API |
| Milvus | 开源的专为向量搜索设计的云原生数据库。性能强悍,功能丰富。覆盖轻量级的原型开发到十亿级向量的大规模生产系统 |
| Pgvector | 开源关系型数据库 PostgreSQL 的扩展,为PostgreSQL增加了向量数据类型和相似性搜索功能 |
| Redis | 开源内存数据结构存储,现已原生支持向量相似性搜索功能 |
| Elasticsearch | 开源分布式搜索和分析引擎,提供了一个基于文档的数据库,结构化、非结构化和向量数据通过高效的列式存储统一管理 |
这里我们使用 Milvus 作为向量存储。
5.3 Milvus 介绍和部署
5.3.1 Milvus架构
Milvus因其强大的性能(可支持数百亿级别的向量存储和检索),以及可扩缩容等相关特点,在实际生产环境下,使用非常普遍。
Milvus的架构如下图所示:
Milvus 的组件解耦良好,其中三个最关键的任务 —— 搜索、数据插入以及索引 / 压缩 —— 被设计为易于并行化的进程,复杂逻辑被分离出来。这确保了相应的查询节点、数据节点和索引节点能够独立地进行纵向和横向扩展,从而优化性能和成本效率。
5.3.2 Milvus Collection及数据类型
Milvus 通过 数据库—Collections—实体 的结构管理数据。Collections 和实体就类似关系型数据库中的表和记录。具体来说,Collection 是一个二维表,具有固定的列和变化的行。每列代表一个字段,每行代表一个实体。
Collection 通过 Collection Schema 来定义有哪些字段以及字段的类型、索引等。
以下是Milvus所支持的数据类型:
一个 Collection Schema 有一个主键、最多四个向量字段和若干标量字段。主键用于唯一标识一个实体,只接受 Int64 或 VarChar 值。插入实体时,默认情况下应包含主键值。但是,如果在创建 Collections 时启用了 AutoId,Milvus 将在插入数据时生成主键值,此时插入的实体中不应包含主键值。
向量字段是最重要的字段,可以分为稠密向量或者是稀疏向量。
稠密向量通常由基于 Transformer Encoder 架构的深度学习模型(如 Sentence-BERT)生成。
稀疏向量用于表示文本中的关键词及其对应权重,其构建方式经历了从早期基于 TF-IDF 等统计方法的传统词袋模型,到如今基于深度学习模型的学习式构建方法的发展演进。
使用深度学习模型构建稀疏向量的原理如下图所示(分词过程和稠密向量生成当中类似):
通过深度学习模型生成一个稀疏向量时, 把这些重要词单独拎出来加权表示。
下面以BGE-M3为例,来展示稠密向量和稀疏向量的不同:
先下载BGE-M3模型
from huggingface_hub import snapshot_download
# 替换成你想存放模型的路径
local_dir_path = "E:/tool/python/ai/my_models/bge-m3"
snapshot_download(
repo_id="BAAI/bge-m3",
local_dir=local_dir_path,
local_dir_use_symlinks=False, # 推荐,直接下载文件而非创建符号链接[reference:5]
resume_download=True, # 支持断点续传[reference:6]
)
print("模型下载完成!")
示例代码
from FlagEmbedding import BGEM3FlagModel
print("脚本开始执行", flush=True)
# 创建BGE-M3模型
model = BGEM3FlagModel(
model_name_or_path=r"E:\tool\python\ai\my_models\bge-m3",
# 返回稠密向量
return_dense=True,
# 返回稀疏向量
return_sparse=True
)
# 转换向量
result = model.encode(["LangChain是2022年10月,由哈佛大学的Harrison Chase(哈里森·蔡斯)发起研发的一个开源框架,用于开发由大语言模型(LLMs)驱动的应用程序。"])
# 打印结果
print(result)
# 获取稠密向量
dense_vector = result["dense_vecs"]
print(f"稠密向量是:{dense_vector}")
# 获取稀疏向量
sparse_vector = result["lexical_weights"]
print(f"稀疏向量是:{sparse_vector}")
# 将id转化为token
sparse_vector_to_token = model.convert_id_to_token(sparse_vector)
print(f"稀疏向量对应的token是:{sparse_vector_to_token}")
标量字段通常用来存储一些元数据,并可以在搜索时通过元数据进行过滤,以提高搜索结果的正确性。
5.3.3 Mivus索引
索引是建立在数据之上的附加结构,可以加快搜索速度。不同字段数据类型适用不同的索引类型。
1)稠密向量
稠密向量可使用 HNSW(分层导航小世界)索引。
HNSW (分层导航小世界)是当下常用的一种基于图的索引算法,可以提高搜索高维浮点数向量时的性能。它具有出色的搜索精度和低延迟,但需要较高的内存开销来维护其分层图结构。该算法构建了一个多层图(类似不同缩放级别的地图),底层包含所有数据点,而上层则由从底层采样的数据点子集组成。在这种层次结构中,每一层都包含代表数据点的节点,节点之间由表示其接近程度的边连接。上层提供远距离跳转,以快速接近目标,而下层则进行细粒度搜索,以获得最准确的结果。其工作原理如下:
- 入口点:搜索从顶层的一个固定入口点开始,该入口点是图中的一个预定节点。
- 贪婪搜索:算法贪婪地移动 到当前层的近邻,直到无法再接近查询向量为止。上层起到导航作用,作为粗过滤器,为下层的精细搜索找到潜在的入口点。
- 层层下降:一旦当前层达到局部最小值,算法就会利用预先建立的连接跳转到下层,并重复贪婪搜索。
- 最后细化:这一过程一直持续到最底层,在最底层进行最后的细化步骤,找出最近的邻居。

另外一种稠密向量所支持的索引类型是FLAT, FLAT 索引是用于对浮点向量进行索引和搜索的最简单、最直接的方法之一。它采用暴力搜索的方式,每个查询向量直接与数据集中的每个向量进行比较,无需任何高级的预处理或数据结构化操作。这种方法能保证准确性,提供 100% 的召回率,因为每一个潜在的匹配项都会被评估。
2)稀疏向量
SPARSE_INVERTED_INDEX 索引是 Milvus 用于高效存储和搜索稀疏向量的一种索引类型。这种索引类型利用倒排索引的原理,为稀疏数据创建高效的搜索结构。
以下是倒排索引示意图:
在实际查询时,先通过倒排索引查询到包含query当中token的文档有哪些,然后计算查询和各个文档之间的相似度分数。
在计算查询和文档之间的相似度分数时,有两种指标类型(metric type):
- IP: 即Inner Product内积,例如查询所对应的稀疏向量为:{27:0.7, 100:0.4},doc1所对应的稀疏向量为:{27:0.5, 100:0.3, 5369:0.6},则相似度分数为similarity = (0.5×0.7) + (0.3×0.4) = 0.35 + 0.12 = 0.47
- BM25: 通过对 query 与文档中重合的词项进行加权求和,综合考虑词频(TF)、逆文档频率(IDF: 反映了一个词在全部文档当中的重要性,词出现的文档数量越少,IDF越高)以及文档长度归一化,从而计算二者的相关性得分。
5.3.4 Milvus部署
Milvus 提供了多个版本以在不同场景下选择合适的使用方式:
- Milvus Lite:本地轻量化运行,通过 pip install pymilvus[milvus-lite] 即可安装。但 Milvus Lite 有一些限制,比如 Milvus Lite 仅支持 FLAT 索引类型。无论在 Collections 中指定了哪种索引类型,它都使用 FLAT 类型。另外,Milvus Lite仅支持在MacOs和Linux上面使用。
- Milvus Standalone:单点部署,支持通过 Docker 部署。
- Milvus Distributed:分布式部署,支持在 Kubernetes 集群上部署。
这里我们使用Milvus Standalone方式部署
1)下载standalone_embed.sh文件
先在windows中cmd打开黑窗口输入命令
命令
curl.exe -fL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
注意:standalone_embed.sh 脚本就是专为在 Linux 环境下快速部署和启动 Milvus 独立模式(Standalone)而设计的自动化脚本。
2)standalone_embed.sh复制到虚拟机
执行后standalone_embed.sh会下载到当前工作目录下,复制到虚拟机中。
先加上执行权限
chmod 777 standalone_embed.sh
执行命令
bash standalone_embed.sh start
出现Start successfully.表示启动成功
官网相关安装步骤:https://milvus.io/docs/zh/install_standalone-docker.md
如果执行bash standalone_embed.sh start命令后,等待几分钟还是没有出现Start successfully,则可能是启动失败了,后面有解决方法。
3)客户端Attu下载
Milvus客户端Attu下载:https://zilliz.com.cn/attu
https://github.com/zilliztech/attu/releases/tag/v3.0.0-beta.6

添加连接

4)安装向量数据库时可能出现的报错
镜像源问题
如果安装时报错,可以换一个镜像源地址
[root@localhost tools]# bash standalone_embed.sh start
Unable to find image 'milvusdb/milvus:v3.0-beta' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers).
See 'docker run --help'.
Start failed.
docker使用以下镜像源地址
vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn",
"https://mirror.baidubce.com"
]
}
重启 Docker 服务:
sudo systemctl daemon-reload
sudo systemctl restart docker
验证配置:运行 docker info,检查输出中 Registry Mirrors 一项是否包含刚刚配置的地址。
权限不足问题
如果执行bash standalone_embed.sh start命令后,等待几分钟还是没有出现Start successfully,则可能是启动失败了
这里可以使用docker logs milvus-standalone命令,发现是权限不足导致启动失败
[2026/06/03 19:15:06.110 +00:00] [FATAL] [paramtable/component_param.go:4413] ["failed to mkdir"] [localStoragePath=/var/lib/milvus/data/] [error="mkdir /var/lib/milvus/data/: permission denied"]
具体步骤
如果已经创建容器则进行删除,没有创建容器则跳过
docker stop milvus-standalone
docker rm milvus-standalone
给/volumes/milvus文件夹添加权限
# 进入脚本所在目录(即之前执行 bash standalone_embed.sh 的目录)
cd /root/tools
# 删除可能已存在但权限不对的目录(可选,如果你没有重要数据)
rm -rf volumes
# 重新创建目录,并赋予容器内用户(uid 1000)写权限
mkdir -p volumes/milvus
chmod -R 777 volumes/milvus
5.4 Milvus创建 Collection
正如前面介绍,Milvus当中的数据,存储到独立的Collection当中。创建Collection可以分为以下步骤:
(1)构建schema信息;
(2)添加索引;
(3)创建collection。
示例代码如下所示:
from pymilvus import MilvusClient, DataType
# 创建客户端对象
client = MilvusClient(
uri="http://192.168.10.170:19530"
)
# 创建Schema的函数
def create_schema():
# 创建Schema,并设置主键自增
schema = MilvusClient.create_schema(
auto_id = True
)
# 添加字段
# 添加主键
schema.add_field(field_name="id",datatype=DataType.INT64,is_primary=True)
# 添加保存稠密向量的字段
schema.add_field(field_name="vector",datatype=DataType.FLOAT_VECTOR,dim=1024)
# 添加保存块信息的字段
schema.add_field(field_name="text",datatype=DataType.VARCHAR,max_length=1500)
# 添加保存元数据的字段
schema.add_field(field_name="metadata",datatype=DataType.JSON)
# 添加保存稀疏向量的字段
schema.add_field(field_name="sparse_vector",datatype=DataType.SPARSE_FLOAT_VECTOR)
# 返回
return schema
# 给稠密向量和稀疏向量字段创建索引的函数
def create_index():
# 创建索引
index_params = MilvusClient.prepare_index_params()
# 给稠密向量字段创建索引
index_params.add_index(
field_name="vector",
index_type="HNSW",
metric_type="L2"
)
index_params.add_index(
field_name="sparse_vector",
index_type="SPARSE_INVERTED_INDEX",
metric_type="IP",
)
# 返回
return index_params
# 创建Collection的函数
def create_collection():
# 创建Collection
client.create_collection(
collection_name="test",
schema=create_schema(),
index_params=create_index()
)
if __name__ == "__main__":
# 创建Collection
create_collection()
# 获取Collection
collection_info = client.list_collections()
print(collection_info)
# 获取Collection的详细信息
collection_details = client.describe_collection("test")
print(collection_details)
结果
['test']
{'collection_name': 'test', 'auto_id': True, 'num_shards': 1, 'description': '', 'fields': [{'field_id': 100, 'name': 'id', 'description': '', 'type': <DataType.INT64: 5>, 'params': {}, 'auto_id': True, 'is_primary': True}, {'field_id': 101, 'name': 'vector', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 1024}}, {'field_id': 102, 'name': 'text', 'description': '', 'type': <DataType.VARCHAR: 21>, 'params': {'max_length': 1500}}, {'field_id': 103, 'name': 'metadata', 'description': '', 'type': <DataType.JSON: 23>, 'params': {}}, {'field_id': 104, 'name': 'sparse_vector', 'description': '', 'type': <DataType.SPARSE_FLOAT_VECTOR: 104>, 'params': {}}], 'functions': [], 'aliases': [], 'collection_id': 467441364983617018, 'consistency_level': 2, 'properties': {'timezone': 'UTC'}, 'num_partitions': 1, 'enable_dynamic_field': False, 'enable_namespace': False, 'created_timestamp': 467441601427537923, 'update_timestamp': 467441601427537923}

5.5 Milvus操作实体
在创建完了collection之后,就可以“增删”操作。
5.5.1 插入实体
将数据构建成List[Dict]形式,其中List表示批量数据,Dict表示符合创建collection时所定义的schema结构的数据,构建完成后,即可通过client.insert方法,将数据插入到指定的collection当中去。
5.5.2 删除实体
删除实体,可以调用MilvusClient的delete方法,该同样也可以通过传递id列表,或者是通过其他字段过滤条件来进行删除,该方法返回结果为一个字典,包含了一个delete_count键,值为删除的数据条数。
示例代码如下:
4.5.5 Milvus操作实体
在创建完了collection之后,就可以“增删”操作。
4.5.5.1 插入实体
将数据构建成List[Dict]形式,其中List表示批量数据,Dict表示符合创建collection时所定义的schema结构的数据,构建完成后,即可通过client.insert方法,将数据插入到指定的collection当中去。
4.5.5.2 删除实体
删除实体,可以调用MilvusClient的delete方法,该同样也可以通过传递id列表,或者是通过其他字段过滤条件来进行删除,该方法返回结果为一个字典,包含了一个delete_count键,值为删除的数据条数。
示例代码如下:
```py
# 创建客户端对象
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from pymilvus import MilvusClient
from FlagEmbedding import BGEM3FlagModel
client = MilvusClient(uri="http://192.168.10.170:19530")
# 向Milvus数据库插入数据的函数
def insert_data():
# 1.加载md文件
doc = UnstructuredMarkdownLoader("files/test.md").load()
# 2.对文档切块
doc_chunks = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", " ", ""],
chunk_size=500,
chunk_overlap=50
# chunk_size=10,
# chunk_overlap=2
).split_documents(doc)
# 3.通过BGE-M3模型获取稠密向量和稀疏向量
model = BGEM3FlagModel(
model_name_or_path=r"E:\tool\python\ai\my_models\bge-m3",
# 返回稠密向量
return_dense=True,
# 返回稀疏向量
return_sparse=True
)
print(f"文档切块后的结果是:{doc_chunks}")
results = model.encode([chunk.page_content for chunk in doc_chunks])
# 获取稠密向量
dense_vectors = results["dense_vecs"]
# 获取稀疏向量
sparse_vectors = results["lexical_weights"]
print(f"稠密向量是:{dense_vectors},稀疏向量是:{sparse_vectors}")
# 4.构建要插入向量数据库的数据list[dict]
insert_data = []
for doc,dense_vector,sparse_vector in zip(doc_chunks,dense_vectors,sparse_vectors):
# 向insert_data中追加数据
insert_data.append({
"vector":dense_vector,
"sparse_vector":sparse_vector,
"text":doc.page_content,
"metadata":doc.metadata
})
# 5.向Milvus数据库中插入数据
res = client.insert(
collection_name="test",
data=insert_data
)
print(f"数据插入成功,响应的数据是:{res}")
# 删除数据
def delete_data():
res = client.delete(
collection_name = "test",
# 过滤条件,仅删除 id 在指定范围内的实体,也可以传递其他字段的过滤条件
filter = "id in [467441364983748320, 467441364983748319]",
)
print(res)
if __name__ == "__main__":
# 插入数据
insert_data()
# 删除数据
# delete_data()
这里需要创建一个test.md文档,向其中写入数据作为测试数据。
5.6 Milvus检索
Milvus 支持多种检索方式。对向量字段,Milvus 支持精确检索(KNN)以及近似近邻检索(ANN)。对标量字段,Milvus 提供条件过滤能力,主要用于配合向量检索进行结果筛选,其能力和定位不同于传统关系型数据库普通检索。
5.6.1 向量检索
通过调用client.search,传入需要检索的query向量,以及需要和query向量进行比较的向量字段,即可进行向量检索。
在执行向量检索时,通常需要关注以下参数:
- 查询向量(query vector):表示待检索对象的向量表示,维度需与集合中向量字段一致。
- 向量字段(anns_field):指定参与检索的向量字段名称。
- TopK(limit):返回相似度最高的结果数量。
- metric_type:指定相似度计算方式,如 COSINE、L2 或 IP,需与索引和向量语义保持一致。
另外向量检索还支持同时使用稠密向量和稀疏向量,进行混合检索,并配置重排序器,来对两路召回结果进行一个重排序。
重排序(Reranker)是指在初步检索(Recall)完成后,对候选结果进行二次排序优化的过程。其目标不是扩大召回范围,而是在已有候选集内提升排序质量和结果相关性。在典型的检索系统中,重排序通常位于向量检索或混合检索之后,用于融合多路检索结果或引入更精细的排序策略。
此处介绍RRFReranker。RRFRanker 策略的主要工作流程如下:
- 收集搜索排名:收集来自向量搜索各路径的结果排名(rank_1、rank_2)。
- 合并排名:根据公式转换各路径的排名(rank_rrf_1、rank_rrf_2)。
计算公式涉及 N,N 代表检索器的数量。ranki (d) 是第 i 个检索器生成的文档 d 的排名位置。k 是一个平滑参数,通常设置为 60。 - 聚合排名:基于合并后的排名对搜索结果进行重新排序,以生成最终结果。
其示意图如下:
向量检索示例代码如下:
from pymilvus import MilvusClient
from FlagEmbedding import BGEM3FlagModel
# 根据稠密向量查询
def search_by_dense_vector():
# 创建客户端对象
client = MilvusClient(uri="http://192.168.10.170:19530")
# 3.通过BGE-M3模型获取稠密向量和稀疏向量
model = BGEM3FlagModel(
model_name_or_path=r"E:\tool\python\ai\my_models\bge-m3",
# 返回稠密向量
return_dense=True,
# 返回稀疏向量
return_sparse=True
)
results = model.encode(["如何提升查询效率"])
# 获取稠密向量
dense_vectors = results["dense_vecs"]
# 根据稠密向量查询Milvus数据库获取查询结果
res = client.search(
collection_name="test",
data=dense_vectors,
anns_field="vector",
search_params={"metric_type": "L2"},
output_fields=["id","text","metadata"]
)
# 遍历获取命中数据
for hits in res:
for hit in hits:
print(hit)
if __name__ == "__main__":
# 测试根据稠密向量查询
search_by_dense_vector()
5.6.2 标量检索
标量检索(或标量查询)是指不涉及向量相似度计算,仅基于标量字段条件对数据进行筛选和返回的检索方式。
相关文章:
LangChain:
LangChain快速入门-01概述
LangChain快速入门-02Model I/O
LangChain快速入门-03RAG(概述与文档加载)
LangChain快速入门-04RAG(文档切分与嵌入)
LangChain快速入门-05RAG(向量数据库)
LangChain快速入门-06Agents
LangGraph:
LangGraph快速入门-01概述
LangGraph快速入门-02状态
LangGraph快速入门-03节点与边
更多推荐



所有评论(0)