Day 08 / 共 20 天 · 第 2 周 索引/嵌入/存储
存储 StorageContext(数据落哪)
昨天说"数据分三处存",今天讲清这"三件套":docstore(文本)、index_store(结构)、vector_store(向量)。理解它们,就知道建好的库怎么持久化、怎么从内存原型平滑升级到生产。
📍 你在整条链的位置
③ 嵌→
④ 建索引→
存储三件套(数据落地)→
⑤检索 ⑥合成
L01
三件套总览
🤔 为什么不把所有东西存一个地方?
因为一个 Node 建好后有三种性质完全不同的数据:它的文本(普通字符串)、它的向量(1536 个浮点数,要专门的相似度检索)、它属于哪个索引(映射关系)。塞一起既不好管、也用不上各自最合适的存储。
StorageContext 统管三件套:文本、结构、向量分开存,各用最合适的后端。
L02
docstore:Node 的仓库
storage/docstore/:BaseDocumentStore + SimpleDocumentStore(默认,内存/JSON)。按 id_ 存取 Node,还记 ref_doc_info(Node 来自哪个原文档)。
💡 为什么文本不直接塞向量库?
有些向量库只擅长存/查向量,不适合存大段文本。所以文本单独放 docstore:检索时向量库返回"Node id",再从 docstore 按 id 取出完整文本给 LLM。分工明确。
ref_doc_info 记"这些 Node 来自哪个 Document + 文档 hash"——支撑 Day 05 的增量更新。L03
index_store:索引的目录
storage/index_store/:存 index_struct(Day 07 的 IndexDict)——每个索引"包含哪些 Node id"的映射。
读法:一个 StorageContext 能承载多个索引(一个向量索引 + 一个摘要索引,共享同一批 docstore 的 Node)。index_store 记"每个索引的结构、包含哪些 Node"。它是轻量的"目录",指向 docstore 里的实际 Node。
L04
vector_store:检索引擎
vector_stores/:SimpleVectorStore(默认,内存)。存向量、提供"给查询向量找最相似 k 个"(Day 09 精读)。
这是 RAG 的"检索心脏"
默认 SimpleVectorStore 用内存 + 暴力算相似度(小数据够用);生产用专业向量库(Chroma/Qdrant/Pinecone/Milvus),百万向量也快(用 HNSW 等近似算法)。换向量库只需换 integration。
L05
StorageContext:统一门面
storage/storage_context.py:53 把三者打包:
storage_context = StorageContext.from_defaults(
docstore=SimpleDocumentStore(),
index_store=SimpleIndexStore(),
vector_store=ChromaVectorStore(...), # 生产:换专业向量库
)
index = VectorStoreIndex.from_documents(docs, storage_context=storage_context)
读法:
from_defaults 不传就全用 Simple*(内存版,入门/测试);传入就用你指定的后端。索引通过它读写数据——它是"存储的统一门面"。Day 01 没传就用默认全内存(所以重启数据没了——要 persist,见下)。L06
persist:建库一次,多次使用
🤔 每次跑程序都重新建库?太浪费
建库要嵌入(花钱花时间)。总不能每次启动都重嵌一遍。
📝 存盘 + 加载
# 第一次:建好后存磁盘
index.storage_context.persist(persist_dir="./storage")
# 以后:直接加载,不用重新嵌入!
from llama_index.core import StorageContext, load_index_from_storage
sc = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(sc)💡 persist 把三件套存磁盘(Simple* 版存成 JSON)
下次
load_index_from_storage 直接加载——省下重读、重切、重嵌。用专业向量库时向量已在库里(Chroma 自己持久化),只需 persist docstore/index_store。这是"建库一次、反复使用"的关键。L07
可插拔后端:原型→生产平滑升级
💡 三件套每个都是"接口 + 多实现"
docstore 可用 Simple/Redis/Mongo/Postgres;vector_store 有几十种(Chroma/Qdrant/Pinecone/Weaviate/pgvector…)。
📝 从笔记本到生产集群,只改 StorageContext
开发:
生产:
→ 业务代码(建索引、查询)一行不改,只换存储后端。
StorageContext.from_defaults()(全内存,零依赖)生产:
StorageContext.from_defaults(vector_store=QdrantVectorStore(...), docstore=RedisDocumentStore(...))→ 业务代码(建索引、查询)一行不改,只换存储后端。
又是"接口统一、实现可换"
本系列反复出现的设计(APISIX/Higress/wasm-go 都这样)。它让你能用同一套代码从原型平滑扩展到生产——这是好框架的标志。
L08
今日小结 + 动手
🧠 今天你应该能回答
- 存储三件套各存什么?为什么要拆开?
- docstore 的 ref_doc_info 支撑什么(Day 05 增量更新)?
- StorageContext 的作用?不 persist 会怎样?
- 怎么从内存后端平滑换成生产后端?
✋ 动手
cd /Users/bitmart/work/codes/github/llama_index/llama-index-core/llama_index/core
sed -n '53,120p' storage/storage_context.py
grep -n "def persist\|def from_defaults" storage/storage_context.py
ls storage/docstore/ storage/index_store/
明天预告 · Day 09:三件套里最关键的向量库单独精读——SimpleVectorStore 的 add 和 query:向量怎么存、"找最相似 k 个"怎么算、metadata 过滤、MMR 多样性检索。