← 返回 Agent 技术总览
🔍 RAG 系列

RAG 检索增强生成:从离线向量化到在线检索全链路

Embedding · FAISS · HNSW · Dense/Sparse/Hybrid 检索 · Reranker · Chunking 策略 · 完整工程实践

🌱
零、为什么需要 RAG?

LLM 有三个内生缺陷,RAG 是最主流的解法:

❌ 知识截止日期
训练数据有截止时间,无法回答「今天的新闻」「最新的 API 文档」
❌ 企业私有知识
公司内部文档、代码库、历史对话,LLM 完全不知道
❌ 幻觉
LLM 会自信地编造答案,有检索到的真实依据能有效抑制

RAG 的核心思路:不是让 LLM「记住」所有知识,而是「用时查阅」。把知识存在外部数据库,用户提问时先检索最相关的片段,再把这些片段塞进 prompt 让 LLM 基于它们回答。

全链路一句话: 文档 → 切块(Chunk)→ 向量化(Embedding)→ 存入向量库 → 用户问题向量化 → ANN 检索 Top-K → 可选 Reranker 精排 → 拼接 Prompt → LLM 生成答案
🔢
一、Embedding:把文字变成向量

Embedding 模型把一段文字(句子、段落)映射成一个高维向量(如 1536 维),语义相近的文字在向量空间里距离也近。这是整个 RAG 的基础。

1. 为什么向量能表达语义?

图1:向量空间里的语义关系
维度2 维度1 「猫」 「小猫」 「kitty」 距离近 → 语义相似 「汽车」 「轿车」 「vehicle」 距离远 → 语义无关 Embedding 模型的训练目标 • 相同语义 → 向量夹角小(相似度高) • 不同语义 → 向量夹角大(相似度低) • 用余弦相似度(cosine)衡量距离 cos(A,B) = A·B / (|A|·|B|) 值域 [-1, 1],越接近 1 越相似

2. 常用 Embedding 模型

模型维度特点适合场景
text-embedding-3-small
OpenAI
1536中英文均衡,API 直接调用通用场景,快速上手
BGE-large-zh
BAAI 开源
1024中文最强之一,可本地部署中文文档检索
Sentence-BERT
HuggingFace
768英文表现好,轻量英文语料、低资源环境
M3E
MokaAI 开源
768中英双语,免费中英混合文档

3. Embedding 的本质:对比学习训练

Embedding 模型通过对比学习训练:给定一对正样本(语义相似的句子对),让它们的向量靠近;给定负样本,让向量远离。训练完成后,模型就学会了把「语义」编码进向量距离。

# 对比学习损失函数(InfoNCE Loss)
正样本对: (「今天天气如何?」, 「北京今天晴,25°C」) → 相似度最大化
负样本对: (「今天天气如何?」, 「Python 如何排序列表?」) → 相似度最小化

# 推理时:把查询和文档都变成向量,计算余弦相似度
similarity = cosine(embed("用户问题"), embed("文档片段"))
✂️
二、Chunking:怎么切文档

文档通常很长,直接向量化整篇文章有两个问题:① 超出 Embedding 模型最大输入长度;② 一个向量混合了太多信息,检索精度下降。Chunking 就是把文档切成合适大小的片段。

1. 五种 Chunking 策略对比

策略方法优点缺点
固定长度切割 每 512 token 切一段 简单快速 可能从句子中间断开,语义不完整
重叠滑窗切割 512 token,重叠 64 token 缓解边界截断问题 重复存储,索引变大
句子/段落边界切割 按「。」「\n\n」等自然边界切 语义完整 长度不均匀
递归字符切割
LangChain 默认
按 ['\n\n', '\n', '. ', ' '] 优先级递归切 兼顾长度和语义边界 实现略复杂
语义切割 ⭐ 用 Embedding 相似度检测「语义断点」 最贴近自然段落 慢,需要额外计算

2. Chunk 大小的选择

太小(<100 token)
上下文不足,LLM 无法基于片段推理。
检索到的是孤立句子。
推荐范围(256~512 token)
语义完整,检索精度与上下文平衡。
大多数场景的最优区间。
太大(>1000 token)
向量语义模糊,检索精度下降。
塞入 prompt 占用太多 token。

3. Parent-Child Chunk 策略(进阶)

存储时用小 chunk(128 token),检索时返回其对应的大 chunk(512 token)作为上下文。这样检索精度高(小 chunk 语义纯净),LLM 上下文也充足(大 chunk 信息完整)

大文档
→ 切为
Parent Chunk(512 token)
→ 再切为
Child Chunk(128 token)入库
→ 检索命中 Child → 返回对应 Parent
🗃️
三、向量索引:FAISS / HNSW 原理

数据库里存了几百万个向量,用户来一个查询向量,怎么快速找到最相似的 Top-K 个?暴力遍历太慢(O(N×D)),需要专门的近似最近邻(ANN)索引算法。

1. 暴力搜索的瓶颈

100 万个 1536 维向量,暴力遍历每次需要计算 100 万次余弦相似度,延迟不可接受。ANN 算法用空间换时间,接受「可能漏掉极少数真正最近邻」来换取 10~100 倍加速。

2. IVF(倒排文件索引)——FAISS 的核心

IVF 的思路:先用 K-means 把所有向量聚成 N 个簇,每个向量属于离它最近的簇。查询时:先找查询向量所属的最近几个簇,只在这些簇里做暴力搜索

图2:IVF 索引——先粗筛簇,再精搜
簇 1 簇 2(命中)✓ 查询 簇 3 跳过(不在近邻簇中) 在此簇内暴力搜 Top-K 跳过(不在近邻簇中)

参数 nlist(簇数):越大精度越高但建索引越慢。nprobe(查询时探索的簇数):越大召回越高但查询越慢。一般设 nlist=1024, nprobe=16~64。

3. HNSW(分层可导航小世界图)——速度和精度最均衡

HNSW 把向量组织成一个多层图结构:上层是稀疏的「高速公路」,下层是密集的「普通道路」。查询时从上层快速定位大致区域,再逐层向下精确搜索。

图3:HNSW 分层图结构
Layer 2 Layer 1 Layer 0 最终结果 ← 从入口节点开始

4. 常用向量数据库对比

工具类型优点适合
FAISS本地库速度极快,支持 GPU 加速,Meta 出品研究、离线批量检索
Chroma轻量 DB零配置,内嵌 Python,开发友好原型开发、小规模
Qdrant向量 DBRust 实现,高性能,支持 payload 过滤生产环境,中大规模
Milvus分布式 DB水平扩展,支持亿级向量大规模生产部署
Pinecone云服务全托管,无需运维快速上线,预算充足
🔎
四、检索策略:Dense / Sparse / Hybrid

1. Dense Retrieval(稠密检索)

就是上面讲的:用 Embedding 模型把查询和文档都变成向量,计算相似度。擅长语义理解,能处理换个说法但意思一样的情况。弱点:对专有名词(如 SKU ID、型号)不敏感——「iPhone 15 Pro Max」和「iPhone 15」的向量可能很近。

2. Sparse Retrieval(稀疏检索)

以 BM25 为代表,基于词频统计(TF-IDF 思想)。擅长精确词匹配,对专有名词、型号、代码变量名很敏感。弱点:不理解语义——「汽车」和「轿车」是两个词,BM25 认为它们不相关。

# BM25 打分公式(简化版)
score(q, d) = Σ IDF(qi) × tf(qi, d) × (k1 + 1) / (tf(qi, d) + k1 × (1-b+b×|d|/avgdl))

# IDF:词越稀有,权重越高
# tf:词频,但用平滑处理避免词频过大的文档占优
# |d|/avgdl:文档长度归一化

3. Hybrid Retrieval(混合检索)⭐ 推荐

同时运行 Dense 和 Sparse,把两路结果合并。常用合并方式是 RRF(Reciprocal Rank Fusion,倒数排名融合)

# RRF 公式:把两路结果的排名倒数相加
RRF_score(d) = 1 / (rank_dense(d) + 60) + 1 / (rank_sparse(d) + 60)

# 举例:
文档A:Dense 排名第1, Sparse 排名第3 → 1/61 + 1/63 ≈ 0.0323
文档B:Dense 排名第2, Sparse 排名第1 → 1/62 + 1/61 ≈ 0.0325
# 文档B 综合得分更高
图4:三种检索策略对比
Dense(向量检索) ✅ 语义理解强 ✅ 同义词/换说法 ❌ 专有名词弱 ❌ 需要 GPU 训练 Sparse(BM25) ✅ 精确词匹配强 ✅ 无需训练 ❌ 语义理解弱 ❌ 换个词就找不到 Hybrid(混合)⭐ ✅ 语义 + 精确 ✅ 召回率最高 ✅ 工程上成熟 用 RRF 合并两路结果
🎯
五、Reranker:精排提升精度

向量检索召回 Top-50,但真正相关的可能只有 3~5 篇。Reranker 对候选结果做精排,用更强的模型重新打分,确保最终传给 LLM 的文档质量最高。

1. 为什么需要两阶段?

阶段一:召回(Retrieval)
目标:高召回,不能漏掉真正相关的文档
方法:向量检索,快(毫秒级),返回 Top-50~100
缺点:精度不够,有噪声
阶段二:精排(Rerank)
目标:高精度,把最相关的排到最前面
方法:Cross-Encoder 对每个(问题, 文档)对打分
缺点:慢(对每对都要做推理),只适合对小候选集做

2. Cross-Encoder vs Bi-Encoder

Bi-Encoder(向量检索用)Cross-Encoder(Reranker 用)
输入问题和文档分别编码问题 + 文档一起输入
速度快(向量可预计算)慢(每对都要推理)
精度中等(无法建模交互)高(能建模问题和文档的细粒度交互)
典型模型BGE, text-embedding-3BGE-Reranker, Cohere Rerank

3. Reranker 的工作流

用户问题
向量检索 Top-50
Reranker 精排
Top-5 传给 LLM
LLM 生成答案
实测效果: 加了 Reranker 后,Top-5 的精度通常能从 ~60% 提升到 ~85%+。代价是增加 50~200ms 延迟(对 50 个候选做 50 次推理)。对质量要求高的场景强烈推荐。
🔄
六、完整 RAG 流水线

离线阶段(一次性)

# 1. 加载文档
docs = load_documents("./knowledge_base/") # PDF, Word, Markdown...

# 2. 切块
chunks = RecursiveCharacterTextSplitter(chunk_size=512, overlap=64).split(docs)

# 3. 向量化
embeddings = embed_model.encode([c.text for c in chunks]) # shape: (N, 1536)

# 4. 存入向量库
vector_db.upsert(ids=[c.id for c in chunks], vectors=embeddings, payloads=chunks)

在线阶段(每次查询)

# 1. 问题向量化
query_vec = embed_model.encode(user_query) # shape: (1536,)

# 2. 向量检索(可选:加 BM25 混合)
candidates = vector_db.search(query_vec, top_k=50)

# 3. Reranker 精排
reranked = reranker.rank(query=user_query, docs=candidates, top_k=5)

# 4. 拼接 Prompt
prompt = f"""基于以下文档回答问题:
{'\n'.join([d.text for d in reranked])}
问题:{user_query}"""

# 5. LLM 生成
answer = llm.generate(prompt)
七、进阶技巧

1. Query 改写(Query Rewriting)

用户问题有时很模糊,直接检索效果差。用 LLM 先把问题改写成更适合检索的形式:

技术做法效果
HyDE
假设文档嵌入
让 LLM 先生成一个假想答案,用假想答案的向量去检索检索向量更接近文档空间
Multi-Query让 LLM 把问题改写成 3~5 个变体,分别检索后合并召回率显著提升
Step-Back Prompting先让 LLM 提炼出「这个问题背后的抽象概念」,用抽象概念检索对需要背景知识的问题效果好

2. 元数据过滤(Metadata Filtering)

给每个 chunk 打上元数据标签(作者、时间、文档类型、产品线),检索时先用 WHERE 条件过滤,再做向量搜索。大幅提升精准度,避免跨领域干扰。

# 只在「产品手册」文档里检索 2024 年之后的内容
results = vector_db.search(
  query_vec,
  filter={"doc_type": "产品手册", "year": {"$gte": 2024}},
  top_k=20
)

3. Self-RAG(检索判断)

不是每个问题都需要检索。Self-RAG 让 LLM 先判断「这个问题需要外部知识吗?」,只有需要时才触发检索,减少不必要的延迟和噪声。

⚠️
八、常见踩坑
问题原因解决方案
检索到的文档不相关Chunk 太大/太小,或问题太短调整 chunk size;用 Query 改写扩充问题
答案正确但没引用来源prompt 没要求 LLM 标注在 prompt 里明确要求「请注明引用自第几段」
检索到正确文档但答案还是错文档里的答案在 chunk 边界被截断增加 chunk overlap;或用 Parent-Child 策略
同一知识库不同版本冲突旧文档没及时更新加 metadata 版本号 + 过滤旧版本
中文检索精度差通用 Embedding 模型中文表现不好换成 BGE-large-zh 或 M3E-large
第一次检索就没发现问题没有评估指标用 RAGAS 框架评估 Faithfulness、Answer Relevancy
RAG 评估工具推荐:RAGAS
自动化评估 RAG 系统质量,核心指标:
Faithfulness:答案是否只来自检索到的文档(防幻觉)
Answer Relevancy:答案是否回答了用户的问题
Context Precision:检索到的文档是否都相关
Context Recall:相关文档是否都被检索到了