RAG 与向量数据库是如何工作的:从图书馆比喻到系统架构
本文也是分 4 个层级,从直觉讲起,一步步走到架构和一点点数学:
- RAG(Retrieval-Augmented Generation)是什么
- 向量数据库怎么存、怎么查
- 检索结果如何合并进 Prompt
- RAG 在理论上如何刻画为"检索 + 生成"的组合模型
Level 1:非技术直觉版------"图书馆 + 聪明翻译官"
先用一个生活类比:
- 大语言模型(LLM) :一个很会写的"翻译官",有很多常识和语言能力,但不了解你公司内部的私有文档。
- 向量数据库 :一个超级图书馆 + 语义搜索引擎,可以在成千上万份文档里,翻出最相关的几段内容。
当你问一个问题时,RAG 做的事是:
- 先把你的问题拿去"图书馆"搜索,找出最相关的几段资料;
- 再把:
- 这些资料 + 你的问题
- 一起交给大模型;
- 大模型一边看"资料",一边组织语言给你答案。
一句话理解:
RAG = 先查资料,再参考资料来回答问题。
Level 2:工程师视角------一条完整的 RAG 流水线
这一层从"写代码的角度"走一遍典型 RAG 流程。
Step 1:预处理 & 向量化(离线阶段)
对你的知识库文档做预处理,一般包含三步:
-
切块(chunking)
- 将长文档拆成较小的段落(比如每段 200~500 字)。
- 每个 chunk 会被当成一条"可检索单元"。
-
向量化(embedding)
- 使用 embedding 模型将每个 chunk 映射到一个向量:
chunki ⟶ vi∈Rd \text{chunk}_i \;\longrightarrow\; \mathbf{v}_i \in \mathbb{R}^d chunki⟶vi∈Rd - 这个向量可以理解为:这段文本在语义空间中的坐标。
- 使用 embedding 模型将每个 chunk 映射到一个向量:
-
存入向量数据库
- 向量库中记录的通常包括:
- 向量 vi\mathbf{v}_ivi
- 原始文本内容
- 元数据:来源文件、页码、更新时间、标签等
- 向量库中记录的通常包括:
Step 2:用户提问 → 向量检索 Top-k
当用户提一个问题 q 时:
-
对问题做 embedding:
q ⟶ vq q \;\longrightarrow\; \mathbf{v}_q q⟶vq
-
向量检索:在向量数据库中查找与 vq\mathbf{v}_qvq 最相似的向量
伪代码示例:
v_q = embed(query) results = vector_db.search(v_q, top_k = k)- 相似度常用:
- 余弦相似度(cosine similarity)
- L2 距离(欧氏距离)
- 内积(inner product)
- 相似度常用:
-
返回结果通常包含:
- 文本片段(chunk 内容)
- 相似度分数
- 元数据(标题、文档名、页码等)
这一步就是:在语义空间中找到"最像这个问题"的几个段落。
Step 3:构造 Prompt ------ 合并检索结果 + 问题
拿到检索出的 Top-k chunks 后,需要构造一个完整的 Prompt 喂给 LLM,例如:
你是一个知识库问答助手。
下面是与问题相关的资料片段(可能存在冗余或冲突):
片段1 - 来自文件 A 第 3 页
...
片段2 - 来自文件 B 第 10 页
...
片段3 - 来自文件 A 第 4 页
...
请严格依据以上资料回答下列问题,如资料中没有答案,请说明"资料不足":
问题:{用户的问题}
实践中通常还会:
- 合并相邻的 chunk(减少上下文碎片)
- 去重、去明显重复的信息
- 按相关度排序,限制总 token 长度
Step 4:LLM 带着"参考资料"生成回答
最后,把构造好的 Prompt 发给大模型:
answer = LLM(prompt_with_context)
- LLM 在生成时,一边"看"你给的资料,一边利用自身的语言组织能力输出答案。
- 回答质量取决于:
- 检索结果是否覆盖了真正需要的信息;
- Prompt 设计是否清晰、约束是否合理;
- 模型自身能力。
一个基础版 RAG 系统的主流程可以概括为:
Query → Embedding → Vector Search → Top-k → Prompt 构造 → LLM 生成
Level 3:向量数据库内部的检索机制 & 结果融合策略
这一层更偏"系统 / 算法"视角,关注向量库如何高效检索以及如何优化检索结果。
向量检索:从暴力搜索到近似最近邻(ANN)
假设你有 NNN 条向量,每条是 ddd 维:
-
暴力搜索(Brute Force) :
对每一条向量 vi\mathbf{v}_ivi 计算一次相似度,复杂度 O(Nd)O(Nd)O(Nd)。
当 NNN 很大(百万级以上)时,速度会很慢。
-
向量数据库一般使用 近似最近邻搜索(Approximate Nearest Neighbor, ANN):
- 图结构:HNSW(Hierarchical Navigable Small World)
- 基于聚类的倒排索引:IVF
- 压缩技术:PQ / OPQ(Product Quantization)等
这些方法的目标是:
在可接受的时间和内存下,快速找到"足够接近真实最近邻"的向量,而不追求严格的精确最优。
相似度度量:语义空间中的"距离"
向量库中常见的相似度 / 距离度量:
-
余弦相似度:
cos(u,v)=u⋅v∥u∥ ∥v∥ \mathrm{cos}(u, v) = \frac{u \cdot v}{\lVert u \rVert \,\lVert v \rVert} cos(u,v)=∥u∥∥v∥u⋅v
-
欧氏距离(L2):
∥u−v∥2 \lVert u - v \rVert_2 ∥u−v∥2
-
内积(Inner Product) 等。
embedding 模型训练时,会让:
- 语义相近的文本 → 向量更接近(夹角更小、欧氏距离更小)
- 语义无关的文本 → 向量更远
因此,向量相似度就可以作为"语义相关度"的近似。
结果融合:不仅仅是"Top-k 拼一块"
实际项目里,你往往会对检索结果做进一步处理:
-
重排(Re-ranking)
- 第一步:用向量库快速找出 Top-50
- 第二步:用更复杂的模型(如 cross-encoder)对这 50 条进行精细打分,再选 Top-5
→ 兼顾速度和精度。
-
MMR(Maximal Marginal Relevance)
- 目标:在保持高相关度的前提下,避免多个 chunk 内容高度重复;
- 让最终选出的片段既相关又"多样"。
-
按元数据过滤 / 加权
- 只检索某个时间范围(例如最近 1 年);
- 只检索特定标签(某个项目 / 客户);
- 权重更高的文档优先展示。
合并进 Prompt:要考虑上下文预算
模型上下文长度是有限的(例如 8k / 32k / 128k tokens),因此需要设计策略:
- 对每个 chunk 估算 token 数,避免超过上限;
- 可以按"相关度降序 + 去重"逐个添加,直到接近 token 上限;
- 按来源分组,例如:
合同文本相关片段
chunk 1\]... \[chunk 2\]... # 邮件记录相关片段 \[chunk 3\]...
这一步看似是"工程细节",但对实际问答效果影响极大。
Level 4:从建模视角看 RAG------"检索 + 生成"的组合模型
最后一层从"理论 / 抽象"角度看 RAG,帮助你对整体形成更高层的理解。
普通 LLM vs RAG:隐式知识 vs 显式知识
-
普通 LLM:
-
训练阶段读过海量语料;
-
最终把这些知识"压缩"到了参数 θ\thetaθ 里(隐式知识);
-
推理时执行:
y∼pθ(y∣x) y \sim p_\theta(y \mid x) y∼pθ(y∣x)
完全不访问外部知识库。
-
-
RAG 模型:
-
知识放在一个外部库 DDD 中(文档、数据库等);
-
推理时,会先根据 query 做一次检索,得到上下文 C(q,D)C(q, D)C(q,D),再生成:
y∼pθ(y∣q,C(q,D)) y \sim p_\theta(y \mid q, C(q, D)) y∼pθ(y∣q,C(q,D))
-
这样做的好处:
- 可以随时更新知识库,而不用改模型参数;
- 可以把隐私 / 最新信息放在库里,由权限、访问控制来管理;
- 易于审计模型"依据了哪些资料"给出结论。
RAG 中的"检索器"和"生成器"
将 RAG 抽象成两个组件:
-
检索器(Retriever):
C(q,D)=Retrieve(q;D) C(q, D) = \mathrm{Retrieve}(q; D) C(q,D)=Retrieve(q;D)
- 输入:query qqq 和文档库 DDD
- 输出:一组与 qqq 相关的文档片段(可以是集合、加权集合等)
-
生成器(Generator):
y∼pθ(y∣q,C(q,D)) y \sim p_\theta(y \mid q, C(q, D)) y∼pθ(y∣q,C(q,D))
在高级用法中,检索器本身也是可学习的:
例如用强化学习或梯度信号,训练检索器让"被选出的文档集合"对最终任务(准确回答、任务成功率)最有帮助。
向量数据库 = 一个高效实现"近似 argmax"的系统
本质上,向量检索就是在做:
Retrieve(q;D)=argmaxd∈Dsim(f(q),f(d)) \mathrm{Retrieve}(q; D) = \arg\max_{d \in D} \mathrm{sim}\bigl(f(q), f(d)\bigr) Retrieve(q;D)=argd∈Dmaxsim(f(q),f(d))
其中:
- f(⋅)f(\cdot)f(⋅):将文本映射到向量的 embedding 模型;
- sim\mathrm{sim}sim:相似度函数(余弦、内积、负 L2 等)。
向量数据库的作用就是:
在 NNN 很大时,依靠精心设计的数据结构(HNSW、IVF、PQ 等),
高效近似地求解这个最大化问题。
多轮 / Agent 化:RAG 的进阶形态
在复杂应用中,RAG 还可以继续进化,例如:
-
多轮迭代检索(Iterative RAG)
- 第一次检索给出初步结果;
- LLM 读完后发现"不够",自动生成新的子问题再检索;
- 重复多轮,逐步聚焦到真正需要的资料。
-
工具化 / Agent 化
- 模型不只"被动接受"检索结果,而是能:
- 决定何时发起检索;
- 决定从哪个知识源检索(合同库、邮件库、代码库等);
- 决定使用什么过滤条件;
- 每一次检索结果都作为新一轮思考的上下文。
- 模型不只"被动接受"检索结果,而是能:
小结
- RAG 的核心思想: 把知识存放在外部可更新的库中,模型推理时先检索、再生成。
- 向量数据库 提供了在"语义空间"中高效搜索相关文本片段的能力,是 RAG 的基础设施。
- 从流程上看:
文档切块 → 向量化 → 入库 → Query 向量化 → 相似度检索 → 合并结果进 Prompt → LLM 生成。 - 从建模上看:
RAG 是一个显式的 "检索器 + 生成器" 组合模型,
可以让模型在不改参数的情况下,使用最新、私有、可控的知识。