Skip to content

RAG 体系 ​

相关笔记:Elasticsearch

RAG 看起来就五步:切块、向量化、存、检索、拼。但真正决定效果的,全在细节里 —— 90% 的 RAG 效果问题,都出在「切块」和「召回」,而不是模型。

1. 什么时候该用 RAG ​

场景选择
模型不知道的私有知识,且要及时更新RAG
模型能力/风格不对(不会说行话、格式总错)微调
只是没说清改 Prompt
知识量很小(几页),且不常变直接塞进上下文
需要跨文档多跳推理RAG + 查询改写/多轮检索

判断口诀:「不知道」用 RAG,「不会说」才微调。

2. 离线管道:解析与切块 ​

2.1 解析(最脏最累的一步) ​

PDF 是 RAG 的第一大坑:表格丢结构、双栏串行、扫描件没文字。

内容类型处理
普通 PDF优先用结构化解析(PyMuPDF / pdfplumber),别用粗略抽取
扫描件OCR(PaddleOCR / 云服务),注意版面还原
表格保留为 Markdown/HTML 表格,别拆成一段文字
Word/HTML按标题层级解析,天然有结构
图片多模态模型生成描述,或直接走多模态检索

一句经验:输入脏,后面全白搭。清洗(去页眉页脚、去乱码、合并断行)值得花掉整个项目 1/3 的时间。

2.2 切块(chunking)策略 ​

策略做法适合
定长 + 重叠512 token,overlap 64通用基线,简单可靠
按结构切按标题层级/段落切,保留章节路径文档类首选
语义切块按句子嵌入的相似度找断点长篇连续叙述
父子块(small-to-big)用小块检索,命中后返回其父块兼顾精度与上下文
句窗(sentence window)命中句子后向前后扩 N 句问答型语料

三条硬经验:

  1. 一定要有 overlap(10%~20%),否则答案正好跨在边界上就丢了;
  2. 每块带上「来源 + 章节标题」(元数据),既提升检索,又方便引用;
  3. 块大小和问题粒度匹配:问「年假几天」用 200~500 token;问「这个方案怎么做」要大一些。

3. Embedding ​

关注点说明
模型选择中文场景优先中文/多语言模型(如 bge-m3、text-embedding-3、qwen-embedding)
维度768 / 1024 / 1536,维度越高越贵越慢
归一化用余弦相似度前要确认模型输出是否已归一化
对称 vs 非对称问题和文档不同分布,有的模型要求加 query: / passage: 前缀,不加会明显掉点
一致性索引和查询必须用同一个模型,换模型要全量重建

注意:换了 embedding 模型 = 重建整个库。所以选型要在项目早期定,别中途换。

4. 索引与向量库 ​

方案适合备注
Milvus百万~亿级专业向量库,运维成本中高
pgvector十万级、已有 PG一库两用,最省心
ES 8.x kNN已有 ES天然支持 BM25 + 向量混合
Qdrant / Weaviate中小规模过滤能力强
Redis / FAISS小规模 / 离线Redis 复用现有栈,FAISS 不是服务

HNSW 参数(最常被问):

参数作用调大
M每个节点的邻居数召回↑、内存↑、建索引慢
ef_construction建索引时的候选集精度↑、建索引慢
ef_search查询时的候选集召回↑、延迟↑

没有通用参数,必须用自己的数据集画 recall-latency 曲线。

5. 在线链路:召回才是命门 ​

用户问题
  ↓ Query 改写(多轮/口语化 → 完整查询)
  ↓ 并行两路:向量检索(语义)+ BM25(关键词)
  ↓ RRF 融合
  ↓ 元数据过滤(租户/时间/权限)
  ↓ Rerank 精排(cross-encoder,取 top 5)
  ↓ 拼 Prompt(带编号 + 引用要求)
  ↓ 模型生成 + 拒答兜底
用户问题
  ↓ Query 改写(多轮/口语化 → 完整查询)
  ↓ 并行两路:向量检索(语义)+ BM25(关键词)
  ↓ RRF 融合
  ↓ 元数据过滤(租户/时间/权限)
  ↓ Rerank 精排(cross-encoder,取 top 5)
  ↓ 拼 Prompt(带编号 + 引用要求)
  ↓ 模型生成 + 拒答兜底

5.1 Query 改写 ​

多轮对话里「它多少钱」必须改写为「XX 套餐多少钱」才能召回。手段:用模型改写(多一次小调用)、或用规则拼接上一轮实体。

5.2 混合检索与 RRF ​

纯向量对专有名词/型号/编号很弱(语义相近但完全不同),所以要加关键词通道。

RRF(Reciprocal Rank Fusion) 是把两路排名融合的标准做法,不需要归一化分数:

score(d) = Σ  1 / (k + rank_i(d))       k 常取 60
score(d) = Σ  1 / (k + rank_i(d))       k 常取 60

它只看名次不看分数,所以对两路分数量纲不一致的情况特别稳。

5.3 Rerank ​

  • 粗排(向量/BM25)召回 50~100 条;
  • 用 Cross-Encoder(如 bge-reranker)逐条打分,取 top 3~5;
  • 代价是慢(要过一遍模型),所以只对少量候选做。

实测经验:加 Rerank 通常是性价比最高的一步,尤其候选池大、问题口语化时。

5.4 元数据过滤与权限 ​

  • 一定要在检索阶段做租户/权限过滤,不能检索完再筛(会泄露、也会污染 topK);
  • 时间过滤对「最新政策」类问题很关键。

6. 生成:拼 Prompt 与拒答 ​

text
资料(编号):
[1] ...
[2] ...

要求:
- 只依据资料回答,资料没有就说「资料中未提及」;
- 每个结论标注来源编号,如 [1];
- 不要编造资料里没有的数字。
资料(编号):
[1] ...
[2] ...

要求:
- 只依据资料回答,资料没有就说「资料中未提及」;
- 每个结论标注来源编号,如 [1];
- 不要编造资料里没有的数字。

拒答策略比「努力作答」更重要:空召回或相似度低于阈值时,直接说「没找到」,不要硬编。

7. 评估:分两段看 ​

层次指标怎么测
检索Recall@K、MRR、NDCG、空召回率人工标注「标准答案在第几块」
生成忠实度(是否来自资料)、答案相关性、引用正确率规则 + LLM-as-judge + 抽检
端到端通过率、人工评分固定评测集回归

建评测集的正确姿势:从真实日志里抽 50~200 条问题 → 标注答案所在文档/片段 → 每次改动跑一遍。

记住顺序:先修检索,再修生成。召不回,模型再强也是编。

8. 进阶 ​

方向解决什么
GraphRAG跨文档多跳、全局总结(实体关系图)
Agentic RAG让 Agent 自己决定「检索几次、换什么查询」
多向量 / ColBERT更细粒度匹配(token 级),但存储成本高
增量更新与删除文档变更时按 docId 删旧块再写新块,别只追加
缓存相同问题的检索结果缓存,省钱省延迟

9. 常见失败模式对照 ​

现象大概率原因先查什么
总是答「未提及」空召回切块粒度、embedding 前缀、相似度阈值
答得对但引用错编号与片段对应错位拼 Prompt 时的编号是否稳定
关键数字错表格被拆坏解析阶段
多轮对话越聊越偏没做 Query 改写改写链路
召回内容相关但不解决问题缺 Rerank加 cross-encoder
老文档还在被引用没做删除/覆盖增量更新逻辑

10. 速查表 ​

问题一句话
什么时候用 RAG私有知识、要更新、不想微调
切块多大200~500 token,overlap 10%~20%,按结构切优先
换 embedding 模型要做什么全量重建索引
为什么加 BM25专有名词/编号类查询向量检索不准
RRF 是什么按名次融合,Σ 1/(k+rank),k≈60
Rerank 值不值通常最划算的一步,粗排 50 取 5
怎么评估 RAG检索看 Recall@K,生成看忠实度
先优化哪先检索后生成,空召回优先修
拒答重要吗重要,空召回必须说「未提及」,别硬编