返回学习路线

AI 学习路线 · 进阶

Part 8 · RAG 优化与评估

站 5 你做出了"能跑"的 RAG。这一站把它做到"能打":


Section 1 · 三大范式再深入(概念日)

Subsection 1 · 记清楚三范式

Naive RAG     索引→检索→生成(你站5做的)Advanced RAG  在检索前后加优化:              预检索:查询改写、HyDE              检索中:混合检索、重排 Rerank              后检索:上下文压缩、融合Modular RAG   检索模块可插拔、可编排,按场景组合
  • Part 8 的目标:把 Naive 升级成 Advanced。

Subsection 2 · 过关自测 能说出 Advanced 在"检索前/中/后"各加了什么 = Section 1 完成。


Section 2 · RAG 变体认识(面试点)

Subsection 1 · 记 5 个变体

T-RAG      时序感知:资料带时间,回答考虑时效性CRAG       纠错增强:检索结果不可靠时,自动"修正检索"Self-RAG   自反思:模型自己判断"要不要检索/答得对不对"RAG-Fusion 多查询融合:把一个问题拆成几个查,结果合并去重Rewrite    查询重写:先把问题改得更清晰,再去检索
  • 面试被问"你了解哪些 RAG 变体",报出这 5 个名字 + 一句话定位即可。

Subsection 2 · 过关自测 能说出其中 3 个各解决什么问题 = Section 2 完成。


Section 3 · 索引优化(概念 + 动手看结构)

Subsection 1 · 记三种索引技巧

元数据索引   每块存"来源/章节/时间"等标签,检索时按标签过滤父子索引     大块当"父"存上下文,小块当"子"做检索,命中子再取父摘要索引     给每块写摘要,先按摘要粗检索,再定位原文

Subsection 2 · 给 rag_tool 加元数据 在 rag_tool.py 的 add_document 里,把 metadata 改成带来源和章节:

python
metadatas=[{"source": file_path, "chapter": blocks[i][:10]} for i in range(len(blocks))]
  • 再跑一次入库,验证 metadata 生效。
  • 这就是"元数据索引"的最简形态:以后可以按章节过滤。

过关:能说出三种索引技巧,并跑通加元数据 = Section 3 完成。


Section 4 · 混合检索:BM25 + 向量(重点日)

向量搜索找"意思相近",关键词搜索(BM25)找"字面相同"。 混合 = 两个都搜,结果合并,更稳。

Subsection 1 · 装库 cmd:pip install rank-bm25

Subsection 2 · 写混合检索 VS Code 新建 hybrid_search.py:

python
import chromadb, jiebafrom rank_bm25 import BM25Okapifrom sentence_transformers import SentenceTransformer, utilfrom pathlib import Path# 准备分块(用站5的数据)client = chromadb.PersistentClient(path="chroma_db")collection = client.get_or_create_collection("company_rules")all_docs = collection.get()["documents"]# ---- 向量检索 ----model = SentenceTransformer("BAAI/bge-small-zh-v1.5")def vector_search(q, k=3):    q_vec = model.encode([q]).tolist()    r = collection.query(query_embeddings=q_vec, n_results=k)    return list(zip(r["documents"][0], r["distances"][0]))# ---- 关键词检索 BM25 ----def tokenize(text):    return [w for w in jieba.cut(text) if w.strip()]# 先装 jieba:pip install jiebabm25 = BM25Okapi([tokenize(d) for d in all_docs])def keyword_search(q, k=3):    scores = bm25.get_scores(tokenize(q))    idx = sorted(range(len(scores)), key=lambda i: -scores[i])[:k]    return [(all_docs[i], float(scores[i])) for i in idx]# ---- 测试 ----q = "迟到怎么处理"vs = vector_search(q)ks = keyword_search(q)print("向量检索:", vs[0][0][:40], "分:", round(vs[0][1],3))print("关键词检索:", ks[0][0][:40], "分:", round(ks[0][1],3))
  • 先装 jieba:cmd pip install jieba
  • 运行
  • 预期看到:两种检索各自给出结果。
  • 真实项目里把两个结果按分数归一化后合并(RRF 融合),就是"混合检索"。

过关:能跑出两种检索并说清各自优势 = Section 4 完成。


Section 5 · Rerank 重排(把结果排得更准)

Subsection 1 · 理解 Rerank

第一步(召回):向量/关键词搜出 10 条,可能不准第二步(精排):用专门的 Rerank 模型,把这 10 条按"和问题真正相关度"重新排序,取前 3
  • 召回要"多而全",精排要"准"——两步走是生产级 RAG 标配。

Subsection 2 · 用 DeepSeek 模拟 Rerank VS Code 新建 rerank_demo.py:

python
import osfrom dotenv import load_dotenvfrom openai import OpenAIload_dotenv()llm = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")# 假设检索回 4 条候选candidates = [    "公司每月15日发工资",    "员工考勤:9:30前打卡,迟到三次扣补贴",    "报销单笔500元以上需审批",    "加班可申请调休,需主管审批",]question = "迟到有什么处罚?"prompt = f"问题:{question}\下面几条资料,按与问题的相关度从高到低排序,只输出序号,不要解释:\" + "\".join(f"{i+1}. {c}" for i, c in enumerate(candidates))+resp = llm.chat.completions.create(model="deepseek-chat", messages=[{"role": "user", "content": prompt}])print("重排结果:", resp.choices[0].message.content)
  • 预期看到:模型把"迟到扣补贴"排到最前。
  • 生产项目用专门的 Rerank 模型(如 bge-reranker、Cohere Rerank),效果更稳;用 LLM 排也行,只是贵。

过关:能跑通并说清"召回 + 精排"两步 = Section 5 完成。


Section 6 · 查询改写 + HyDE

Subsection 1 · 查询改写 思路:用户问题可能含糊,先让 LLM 把问题改清晰,再去检索。 新建 rewrite.py:

python
import osfrom dotenv import load_dotenvfrom openai import OpenAIload_dotenv()llm = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")raw = "报销那个事儿"prompt = f"把用户这句含糊的问题改写成适合检索的清晰问题,只输出改写结果:\{raw}"resp = llm.chat.completions.create(model="deepseek-chat", messages=[{"role": "user", "content": prompt}])print("改写后:", resp.choices[0].message.content)
  • 预期看到:公司报销需要什么流程/条件? 之类清晰问题。

Subsection 2 · HyDE 概念

HyDE(假设性文档嵌入)= 先让 LLM 根据问题"写一个假设答案",再用这个假设答案去检索原因:答案往往比问题更容易和资料"撞上"相似语义
  • 记住名字和思路即可,实现可选。

过关:能跑通改写 + 说清 HyDE 思路 = Section 6 完成。


Section 7 · 后检索优化:上下文压缩

Subsection 1 · 理解压缩 检索回的原文可能又长又有噪音。压缩 = 只把和问题相关的句子留给模型,省 token + 提准度。

Subsection 2 · 跑压缩 demo 新建 compress.py:

python
import osfrom dotenv import load_dotenvfrom openai import OpenAIload_dotenv()llm = OpenAI(api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")question = "加班怎么算"context = """公司实行弹性工作制,工作日加班1小时以上可申请调休。调休需在当月内使用,过期作废。加班需提前在系统登记并经主管审批。法定节假日加班按国家规定支付加班费。"""prompt = f"问题:{question}\从下面资料中提取与问题最相关的 1-2 句话,其余删掉:\{context}"resp = llm.chat.completions.create(model="deepseek-chat", messages=[{"role": "user", "content": prompt}])print("压缩后:", resp.choices[0].message.content)
  • 预期看到:只保留"加班可申请调休/需审批"相关内容。

过关:能跑通压缩并说清它解决什么 = Section 7 完成。


Section 8 · RAGAS 评估(用量化分数说话)

Subsection 1 · 装 RAGAS cmd:pip install ragas langchain-openai

ragas 依赖较重,如果安装报 ChatVertexAI 相关 ImportError,是 langchain_community 版本不兼容,跑 pip install "langchain-community<0.4" 降级即可。

Subsection 2 · 跑评估 新建 evaluate_ragas.py:

python
import osfrom dotenv import load_dotenvfrom langchain_openai import ChatOpenAIfrom ragas import evaluatefrom ragas.metrics.collections import faithfulness, answer_relevancyfrom ragas.llms import LangchainLLMWrapperfrom datasets import Datasetload_dotenv()# ragas 不接受原生 openai SDK,需要用 langchain 的 ChatOpenAI 包装llm = ChatOpenAI(model="deepseek-chat", api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")ragas_llm = LangchainLLMWrapper(llm)# 手工准备一组"问题-回答-资料",评估质量data = {    "question": ["迟到有什么处罚?", "报销要什么手续?"],    "answer": ["迟到三次扣发当日补贴。", "单笔500元以下凭发票报销,以上需审批单。"],    "contexts": [["员工9:30前打卡,迟到3次以上扣发当日补贴。"], ["单笔500元以下凭发票直接报销,以上需附审批单。"]],}dataset = Dataset.from_dict(data)# 新版 ragas:context_relevancy 已移除,用 faithfulness + answer_relevancy 两个核心指标score = evaluate(dataset, metrics=[faithfulness, answer_relevancy], llm=ragas_llm)print(score)
  • 运行(第一次会下载评估用的模型,稍等)
  • 预期看到:两个指标分数(0~1):
faithfulness      忠实度:回答有没有照着资料说(不胡编)answer_relevancy  答案相关度:答非所问会低
  • 记下分数,这就是你 RAG 的"体检报告"。
  • 新版变化:① from ragas.metrics import 改成 from ragas.metrics.collections import;② context_relevancy 已移除;③ LLM 必须用 langchain 包装,不能直接传原生 OpenAI client。

过关:能跑出三个分数,并说出每个分数代表什么 = Section 8 完成。


Section 9 · 部署一个开源 RAG 项目对照

Subsection 1 · 部署 FastGPT

  • 打开 https://github.com/labring/FastGPT 看 README。
  • 按 README 的 Docker 部署方式启动(需要先装 Docker Desktop:https://www.docker.com/products/docker-desktop/ 下载安装)。
  • 启动后进入 FastGPT 网页,导出一份知识库,问几个问题。
  • 目的:看看"生产级 RAG 产品"长什么样,对照你的 rag_tool 找差距。
  • Docker 装不动/资源不够:改看 README 的架构图 + 功能截图,写一篇"它比我的强在哪"笔记,也算完成。

过关:部署成功或写出对比笔记 = Section 9 完成。


Section 10 · 站 6 验收

勾选

  • 能说出三大范式,Advanced 在检索前/中/后加什么
  • 能说出 5 个 RAG 变体各解决什么
  • 跑通元数据索引(Section 3)
  • 跑通 BM25+向量混合检索(Section 4)
  • 跑通 Rerank,理解"召回+精排"(Section 5)
  • 跑通查询改写 + 理解 HyDE(Section 6)
  • 跑通上下文压缩(Section 7)
  • RAGAS 分数跑出来,能解读(Section 8)
  • FastGPT 部署或对比笔记完成(Section 9)

写 300 字Part 6 总结:你的 RAG 做了哪些优化,哪个优化效果最明显,RAGAS 分数多少。

全勾选 = Part 8 通过 → 进入下一站 Part 9 · LangChain,15 天)。