LLM Zoomcamp 2026 第一模块实战:以课程笔记为知识库,从零构建 Agentic RAG
发布时间:2026/9/17 1:24:24 作者:尧图编辑部 阅读量:1,286

LLM Zoomcamp 2026 第一模块实战以课程笔记为知识库从零构建 Agentic 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 2026 期第一模块Agentic RAG的课后作业 cohorts/2026/01-agentic-rag/homework.md 展开完整还原作业要求的数据准备、关键词检索、RAG 管线、分块chunking与函数调用 Agent 全流程。读者按本文操作可以独立完成把课程笔记当成知识库的 RAG 系统并把它升级为会自行决定何时搜索、搜索什么的智能体同时掌握输入 token 用量统计与滑动窗口分块这两种关键实战技能。作业背景知识库换成课程笔记本身课程模块本身cohorts/2026/01-agentic-rag/README.md讲的是先用关键词搜索构建一个最小可用的 RAG 管线再引入函数调用让 LLM 自己决定何时搜索、搜索什么从而把固定管线变成 Agent。本作业与模块走的是同一条路径唯一的区别是知识库不再是课程 FAQ而是课程笔记本身。课程仓库按期数 模块组织2026 期一共有七个模块01-agentic-ragAgentic RAG02-vector-search向量检索03-orchestration编排04-evaluation评估05-monitoring监控06-best-practices最佳实践07-project-example项目示例每个模块目录下存放着一组带编号的 Markdown 课程页例如cohorts/2026/01-agentic-rag/01-intro.md、02-environment.md直到16-other-frameworks.md。这些页面就是学员上课时实际阅读的内容本次作业直接把它们抓下来作为 RAG 的知识库。注意作业原文中给出的目录示意是cohorts/2026/01-agentic-rag/lessons/01-intro.md这样的形态对应课程仓库的某个历史提交在当前仓库中课程页直接存放在各模块目录下。作业通过路径过滤器只保留含/lessons/的 Markdown 文件意图是只抓课程页、不抓顶层 README 等无关文件这个过滤思路在任何目录结构下都成立。环境准备作业要求按照模块课程的 02-environment.md 准备环境核心是安装uv、初始化项目、添加依赖requests、minsearch、openai、jupyter、python-dotenv并把 OpenAI API Key 写入.env文件同时把.env加入.gitignore。在此基础上本作业多需要一个额外的库gitsource它负责从 GitHub 仓库下载文件uv add gitsourceLLM 方面作业推荐使用 OpenAI 的gpt-5.4-mini但也可以换用任意模型与提供商——只需相应调整 client 与 usage 字段的读取方式。模块课程的 code/pyproject.toml 给出了完整的依赖清单其中minsearch0.0.11、openai2.37.0、toyaikit0.0.11都是本作业会直接用到的库。数据准备用 gitsource 拉取课程页为了保证所有人使用完全一致的数据作业固定使用课程仓库的某个提交8c1834d并通过gitsource提供的GithubRepositoryDataReader拉取from gitsource import GithubRepositoryDataReader reader GithubRepositoryDataReader( repo_ownerDataTalksClub, repo_namellm-zoomcamp, commit_id8c1834d, allowed_extensions{md}, filename_filterlambda path: /lessons/ in path, ) files reader.read()各参数的含义与作用参数值作用repo_ownerDataTalksClubGitHub 仓库所属组织repo_namellm-zoomcamp要下载的仓库名commit_id8c1834d固定提交确保数据一致allowed_extensions{md}只检查 Markdown 文件filename_filterlambda path: /lessons/ in path只保留课程页剔除顶层 README 等文件GithubRepositoryDataReader会下载整个仓库并遍历其中所有文件但由于限定了allowed_extensions{md}实际只检查 Markdown再叠加filename_filter最终只留下各模块课程页。files中的每个文件都有parse()方法返回一个包含filename与content的字典汇总成文档列表documents [] for file in files: doc file.parse() documents.append(doc)Q1 验证点对documents计数即可得到课程页总数。这也是作业的第一道题选项为 24 / 72 / 240 / 720。Q2 索引与检索minsearch 的 text / keyword 字段检索是 RAG 的地基。模块课程 05-search.md 详细讲解了 minsearch 的用法任何搜索引擎本质都是score sim(query, document)——对每条文档计算相似度、排序、取 Top N。minsearch 是一个纯内存的轻量关键词搜索引擎无需 Docker概念与 Elasticsearch / Lucene 一脉相承text 字段、keyword 字段、boost、filter 都来自 Elasticsearch 的术语。关键词字段与文本字段的分工是理解本作业的关键text 字段被分词split、lowercase、去停用词后参与相关性排序是你搜什么的字段keyword 字段做精确匹配等价于 SQL 的WHERE course ...用来把搜索空间限制到某个子集不受排序与 boost 影响。作业要求用content作为 text 字段、filename作为 keyword 字段来建索引from minsearch import Index index Index( text_fields[content], keyword_fields[filename], ) index.fit(documents)fit的命名来自 scikit-learn 的惯例——对数据拟合一个索引。随后用下面的查询进行检索这也是作业的第二道题How does the agentic loop keep calling the model until it stops?search_results index.search( query, num_results5, boost_dict{content: 1.0}, # 只有 content 一个 text 字段时 boost 可省略 )Q2 验证点查看第一条结果的filename字段即可从四个候选答案中选出正确项。候选答案涉及cohorts/2026/01-agentic-rag/lessons/03-rag.md、14-agentic-loop.md、04-evaluation/lessons/13-llm-as-judge.md、06-best-practices/lessons/02-hybrid-search.md——检索目标agentic loop对应的正是模块课程里的 14-agentic-loop.md 一课。关于 boost 与 filter多字段场景下可以通过boost_dict调整字段权重例如 FAQ 场景里{question: 3.0, section: 0.5}问句命中比章节名命中更有信息量单字段场景下通常不需要 boost。keyword 字段配合filter_dict使用例如 FAQ 场景按course过滤保证答案只来自本课程。Q3 RAG改造 RAGBase 并读取 token 用量有了索引就可以构建 RAG 助手。模块课程准备了两个可复用文件code/ingest.py——数据加载与索引构建code/rag_helper.py——RAG 逻辑search、prompt、LLMrag_helper.py中的RAGBase类把index和llm_client作为构造参数注入而不是模块级全局变量因此可以传入任意带search方法的索引对象和任意 OpenAI 兼容 client也便于子类化覆写局部逻辑。核心方法如下search(query, num_results5)委托给index.searchFAQ 场景下使用boost_dict{question: 3.0, section: 0.5}与filter_dict{course: self.course}build_context(search_results)把检索结果格式化成上下文文本build_prompt(query, search_results)套用PROMPT_TEMPLATE拼出QUESTION: ...CONTEXT: ...的 promptllm(prompt)把developer角色的INSTRUCTIONS与用户 prompt 一起发给llm_client.responses.create(modelself.model, inputinput_messages)只返回response.output_textrag(query)串联 search → build_prompt → llm返回答案字符串。问题在于RAGBase是为 FAQ 的section/question/answer三字段结构写的而本作业的文档只有filename和content。作业给出了两种解决方案自己实现 RAG 流程完全绕开 FAQ 特定逻辑沿用RAGBase只改与 FAQ 结构相关的两处search改为查我们的content/filename索引与build_context改为直接输出doc[content]而非拼section/Q:/A:。推荐方案 2改动最小。改造后的search与build_context大致如下class LessonRAG(RAGBase): def search(self, query, num_results5): return self.index.search(query, num_resultsnum_results) def build_context(self, search_results): return \n\n.join(doc[content] for doc in search_results).strip()Q3 的核心难点在 token 统计作业要求回答给模型发送了多少输入prompttoken。选输入 token 而非价格是因为价格随模型与提供商不同而变但 prompt 的大小对所有人是一致的。大多数 LLM API 都会在响应对象上暴露 token 用量如response.usage.input_tokens或prompt_tokens。为此需要修改rag_helper.py把llm从只返回文本改为返回整个响应对象把rag改为同时返回答案与 usage可以返回一个 tuple或定义一个小的 dataclass。改造后的示意def llm(self, prompt): input_messages [ {role: developer, content: self.instructions}, {role: user, content: prompt} ] return self.llm_client.responses.create( modelself.model, inputinput_messages ) # 返回整个 response而不是 response.output_text def rag(self, query): search_results self.search(query) prompt self.build_prompt(query, search_results) response self.llm(prompt) return response.output_text, response.usage.input_tokensQ3 验证点用改造后的 RAG 回答 How does the agentic loop keep calling the model until it stops?打印 usage 中的输入 token 数与 700 / 7000 / 70000 / 700000 四个选项对比选最接近的。Q4 分块chunk_documents 滑动窗口课程页很长有的长达数千字符。长文档会让检索变得不精确页面深处的一次匹配会拖进整个页面作为上下文。常见解法是分块chunking把每个页面切成更小的、互相重叠的片段再对片段建索引。gitsource提供了现成的chunk_documents辅助函数底层是滑动窗口算法from gitsource import chunk_documents chunks chunk_documents(documents, size2000, step1000)窗口机制以size2000、step1000为例每个 chunk 是页面文本中一个长度为size2000字符的窗口窗口每次向前滑动step1000字符因为step size相邻 chunk 重叠size - step1000字符跨越边界的段落会在某个 chunk 中保持完整每个 chunk 保留原始字段如filename并新增start该 chunk 在页面中的起始偏移量与contentchunk 文本。Q4 验证点统计chunks的数量与 70 / 295 / 1100 / 4500 四个选项对比。分块的价值可以在 07-project-example/07-chunking.md 模块课程中找到佐证长文档切块后检索粒度从整页细化为页内片段召回的是真正相关的那一段文本。Q5 分块后的 RAG验证 token 下降分块使每次请求更小因为发送给 LLM 的上下文变小了。作业要求量化这个收益对 Q4 的chunks建索引同样content为 text 字段、filename为 keyword 字段把 RAG 指向 chunk 索引用与 Q3 完全相同的方式读取输入 token回答同一个查询对比 Q3 与 Q5 的输入 token 数。Q5 验证点判断分块版本少用了多少输入 token选项为大致相同 / 少 3× / 少 10× / 少 30×。由于分块后上下文从整页缩小为单个 chunk输入 token 通常会出现数量级级别的下降。Q6 转成 Agentsearch 工具 agentic loop到目前为止搜索只执行一次用的还是用户的原话查询。现在把它 agentic给 LLM 一个search工具让它自己决定何时搜索、搜索什么。作业建议使用模块课程 15-frameworks.md 中介绍的教学用小型 Agent 库toyaikit也可以使用 OpenAI Agents SDK、PydanticAI、LangChain或自己手写循环uv add toyaikit第一步定义带类型提示与 docstring 的 search 函数大多数 Agent 框架会读取函数的类型提示type hint与 docstring 来自动生成工具 schemaJSON Schema所以不用手写 schemadef search(query: str) - list[dict]: Search the course lessons for entries matching the given query. return chunk_index.search( query, num_results5, )这里使用 Q4 的 chunk 索引。函数名、类型提示、docstring 三者共同决定工具对外暴露的形态。第二步用 toyaikit 组装 Agentfrom toyaikit.llm import OpenAIClient from toyaikit.tools import Tools from toyaikit.chat import IPythonChatInterface from toyaikit.chat.runners import OpenAIResponsesRunner, DisplayingRunnerCallback agent_tools Tools() agent_tools.add_tool(search) # 不传 schema让框架从 type hint docstring 生成 chat_interface IPythonChatInterface() callback DisplayingRunnerCallback(chat_interface) runner OpenAIResponsesRunner( toolsagent_tools, developer_promptinstructions, chat_interfacechat_interface, llm_clientOpenAIClient(modelgpt-5.4-mini), )作业给出的 Agent 指令刻意引导它多搜索几次Youre a course teaching assistant. Answer the students question using the search tool. Make multiple searches with different keywords before answering.然后提问How does the agentic loop work, and how is it different from plain RAG?result runner.loop(promptquestion, callbackcallback)第三步理解 agentic loop 的内部机制Agent 背后就是模块课程 14-agentic-loop.md 手写的那个while True循环模型返回响应 → 若含函数调用则执行并回填结果 → 再次调用模型 → 直到模型返回不再含工具调用的最终答案。Agent 三要素是instructionsdeveloper消息、toolssearch与memory不断追加的消息历史。toyaikit 的 runner 正是封装了这个循环它的LoopResult还提供all_messages完整对话历史、token 数与cost由 token 用量计算方便调试多轮 Agent。Q6 验证点Agent 会自行决定搜索次数因此答案会随运行略有波动——统计它调用search的次数与 0 / 4 / 10 / 20 对比选最接近的作业用gpt-5.4-mini实测换模型或提供商次数可能不同。作业要点小结步骤核心知识点验证方式数据准备GithubRepositoryDataReaderfilename_filter统计课程页数量Q2 检索minsearch text / keyword 字段检查首条结果filenameQ3 RAG改造RAGBase的search/build_context读取 usage 输入 tokenQ4 分块chunk_documents滑动窗口统计 chunk 数量Q5 分块 RAG上下文变小 → token 下降对比 Q3 与 Q5 输入 tokenQ6 Agent函数调用 agentic loop统计search调用次数所有代码与数据都可以在当前仓库中找到依据环境与依赖见 cohorts/2026/01-agentic-rag/02-environment.md 与 code/pyproject.tomlRAG 辅助类见 code/rag_helper.pyAgent 循环原理见 14-agentic-loop.mdtoyaikit 用法见 15-frameworks.md。作业还鼓励learning in public公开分享学习过程并提供了 LinkedIn / Twitter 的示例文案模板完成后可在课程平台提交结果若答案与选项不完全一致选择最接近的即可。【免费下载链接】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),仅供参考