推荐一位博主:AI_Julie,她讲的系列课程都很不错,浅显易懂。
1. RAG 基础
1.1 什么是 RAG
检索增强生成(Retrieval-Augmented Generation,RAG)是一种结合了检索(Retrieval)与生成(Generation)技术的自然语言处理模型架构。其工作流程主要分为两个阶段:
-
检索阶段:当模型接收到查询(如问题或提示)时,它会在一个大型文档集合中检索与查询最相关的文档或信息片段。这些文档通常存储在专门的数据库中,如 Wikipedia 或其他专业知识库。检索过程基于相似性度量,目的是找到最相关的信息作为后续生成的参考。
-
生成阶段:模型利用检索到的文档作为额外的上下文信息,通过序列到序列(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 有很多分割方法
| characterText | recursiverCharacter | MarkdownHeader | semanticChunker |
|---|---|---|---|
| 按照固定分隔符来进行一刀切 | 递归多级分割,可以 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 模型选择
- 语言与领域:中文/多语言模型;医疗/法律模型等
- 序列长度:长文档需要更长的上下文 embedding
- 本地化 or 商业:用 api 还是本地的模型,这是个问题
- 多维度:速度、质量、效果等
常用的模型: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 算法来进行
5.2 向量数据库的使用
- 自己的文本语料,也就是之前切好的 chunk 进行向量化
- 得到的多维向量存入 FAISS
- 用户 query 时,进行检索
- 返回相似度最高的那个向量
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
- 用户的 query 要进行一次 embedding 然后进行检索得到
concrete_ctx - 用户的 query 经过 LLM 生成一个抽象的问题,该问题再进行一次 embedding 然后再去检索得到
background_ctx - 将两者拼起来得到
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 最相关且与已选结果最不相似的文档,兼顾相关性与多样性,可以避免返回内容重复度高的文档,算法如下
- 还没有已选文档,选择和 query 最相关的
- 在剩余文档里选择一条和 query 最相关的,但不能和已选集合中和 query 最相关的那一条太像
- 重复上面的逻辑,避开前面选择的两条
- 直到选满 top_k 个
- 再用 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
具体流程:
- 从 query 中提取出
seed entity,比如人、组织、事件等 - 从图中提取出与
seed entity相关的子图 ,比如所有与seed entity相关的节点和边 - 从子图中提取出与
seed entity相关的三元组 ,比如(subject1, predicate1, object1)、(subject2, predicate2, object2)等 - 用这些三元组上下文,再用 LLM 生成答案
- 用这些答案,再用 LLM 生成答案
8.2 Agentic RAG
原理: 由 Agent (LLM + tools) 自主决定何时检索,检索什么,是否继续检索,把 RAG 从「固定一次检索」变为多步决策循环
所以要是大模型的能力够强,是不是之前所有的其他 RAG 策略都没用了?我想不是,从成本,隐私,速度的角度来看还是有很多地方值得多多考虑
8.3 Self-RAG
原理:生成前后由 LLM 做四步自我反思 reflection tokens
- Retrievew?:要不要查资料
- 否 -> 跳过检索,直接用 LLM 生成
- 是 -> 进入向量检索
- IsRel?:文档是否相关
- 按相似度从高到低逐条检验
- 否 -> 换下一条候选文档,直到找到相关文档 or 全部淘汰
- 全部不相干 -> 返回无法回答
- IsSup?:答案是否有依据,是否编造?
- 否 -> 带着严格复述文档的约束重新生成
- 是 -> 进入下一步
- IsUse?:答案是否完整好用?
- 否 -> 基于同一文档再生成一次
- 是 -> 输出最终答案
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 Precision | Answer 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.90 | query 重写,优化 response prompt |
| Faithfulness | ≥ 0.75 | ≥ 0.85 | ≥ 0.95 | 强化 system prompt,先提高 recall |
一些常见的问题:
- 没有 Ground Trurh 就跑评估
- 用同一个 LLM 即生成答案又评估,评估 LLM 能力应该比生成 LLM 更强
- 只看 Faithfulness 不看 Recall,出现“答得不够”情况
- 只看平均分担不看样本分布
- 评估集太小,最少 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 | 需要相关性标注(哪些文档是相关的) |
核心指标:
- Context Precision@k:前 k 个结果里相关文档的比例。
- Context Recall@k:所有相关文档中,有多少被召回进了 top_k。
- MRR(Mean Reciprocal Rank):第一个相关文档排在第几位,取倒数。越靠前分越高(排第 1 得 1,排第 2 得 0.5)。
- MAP(Mean Average Precision):综合考虑每个相关文档的排序位置的平均精度。
- NDCG(Normalized Discounted Cumulative Gain):既看相关性强弱(分级相关),又对越靠后的位置做折扣惩罚,是排序质量最常用的综合指标。
三者都基于同一前提:每个 query 检索出一个有序的文档列表,并且已知哪些文档相关(Ground Truth)。区别在于对「相关」的定义和位置惩罚方式不同。
11.1 MRR(Mean Reciprocal Rank)
关注第一个相关文档排在第几位,只看首命中,适合单答案的问答场景。
流程:
- 对每个 query,从上往下扫检索结果。
- 遇到第一个相关文档,记下位置 rank(第 1 位为 1,第 3 位为 3)。
- 计算 1/rank(排第 1 得 1,第 2 得 0.5,第 3 得 0.33…;一个都没命中记 0)。
- 对所有 query 的倒数排名求平均。
11.2 MAP(Mean Average Precision)
关注全部相关文档的整体排序质量(二元相关),适合一个 query 有多个正确答案的场景。先算单条 query 的 AP(Average Precision),只在命中相关文档的位置计算 Precision@k,再求平均:
其中 R 是相关文档总数,rel(k) 表示第 k 位是否相关(0/1),P(k) 是前 k 个结果的精度。
流程(单条 query):
- 从上往下扫,维护「到目前为止命中的相关文档数」。
- 每遇到一个相关文档(位置 k),计算 Precision@k = 已命中相关数 / k。
- 只把这些命中位置的 Precision 累加。
- 除以相关文档总数 R,得到 AP。
- 对所有 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]:
流程:
- 给每个文档标相关性分级(如 3 = 完全相关,2 = 较相关,1 = 弱相关,0 = 不相关)。
- 按检索器给出的实际顺序算 DCG@k。
- 把文档按相关性理想排序,算 IDCG@k。
- 两者相除归一化到 [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 与两者信息高度重叠,可按需选用。