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 改成带来源和章节:
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:
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:
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:
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:
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:
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 天)。
