
LLM Zoomcamp 的 RAG Helper用 ingest.py 与 RAGBase 把 RAG 流水线封装成可复用模块【免费下载链接】llm-zoomcampLLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI system that answers questions about your knowledge base. Register here 项目地址: https://gitcode.com/GitHub_Trending/ll/llm-zoomcamp导读在 LLM Zoomcamp 的 Agentic RAG 模块中前几课分别实现了搜索、提示词构建和 LLM 调用RAG 流水线已经能跑通但每次使用都要重复粘贴同样的代码。本篇指南基于课程第 8 课 08-rag-helper.md讲解如何把这些逻辑收敛到两个可复用文件——ingest.py数据加载与索引构建和rag_helper.py搜索、提示词、LLM 调用三合一的RAGBase类——并在 Notebook 中一行导入、即插即用。读完本文你将掌握一套面向小数据集的可复用 RAG 工程骨架并理解索引即依赖的模块化设计如何为后续替换持久化搜索后端sqlitesearch铺平道路。为什么需要 RAG Helper从零散函数到两个文件在前几课中我们是分步搭建 RAG 流水线的05-search.md 用 minsearch 构建了关键字搜索并把搜索封装成search(question)函数06-building-prompt.md 定义了INSTRUCTIONS、build_context、build_prompt07-llm.md 通过 OpenAI Responses API 实现了llm()最后用rag()把三者串起来。流水线是工作的但存在一个明显问题这些函数和index、openai_client等全局变量耦合在单个 Notebook 中。每次新建一个实验场景都要把代码重新抄一遍索引和客户端也被钉死在文件里难以替换。课程的解决思路是把代码整理成两个可复用的文件这也是整门课程后续所有模块的基础设施code/ingest.py负责加载数据、构建搜索索引即搜索之前要做的一切code/rag_helper.py负责RAG 核心逻辑搜索、提示词构建、LLM 调用。此后在 Notebook 中只需from ingest import ...、from rag_helper import RAGBase即可直接使用。ingest.py数据加载与索引构建ingest.py只做两件事从 DataTalksClub 拉取 FAQ 数据以及用 minsearch 构建内存索引。完整实现见 code/ingest.py源码与文档完全一致import requests from minsearch import Index def load_faq_data(): docs_url https://datatalks.club/faq/json/courses.json response requests.get(docs_url) courses_raw response.json() documents [] url_prefix https://datatalks.club/faq for course in courses_raw: course_url f{url_prefix}{course[path]} course_response requests.get(course_url) course_response.raise_for_status() course_data course_response.json() documents.extend(course_data) return documents def build_index(documents): index Index( text_fields[question, section, answer], keyword_fields[course] ) index.fit(documents) return indexload_faq_data()两级 JSON 拉取load_faq_data()的工作方式值得拆解先请求courses.json这个索引文件拿到所有课程的元数据每条包含一个path字段以https://datatalks.club/faq为前缀拼接出每门课的 FAQ 地址逐个请求用raise_for_status()在请求失败时立即抛出异常避免静默拿到损坏数据把所有课程的文档extend进同一个列表并返回。注意课程数据包含不止一门课如 ML Engineering、Data Engineering、MLOps 等 Zoomcamp所以最终documents里混有多个课程的 FAQ 条目这为后面的course关键字过滤埋下伏笔。build_index()minsearch 索引的字段划分build_index()使用 minsearch 的Index构建索引字段划分沿用了 05-search.md 的设计text_fields文本字段question、section、answer。搜索引擎会把这些字段分词、转小写、去停用词用于相关性打分与排序keyword_fields关键字字段course。关键字字段不做分词只做精确匹配相当于 SQL 里的WHERE course llm-zoomcamp用于把检索范围限制在某一门课内。index.fit(documents)的名称沿袭 scikit-learn 的惯例——像在数据上拟合模型一样在文档上拟合索引。FAQ 数据集规模不大约 1100 条文档minsearch 的内存索引在启动时构建耗时不足一秒这正是第一阶段选择它的原因。这个文件还预留了演进空间课程说明中明确提到后续会在同一个文件中加入 sqlitesearch 支持用于持久化搜索索引见 09-data-ingestion.md。rag_helper.py把 RAG 逻辑封装成 RAGBase 类rag_helper.py的前半部分是两条 Prompt 常量与 06-building-prompt.md 中定义的内容一脉相承INSTRUCTIONS Your task is to answer questions from the course participants based on the provided context. Use the context to find relevant information and provide accurate answers. If the answer is not found in the context, respond with I dont know. PROMPT_TEMPLATE QUESTION: {question} CONTEXT: {context} .strip()INSTRUCTIONS是固定不变的系统级指令告诉模型只依据给定上下文作答找不到答案就说 I dont know.这是把回答锚定在知识库、抑制幻觉的关键PROMPT_TEMPLATE是每次变化的用户提示词模板预留了{question}和{context}两个占位符供每轮请求填充。为什么用类而不是继续用全局函数课程原文给出了非常清晰的工程理由在前几课的 Notebook 中index和openai_client是全局变量函数直接闭包引用它们。一旦把函数抽到独立文件这些全局变量就不存在了。两条出路把全局变量 import 回来文件就被绑死在某个特定索引和某个特定客户端上换个索引或换家模型就得改代码复用性差把依赖装进类里索引和 LLM 客户端变成构造函数参数创建对象时想传什么就传什么。课程选择了后者。类的另一个好处是可以继承将来想替换其中某一块例如把 OpenAI 换成本地模型只需子类化RAGBase并覆写对应方法其余部分原样保留。RAGBase 的构造函数与六个方法class RAGBase: def __init__( self, index, llm_client, instructionsINSTRUCTIONS, prompt_templatePROMPT_TEMPLATE, coursellm-zoomcamp, modelgpt-5.4-mini ): self.index index self.llm_client llm_client self.instructions instructions self.course course self.prompt_template prompt_template self.model model构造函数有两条必传依赖、四个带默认值的参数参数是否必传默认值说明index必传—任何带search方法的索引对象minsearch、sqlitesearch 均可llm_client必传—OpenAI 风格的 LLM 客户端如OpenAI()instructions可选INSTRUCTIONS覆写系统指令prompt_template可选PROMPT_TEMPLATE覆写提示词模板course可选llm-zoomcamp检索过滤用的课程关键字model可选gpt-5.4-mini调用的模型名index的抽象约定是只要有search方法就行——这正是后续用 sqlitesearch 无缝替换 minsearch 的接口基础。1.search()委托给索引并应用提升与过滤def search(self, query, num_results5): boost_dict {question: 3.0, section: 0.5} filter_dict {course: self.course} return self.index.search( query, num_resultsnum_results, boost_dictboost_dict, filter_dictfilter_dict )search方法把 boost/filter 规则固化下来question字段权重提到 3.0命中问题的关键词比命中章节名更有信号价值section降到 0.5同时强制只返回course等于当前课程的结果。相比前序课程中 05-search.md 里question: 2.0的示例这里把问题字段的权重进一步提高说明这些数值是可调的超参数可以按数据集反复实验。2.build_context()把检索结果格式化成上下文文本def build_context(self, search_results): lines [] for doc in search_results: lines.append(doc[section]) lines.append(Q: doc[question]) lines.append(A: doc[answer]) lines.append() return \n.join(lines).strip()每条文档被展开成章节名 Q A三行文本块多个文档用空行分隔。这一步把检索返回的字典列表预处理成 LLM 易读的字符串——与 06-building-prompt.md 中的build_context完全一致。3.build_prompt()组装最终提示词def build_prompt(self, query, search_results): context self.build_context(search_results) return self.prompt_template.format( questionquery, contextcontext )用str.format把查询和上下文填进PROMPT_TEMPLATE的占位符。因为模板是可配置的想调整提示词结构只需换一个prompt_template字符串。4.llm()调用 LLM 客户端def llm(self, prompt): input_messages [ {role: developer, content: self.instructions}, {role: user, content: prompt} ] response self.llm_client.responses.create( modelself.model, inputinput_messages ) return response.output_text这里沿用了 07-llm.md 介绍的消息历史格式发送两条消息——developer角色携带固定的INSTRUCTIONS系统级行为约束user角色携带每次变化的提示词。调用的是 OpenAI 的Responses APIresponses.create而非旧的 chat completionsresponse.output_text是直达答案文本的快捷属性免去逐层解析response.output[0].content[0].text的麻烦。5.rag()流水线收口def rag(self, query): search_results self.search(query) prompt self.build_prompt(query, search_results) answer self.llm(prompt) return answerrag()是面向使用者的唯一入口搜索 → 构建提示词 → 调 LLM三步串成一条完整的 RAG 链路。六个方法形成清晰的分层——外层使用者只关心rag()内部每一环又可单独覆写。在 Notebook 中使用导入即用课程给出的使用方式非常简洁完整参考见 code/notebook.ipynbfrom dotenv import load_dotenv load_dotenv() from ingest import load_faq_data, build_index from rag_helper import RAGBase from openai import OpenAI documents load_faq_data() index build_index(documents) openai_client OpenAI() assistant RAGBase( indexindex, llm_clientopenai_client, ) answer assistant.rag(I just discovered the course. Can I join now?) print(answer)几个细节值得注意load_dotenv()从.env文件加载OPENAI_API_KEY之后OpenAI()无需显式传 key环境配置见 02-environment.md只传两个必选参数instructions、prompt_template、course、model全部走rag_helper.py里的默认值数据流一目了然load_faq_data()→build_index()完成索引RAGBase拿到索引与客户端后即可回答。覆写默认行为定制指令与更多问题默认指令不一定满足所有场景。课程展示了如何传入自定义指令来改变模型行为custom_instructions Youre a course teaching assistant. Answer the QUESTION based on the CONTEXT from the FAQ database. Use only the facts from the CONTEXT when answering the QUESTION. .strip() assistant RAGBase( indexindex, llm_clientopenai_client, instructionscustom_instructions, )同样的索引、同样的客户端只换一段指令模型就换了一种人设与约束。这种只覆写单一依赖的能力正是构造函数参数化设计的红利。接着可以连续追问多轮assistant.rag(How do I get a certificate?) assistant.rag(Can I still join the course after it started?)每轮调用都会独立完成检索、提示词组装与生成答案引用的是 FAQ 中的具体条目而不是模型的通用知识——这正是 RAG 与裸 LLM 的本质区别。模块化设计的实战回报无缝切换持久化索引RAGBase的接口抽象在下一课 09-data-ingestion.md 中得到直接验证当数据集变大、需要持久化索引时课程用 sqlitesearch与 minsearch 同 API 的 SQLite FTS5 封装替换内存索引RAG 代码一行不改from sqlitesearch import TextSearchIndex sqlite_index TextSearchIndex( text_fields[question, section, answer], keyword_fields[course], db_pathfaq.db ) assistant RAGBase( indexsqlite_index, llm_clientopenai_client, )正如课程 09 课所强调的minsearch 是纯内存索引进程一停数据即失而 sqlitesearch 把索引写入faq.db文件支持一个进程写入、另一个进程查询。这种能力之所以能零成本获得正是因为RAGBase只依赖index.search(query, boost_dict, filter_dict, num_results)这一统一接口——如果后端 API 不同才需要子类化RAGBase覆写search方法去适配。小结课程后续模块的地基这两个文件的价值远超第 8 课本身。从模块 README.md 可以看到它们是整个 Agentic RAG 模块的公共基础设施后续 09-data-ingestion.md 用它接持久化索引第 13 课函数调用、第 14 课 agentic loop 也建立在RAGBase之上再往后04-evaluation 与 05-monitoring 模块中的同名文件都是这一设计的延续与演化。整体分工可以概括为三句话code/ingest.py 负责数据准备拉取 FAQ、构建索引code/rag_helper.py 负责RAG 流水线搜索、提示词、LLMNotebook 只负责把它们组装起来并可按需覆写指令、模板、课程过滤与模型。这种数据加载 / 检索生成 / 装配使用三层分离正是把实验代码演进为可维护工程的第一步。对任何准备长期迭代的 RAG 项目来说这套模式都值得直接借鉴。【免费下载链接】llm-zoomcampLLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI system that answers questions about your knowledge base. Register here 项目地址: https://gitcode.com/GitHub_Trending/ll/llm-zoomcamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考