当前位置: 首页 > news >正文

构建AI智能体:十五、超越关键词搜索:向量数据库如何解锁语义理解新纪元

一、前言

        在如今的数字时代,数据的形式正以前所未有的速度变得多样化。文本、图片、音频、视频等非结构化数据占据了数据总量的80%以上。传统数据库(如MySQL)擅长处理“张三的年龄是25岁”这类结构化数据,但对“一张有夕阳、狗和海滩的图片”或“一篇讨论量子计算前景的文章”却无能为力。

        我们可以轻松理解这些内容背后的含义和关联,但计算机需要一种方式来“理解”和“比较”它们。向量数据库(Vector Database) 就是专为解决这一问题而生的新型数据库,它是AI基础设施中至关重要的一环,被誉为AI应用的“长期记忆体”和“检索大脑”。

二、什么是向量数据库

        首先需要回顾一下向量和Embedding的含义,具体可以参考章节《构建AI智能体:十二、给词语绘制地图:Embedding如何构建机器的认知空间

向量:在AI和机器学习领域,向量是一组数字的有序列表,可以表示任何数据对象(如一段文字、一张图片)在高维空间中的位置。

嵌入(Embedding):通过AI模型(如BERT、CNN、CLIP等)将非结构化数据转换为向量的过程,称为“嵌入”。这个转换过程捕获了数据的深层语义特征。

示例:单词“国王”通过模型转换后,可能得到一个包含300个数字的向量。在数学上,这个向量与“男人”、“女王”、“女人”的向量存在某种关系(如“国王” - “男人” + “女人” ≈ “女王”)。

        向量数据库是一种专门用于存储、索引和查询高维向量的数据库。它的核心功能是执行近似最近邻(ANN)搜索,即快速找到与查询向量最相似的向量集合。

三、与传统数据库区别

特性

传统数据库 (SQL/NoSQL)

向量数据库

数据模型

结构化数据(行/列)、文档、键值对

高维向量

查询方式

精确匹配(WHERE age = 25)、范围查询

相似性搜索(找到与这个图片最像的10张图片)

索引技术

B树、哈希索引等

HNSWIVF-PQLSH等专门为高维空间设计的ANN索引

核心能力

事务一致性、完整性

语义理解、相似性检索

适用场景

电商订单、用户信息管理等

AI推荐、语义搜索、图像识别等

四、向量数据库的核心原理

        在高维空间中(维度可达成百上千维),进行精确的最邻近搜索计算量极大,速度极慢。向量数据库使用近似最近邻(ANN)算法,在可接受的精度损失下,极大提升搜索速度。

  • 索引(Indexing):数据库不会暴力比对所有向量,而是预先构建索引,将向量组织成一种易于搜索的结构。

  • HNSW:像构建一个多层的高速公路网络,先从顶层进行粗粒度搜索,再逐层细化,快速逼近目标。

  • IVF-PQ:先将向量空间聚类成多个“单元”,搜索时只找最可能的几个单元。再用产品量化技术压缩向量,减少计算和存储开销。

  • 距离度量(Distance Metric):如何衡量两个向量的相似性?常用方法有:

  • 余弦相似度(Cosine Similarity):衡量向量方向上的差异,忽略其大小。非常适合文本数据。

  • 欧氏距离(Euclidean Distance):衡量向量在空间中的实际距离。

  • 点积(Dot Product):与余弦相似度相关,但也受向量大小影响。

扩展:通俗的介绍ANN算法

        想象一下,你是一个图书管理员,管理着一个有100万本书的图书馆。一个顾客问你:“请帮我找一本和《三体》最相似的书。”

  • 愚蠢但精确的方法(暴力搜索):你拿起《三体》,然后一页一页地和其他999,999本书逐字逐句地对比。这绝对能找到最相似的那本,但你找到的时候,顾客可能已经老了。这就像计算机中的精确最近邻搜索,结果完美,但速度慢到无法接受。

  • 聪明但近似的方法(ANN算法):你是一个聪明的管理员。你事先做了功课:你给书分了类(建立了索引):科幻区、文学区、历史区……你知道《三体》是科幻小说。你还给书贴了标签:有“外星人”、“物理学”、“悬疑”标签的书大概率在科幻区。当顾客提出同样的问题时,你不会去检查所有100万本书。你直接冲向科幻区,然后只在这个区的几千本书里快速寻找和《三体》最像的。你甚至可能只看了有“外星人”标签的那个书架。

  • 结果就是:你极快地找到了一本非常相似的书(比如《银河帝国》),但它是不是100万本中绝对最相似的那本?不一定。可能历史区有一本讲科学史的书某个角度也很像,但你忽略了它。

        这个“聪明的办法”就是 ANN 算法的核心思想:ANN(Approximate Nearest Neighbor),近似最近邻。它的精髓就是:用极高的效率(速度)和可接受的内存占用,换来一个“差不多”的、非常接近正确的搜索结果。它不保证找到的是绝对最近的,但能保证找到的是非常近的。

        为什么需要它? 因为在处理高维数据(比如图片、文本的1024维向量)时,暴力搜索的计算量是灾难性的,ANN 是唯一可行的解决方案。

五、常见的向量数据库

  1. Pinecone:全托管的云端向量数据库,以易用性、高性能和强大的API著称,是快速构建原型和应用的理想选择。

  2. Chroma:轻量级、开源,特别专注于AI原生应用和LLM(大语言模型)生态,易于集成和本地部署。

  3. Weaviate:开源向量搜索引擎,不仅支持向量搜索,还具备GraphQL接口,可以像图数据库一样处理数据对象之间的关系。

  4. Milvus / Zilliz Cloud:Milvus是开源领域功能最全面、最受欢迎的向量数据库之一,专为海量向量数据设计。Zilliz是其背后的商业公司,提供全托管云服务。

  5. Qdrant:一个用Rust编写的高性能、开源向量数据库和搜索引擎,提供丰富的API和云服务。

  6. FAISS:核心算法库,非数据库。由Meta AI开发。提供最广泛、最前沿的ANN索引算法。支持CPU和GPU。在内存中进行裸向量检索时速度极快,尤其是GPU版本,是衡量其他数据库性能的基准。

注意:FAISS是一个极其高效的向量相似性搜索库(Library),而不是一个完整的、功能齐备的数据库(Database)。FAISS 是 Facebook AI 团队开源的一个用于高效相似性搜索和密集向量聚类的库。它提供了大量的算法,针对不同的数据集大小和精度要求,可以组合出最优的索引和搜索方式。

六、向量数据库的运用

1. 将数据导入到向量数据库

  • 第一步:数据清洗与准备

确保原始数据(如文本文档、图片)的质量,进行必要的预处理。

  • 第二步:数据向量化(Embedding)

使用预训练的Embedding Model将原始数据转换成向量。

  • 文本:可使用bge-m, Qwen3-Embedding, Jina-Embedding 等模型。

  • 图片:可使用CLIP, ResNet等模型。

选择合适的模型至关重要,它直接决定了向量的质量和后续检索的效果。

  • 第三步:数据与元数据一同导入

将生成的向量及与其关联的元数据(Metadata)一同存入向量数据库。

  • 向量(Vector):生成的Embedding数字数组。

  • 唯一ID(ID):用于唯一标识每个数据点,方便后续的更新或删除。

  • 元数据(Metadata):描述向量的附加信息,是实现高级检索的关键。例如:文本来源的文件名、章节、URL、商品的类别、品牌、价格等

示例:指定模型将数据向量化

# 导入必要的库
import os  # 用于访问操作系统环境变量
from openai import OpenAI  # 导入OpenAI客户端库
# 初始化OpenAI客户端,配置为连接阿里云百炼服务
client = OpenAI(# 从环境变量DASHSCOPE_API_KEY获取API密钥,用于身份验证# 如果没有设置环境变量,可以在此直接替换为您的API密钥字符串(但不建议在代码中硬编码密钥)api_key=os.getenv("DASHSCOPE_API_KEY"),# 指定API的基础URL,这里指向阿里云百炼服务的兼容模式端点# 这使得可以使用OpenAI库的标准格式调用阿里云的API服务base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 创建文本嵌入向量
completion = client.embeddings.create(model="text-embedding-v4",  # 指定使用的嵌入模型版本input='我想知道迪士尼的退票政策',  # 输入需要转换为向量的文本内容# 指定生成的向量维度(长度),1024维是常见的选择# 此参数仅text-embedding-v3及更高版本支持dimensions=1024,# 指定向量值的编码格式为浮点数(float)# 这是最常用的格式,提供高精度表示encoding_format="float"
)
# 将API响应结果转换为JSON格式字符串并打印输出
# model_dump_json()方法将返回的对象序列化为JSON字符串
print(completion.model_dump_json())

执行步骤:

 1. 导入必要的库:os 和 openai(需要安装openai库,如果使用百炼,需要确保版本支持)

 2. 创建OpenAI客户端,指定api_key和base_url

 3. 调用embeddings.create方法,传入模型名、输入文本、向量维度和编码格式

 4. 将返回的嵌入对象转换为JSON字符串并打印

注意:

        这里我们使用 embeddings 模型 "text-embedding-v4",并指定维度为1024。 同时,我们打印出返回的JSON字符串。

返回结果:

图片

    {"data":[{"embedding":[0.0026341788470745087,-0.03762197867035866,-0.024403059855103493,-0.010764381848275661,0.01541021279990673,0.0007359159062616527,0.018554862588644028,-0.02278093248605728,-0.05350175499916077,0.023905038833618164,-0.02632400020956993,-0.009014192037284374,0.001929833902977407,0.0883917286992073,0.003021924290806055,0.015310607850551605,0.05139583349227905,-0.04928991198539734,-0.053074877709150314,
    中间省略部分....
    -0.004830809775739908,-0.0026359574403613806,-0.021955639123916626,0.01802838407456875,0.0019369485089555383,0.0010609639575704932,-0.01535329595208168,0.018014153465628624,-0.04493578150868416,0.0027284470852464437,-0.05347329378128052,-0.047639328986406326,-0.03625597432255745],"index":0,"object":"embedding"}],"model":"text-embedding-v4","object":"list","usage":{"prompt_tokens":8,"total_tokens":8},"id":"4f3c37d0-1b7b-99bb-ba7d-878b0e73a153"}

    2. 将Embedding 和元数据一起存储在FAISS

    • FAISS 适合探索和实验,适合于数据不需要频繁更新的场景,同时拥有极致的向量搜索速度,且不需要复杂的元数据过滤;

    • FAISS 本身只存储和检索向量,不存储元数据,我们需要在FAISS 之外维护一个元数据的“查找表”,并通过向量在FAISS 中的唯一ID将两者关联起来。

    • 最直接有效的方法是使用FAISS 的IndexIDMap,允许我们为每个向量指定一个自定义的、唯一的64位整数ID。然后,可以用这个ID作为元数据存储的键。

    示例:构建一个迪士尼政策权限相关的检索系统

    • 第一步:准备数据,创建示例文本和对应的元数据。

    • 第二步:生成向量,基于百炼text-embedding-v4 生成每个文本的向量。

    • 第三步:创建元数据存储,使用一个简单的Python 列表来存储元数据。列表的索引将作为每个数据点的唯一ID。

    • 第四步:构建FAISS 索引:

    • 使用faiss.IndexFlatL2 创建一个基础的索引,这里使用L2距离(欧氏距离)进行精确搜索。

    • 用faiss.IndexIDMap将基础索引包装起来,这样就可以添加带有自定义ID的向量了。

    • 第五步:添加数据到索引:将生成的向量和对应的ID(即元数据列表的索引)添加到IndexIDMap中。

    • 第六步:执行搜索:

    • 对一个新的查询文本生成向量。

    • 在FAISS 索引中搜索最相似的向量。

    • FAISS 会返回最相似向量的ID。

    • 第七步:检索元数据,使用返回的ID,从元数据存储中查找到原始文本和元数据。

    import os
    import numpy as np
    import faiss
    from openai import OpenAI
    # Step1. 初始化 API 客户端
    try:client = OpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"),base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
    except Exception as e:print("初始化OpenAI客户端失败,请检查环境变量'DASHSCOPE_API_KEY'是否已设置。")print(f"错误信息: {e}")exit()
    # Step2. 准备示例文本和元数据
    # 在实际应用中,这些数据可能来自数据库、文件等
    documents = [{"id": "doc1","text": "迪士尼乐园的门票一经售出,原则上不予退换。但在特殊情况下,如恶劣天气导致园区关闭,可在官方指引下进行改期或退款。","metadata": {"source": "official_faq_v1.pdf", "category": "退票政策", "author": "Admin"}},{"id": "doc2","text": "购买“奇妙年卡”的用户,可以享受一年内多次入园的特权,并且在餐饮和购物时有折扣。","metadata": {"source": "annual_pass_rules.docx", "category": "会员权益", "author": "MarketingDept"}},{"id": "doc3","text": "对于在线购买的迪士尼门票,如果需要退票,必须在票面日期前48小时通过原购买渠道提交申请,并可能收取手续费。","metadata": {"source": "online_policy.html", "category": "退票政策", "author": "E-commerceTeam"}},{"id": "doc4","text": "园区内的“加勒比海盗”项目因年度维护,将于下周暂停开放。","metadata": {"source": "maintenance_notice.txt", "category": "园区公告", "author": "OpsDept"}}
    ]
    # Step3. 创建元数据存储和向量列表
    # 我们使用一个简单的列表来存储元数据。列表的索引将作为FAISS的ID。
    # 这种方式简单直接,适用于中小型数据集。
    # 对于大型数据集,可以考虑使用字典或数据库(如Redis, SQLite)
    metadata_store = []
    vectors_list = []
    vector_ids = []
    print("正在为文档生成向量...")
    for i, doc in enumerate(documents):try:# 调用API生成向量completion = client.embeddings.create(model="text-embedding-v4",input=doc["text"],dimensions=1024,encoding_format="float")# 获取向量vector = completion.data[0].embeddingvectors_list.append(vector)# 存储元数据,并使用列表索引作为唯一IDmetadata_store.append(doc)vector_ids.append(i) # 自定义ID与列表索引一致print(f"  - 已处理文档 {i+1}/{len(documents)}")except Exception as e:print(f"处理文档 '{doc['id']}' 时出错: {e}")continue
    # 将向量列表转换为NumPy数组,FAISS需要这种格式
    vectors_np = np.array(vectors_list).astype('float32')
    vector_ids_np = np.array(vector_ids)
    # Step4. 构建并填充 FAISS 索引
    dimension = 1024  # 向量维度
    k = 3             # 查找最近的3个邻居
    # 创建一个基础的L2距离索引
    index_flat_l2 = faiss.IndexFlatL2(dimension)
    # 使用IndexIDMap来包装基础索引,能够映射我们自定义的ID
    # 这就是关联向量和元数据的关键!
    index = faiss.IndexIDMap(index_flat_l2)
    # 将向量和它们对应的ID添加到索引中
    index.add_with_ids(vectors_np, vector_ids_np)
    print(f"\nFAISS 索引已成功创建,共包含 {index.ntotal} 个向量。")
    # Step5. 执行搜索并检索元数据
    query_text = "我想了解一下迪士尼门票的退款流程"
    print(f"\n正在为查询文本生成向量: '{query_text}'")
    try:# 为查询文本生成向量query_completion = client.embeddings.create(model="text-embedding-v4",input=query_text,dimensions=1024,encoding_format="float")query_vector = np.array([query_completion.data[0].embedding]).astype('float32')# 在FAISS索引中执行搜索# search方法返回两个NumPy数组:# D: 距离 (distances)# I: 索引/ID (indices/IDs)distances, retrieved_ids = index.search(query_vector, k)# Step6. 展示结果print("\n--- 搜索结果 ---")# `retrieved_ids[0]` 包含与查询最相似的k个向量的IDfor i in range(k):doc_id = retrieved_ids[0][i]# 检查ID是否有效if doc_id == -1:print(f"\n排名 {i+1}: 未找到更多结果。")continue# 使用ID从我们的元数据存储中检索信息retrieved_doc = metadata_store[doc_id]print(f"\n--- 排名 {i+1} (相似度得分/距离: {distances[0][i]:.4f}) ---")print(f"ID: {doc_id}")print(f"原始文本: {retrieved_doc['text']}")print(f"元数据: {retrieved_doc['metadata']}")
    except Exception as e:print(f"执行搜索时发生错误: {e}")

    执行结果:

    正在为文档生成向量...
    -已处理文档1/4
    -已处理文档2/4
    -已处理文档3/4
    -已处理文档4/4
    FAISS 索引已成功创建,共包含4 个向量。
    正在为查询文本生成向量: '我想了解一下迪士尼门票的退款流程'
    ---搜索结果---
    ---排名1 (相似度得分/距离: 0.3222) ---
    ID: 2
    原始文本: 对于在线购买的迪士尼门票,如果需要退票,必须在票面日期前48小时通过原购买渠道提交申请,并可能收取手续费。
    元数据: {'source': 'online_policy.html', 'category': '退票政策', 'author': 'E-commerceTeam'}
    ---排名2 (相似度得分/距离: 0.3312) ---
    ID: 0
    原始文本: 迪士尼乐园的门票一经售出,原则上不予退换。但在特殊情况下,如恶劣天气导致园区关闭,可在官方指引下进行改期或退款。
    元数据: {'source': 'official_faq_v1.pdf', 'category': '退票政策', 'author': 'Admin'}
    ---排名3 (相似度得分/距离: 1.0135) ---
    ID: 1
    原始文本: 购买“奇妙年卡”的用户,可以享受一年内多次入园的特权,并且在餐饮和购物时有折扣。
    元数据: {'source': 'annual_pass_rules.docx', 'category': '会员权益', 'author': 'MarketingDept'}

    距离越小表示越相近,越大表示越不相关;

    七、总结

            向量数据库并非要取代传统数据库,而是对其能力的重要补充。它将数据从简单的字符和数字提升到了富含语义的数学表示,使计算机能够真正地“理解”和“联想”非结构化数据。随着生成式AI和大语言模型的爆发,向量数据库作为其记忆和知识检索的核心组件,正在成为现代AI技术栈中不可或缺的基础设施。

    http://www.dtcms.com/a/353136.html

    相关文章:

  • 使用 html2canvas + jspdf 实现页面元素下载为pdf文件
  • Transformer 模型在自动语音识别(ASR)中的应用
  • 华为L420国产笔记本(统信UOS桌面专业版1070)安装openEuler2403虚拟机
  • 基于Spring Boot的民宿服务管理系统-项目分享
  • Python 并行计算进阶:ProcessPoolExecutor 处理 CPU 密集型任务
  • Java设计模式之《外观模式》
  • 广东省省考备考(第八十八天8.27)——判断推理(第八节课)
  • 31. 什么是字符串常量池
  • 2025.8.27链表_链表逆置
  • 【Python-1】字符串连接方式“+“和“,”的区别
  • AI + 旅游 = 应用案例分析(一)
  • TDengine IDMP 5 个实测场景让监控变简单
  • 【和春笋一起学C++】(三十七)类的析构函数
  • rust语言(1.88.0)sqlite数据库rusqlite库(0.37.0)学习笔记
  • Linux 服务器故障全解析:常见问题及处理方法大全
  • 极简风格PDF格式转换解决方案
  • java将doc文件转pdf
  • PDF补丁丁:开源多年,完全免费的多功能 PDF 工具箱
  • Magicodes.IE.Pdf 生成导出PDF文件 bytes Stream FileStreamResult 下载
  • 像WPS Office 一样处理pdf页面尺寸
  • OpenGL 视差贴图
  • UVa11607 Cutting Cakes
  • Java垃圾回收器:从SerialGC到G1的演进/速通
  • 流程控制语句(1)
  • 如何检查 Linux 系统的内存使用情况
  • vue2实现背景颜色渐变
  • 【目标检测】论文阅读4
  • 分布式2PC理论
  • 大脑的藏宝图——神经科学如何为自然语言处理(NLP)的深度语义理解绘制新航线
  • (11)用于无GPS导航的制图师SLAM(一)