尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

RAG Refresher Notebook:用Jupyter Notebook逐层拆解RAG核心技术链路

RAG Refresher Notebook:用Jupyter Notebook逐层拆解RAG核心技术链路 这次我们来看一个以 Jupyter Notebook 为核心载体的 RAG 技术复习项目RAG Refresher Notebook。它不是一个新的向量数据库也不是一个开箱即用的 Web 应用而是一套把“检索增强生成”完整链路拆解到 Notebook 单元格里的交互式教程/实验模板。你可以把它理解成一份“可执行的技术备忘录”——每看一个概念旁边就能直接跑一段代码验证不用先搭一套工程环境再去查文档。这个项目最值得关注的点有三个第一它把 RAG 的主链路文档加载、文本分块、向量化、检索、重排、生成压缩到一个个可以独立运行的 Notebook 步骤里适合快速找回知识体系第二它对硬件要求很低核心计算集中在 Embedding 模型和 LLM 推理上哪怕只有一个 8G 显存的消费级显卡甚至纯 CPU 环境也能跑通最小链路第三它天然适合“边读边测”每个模块都可以单独改参数看效果对理解 RAG 知识库指标、分块策略、检索质量非常有帮助。这篇博文会基于 RAG Refresher Notebook 这个主题拆解它的核心能力、适用场景、运行环境、落地实践路径并给出完整的代码流程、接口封装示例、常见问题排查清单和工程化建议。1. 核心能力速览能力项说明项目类型RAG 技术复习/实验模板基于 Jupyter Notebook 逐步执行核心功能文档加载、文本分块、向量化、向量检索、重排、LLM 生成运行方式Jupyter Notebook / JupyterLab / Anaconda 环境硬件要求常规 CPU 可运行GPU 可加速 Embedding 与 LLM 推理显存需求需按模型版本测试显存占用低Embedding 模型通常较小LLM 部分取决于所选模型实际占用需以本机测试为准是否支持 CPU支持适合学习与小规模验证是否支持 API可自行封装为 FastAPI 服务Notebook 本身不内置 Web API是否支持批量任务可在 Notebook 中用循环批量处理文本也可导出脚本做批量实验适合场景RAG 知识体系复习、检索质量调优、分块策略实验、接口原型验证不适用场景大规模生产检索服务、高并发在线推理、复杂权限管理这套 Notebook 最大的价值不在于“跑通”而在于“跑通之后你能看到中间每一层发生了什么”。常规的 RAG 框架会把加载、分块、嵌入、检索、生成全部封装好你很难感知哪一步出了问题。而 Refresher Notebook 的设计思路是逐层拆开方便你针对某一个环节单独调整。2. 适用场景与使用边界RAG Refresher Notebook 适合这几类人想系统复习 RAG 技术栈的开发者。从文档加载到最后生成回答每一步都有代码可以对照。需要做 RAG 知识库实验的算法工程师。分块大小、重叠区间、Top-K、重排模型这些参数都可以在 Notebook 里快速对比。准备技术面试的候选人。用 Notebook 把完整流程跑一遍比单纯背概念记忆更牢固。想从 Notebook 原型过渡到 API 服务的后端工程师。先把链路在 Notebook 里调通再封装成接口。不适合什么场景如果目标是搭建一个多租户、高并发、带权限管理的生产级知识库问答系统直接拿 Notebook 当服务跑就不合适。Notebook 的价值在实验和验证不在工程承载。这里必须强调使用边界。RAG 项目经常涉及私有文档、内部知识库、版权材料和个人信息。在测试和部署时务必确认你使用的文档是否有合法授权是否涉及隐私数据是否允许被索引和存储。涉及人脸、声音、身份信息等敏感内容时更要做脱敏处理。不要把未授权的材料随意灌入本地向量库也不要把私有知识库的检索结果直接对外提供商用服务。合规问题不是技术问题但比技术问题更容易造成长期影响。3. 环境准备与前置条件RAG Refresher Notebook 的核心运行载体是 Jupyter Notebook。第一步要准备的不是一个复杂推理框架而是一个干净可用的 Python 环境。推荐使用 Anaconda 创建独立虚拟环境避免依赖冲突。3.1 安装 Anaconda 或 Miniconda如果电脑上还没有 Python 环境建议先安装 Anaconda。它自带 Jupyter Notebook、conda 包管理器和常用科学计算库省去很多手动配置时间。安装完成后在终端创建独立环境conda create -n rag_refresher python3.10 conda activate rag_refresherPython 版本不建议盲目选最新3.10 或 3.11 对主流机器学习库的兼容性更稳妥。如果机器上已经装好了 Python也可以用 venv 管理python -m venv rag_refresher source rag_refresher/bin/activate # Windows 为 rag_refresher\Scripts\activate3.2 安装 Notebook 与 RAG 依赖激活环境后安装 Jupyter Notebook再安装 RAG 链路需要的核心库pip install jupyter notebook pip install langchain langchain-community langchain-huggingface pip install sentence-transformers faiss-cpu chromadb pip install pypdf python-docx beautifulsoup4 # 如果使用 OpenAI 兼容接口可安装 pip install openai # 如果本机有 NVIDIA GPU 并想用 GPU 加速 pip install torch --index-url https://download.pytorch.org/whl/cu121以上是通用安装模板实际版本号以你本机环境为准。如果网络环境访问默认 PyPI 源较慢可以替换为国内镜像源pip install jupyter notebook -i https://pypi.tuna.tsinghua.edu.cn/simple3.3 Jupyter Notebook 与 JupyterLab 怎么选很多人在实际使用时会问 Jupyter Notebook 和 JupyterLab 的区别。简单说Jupyter Notebook 是单文档交互环境打开一个.ipynb文件就是一个页面适合逐步执行单个实验JupyterLab 是更完整的 IDE 式界面支持多标签页、文件树、终端、拖拽单元格并且左侧可以显示长 Notebook 的标题总览。对于 RAG Refresher Notebook 这种章节结构清晰的教程型项目JupyterLab 的体验更好因为章节标题可以在侧边栏直接跳转。但如果你只想快速跑一个文件经典 Notebook 也完全够用。启动方式相同jupyter notebook启动后终端会输出访问地址默认是http://127.0.0.1:8888。如果端口被占用可以指定端口jupyter notebook --port 88993.4 模型文件准备RAG 链路至少需要两类模型Embedding 模型用于把文本转成向量。生成模型LLM用于根据检索结果生成回答。Embedding 模型建议选择体积较小的开源模型例如BAAI/bge-small-zh-v1.5或shibing624/text2vec-base-chinese。这类模型体积小CPU 也能跑适合在 Notebook 里做实验。第一次使用时sentence-transformers会从 Hugging Face 下载模型需要网络可访问如果下载困难可以提前在模型库官网下载后放到本地缓存目录。LLM 部分的选择取决于本机资源。如果没有 GPU可以接一个 OpenAI 兼容的在线 API或者使用 Ollama 跑一个本地量化模型。Notebook 的价值在于链路验证生成模型用在线 API 不影响实验效果。4. RAG 核心概念先复习再动手RAG Refresher Notebook 里的“Refresher”包含两层含义一是刷新你对 RAG 主流程的记忆二是刷新检索结果——通过查询改写、重排等方式让最终生成质量更好。完整 RAG 主链路如下文档加载读取 PDF、Word、Markdown、HTML 等格式的原始文档。文本分块把长文档切成固定大小、带重叠的文本片段。向量化用 Embedding 模型把每个文本片段转成向量。向量入库把向量和原始文本一起存入向量数据库。查询向量化用户输入问题后用同样的 Embedding 模型转换成向量。相似度检索在向量库中找出最相似的 Top-K 片段。重排可选用重排模型对检索结果重新排序提升精准度。组装 Prompt把问题和检索片段拼成提示词。LLM 生成把 Prompt 交给语言模型生成最终回答。RAG 知识库指标也在这个阶段一起理解会更容易。常见的检索质量指标包括召回率、命中率、MRR、NDCG生成质量指标包括忠实度、答案相关性、上下文相关性。Notebook 里可以把每一轮检索命中的文档序号、相似度分数打印出来再手动对照标准答案观察不同分块策略对指标的影响。5. 在 Notebook 里跑通最小 RAG 链路下面这套代码是通用流程不管你的 RAG Refresher Notebook 是中文还是英文版本链路结构基本一致。重点看中间每一步如何处理数据。5.1 文档加载与分块先把测试文档加载进来。这里以本地文本文件为例实际项目中可能是 PDF 或者 Word。# 读取本地文本 from pathlib import Path text Path(./docs/sample.txt).read_text(encodingutf-8) print(f文档长度: {len(text)} 字符)然后是分块。分块是 RAG 中影响检索效果最明显的环节之一。块太小会导致语义不完整块太大会引入噪声。推荐先做基础实验from langchain.text_splitter import RecursiveCharacterTextSplitter splitter RecursiveCharacterTextSplitter( chunk_size300, chunk_overlap50, separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_text(text) print(f分块数量: {len(chunks)}) print(f第一个块: {chunks[0][:100]}...)chunk_size和chunk_overlap是 RAG 实验里最值得反复调整的两个参数。你可以把 200、300、500 都跑一遍看哪个参数下检索命中率更高。后面做批量实验时可以用循环自动比较。5.2 向量化与向量库构建用sentence-transformers加载一个中文 Embedding 模型然后把分好的文本块全部转成向量。from sentence_transformers import SentenceTransformer embed_model SentenceTransformer(BAAI/bge-small-zh-v1.5) vectors embed_model.encode(chunks, normalize_embeddingsTrue) print(f向量维度: {vectors.shape})normalize_embeddingsTrue会让向量归一化后续用余弦相似度计算可以直接用内积。向量库可以用 FAISS方便内存中快速检索也可以用 ChromaDB方便持久化保存。import faiss import numpy as np dim vectors.shape[1] index faiss.IndexFlatIP(dim) index.add(np.asarray(vectors, dtypenp.float32)) print(f索引中的向量数: {index.ntotal})5.3 检索与重排用户输入问题后用同一个 Embedding 模型编码问题再到 FAISS 索引里检索 Top-K 个最相似的文本块。question 什么是检索增强生成 question_vec embed_model.encode([question], normalize_embeddingsTrue) top_k 5 distances, indices index.search( np.asarray(question_vec, dtypenp.float32), top_k ) for i, (dist, idx) in enumerate(zip(distances[0], indices[0])): print(f第 {i1} 名相似度 {dist:.4f}) print(chunks[idx][:120]) print(---)这一步就是整个链路中最核心的观察点。如果检索出来的前三名明显和问题无关大概率是分块策略有问题或者是 Embedding 模型和文档领域不匹配。如果对检索精度要求更高可以再加一个重排步骤。常见做法是用CrossEncoder对检索结果重新打分from sentence_transformers import CrossEncoder reranker CrossEncoder(BAAI/bge-reranker-base) pairs [[question, chunks[idx]] for idx in indices[0]] scores reranker.predict(pairs) ranked_idx np.argsort(scores)[::-1] for rank, pos in enumerate(ranked_idx): original_idx indices[0][pos] print(f重排后第 {rank1} 名分数 {scores[pos]:.4f}) print(chunks[original_idx][:120]) print(---)重排会显著增加延迟但对答案精准度提升明显。RAG 知识库指标中的 MRR 和 NDCG通常会因为加入重排而改善。5.4 组装 Prompt 并调用 LLM 生成检索到相关片段后把片段拼进 Prompt 中再交给语言模型。下面是一个 OpenAI 兼容接口的调用示例from openai import OpenAI client OpenAI( api_keyyour-api-key, # 替换为真实密钥或从环境变量读取 base_urlhttps://api.example.com/v1 # 替换为实际服务地址 ) context \n\n.join( [chunks[indices[0][i]] for i in range(len(indices[0]))] ) prompt f请根据以下文档内容回答问题。 文档内容 {context} 问题{question} 回答 response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是一个严谨的问答助手只能根据给定文档回答。}, {role: user, content: prompt} ], temperature0.2 ) print(response.choices[0].message.content)如果不想用在线 API也可以接 Ollama 本地模型ollama run qwen2.5:7b然后通过http://127.0.0.1:11434/v1作为base_url调用。6. 检索质量评估与指标观察RAG Refresher Notebook 不只是跑通链路更要帮你建立“如何评估 RAG 效果”的直觉。在 Notebook 中可以准备一个小规模测试集包含问题和对应的标准答案。评估流程test_queries [ {question: 什么是 RAG, golden_chunk: 检索增强生成是一种结合检索与生成的技术}, {question: 为什么需要向量数据库, golden_chunk: 向量数据库用于存储和检索高维向量}, ] hit_count 0 for item in test_queries: q_vec embed_model.encode([item[question]], normalize_embeddingsTrue) _, idxs index.search(np.asarray(q_vec, dtypenp.float32), 3) retrieved [chunks[i] for i in idxs[0]] if any(item[golden_chunk] in c for c in retrieved): hit_count 1 hit_rate hit_count / len(test_queries) print(fTop-3 命中率: {hit_rate:.2f})这只是一个简化版评估示例。更严谨的做法是手动标注每个测试问题的标准答案文本然后计算 MRR、NDCG 等指标。做指标观察时建议每次只改一个变量比如只改chunk_size或者只改top_k否则很难判断效果变化来自哪个环节。RAG 知识库指标不是越高越好要结合业务场景看。如果知识库答案允许用户自己翻找召回率更重要如果目标是直接给用户一段确定答案NDCG 这种“排序越靠前越好”的指标更值得关注。7. 批量实验与参数对比Notebook 的单元格执行方式非常适合做批量实验。你可以把分块、检索、评估封装成函数然后遍历参数组合。results [] for chunk_size in [200, 400, 800]: for top_k in [2, 4, 6]: # 重新分块重新建索引重新评估 splitter RecursiveCharacterTextSplitter( chunk_sizechunk_size, chunk_overlapint(chunk_size * 0.15), separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_text(text) vectors embed_model.encode(chunks, normalize_embeddingsTrue) index faiss.IndexFlatIP(vectors.shape[1]) index.add(np.asarray(vectors, dtypenp.float32)) hits 0 for item in test_queries: qv embed_model.encode([item[question]], normalize_embeddingsTrue) _, idxs index.search(np.asarray(qv, dtypenp.float32), top_k) retrieved [chunks[i] for i in idxs[0]] if any(item[golden_chunk] in c for c in retrieved): hits 1 results.append({ chunk_size: chunk_size, top_k: top_k, hit_rate: hits / len(test_queries) }) for r in results: print(r)这种循环在 Notebook 里非常直观每跑完一个参数组合就能看到结果不需要写复杂的任务管理器。如果测试集比较大建议把中间结果保存为 CSV避免重复计算。8. 把 Notebook 链路封装成 APINotebook 调通之后常见的下一步是封装成一个 Web API方便其他服务调用。可以用 FastAPI 做一个最小接口把向量库加载和生成逻辑包进去。先安装 FastAPI 服务依赖pip install fastapi uvicorn然后创建一个app.pyfrom fastapi import FastAPI, HTTPException from pydantic import BaseModel from sentence_transformers import SentenceTransformer import faiss import numpy as np app FastAPI() embed_model SentenceTransformer(BAAI/bge-small-zh-v1.5) chunks [] # 实际使用时应从文件或数据库加载 index faiss.IndexFlatIP(768) class QueryRequest(BaseModel): question: str top_k: int 5 app.post(/retrieve) def retrieve(req: QueryRequest): qv embed_model.encode([req.question], normalize_embeddingsTrue) distances, indices index.search( np.asarray(qv, dtypenp.float32), req.top_k ) results [ {text: chunks[idx], score: float(dist)} for dist, idx in zip(distances[0], indices[0]) ] return {question: req.question, results: results}启动服务uvicorn app:app --host 0.0.0.0 --port 8000调用接口curl -X POST http://127.0.0.1:8000/retrieve \ -H Content-Type: application/json \ -d {question: 什么是RAG, top_k: 3}接口封装完成后建议只在可信网络内开放设置访问密钥或使用内网访问不要把没有鉴权的知识库检索服务直接暴露到公网。9. 资源占用与性能观察RAG Refresher Notebook 虽然是一个“轻量复习项目”但性能问题同样不能忽视。以下是几个需要重点观察的资源维度。9.1 Embedding 模型资源占用Embedding 模型通常参数量较小。以bge-small-zh-v1.5为例模型文件只有几百 MBCPU 上编码一段几万字的文档也不会太慢。如果使用更大的 Embedding 模型比如bge-large-zh-v1.5向量维度更高检索速度和内存占用都会上升但语义理解能力也会更好。具体显存占用需要以实际模型版本和本机硬件为准。9.2 向量库内存占用向量库占用内存的大致估算思路向量维度乘 4 字节float32再乘向量数量。例如 10000 条 768 维向量大约占用10000 * 768 * 4 / 1024 / 1024 ≈ 29 MB。实际项目中还有索引结构、原文存储等开销但整体来说向量库在 Notebook 场景下压力不大。9.3 LLM 调用的瓶颈LLM 生成是整条链路中延迟最高的部分。在 Notebook 中做实验时建议先生成一次回答观察耗时如果延迟过高优先检查网络请求超时设置而不是盲目加大上下文。import time start time.time() response client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], timeout60 ) print(fLLM 生成耗时: {time.time() - start:.2f} 秒)如果使用的是本地 Ollama 模型首次推理会加载模型耗时明显偏长。第二次调用时模型已经常驻内存速度会快很多。9.4 Notebook 长时间运行如果是在本地运行 Jupyter Notebook长时间挂机一般没有太大问题。如果是在远程服务器或云开发环境中运行尤其是魔搭社区这类在线 Notebook 平台长时间不操作可能会断开连接。保活的方式通常是定时发送心跳请求或者在本地跑完实验后马上保存结果。最稳妥的方案是关键结果落盘保存而不是只留在 Notebook 输出里。10. 常见问题与排查方法问题现象可能原因排查方式解决方案Notebook 启动后页面打不开端口被占用或服务未启动检查终端日志和端口占用更换端口启动jupyter notebook --port 8899Kernel 一直显示 Busy某个单元格在长时间执行查看单元格左侧执行标记等待或点击 Kernel - Interrupt 终止模型下载失败网络无法访问模型库检查下载地址和网络手动下载模型后放入本地缓存目录分块后检索命中率低chunk_size 不适合当前文档打印每个块的文本内容检查完整性调整 chunk_size 和 chunk_overlap检索结果相似度都很低Embedding 模型与文档领域不匹配打印相似度分数换领域匹配的 Embedding 模型FAISS 索引维度不一致查询向量和文档向量不是同一模型生成打印两个向量维度统一 Embedding 模型重排后效果反而变差重排模型与查询类型不匹配对比重排前后 Top-K 命中换重排模型或调整候选集大小LLM 回答内容与文档无关检索结果中噪声过多或 Prompt 指令不明确查看最终 Prompt 中检索片段缩小 top_k加强 system 指令接口 API 返回超时LLM 生成时间过长或网络问题查看服务端日志增加 timeout或换更快的生成模型批量任务跑一半卡住某个文档格式异常或网络中断添加日志输出当前索引用 try-except 跳过异常数据并记录排查 RAG 链路问题时最重要的原则是“逐层检查”。先确认文档加载成功再确认分块结果合理然后看检索相似度是否正常最后才检查 LLM 生成。很多问题表面出在生成质量上实际根源在检索环节。11. 进阶方向Agentic RAG 与多模态 RAGRAG Refresher Notebook 跑通的是基础链路。如果你已经掌握主流程下一步可以往这些方向扩展。11.1 Agentic RAG传统的单轮 RAG 只能做一次检索、一次生成。Agentic RAG 则把检索决策交给 Agent 模型让模型自己决定是否需要改写查询、是否需要检索多次、是否需要调用工具。在 Notebook 中可以先从“查询改写”开始实验用户提问后先让 LLM 判断问题是否需要改写。如果问题包含代词、指代关系先改写成一个独立可检索的查询。用改写后的查询去向量库检索再做生成。这样的好处是提高复杂问题的检索命中率代价是多一次 LLM 调用延迟更高。11.2 多模态 RAG如果知识库中包含图片、表格、PDF 扫描件可以尝试多模态 RAG。常见做法有两种用多模态 Embedding 模型直接编码图片和文本。对图片做 OCR 或视觉语言模型描述把描述文本纳入向量库。第二种方式对基础设施要求更低适合在 Notebook 中做原型验证。先对图片生成文字描述再把描述文本分块、向量化纳入现有检索链路。11.3 RAG 框架与 MCP当链路复杂度提升后手动在 Notebook 里维护所有逻辑会变得吃力。这时可以迁移到 RAG 框架比如 LangChain、LlamaIndex、Dify 等。Dify 这类低代码平台的好处是内置了知识库管理、检索测试、指标观察和 API 发布能力适合把 Notebok 实验中的最优参数固化到产品里。MCPModel Context Protocol可以作为模型与外部工具之间的统一接口把向量检索、文档加载、数据库查询封装成标准工具由 Agent 按需调用。Notebook 实验阶段可以先不管工程化细节但理解 MCP 的定位后续迁移会更有方向感。12. 最佳实践与合规提醒最后整理几点使用 RAG Refresher Notebook 的实际建议。12.1 第一次运行先小规模验证不要一上来就灌入几万篇文档。先用 5 到 10 篇代表性文档跑通全流程确认分块、检索、生成结果都正常后再逐步扩大语料规模。小规模验证时每一条检索结果都可以人工检查。12.2 实验参数要有记录在 Notebook 里做参数对比时建议在每个单元格开头记录参数说明并把结果保存到独立目录experiments/ ├── chunk_200_top2.csv ├── chunk_200_top5.csv └── chunk_400_top5.csv没有记录的实验等于没做。RAG 调优最大的坑是“感觉换了参数效果变好了”但你不知道自己换了哪个参数。12.3 模型、数据、结果分目录管理建议按以下结构组织文件project/ ├── docs/ # 原始文档 ├── models/ # 本地模型缓存 ├── vector_store/ # 向量库持久化 ├── experiments/ # 实验记录 └── notebooks/ # Notebook 文件12.4 接口服务要控制访问范围如果按照第八节的方式把链路封装成 API必须设置认证和访问控制。最简单的方式是为 API 增加一个固定的请求头密钥from fastapi import Header, HTTPException API_KEY your-internal-key def verify_key(x_api_key: str Header(defaultNone)): if x_api_key ! API_KEY: raise HTTPException(status_code401, detailInvalid API Key)12.5 合规边界再强调一次文档授权、隐私保护、版权合规是 RAG 项目绕不开的底线。使用公开数据集做实验没问题但把内部会议纪要、个人聊天记录、未公开的商业文档灌进向量库前必须确认是否有权限。商用前要复核检索结果是否包含敏感内容是否可能产生侵权风险。13. 总结与下一步RAG Refresher Notebook 的核心价值不是提供一个封装好的黑盒而是把 RAG 主链路拆成可逐步执行的实验模板让你能看见文档加载、分块、向量化、检索、重排、生成每一层发生了什么。对于想快速复习 RAG 技术、调试知识库检索效果、准备技术面试、或者从零搭建 RAG 原型的开发者来说这是一套成本很低的起步路径。建议先按第三、五节搭好环境并跑通最小链路再重点做分块参数对比和检索指标观察。最容易踩的坑是分块策略不合理导致检索命中率低以及模型下载失败导致链路中断——前者靠实验记录解决后者靠提前下载好模型文件解决。后续扩展方向也很清晰链路稳定后可以加入重排模型优化排序质量用批量实验寻找最优参数组合再封装成 FastAPI 接口供业务调用更复杂的场景可以研究 Agentic RAG、多模态 RAG或迁移到 LangChain、Dify 等框架落地。把这套 Notebook 跑一遍RAG 的完整知识体系基本上就能重新捡起来了。之后再做 RAG 知识库项目你至少知道每一步为什么这样设计出了问题也知道往哪一层定位。
返回列表