知识点思维导图
30 个知识节点
RAG(13) - 向量数据库
读完后,你应能完成以下任务:
- 绘制“RAG(19) - 向量数据库 / 与进阶篇的分工”的关键对象与数据流,解释“继续阅读 Milvus(01) - Schema、索引、过滤与版本 和 Milvus(02) - 电子书离线建库与引用问答;”,并用源码位置、日志或 Trace 标注证据。
- 为“RAG(19) - 向量数据库 / 它存什么:向量 + 原文 + 元数据”设计正常与异常输入,验证“vector 是检索的依据,但它不可读,所以必须连原文一起存;”,输出首个偏差位置与回归测试结果。
- 实现“RAG(19) - 向量数据库 / 它做什么:add 和 search 两个核心操作”的最小代码或配置,检验“所以真实向量库不这么干。”,输出命令、结果与 Diff,并说明不适用边界。
一句话目标:读完你能讲清向量数据库到底存什么、做什么,知道它和普通数据库的区别,并能说出它内部「检索」是怎么回事。
一、与进阶篇的分工
本篇只负责讲清向量、原文、元数据和 ANN 在 RAG 中的职责。 需要落到专用向量库时, 继续阅读 Milvus(01) - Schema、索引、过滤与版本 和 Milvus(02) - 电子书离线建库与引用问答; RAG 主线不再复制同一套 Milvus 配置。
二、向量数据库的真实应用场景
知识库入库后,你手里有了几万个 chunk,每个都转成了一个 1536 维的向量。 用户来了个问题,也转成向量。 现在的任务是:从这几万个向量里,快速找出和问题向量最接近的那几个。
你可能想:存进 MySQL 不就行了?
问题是,
普通数据库擅长的是「精确匹配」——WHERE id = 5、WHERE name = '张三'。
但向量检索要的是「最接近」——没有哪两个向量会完全相等,你要的是夹角最小的 topK。
MySQL 没有为这种「相似度排序」优化,几万条逐个算还能扛,几百万条就慢得没法用了。
向量数据库(Chroma、Milvus、pgvector 等)就是专门干这件事的:存海量向量, 并能很快地找出和查询向量最相近的若干个。
三、它存什么:向量 + 原文 + 元数据
一条向量库记录通常包含三部分,缺一不可:
{
"vector": [0.21, -0.07, ...], # 向量,用来算相似度
"text": "报销需在 30 天内提交", # 原文,检索到后要拼进 prompt 给模型
"metadata": {"source": "财务手册", # 元数据,用来做引用来源和过滤
"doc_id": 12, "dept": "finance"}
}
- vector 是检索的依据,但它不可读,所以必须连原文一起存;
- text 是检索命中后真正交给模型的内容;
- metadata 有两个用途:一是生成引用来源(告诉用户答案来自《财务手册》),二是做过滤检索(比如「只在 finance 部门的文档里搜」)。
很多人只记得存向量,忘了元数据。 结果检索到了内容却说不出来自哪、也没法按权限过滤,这在企业知识库里是硬伤。
四、它做什么:add 和 search 两个核心操作
向量库的接口千变万化,核心就两个:
add(vector, text, metadata) 入库:把一条记录存进去
search(query_vector, top_k) 检索:找出和 query_vector 最相近的 top_k 条
search 是灵魂。
最朴素的实现叫「暴力检索」:拿查询向量和库里每一条都算一次相似度,排序取 topK。
逻辑简单,但数据量一大就慢——每次查询都要扫全库。
所以真实向量库不这么干。 它们用近似最近邻(ANN)索引, 典型的是 HNSW:预先把向量组织成一张「邻居图」, 查询时顺着图走几步就能逼近最相近的那批, 不用扫全库。 代价是结果是「近似最优」而非「绝对最优」,但换来的是百倍千倍的速度,这笔账在工程上很划算。
HNSW 的检索机制是在多层邻接图上从稀疏高层快速接近目标区域,再在稠密底层扩展候选。 搜索宽度越大,Recall@K 通常越高,但 CPU 与延迟也越高; 索引连边越多,构建和内存成本也会上升。 生产选型必须用标注查询同时压测召回、P95 延迟、过滤后候选量和内存,不能只比较单次查询速度。
五、工程上真正会踩的坑(本篇独有)
- 以为换个向量库就能提升检索效果。向量库只负责「存得下、查得快」,检索准不准取决于 embedding 模型和切分质量。库选型解决的是性能和规模,不是召回质量。
- 入库时不存原文,只存向量。向量不可读,检索到一个向量却拿不到对应文本,等于白存。原文必须随向量一起入库。
- 忽视元数据过滤。企业场景里「A 部门的人不能搜到 B 部门的机密文档」,靠的就是 metadata 过滤 + 向量检索结合,不是纯向量检索。建库时就要规划好要按哪些字段过滤。
- 拿暴力检索上生产。demo 的逐条遍历几万条还行,几百万条必须上 HNSW 这类索引,否则每次查询都扫全库,延迟爆炸。
六、一句话面试答法
向量数据库和普通数据库差在哪? 普通数据库做精确匹配,向量库做相似度检索——找出和查询向量夹角最小的 topK。它存的是「向量 + 原文 + 元数据」三件套:向量用来算相似度,原文用来喂给模型,元数据用来做引用和权限过滤。数据量大时它用 HNSW 这类近似最近邻索引避免扫全库,用一点精度换巨大的速度。Chroma、Milvus、pgvector 都是常见选择。
七、动手实践:23 向量数据库
用 dict + list 实现一个最小向量库 MiniVectorStore,
支持 add(入库) 和 search(检索 topK),
把向量库的内核讲清楚。
7.1 在线运行
直接使用本文“可运行源码”中的沙盒执行; 源码、复制内容和实际运行入口保持一致。
零依赖,纯标准库。
7.2 预期输出
向量库已入库 3 条记录
查询:年会什么时候开
相似度 0.099 [活动通知] 公司年会定在每年一月的第三个周五举办
--------------------------------------------------
查询:年假有几天
相似度 0.139 [人事制度] 年假根据工龄计算,满一年享受五天
--------------------------------------------------
入库三条资料后,两个查询各自命中了最相关的那条,并带回了元数据里的来源。 注意「年会」和「年假」只差一字,但向量库靠整体词重叠把它们区分开了。
7.3 代码↔概念对应
| 概念 | 在 main.py 哪里 |
|---|---|
| 文本 → 向量(这里用词频近似) | embed |
| 稀疏向量余弦相似度 | cosine |
| 向量库(list 存记录) | MiniVectorStore |
| add:向量 + 原文 + 元数据入库 | MiniVectorStore.add |
| search:算相似度取 topK | MiniVectorStore.search |
| 入库时就算好向量 | add 里 embed(text) |
7.4 和真实向量库的差距
这个 demo 是「暴力检索」:每次 search 都和库里每条比一遍,数据量一大就慢。
真实向量库(Chroma / Milvus / pgvector)会用 HNSW 等近似最近邻索引避免全量遍历,
还支持按元数据过滤、持久化和并发。
但它们的内核都是这里的「存向量 + 算相似度取 topK」。
embed 在真实项目里换成 embedding 模型输出的稠密向量。
7.5 可运行源码:向量数据库
下方代码就是在线沙盒实际执行的完整源码。
main.py
八、总结
- 它存什么:向量 + 原文 + 元数据:vector 是检索的依据,但它不可读,所以必须连原文一起存;
- 工程上真正会踩的坑(本篇独有):向量库只负责「存得下、查得快」,检索准不准取决于 embedding 模型和切分质量。
- 一句话面试答法:它存的是「向量 + 原文 + 元数据」三件套:向量用来算相似度,原文用来喂给模型,元数据用来做引用和权限过滤。
- 工程边界:生产选型必须用标注查询同时压测召回、P95 延迟、过滤后候选量和内存,不能只比较单次查询速度。
- 实现机制:metadata 有两个用途:一是生成引用来源(告诉用户答案来自《财务手册》),二是做过滤检索(比如「只在 finance 部门的文档里搜」)。
- 选型依据:Chroma、Milvus、pgvector 都是常见选择。
学完自测
选择所有正确答案;提交后逐项核对判断依据。