推荐一位博主:AI_Julie,她讲的系列课程都很不错,浅显易懂。

1. RAG 基础

1.1 什么是 RAG

检索增强生成(Retrieval-Augmented Generation,RAG)是一种结合了检索(Retrieval)与生成(Generation)技术的自然语言处理模型架构。其工作流程主要分为两个阶段:

  1. 检索阶段:当模型接收到查询(如问题或提示)时,它会在一个大型文档集合中检索与查询最相关的文档或信息片段。这些文档通常存储在专门的数据库中,如 Wikipedia 或其他专业知识库。检索过程基于相似性度量,目的是找到最相关的信息作为后续生成的参考。

  2. 生成阶段:模型利用检索到的文档作为额外的上下文信息,通过序列到序列(seq2seq)模型(如 Transformer)生成回答或内容。

1.2 为什么要有 RAG

其实现在大模型已经很厉害了,检索和生成都可以靠大模型本身来实现。但是各个公司有自己的知识库,自己的知识壁垒不想暴露出来,就需要自己写一个 RAG 系统,把自己的知识库给分解成一块一块供 ai 学习参考。

  • Vector RAG pipeline:

    • Build:documentparser 文档解析 -> chunksplit 固定窗口分块 -> embedder 转换向量 -> vectorstore 向量存储
    • Ask:query -> embedder 转换向量 -> vector store 搜索 -> retriever 去冗余得 chunk -> generate 生成答案
  • Graph RAG pipeline:

    • Build:documentparser 文档解析 -> chunksplit 分块 -> LLM 抽取实体与关系 -> graphDoc[nodes, relationships, source] -> knowledge graph 建图存储
    • Ask:query -> extract seed entity 提取种子实体 -> BFS 遍历子图 -> 三元组上下文 -> generate 生成答案

2. DocumentParser

2.1 WashData

说来也怪,比如把 pdf 等文档进行解析,会有各种各样的问题,比如编码类型,空格,无标志符等,都是冗余的东西,需要清洗的。同时这一步也决定了你后续分块,向量化的质量。

我想做一个桌游的 agent,比如可以把规则输入后进行规则的讲解和推荐玩法。但是呢这个桌游的规则 pdf,有很多很多的图。图与图之间也有很多关联。还有很多专属于某个特定桌游的术语。对于数据清洗来说是一个很大的难题。

  • 针对不同的文档格式有不同的工具
    • 对于 PDF 推荐: pdf-inspector
    • 对于其他格式,推荐先转为 markdown 格式再做修改

2.2 父子文档分块

把清洗后的 text 进行 split,先切为大块的 parentchunk,再把 parentchunk 切为小一点的 childchunk

关键点是 parentchunk 和 childchunk。比如在 query 之后,命中了一个 childchunk,它是带有 paren_id 的,这样就可以连带着找到父亲 chunk。

这样的好处是不会漏掉细节,毕竟你找到了一大块 chunk。坏处是,如果 parentchunk 太大了,就会消耗很多 token。

3. Chunk Split

3.1 langchain text split

langchain 有很多分割方法

characterTextrecursiverCharacterMarkdownHeadersemanticChunker
按照固定分隔符来进行一刀切递归多级分割,可以 overlap根据文本的标题来分割根据语义来分割

常用是 recursiverCharacter 可以看看这个函数

recursive = RecursiveCharacterTextSplitter(
	chunk_size = 40, # 这里按照40个字符进行分割
	chunk_overlap = 5, # 两个块之间有5个字符的重叠,防止块间语义缺失
	separators = ["\n\n", "\n", "。", ",", ""], # 分割优先级
)

这里的逻辑是:先尝试用 \n\n 进行分割,若得到块仍然超过 chunksize 则选择用 \n 继续分割,依次递归,直到所有块都满足大小

对于 semanticChunker 来说也是不错的,就是需要语义大模型来进行分割,耗时多。

4. embedder

4.1 模型选择

  1. 语言与领域:中文/多语言模型;医疗/法律模型等
  2. 序列长度:长文档需要更长的上下文 embedding
  3. 本地化 or 商业:用 api 还是本地的模型,这是个问题
  4. 多维度:速度、质量、效果等

常用的模型:BAAI/bge-large-zh-v1.5 、Qwen3-Embedding、MiniLM 等

embedder 的作用就是把 chunk 给向量化,所以代码也很简单

model = SentenceTransfomer("BAAI/bge-large-zh-v1.5")
 
sentences = {
	"大家好,我是damiandicky",
	"现在在字节跳动工作",
	"请多多关照",
}
 
embeddings = model.encode(sentences, normalize_embeddings=true)

5. VectorStore

5.1 什么是向量数据库

面向高纬向量的存储与 ANN 查询系统的数据库。传统数据库 mysql 这些在高纬下不可行,需要用 ANN 算法来进行

常用的向量数据库:FAISS,Chroma

5.2 向量数据库的使用

  1. 自己的文本语料,也就是之前切好的 chunk 进行向量化
  2. 得到的多维向量存入 FAISS
  3. 用户 query 时,进行检索
  4. 返回相似度最高的那个向量

6.检索策略

6.1 hybrid search 混合检索

主要由 keywords search 关键词搜索和 semantic search 语义搜索组成。

  • keywords search:常用 BM25 词频-逆文档加权排序函数
  • semantic search:query 与文本语料 embedding 后做 top-k 最近邻
    将两者得出的分数进行归一化。

6.2 Reciprocal Rank Fusion 多路多排名融合

适用于尺度不一的时候。比如 keywords search 得出的是分数排名,semantic search 给的是匹配度排名,两者尺度不同,不能直接相加。RFF 就直接算出这两者的排名。

RFF 分数 = 每一路贡献相加
每一路贡献 = 1 / (k + 排名)
k 是平滑常数,避免 rank = 1 的权重过大

感觉这个和混合检索的归一化没什么区别?

7. Rerank crossencoding

7.1 Bi-Encoder

  1. 用户的 query 要进行一次 embedding 然后进行检索得到 concrete_ctx
  2. 用户的 query 经过 LLM 生成一个抽象的问题,该问题再进行一次 embedding 然后再去检索得到 background_ctx
  3. 将两者拼起来得到 final_prompt 再送给 LLM 进行最后的答案输出

这个的优势就是很快就能得出 final_prompt,但是精度一般

7.2 Cross-Encoder

Bi-Encoder 粗筛
encode(query) -> q_vec
encode(doc)   -> d_vec
score = q_vec + d_vec
Cross-Encoder 精筛
encode([query, doc]) -> score
query 与 doc 深度交互

流程上就是:query -> Bi-Encoder -> 筛选出 Top-K -> Cross-Encoder -> 筛选出 Top-N,K 小于 N,精度就更高了

7.3 Maximal Marginal Relevance

原理:贪心迭代,每次选出与query 最相关且与已选结果最不相似的文档,兼顾相关性与多样性,可以避免返回内容重复度高的文档,算法如下

  1. 还没有已选文档,选择和 query 最相关的
  2. 在剩余文档里选择一条和 query 最相关的,但不能和已选集合中和 query 最相关的那一条太像
  3. 重复上面的逻辑,避开前面选择的两条
  4. 直到选满 top_k 个
  5. 再用 Cross-Encoder 进行精排,得出 top_n 个

公式:score = lambda · sim(doc, query) - (1-lambda) · max_sim(doc, selected)

  • sim(doc, query) 是文档与 query 的相似度,可以是余弦相似度、欧氏距离等
  • max_sim(doc, selected) 是文档与已选集合中文档的相似度中的最大相似度
  • lambda 的作用是调节权重,lambda 越大,越关注相关性,lambda 越小,越关注多样性

8. 高级 RAG 架构

上述的都是 向量 RAG ,下面是其他架构

8.1 Graph RAG

原理:从文本抽取实体与关系构建图,检索时沿子图或社区摘要获取证据
适合:多跳关系推理,全局问题 “整体主题讲的是什么”

建图流程:
RAWText -> split -> chunks convert graphDoc -> graphDoc[nodes, relationships, source] -> 建立邻接表

检索流程:
VectorRAG:  query -> embedding -> Top_K 相似 chunk -> LLM
GraphRAG:   query -> extract seed entity -> BFS 子图 -> 三元组上下文 -> LLM

具体流程:

  1. 从 query 中提取出 seed entity ,比如人、组织、事件等
  2. 从图中提取出与 seed entity 相关的子图 ,比如所有与 seed entity 相关的节点和边
  3. 从子图中提取出与 seed entity 相关的三元组 ,比如 (subject1, predicate1, object1)、(subject2, predicate2, object2) 等
  4. 用这些三元组上下文,再用 LLM 生成答案
  5. 用这些答案,再用 LLM 生成答案

8.2 Agentic RAG

原理: 由 Agent (LLM + tools) 自主决定何时检索,检索什么,是否继续检索,把 RAG 从「固定一次检索」变为多步决策循环

所以要是大模型的能力够强,是不是之前所有的其他 RAG 策略都没用了?我想不是,从成本,隐私,速度的角度来看还是有很多地方值得多多考虑

8.3 Self-RAG

原理:生成前后由 LLM 做四步自我反思 reflection tokens

  1. Retrievew?:要不要查资料
    1. 否 -> 跳过检索,直接用 LLM 生成
    2. 是 -> 进入向量检索
  2. IsRel?:文档是否相关
    1. 按相似度从高到低逐条检验
    2. 否 -> 换下一条候选文档,直到找到相关文档 or 全部淘汰
    3. 全部不相干 -> 返回无法回答
  3. IsSup?:答案是否有依据,是否编造?
    1. 否 -> 带着严格复述文档的约束重新生成
    2. 是 -> 进入下一步
  4. IsUse?:答案是否完整好用?
    1. 否 -> 基于同一文档再生成一次
    2. 是 -> 输出最终答案

9. Graph RAG 进阶

9.1 向量检索的不足

向量检索容易出现的三种问题

场景向量检索表现例子
精确关键词匹配语义压缩丢失罕见词元搜错误码 ECONNRESET,语义相近的「连接超时」「网络异常」被召回,真正带该字符串的日志反而排后
多跳推理无法跨文档串联关系问「谁负责上线了会触发 A 服务崩溃的那个功能?」需先从变更记录找到功能负责人,再从事故复盘关联到 A 服务——单次向量检索只能命中其中一跳
数值/日期精度嵌入模糊化数值细节问「2024-03-15 的 GMV 是多少?」向量可能拉回「3 月中旬营收不错」这类叙述,却对不上具体日期和精确数字
GraphRAG 能通过图遍历实现多跳推理,但是构建成本高需要图数据库

9.2 知识图谱

三要素:节点 node,关系 relationship,属性 properties

向量检索每次只能检索单个语义片段,无法串联多文档的推理链,而图遍历可以根据三要素在一次查询中完成所有跳转

常用:Neo4j Graph Intelligence Platform

9.3 混合检索

顾名思义是超级大混合检索:向量检索+ BM25 关键词检索+图谱检索,通过 RRF,合并结果,为什么不能直接用加权来算呢?是因为要考虑尺度的统一,归一化,每一路的检索都有分数可量化等

10. RAG Evaluation

这四个指标是 RAGAS 那套经典评测,分数大致都在 0~1,越高越好。四条一起看才完整:Recall 低是检索漏;Precision 低是检索脏;Faithfulness 低是生成幻觉;Relevancy 低是生成跑题。

检索质量生成质量
完整性Context Recall
精确性Context PrecisionAnswer Relevancy
可信度Faithfulness

10.1 Context Recall(检索完整性)

问的是:该召回的证据有没有漏掉。标准答案里的每一条 claim(原子事实),检索到的 chunks 能不能覆盖。漏了关键 chunk,后面生成再强也补不回来。

需要:question + retrieved contexts + reference(标准答案)

做法:把 GT (Ground Truth,即上面的 reference) 拆成若干 claim,看每条能不能从检索上下文里推出来。

Context Recall = GT 中能被检索上下文支撑的 claims / GT 中 claims 总数

例:GT 有 4 条 claim,检索只覆盖了 3 条 → 0.75

10.2 Context Precision(检索精确性)

问的是:召回的 chunks 里有多少是有用的,而且有用的排得靠不靠前。噪声 chunk 多、相关的排在后面,都会拉低分数(污染 prompt、浪费 token)。

需要:question + retrieved contexts(有 reference 时更稳)

做法:对每个 rank k(检索结果的排名) 算 Precision@k(相关文档的平均精度),只在该位置相关(v_k = 1)时计入,相当于「相关文档的平均精度」。

Precision@k = 前 k 条中相关条数 / k
Context Precision@K = Σ (Precision@k · v_k) / Top-K 中相关条数

例:检索 3 条,相关情况 [相关, 无关, 相关]

  • k=1:前 1 条 = [相关] → 相关 1 条 → 1/1 = 1.0。第 1 位是相关的,v_1=1 → 计入
  • k=2:前 2 条 = [相关, 无关] → 相关 1 条 → 1/2 = 0.5。第 2 位不相关,v_2=0,0.5 × 0 = 0 → 不计入
  • k=3:前 3 条 = [相关, 无关, 相关] → 相关 2 条 → 2/3 ≈ 0.67。第 3 位相关,v_3=1 → 计入
  • Context Precision@K = (1.0 + 0.67) / 2 ≈ 0.83

如果相关的都在最前面,分数会更高。

10.3 Answer Relevancy(生成精确性 / 切题)

问的是:答的是不是这个问题。不查事实对不对,只查是否切题、是否答全、是否掺废话。忠实但答非所问,这条会很低。

需要:question + answer(不需要 GT)

做法:用 LLM 从答案反推 N 个问题(默认 N=3),再和原问题做 embedding 余弦相似度,取平均。直觉:答案对题,反推出来的问题应接近原问。

Answer Relevancy = (1/N) Σ cos(E_gi, E_o)

「我不知道」这类非承诺回答会被压到接近 0。余弦理论上可到 -1,所以分数偶尔会略出 [0,1]。

例:问「法国在哪、首都是哪?」只答「在西欧」→ 反推问题都围着地理位置转,首都那一半丢了,相似度下降。

10.4 Faithfulness(生成可信度)

问的是:答案里的句子能不能从检索上下文推出来。防幻觉:上下文没写的数字、日期、因果,模型自己编出来就会扣分。

需要:answer + retrieved contexts(不需要 GT)

做法:把答案拆成 claims,逐条核对能否被 context 支持。

Faithfulness = 答案中被上下文支撑的 claims / 答案中 claims 总数
例:上下文写「爱因斯坦 1879-03-14 生于德国」;答案写成「1879-03-20 生于德国」
→ 拆成两条 claim:
	①生于德国 ✅
	②生于 03-14 ❌(答案写成 03-20)
→ 1/2 = 0.5

注意:它只保证「跟检索到的东西一致」,不保证检索本身对。检索错了、答案忠实复述错上下文,Faithfulness 仍可能很高。

10.5 参考阈值与优化

RAGAS 官方没有规定及格线。分数依赖评测 LLM、embedding、题型(开放问答 vs 客服 FAQ)。下面是业界常用的经验门槛:

指标勉强能用生产常见目标很好优化方向
Context Recall≥ 0.75≥ 0.80~0.85≥ 0.95加大 top_k,缩小 chunksize,换 embedding
Context Precision≥ 0.60≥ 0.70~0.75≥ 0.90加 rerank,提高相似度阈值,改善文档质量
Answer Relevancy≥ 0.70≥ 0.80≥ 0.90query 重写,优化 response prompt
Faithfulness≥ 0.75≥ 0.85≥ 0.95强化 system prompt,先提高 recall

一些常见的问题:

  1. 没有 Ground Trurh 就跑评估
  2. 用同一个 LLM 即生成答案又评估,评估 LLM 能力应该比生成 LLM 更强
  3. 只看 Faithfulness 不看 Recall,出现“答得不够”情况
  4. 只看平均分担不看样本分布
  5. 评估集太小,最少 100 条

11. RAGAS 与 RRM

详情:RAGAS (Retrieval Augmented Generation Assessment)

Retrieval Ranking Metrics(检索排序指标)是评估检索阶段质量的一类指标。它 RAGAS 四大指标(Context Recall / Context Precision / Answer Relevancy / Faithfulness)关注点不同:

维度RAGAS 指标Retrieval Ranking Metrics
评估对象用 LLM 判断语义层面的召回/精度/忠实度用排序位置判断检索器好坏
是否需要 LLM需要(LLM-as-judge)不需要,纯数学计算
是否需要标注需要 Ground Truth需要相关性标注(哪些文档是相关的)

核心指标:

  1. Context Precision@k:前 k 个结果里相关文档的比例。
  2. Context Recall@k:所有相关文档中,有多少被召回进了 top_k。
  3. MRR(Mean Reciprocal Rank):第一个相关文档排在第几位,取倒数。越靠前分越高(排第 1 得 1,排第 2 得 0.5)。
  4. MAP(Mean Average Precision):综合考虑每个相关文档的排序位置的平均精度。
  5. NDCG(Normalized Discounted Cumulative Gain):既看相关性强弱(分级相关),又对越靠后的位置做折扣惩罚,是排序质量最常用的综合指标。

三者都基于同一前提:每个 query 检索出一个有序的文档列表,并且已知哪些文档相关(Ground Truth)。区别在于对「相关」的定义和位置惩罚方式不同。

11.1 MRR(Mean Reciprocal Rank)

关注第一个相关文档排在第几位,只看首命中,适合单答案的问答场景。

流程:

  1. 对每个 query,从上往下扫检索结果。
  2. 遇到第一个相关文档,记下位置 rank(第 1 位为 1,第 3 位为 3)。
  3. 计算 1/rank(排第 1 得 1,第 2 得 0.5,第 3 得 0.33…;一个都没命中记 0)。
  4. 对所有 query 的倒数排名求平均。

11.2 MAP(Mean Average Precision)

关注全部相关文档的整体排序质量(二元相关),适合一个 query 有多个正确答案的场景。先算单条 query 的 AP(Average Precision),只在命中相关文档的位置计算 Precision@k,再求平均:

其中 R 是相关文档总数,rel(k) 表示第 k 位是否相关(0/1),P(k) 是前 k 个结果的精度。

流程(单条 query):

  1. 从上往下扫,维护「到目前为止命中的相关文档数」。
  2. 每遇到一个相关文档(位置 k),计算 Precision@k = 已命中相关数 / k。
  3. 只把这些命中位置的 Precision 累加。
  4. 除以相关文档总数 R,得到 AP。
  5. 对所有 query 的 AP 求平均,得到 MAP。

例:相关文档共 2 个,检索结果相关性为 [✅, ❌, ✅, ❌]

  • 位置 1 命中:P@1 = 1/1 = 1.0
  • 位置 3 命中:P@3 = 2/3 ≈ 0.667
  • AP = (1.0 + 0.667) / 2 = 0.833

11.3 NDCG(Normalized Discounted Cumulative Gain)

支持分级相关(不只是相关/不相关,可以是 0/1/2/3 分),并对靠后的位置做对数折扣,是排序评估最全面的指标。

NDCG 分三层递进:先有 CG(Cumulative Gain,累积增益)——把前 k 位的相关性分级直接相加,但完全不看排序位置,所以 [3, 1, 2] 和 [1, 2, 3] 的 CG 都是 6,分不出好坏。

第一步,CG 加位置折扣得到 DCG:

rel_i 是第 i 位文档的相关性分级,分母 是折扣,排得越靠后贡献被压得越小。

第二步,把所有文档按相关性从高到低理想排列,算出的 DCG 即理论最大值 IDCG。

第三步,归一化到 [0, 1]:

流程:

  1. 给每个文档标相关性分级(如 3 = 完全相关,2 = 较相关,1 = 弱相关,0 = 不相关)。
  2. 按检索器给出的实际顺序算 DCG@k。
  3. 把文档按相关性理想排序,算 IDCG@k。
  4. 两者相除归一化到 [0, 1],1 表示排序完美。

例:k = 3,实际排序相关性 [3, 1, 2]

  • DCG = 3/log₂2 + 1/log₂3 + 2/log₂4 = 3 + 0.631 + 1 = 4.631
  • 理想排序 [3, 2, 1]:IDCG = 3 + 1.262 + 0.5 = 4.762
  • NDCG = 4.631 / 4.762 ≈ 0.973

11.4 三者对比

指标相关性类型位置惩罚典型用途
MRR二元,只看第一命中只关心首个单答案问答
MAP二元,看全部相关隐式(Precision@k)多答案检索
NDCG分级(可 0/1/2/3)对数折扣,显式需要区分「多相关」的排序

MRR/MAP 只需二元标注,NDCG 需要分级标注。实际工程中分级标注成本高,最常落地的是 MRR 与 NDCG@k(k 取 5 或 10);MAP 与两者信息高度重叠,可按需选用。