Day 07 / 共 20 天 · 第 2 周 索引/嵌入/存储

索引 Index(流水线第 ④ 步)

Node 都有向量了,要组织成"能快速检索"的结构——这就是索引。今天精读最核心的 VectorStoreIndex,看清 Day 01"5 行 RAG"第二行 from_documents 里到底藏了什么。

📍 你在整条链的位置
② 切 ③ 嵌 ④ 建索引 (存储 D8-9) ⑤检索 ⑥合成
L01

索引是什么

🤔 一堆带向量的 Node,怎么"快速找到相关的"? 你有 10 万个 Node,用户提问时要在里面找最相关的几个。总不能每次都把 10 万个翻一遍——需要"组织好、能快速查"的结构。
💡 索引 = 组织好、能高效检索的数据结构(像书末的索引页) 不同索引 = 不同的组织方式 = 不同的检索策略:VectorStoreIndex(按向量组织,语义检索,最常用)、SummaryIndex(列表,全遍历)、KeywordTableIndex(关键词倒排)、PropertyGraphIndex(知识图谱)——Day 10 概览。今天聚焦 VectorStoreIndex。
L02

BaseIndex:统一接口

indices/base.pyBaseIndex——所有索引共享同一套"用法":

index = XxxIndex.from_documents(docs)   # 建(最常用入口)
index.as_retriever()                     # 变检索器(只检索,Day 11)
index.as_query_engine()                  # 变查询引擎(检索+生成,Day 12)
index.insert(doc) / index.delete(id)     # 增删维护
💡 换索引类型只换类名,用法不变 VectorStoreIndex 换成 SummaryIndex?只改类名,from_documents/as_query_engine 全一样。又是"统一接口"——学会一个,全会。
L03

VectorStoreIndex:主流 RAG 索引

indices/vector_store/base.py:36class VectorStoreIndex(BaseIndex[IndexDict])——把 Node 嵌入成向量存进向量库,检索时按向量相似度找。

90% 的 RAG 用它 建库时:每个 Node 嵌入(Day 6)→ 存进向量库(Day 9)。查询时:问题嵌入 → 在向量库找最相似的 Node(Day 6 的向量空间那张图)。它的"索引结构"很轻(主要是 Node 到向量库的映射),真正的向量存在向量库里。
L04

from_documents 一行 = 好几天的活

VectorStoreIndex.from_documents(docs) 内部拆成: 切块 Day4 嵌入 Day6 存向量库 Day9 记映射 index_struct
from_documents 一行,内部跑完切块+嵌入+入库——就是 Day 05 那条隐式管道 + 建索引。
现在你能看穿这一行了 Day 01 觉得神奇的 from_documents,其实就是前几天学的步骤打了个包。想省事用它,想精细控制就显式用 IngestionPipeline(Day 5)+ 手动建索引。
L05

建索引核心两步

indices/vector_store/base.py:260-283_build_index_from_nodes_add_nodes_to_index:219):

def _add_nodes_to_index(self, index_struct, nodes, ...):
    # ① 给没有 embedding 的 Node 现算向量(调 embed_model,Day6)
    nodes = self._get_node_with_embedding(nodes, ...)
    # ② 存进向量库 + 在 index_struct 记录映射
    self._vector_store.add(nodes)          # Day9 的 vector_store.add
    for node in nodes: index_struct.add_node(node)
💡 核心就两步:嵌入 + 入库 ① Node 没向量就现算;② 存向量库。_use_async 时并发嵌入(嵌入调 API,并发快)。大批量分批处理,避免一次嵌太多。
L06

数据到底存在哪(预告 Day 08)

💡 三样东西分开存 建索引后,数据分三处(Day 08 详讲的"三件套"):向量→ vector_store(向量库);Node 文本/metadata→ docstore(文档库);索引结构(哪些 Node 属于这个索引的映射,即 IndexDict)→ index_store。
读法:为什么分三处?因为三种数据性质不同、适合不同后端(向量放专业向量库、文本放文档库、结构放本地)。VectorStoreIndex 的 index_struct 相对轻——重活在 vector_store。明天专讲 StorageContext。
L07

索引"变身":检索器 / 查询引擎

📝 索引建好后的两个变身
retriever = index.as_retriever(similarity_top_k=5)  # → 检索器:只找,返回 5 个相关 Node
query_engine = index.as_query_engine()               # → 查询引擎:找 + 用 LLM 生成答案
读法:Day 01 第三行 index.as_query_engine() 就是它——把索引包装成"能问答的引擎"。索引负责"存和检索",query_engine 在检索之上加"用 LLM 生成"。similarity_top_k(检索几个)在这里传。第 3 周拆它们内部。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 索引是什么?VectorStoreIndex 用什么组织 Node?
  • from_documents 一行内部做了哪几步?
  • _add_nodes_to_index 的核心两步?
  • 向量/文本/索引结构分别存在哪三处?

✋ 动手

cd /Users/bitmart/work/codes/github/llama_index/llama-index-core/llama_index/core
sed -n '36,80p' indices/vector_store/base.py
sed -n '219,290p' indices/vector_store/base.py
grep -n "def from_documents\|def as_retriever\|def as_query_engine" indices/base.py | head
明天预告 · Day 08:数据存在哪讲透——StorageContext 三件套(docstore/index_store/vector_store)。怎么统一管理、怎么 persist 到磁盘、怎么加载复用(省下重新建库)。
← Day 06 Day 08 · 存储 →