尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LlamaIndex 系列【12】向量存储索引(VectorStoreIndex)

LlamaIndex 系列【12】向量存储索引(VectorStoreIndex) 文章目录前言1. 基础概念1.1 向量嵌入1.2 向量相似度1.3 嵌入模型1.4 文本向量化1.5 Top K 检索2. VectorStoreIndex2.1 核心依赖2.2 核心初始化 快捷构建2.3 核心流程构建索引 / 新增节点2.4 Retriever 生成2.5 删除能力2.6 关键设计总结 踩坑清单3. 将数据载入索引3.1 配置嵌入模型3.2 直接加载文档3.3 使用数据接入流水线创建节点3.4 直接创建与管理节点4. 文档管理4.1 新增Insertion4.2 删除Deletion4.3 更新Update4.4 刷新Refresh4.5 文档追踪Document Tracking前言VectorStoreIndex是目前使用最广泛的索引类型。向量存储索引会将文档拆分为多个节点接着为每个节点的文本生成向量嵌入vector embeddings供大模型后续查询使用。1. 基础概念1.1 向量嵌入向量嵌入是大模型应用的核心基础常直接简称为嵌入或向量是文本语义的数值化表达。两段含义相近的文本即便文字表述差别很大它们对应的嵌入向量在数值上也会非常相似。向量以浮点数值数组形式承载原始数据的深层语义、上下文关联与特征分布将不可计算的自然语言与多媒体信息转化为机器可量化、可运算、可比对的结构化数值表达向量中每一位数值代表抽象语义特征的权重分布向量长度称为向量维度维度越高特征表达能力越强语义内容越相近的文本 / 素材其对应向量在高维空间中的几何距离越近语义越相似。举个直观例子文本A春天花开天气温暖向量A[0.21, 0.66, -0.11, 0.90]文本B语义接近春日暖和百花开放向量BA和B数字几乎一样[0.23, 0.64, -0.09, 0.88]文本CA和C数字完全不一样Java 并发编程实战这种数值上的相似关系支撑起语义检索用户输入查询语句后LlamaIndex能够找到和查询语义相关的文本而不只是简单的关键词匹配。这也是检索增强生成RAG以及大模型工作机制的核心组成部分。1.2 向量相似度可通过多种数学公式判断两个向量的相似程度其中余弦相似度Cosine Similarity是衡量两个向量方向相似程度的经典算法通过计算两个多维向量夹角的余弦值判定相似度大小。余弦相似度公式Similarity ( A ⃗ , B ⃗ ) cos ⁡ ( θ ) A ⃗ ⋅ B ⃗ ∥ A ⃗ ∥ ⋅ ∥ B ⃗ ∥ \text{Similarity}(\vec{A}, \vec{B}) \cos(\theta) \frac{\vec{A} \cdot \vec{B}}{\|\vec{A}\| \cdot \|\vec{B}\|}Similarity(A,B)cos(θ)∥A∥⋅∥B∥A⋅B​1.3 嵌入模型嵌入模型Embedding Model是一种专门用于将非结构化数据文本、图像、音频等转化为结构化数值向量的AI模型将不可计算的自然语言、视觉信息映射为固定长度、高密度的浮点数数组向量从而让机器能够量化、比对、计算数据的语义关联。厂商API模型推荐智谱AI Embedding系列国产首选OpenAI Embedding国际主流阿里千问Embedding国产备选开源嵌入模型推荐BAAI Embedding国产开源标杆Sentence-BERT国际开源主流Word2vec传统开源模型嵌入模型有很多种类在性能、效果和算力开销上各有差异。LlamaIndex默认使用OpenAI的text-embedding-ada-002嵌入模型如果使用其他大模型通常需要配套更换对应的嵌入模型。1.4 文本向量化调用大模型服务接口将全部文本转换成嵌入向量这也就是常说的文本向量化。如果文本体量很大生成嵌入会耗费较长时间因为需要多次接口往返调用。当你需要检索向量时查询语句本身也会被转为向量随后VectorStoreIndex通过数学计算 依托底层向量存储通过向量相似度计算基于语义相似度对已有向量进行排序。1.5 Top K 检索排序完成后向量存储索引会返回相似度最高的向量所对应的文本块。返回结果的数量记为k控制返回条数的参数就叫做top_k。这类检索方式也因此常被称为top-k 语义检索。Top-k检索是向量索引最简单的查询方式在查询相关章节中你会了解更复杂精细的检索策略。2. VectorStoreIndex基于向量库的索引实现是LlamaIndex最核心的RAG索引继承BaseIndex[IndexDict]本身不直接存储向量只是封装了「Node向量化 写入向量库 文档存储兜底 检索入口」的流程。classVectorStoreIndex(BaseIndex[IndexDict]): Vector Store Index. Args: use_async (bool): Whether to use asynchronous calls. Defaults to False. show_progress (bool): Whether to show tqdm progress bars. Defaults to False. store_nodes_override (bool): set to True to always store Node objects in index store and document store even if vector store keeps text. Defaults to False index_struct_clsIndexDictdef__init__(self,2.1 核心依赖核心依赖组件BasePydanticVectorStore底层向量存储Chroma、Pinecone、Milvus、Qdrant等都实现该接口IndexDict索引结构体维护node_id→ 向量库存储id的映射docstore文档存储兜底保存Node文 本embed_model向量化模型支持全局Settings.embed_model或实例单独传入VectorIndexRetriever配套检索器as_retriever()生成关键属性属性作用_use_async是否使用异步批量向量化、写入_store_nodes_override强制把Node存入docstore无视向量库是否存文本_embed_model向量化模型实例_insert_batch_size批量写入分片大小默认2048index_structIndexDict维护node与向量库id映射_vector_store底层向量库实例_docstore文档存储实例2.2 核心初始化 快捷构建__init__优先解析embed_model优先实例传入其次全局Settings.embed_model调用父类BaseIndex初始化节点、存储上下文、转换链路、回调管理支持直接传入nodes实例化时直接构建索引from_vector_store绑定已有向量库直接加载成索引不需要重新灌入数据强校验vector_store.stores_text必须为True向量库内部保存文本否则无法直接加载自动构造StorageContextvector_store传入docstore默认内存nodes[]不再新增节点只是封装已有向量库⚠️ 坑点如果向量库不存文本不能用from_vector_store必须走节点重建索引。2.3 核心流程构建索引 / 新增节点入口build_index_from_nodes()→_build_index_from_nodes()→_add_nodes_to_index/_async_add_nodes_to_index整体流程过滤无效节点剔除Embedding模式下无内容的Node分批处理按insert_batch_size切割节点批次批量向量化同步_get_node_with_embedding→embed_nodes异步_aget_node_with_embedding→async_embed_nodes返回id_to_embed_map给每个node填充.embedding写入底层向量库vector_store.add/async_add拿到向量库返回的存储id分支判断向量库不存文本 / 强制兜底node写入docstoreindex_struct维护映射向量库自带文本普通文本Node不存docstore只存ImageNode、IndexNode写入index_store持久化索引结构对外API区分方法能力insert_nodes()同步新增节点追加到已有索引ainsert_nodes()异步新增节点build_index_from_nodes()全新构建索引覆盖式2.4 Retriever 生成as_retriever()懒导入VectorIndexRetriever避免循环导入传入自身index、index_struct里全部node_ids、回调、对象映射返回检索器后续可调用retriever.retrieve()做相似度检索整个RAG链路VectorStoreIndex.as_retriever() → VectorIndexRetriever → vector_store.similarity_search2.5 删除能力delete_nodes/adelete_nodes按node_ids删除调用向量库删除接口删除向量delete_ref_doc/adelete_ref_doc按文档id删除向量库按ref_doc_id批量删除清理index_struct中该文档关联的所有node映射可选删除docstore里的ref_doc元信息异步版本使用asyncio.gather并发执行删除任务2.6 关键设计总结 踩坑清单✅ 设计亮点解耦索引层和底层向量存储完全隔离只依赖BasePydanticVectorStore无缝切换向量库存储自适应根据向量库是否存文本自动决定是否冗余保存Node兼顾性能和可用性同步/异步双链路批量向量化、写入、删除都支持async适合高吞吐数据灌入两种删除粒度支持单节点删除、原始文档级批量删除⚠️ 高频踩坑点from_vector_store要求vector_store.stores_textTrue否则直接抛错ref_doc_info在向量库自带文本存储时不可用默认不会自动存普通文本Node到docstore如果向量库丢失数据无法恢复原始文本需要开启store_nodes_overrideTrue节点会过滤空内容会静默跳过无embedding内容的node不会直接报错删除默认不会自动删docstore必须手动传delete_from_docstoreTrue3. 将数据载入索引本节介绍LlamaIndex中向VectorStoreIndex灌入数据的三种主流方案快速构建、IngestionPipeline精细化处理、手动构造节点同时说明默认行为、关键参数、适用场景以及动态文档更新能力。3.1 配置嵌入模型RAG需要embedding模型。若不配置框架默认回退到OpenAI并报错。新建.env文件配置阿里云百炼DASHSCOPE_API_KEYDASHSCOPE_API_KEYsk-3需要再安装相关依赖pip install python-dotenv pip install llama-index-embeddings-openai-like代码示例fromdotenvimportload_dotenvfromllama_index.embeddings.openai_likeimportOpenAILikeEmbeddingfromllama_index.coreimportSettingsimportos# 加载 DASHSCOPE_API_KEYload_dotenv()api_keyos.environ[DASHSCOPE_API_KEY]API_BASEhttps://ws-jfb8j8mx0n7e2k6a.cn-beijing.maas.aliyuncs.com/compatible-mode/v1# 全局向量模型配置# Settings.embed_model OpenAILikeEmbedding(# model_nametext-embedding-v3,# api_keyapi_key,# api_baseAPI_BASE,# )## 局部指定向量模型embed_modelOpenAILikeEmbedding(model_nametext-embedding-v3,api_keyapi_key,api_baseAPI_BASE,)3.2 直接加载文档使用向量索引最快速便捷的方式是直接加载文档集合调用静态方法from_documents一键完成文档解析、文本分块、向量化、构建索引全流程。代码示例fromllama_index.coreimportVectorStoreIndex,SimpleDirectoryReader,Settings readerSimpleDirectoryReader(input_dir./data,raise_on_errorTrue)documentsreader.load_data(show_progressTrue)indexVectorStoreIndex.from_documents(documents,embed_modelembed_model,show_progressTrue) 提示如果在终端/命令行脚本中执行from_documents可以传入参数show_progressTrue在索引构建、向量化过程中展示tqdm进度条方便观测处理进度。调用from_documents的内部完整流程输入Document文档列表使用默认节点解析器SentenceSplitter将长文档切分为若干文本块chunk将每一块文本封装为 [Node对象]调用全局Embedding模型批量为每个Node生成向量嵌入将Node与向量写入底层存储生成VectorStoreIndex实例。构建完成后的索引对象⚠️ 默认存储特性在不额外指定StorageContext和外部向量库时VectorStoreIndex默认使用内存向量存储程序退出后所有向量与节点数据会丢失无法持久化。持久化到 Chroma、Qdrant、Milvus 等真实向量库的用法请参考后续【使用向量存储】章节。 提示批次写入参数VectorStoreIndex默认按照2048 个节点为一批批量执行向量化并写入向量存储对应参数insert_batch_size2048。内存紧张场景调小批次避免单次向量化占用过高内存对接远程向量数据库云向量库合理调优批次可以减少网络请求次数提升灌入吞吐量该参数可以在from_documents或者直接实例化VectorStoreIndex时传入indexVectorStoreIndex.from_documents(documents,insert_batch_size512)3.3 使用数据接入流水线创建节点from_documents属于一键封装的快捷方式内部分块、元数据提取、向量化逻辑都是默认配置。如果需要精细化、可定制、可复用的数据预处理链路官方推荐使用IngestionPipeline数据接入流水线。你可以自由编排多个转换组件自定义文本分片规则、自动抽取摘要/标题、自定义Embedding、开启缓存加速等。fromllama_index.coreimportDocumentfromllama_index.embeddings.openaiimportOpenAIEmbeddingfromllama_index.core.node_parserimportSentenceSplitterfromllama_index.core.extractorsimportTitleExtractorfromllama_index.core.ingestionimportIngestionPipeline,IngestionCache# 定义流水线与数据转换组件pipelineIngestionPipeline(transformations[# 分句分片器设置块大小25无重叠SentenceSplitter(chunk_size25,chunk_overlap0),# 元数据提取器自动为节点抽取标题存入node.metadataTitleExtractor(),# Embedding模型为节点生成向量OpenAIEmbedding(),],# 可选开启IngestionCache已经处理过的节点会缓存避免重复向量化# cacheIngestionCache())# 执行流水线输入文档自动执行所有transform直接输出Node列表nodespipeline.run(documents[Document.example()])# 拿到nodes之后再构建向量索引indexVectorStoreIndex(nodes)核心优势可复用同一条流水线可以给多个索引、多个数据集使用可观测每一步转换独立方便调试分块效果、元数据抽取结果支持缓存IngestionCache避免重复消耗Embedding接口额度组件丰富支持自定义清洗、实体抽取、多模态节点处理等。 提示流水线高级用法、缓存配置、异步执行等内容可查阅后续【数据接入流水线文档】。3.4 直接创建与管理节点如果需要完全自主控制节点自己实现分块逻辑、自定义节点元数据、特殊业务组装逻辑可以手动实例化TextNode文本节点组装成节点列表后直接传入VectorStoreIndex构造器跳过自动文档解析与默认分块流程。代码示例fromllama_index.core.schemaimportTextNode# 手动构造文本节点可自定义node_id、metadata等node1TextNode(texttext_chunk,id_node_id)node2TextNode(texttext_chunk,id_node_id)nodes[node1,node2]# 直接基于节点列表初始化向量索引indexVectorStoreIndex(nodes)适用场景数据源不是文件数据库、接口返回文本等自研复杂分块策略需要手动控制node_id方便后续精准更新/删除多模态场景混合TextNode/ImageNode。4. 文档管理实际业务中知识库数据往往不是一次性全量灌入而是持续新增、修改、删除文档。VectorStoreIndex原生内置新增节点、按文档 ID 删除、更新文档、批量刷新索引能力适合增量同步知识库无需每次全量重建索引。大多数LlamaIndex索引结构都支持新增、删除、更新和刷新操作。4.1 新增Insertion索引初始化完成后可以向向量索引追加新文档。文档内部会自动完成文本分片、生成Node节点、向量化最终写入底层向量存储。代码示例# 导入向量索引与文档对象fromllama_index.coreimportVectorStoreIndex,Document# 基于空节点初始化向量索引indexVectorStoreIndex([])text_chunks[text_chunk_1,text_chunk_2,text_chunk_3]doc_chunks[]fori,textinenumerate(text_chunks):# 手动指定文档id后续更新/删除/刷新依赖该iddocDocument(texttext,id_fdoc_id_{i})doc_chunks.append(doc)# 循环新增文档到向量索引fordoc_chunkindoc_chunks:index.insert(doc_chunk) 提示如果是通过SimpleDirectoryReader加载文件也可以直接把读取出来的documents列表批量新增。4.2 删除Deletion支持通过**文档 ID **删除整条文档该文档拆分出来的全部节点与向量都会被清理。# 根据文档id删除文档delete_from_docstoreTrue 同步清理docstore中的节点数据index.delete_ref_doc(doc_id_0,delete_from_docstoreTrue)delete_from_docstore默认值为False多用于多个索引共用同一份docstore、节点复用的场景。即便该参数为False对应节点也不会参与检索——节点映射会从index_struct中移除索引结构管控可检索节点清单。4.3 更新Update索引内已存在相同id_的文档时可直接覆盖更新文档内容适配文档信息变更场景。代码示例# 前提文档已设置唯一 doc_iddoc_chunks[0].textBrand new document textindex.update_ref_doc(doc_chunks[0])4.4 刷新Refresh提前为所有文档配置id_后可一键批量刷新索引文档ID相同、文本变更 → 自动更新文档ID不存在 → 自动新增入库返回布尔数组标记每份输入文档是否触发变更代码示例# 修改已有文档保持doc_id不变doc_chunks[0]Document(textSuper new document text,id_doc_id_0)# 新增索引中不存在的文档doc_chunks.append(Document(textThis isnt in the index yet, but it will be soon!,id_doc_id_3,))# 批量刷新索引refreshed_docsindex.refresh_ref_docs(doc_chunks)# refreshed_docs[0]、refreshed_docs[-1] 预期为 True打印返回结果可以直观查看哪些文档执行了刷新/新增print(refreshed_docs)# [True, False, False, True]该能力非常适合持续更新的文件目录自动同步场景。搭配SimpleDirectoryReader时开启filename_as_idTrue可自动以文件名作为文档ID。4.5 文档追踪Document Tracking当开启docstore存储节点向量库不存储文本 /store_nodes_overrideTrue可以通过ref_doc_info查询入库文档与节点的映射关系。⚠️ 注意如果向量库自身保存文本ref_doc_info当前暂不支持会抛出NotImplementedError代码示例print(index.ref_doc_info) {doc_id_1: RefDocInfo(node_ids[071a66a8-3c47-49ad-84fa-7010c6277479], metadata{}), doc_id_2: RefDocInfo(node_ids[9563e84b-f934-41c3-acfd-22e88492c869], metadata{}), doc_id_0: RefDocInfo(node_ids[b53e6c2f-16f7-4024-af4c-42890e945f36], metadata{}), doc_id_3: RefDocInfo(node_ids[6bedb29f-15db-4c7c-9885-7490e10aa33f], metadata{})} 返回字典的key为文档IDvalue中保存该文档拆分得到的全部节点ID同时保留原始文档的metadata元数据。
返回列表