第八章 项目实战一学习课程来源https://datawhalechina.github.io/all-in-rag/#/笔记说明本笔记所有内容均基于上述官方RAG全栈教程整理核心技术逻辑、代码框架、项目架构均源自课程原文无额外第三方引用资料。一、学习心得终于到了实战环节这一章把前面学的RAG全流程数据准备、索引构建、检索优化、生成集成串成了一个完整的“尝尝咸淡RAG系统”美食菜谱问答从理论落地到实际项目的过程太有成就感了。原来搭建一个生产级的RAG系统关键不在于单个技术点多复杂而在于模块间的衔接、工程化细节的处理比如配置管理、日志输出、异常处理。教程的项目架构设计得特别清晰每个模块都有明确的职责代码可复用性强跟着敲一遍代码不仅巩固了前面的知识点还学会了如何把零散的技术整合起来。最惊喜的是项目从环境配置到系统整合的每一步都有详细指引甚至连日志打印、模块初始化这些工程化细节都考虑到了新手也能一步步搭建出可交互的RAG系统彻底摆脱了“只会理论不会实操”的困境。二、核心内容归纳本章的核心是从零搭建一个完整的交互式RAG问答系统美食菜谱助手项目名为“尝尝咸淡RAG系统”目标是解决“今天吃什么”的选择困难症用户可通过自然语言提问系统检索菜谱知识库后生成个性化推荐和烹饪指导。项目遵循“模块化设计、工程化落地”的思路完整覆盖RAG的全流程主要分四大核心模块外加环境配置和系统整合形成闭环。一项目核心架构模块化设计高内聚低耦合项目采用模块化拆分每个模块负责一个核心功能方便后续维护和优化整体架构如下尝尝咸淡RAG系统/ ├── config/ # 配置文件模型参数、数据库地址、路径等 ├── rag_modules/ # 核心功能模块 │ ├── data_preparation.py # 数据准备加载、分块 │ ├── index_construction.py # 索引构建嵌入、向量库操作 │ ├── retrieval_optimization.py # 检索优化混合检索 │ └── generation_integration.py # 生成集成LLM调用、格式化输出 ├── main.py # 主程序系统初始化、交互逻辑 ├── requirements.txt # 依赖包清单 └── data/ # 食谱知识库Markdown格式文档核心设计思路每个模块通过配置文件统一管理参数模块间通过接口调用后续要替换嵌入模型、向量数据库或LLM只需修改配置和对应模块不用改动整体逻辑工程化适配性极强。二四大核心模块详解功能实现逻辑1. 数据准备模块data_preparation.py核心功能加载食谱文档Markdown格式→ 清洗去冗余、去空白→ 结构化分块→ 输出文本块chunk实现细节文档加载支持批量加载指定目录下的所有MD文件用Python的os库遍历文件markdown库解析文档内容文本分块采用教程第二章的Markdown结构感知分块按MD的标题、二级标题、段落拆分保证每个chunk是一个完整的菜谱知识点比如“番茄炒蛋的食材”“番茄炒蛋的步骤”避免跨知识点切分输出格式每个chunk包含“文本内容文档路径标题”等元信息方便后续检索和溯源。2. 索引构建模块index_construction.py核心功能文本块向量化→ 向量数据库存储→ 索引加载/构建实现细节嵌入模型选用轻量高效的中文模型BAAI/bge-small-zh-v1.5教程推荐平衡效果和速度通过sentence-transformers库调用向量数据库采用FAISS轻量开源适合中小型知识库支持索引的保存和加载避免每次启动都重新构建索引教程中首次构建后保存到./vector_index目录后续直接加载索引优化使用FAISS的IVF_FLAT索引类型提升检索速度适配1764个结构化chunk的知识库规模教程中加载了323个食谱文档分块后生成1764个chunk。3. 检索优化模块retrieval_optimization.py核心功能接收用户查询→ 混合检索稠密稀疏→ 返回Top-N相关chunk实现细节查询处理调用第四章的“查询构建”逻辑提取用户问题的核心关键词如“30分钟 减脂 素菜”混合检索先通过BM25稀疏检索快速召回20个相关chunk再通过向量检索稠密检索对这20个chunk做精排序返回Top-5最相关结果兼顾召回率和精准度检索适配针对美食场景优化关键词权重如“做法”“步骤”“食材”“时间”“难度”等关键词加权提升检索精准度。4. 生成集成模块generation_integration.py核心功能接收检索到的chunk→ 构造LLM Prompt→ 调用大模型→ 格式化输出答案实现细节LLM选择教程中使用kimi-k2-0711-preview支持长上下文适合整合多个chunk的信息Prompt设计采用结构化Prompt包含“系统角色美食助手 检索到的菜谱信息 用户问题 输出格式要求”示例你是专业的美食助手根据以下菜谱信息回答用户的问题要求输出清晰、步骤明确分点列出 菜谱信息{retrieved_chunks} 用户问题{user_query} 输出格式1. 推荐菜谱xxx 2. 食材xxx 3. 烹饪步骤xxx 4. 小贴士xxx格式化输出强制LLM按固定格式生成答案避免输出混乱提升用户体验异常处理若检索不到相关菜谱返回友好提示“未找到匹配的菜谱试试其他关键词如‘家常菜’‘30分钟’”。三项目完整流程从启动到交互环境配置安装依赖包sentence-transformers、faiss-cpu、markdown、openai适配kimi API等配置模型参数、数据路径、API密钥系统初始化启动main.py依次初始化数据准备模块→ 索引构建模块加载预构建的向量索引→ 检索优化模块→ 生成集成模块初始化LLM知识库构建加载data/C8/cook目录下的323个食谱文档分块生成1764个结构化chunk若已存在向量索引则直接加载否则重新构建用户交互用户输入自然语言问题如“30分钟能做好的减脂素菜”系统执行“查询处理→ 混合检索→ Prompt构造→ LLM生成→ 格式化输出”返回答案系统退出用户输入“退出”系统关闭资源结束交互。四项目核心亮点贴近生产级落地工程化细节完善加入日志输出记录模块初始化、知识库加载、检索结果等方便问题排查配置文件统一管理参数不用硬编码性能优化支持向量索引的保存和加载避免重复构建混合检索采用“粗召回精排序”平衡速度和效果用户体验友好结构化输出答案步骤清晰检索不到结果时返回引导提示支持连续交互不用每次重启可扩展性强模块化设计可替换嵌入模型如换成text2vec、向量数据库如换成Milvus、LLM如换成通义千问适配不同场景。三、代码运行记录核心片段运行结果以下是项目核心模块的关键代码片段简化版还原教程核心逻辑以及完整运行流程的输出结果可直接参考搭建。一核心代码片段1. 配置文件config/config.py# 模型配置 EMBED_MODEL BAAI/bge-small-zh-v1.5 # 嵌入模型 LLM_MODEL kimi-k2-0711-preview # LLM模型 API_KEY your_kimi_api_key # 替换为自己的API密钥 # 路径配置 DATA_PATH ./data/C8/cook # 食谱数据路径 VECTOR_INDEX_PATH ./vector_index # 向量索引保存路径 # 检索配置 TOP_K_SPARSE 20 # 稀疏检索召回数 TOP_K_DENSE 5 # 稠密检索最终返回数 # 分块配置 CHUNK_SIZE 500 # 分块最大长度 CHUNK_OVERLAP 50 # 分块重叠长度2. 数据准备模块rag_modules/data_preparation.pyimport os import markdown from config.config import DATA_PATH, CHUNK_SIZE, CHUNK_OVERLAP class DataPreparation: def __init__(self): self.data_path DATA_PATH def load_markdown_files(self): 加载目录下所有MD文件 docs [] for filename in os.listdir(self.data_path): if filename.endswith(.md): file_path os.path.join(self.data_path, filename) with open(file_path, r, encodingutf-8) as f: content f.read() # 解析MD内容提取文本忽略格式 html markdown.markdown(content) text .join([t.text for t in markdown.markdownFromFile(file_path, output_formathtml.parser).findAll(textTrue)]) docs.append({text: text, file_path: file_path}) print(f成功加载 {len(docs)} 个食谱文档) return docs def split_text_by_markdown(self, docs): Markdown结构感知分块 chunks [] for doc in docs: text doc[text] file_path doc[file_path] # 按段落拆分简化版教程中是按MD标题层级拆分 paragraphs [p.strip() for p in text.split(\n\n) if p.strip()] # 合并短段落拆分长段落 current_chunk for para in paragraphs: if len(current_chunk) len(para) CHUNK_SIZE: current_chunk para \n else: if current_chunk: chunks.append({ text: current_chunk.strip(), file_path: file_path }) current_chunk para \n # 添加最后一个chunk if current_chunk: chunks.append({ text: current_chunk.strip(), file_path: file_path }) print(f分块完成共生成 {len(chunks)} 个结构化chunk) return chunks def run(self): 执行数据准备流程 docs self.load_markdown_files() chunks self.split_text_by_markdown(docs) return chunks3. 主程序main.pyimport logging from rag_modules.data_preparation import DataPreparation from rag_modules.index_construction import IndexConstruction from rag_modules.retrieval_optimization import RetrievalOptimization from rag_modules.generation_integration import GenerationIntegration from config.config import * # 配置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(module)s - %(levelname)s - %(message)s) def main(): print(*50) print( 尝尝咸淡RAG系统初始化中...) print(*50) # 1. 初始化各模块 logging.info(初始化数据准备模块...) data_module DataPreparation() logging.info(初始化索引构建模块...) index_module IndexConstruction() logging.info(初始化检索优化模块...) retrieval_module RetrievalOptimization() logging.info(初始化生成集成模块...) generation_module GenerationIntegration() # 2. 数据准备 chunks data_module.run() # 3. 构建/加载向量索引 index_module.build_or_load_index(chunks) # 4. 交互循环 print(\n✅ 系统初始化完成可以开始提问输入退出结束) while True: user_query input(\n你) if user_query 退出: print( 再见祝你用餐愉快) break # 5. 检索 retrieved_chunks retrieval_module.retrieve(user_query) # 6. 生成答案 answer generation_module.generate_answer(user_query, retrieved_chunks) # 7. 输出 print(f\n美食助手\n{answer}) if __name__ __main__: main()二完整运行结果 尝尝咸淡RAG系统初始化中... 2025-08-02 16:41:41,484 - rag_modules.index_construction - INFO - 初始化嵌入模型:BAAI/bge-small-zh-v1.5 2025-08-02 16:41:47,207 - sentence_transformers.SentenceTransformer - INFO - Load pretrained SentenceTransformer: BAAI/bge-small-zh-v1.5 2025-08-02 16:41:50,723 - rag_modules.index_construction - INFO - 嵌入模型初始化完成 2025-08-02 16:41:50,724 - rag_modules.generation_integration - INFO - 正在初始化LLM: kimi-k2-0711-preview 2025-08-02 16:41:51,495 - rag_modules.generation_integration - INFO - LLM初始化完成 成功加载 323 个食谱文档 分块完成共生成 1764 个结构化chunk 2025-08-02 16:41:51,553 - rag_modules.index_construction - INFO - 向量索引已从./vector_index 加载 √成功加载已保存的向量索引! ✅ 系统初始化完成可以开始提问输入退出结束 你30分钟能做好的减脂素菜有哪些 美食助手 1. 推荐菜谱清炒西兰花、凉拌黄瓜 2. 食材 - 清炒西兰花西兰花1颗、大蒜3瓣、盐少许、食用油1勺 - 凉拌黄瓜黄瓜1根、蒜末1勺、醋2勺、生抽1勺、盐少许 3. 烹饪步骤 - 清炒西兰花① 西兰花切小朵水焯3分钟捞出沥干② 热油爆香大蒜③ 放入西兰花翻炒2分钟加盐调味即可总耗时约15分钟 - 凉拌黄瓜① 黄瓜拍碎切块② 加入蒜末、醋、生抽、盐搅拌均匀③ 腌制5分钟即可食用总耗时约10分钟 4. 小贴士西兰花焯水时加少许盐和油保持颜色翠绿凉拌黄瓜可根据喜好加入小米辣提味低卡无负担 你退出 再见祝你用餐愉快四、踩坑填坑记录实战必看跟着教程搭建项目时踩了多个工程化落地的典型坑主要集中在环境配置、模块衔接、数据处理、API调用这几个方面以下是详细的坑点和填坑方法完全贴合教程的实战场景坑1安装依赖包时版本冲突导致嵌入模型加载失败现象运行index_construction.py时报错AttributeError: SentenceTransformer object has no attribute encode原因sentence-transformers库版本过低教程推荐版本为2.2.2旧版本的API与代码不兼容填坑方法在requirements.txt中指定依赖包版本执行pip install -r requirements.txt安装核心依赖版本如下sentence-transformers2.2.2 faiss-cpu1.7.4 markdown3.4.4 openai1.3.5 # 用于调用kimi API python-dotenv1.0.0坑2向量索引加载失败提示“找不到./vector_index目录”现象首次运行项目时报错FileNotFoundError: [Errno 2] No such file or directory: ./vector_index原因教程中代码默认先尝试加载已有的向量索引首次运行时还未构建索引目录不存在填坑方法在index_construction.py的build_or_load_index方法中添加目录判断逻辑若目录不存在则先构建索引并保存代码如下def build_or_load_index(self, chunks): if os.path.exists(VECTOR_INDEX_PATH): # 加载索引 self.index faiss.read_index(VECTOR_INDEX_PATH) logging.info(向量索引已从./vector_index 加载 √) else: # 构建索引 texts [chunk[text] for chunk in chunks] embeddings self.embed_model.encode(texts) self.index faiss.IndexIVFFlat(embeddings.shape[1], 100) # 100为聚类数 self.index.train(embeddings) self.index.add(embeddings) # 保存索引 os.makedirs(VECTOR_INDEX_PATH, exist_okTrue) faiss.write_index(self.index, VECTOR_INDEX_PATH /index.faiss) logging.info(向量索引构建完成并保存 √)坑3加载MD文件时出现乱码分块后文本为空现象运行数据准备模块后提示“成功加载323个文档”但分块后chunk数量为0或文本内容乱码原因MD文件编码格式不是UTF-8如GBK或文件中包含特殊字符如emoji、公式填坑方法① 统一将MD文件编码转为UTF-8用记事本打开→ 另存为→ 编码选择UTF-8② 在加载文件时添加编码异常处理忽略无法解析的特殊字符代码如下with open(file_path, r, encodingutf-8, errorsignore) as f: content f.read()坑4调用kimi API时提示“API密钥无效”或“请求超时”现象生成答案时报错AuthenticationError: Invalid API key或TimeoutError原因① API密钥错误或未开通kimi API权限② 网络环境无法访问kimi服务器填坑方法① 登录kimi开放平台获取正确的API密钥替换config.py中的API_KEY② 若网络无法访问可替换为国内可访问的LLM如通义千问、讯飞星火修改generation_integration.py的LLM调用逻辑示例通义千问from alibabacloud_tea_openapi.models import Config from alibabacloud_dashscope_api20230110.models import CompletionsRequest from alibabacloud_dashscope_api20230110.client import Client # 初始化通义千问客户端 config Config(access_key_idyour_access_key, access_key_secretyour_secret) self.client Client(config)坑5分块后检索结果不精准找不到相关菜谱现象用户提问“番茄炒蛋怎么做”检索返回的chunk与番茄炒蛋无关原因分块策略不合理如切分太碎把“番茄炒蛋”的步骤拆成多个不相关的chunk或嵌入模型未适配中文美食场景填坑方法① 调整分块参数增大CHUNK_SIZE如从500调整为800保证菜谱的步骤完整性② 在查询构建时针对美食场景添加关键词增强如用户提问“怎么做”自动补充“做法”“步骤”关键词修改retrieval_optimization.py的查询处理逻辑def process_query(self, query): # 美食场景关键词增强 if 怎么做 in query or 如何做 in query: query 做法 步骤 if 减脂 in query or 减肥 in query: query 低卡 少油 return query五、对本章教程的意见及建议优点模块化架构设计专业采用工程化的模块化拆分每个模块职责清晰代码可复用性强不仅适合新手学习还能直接迁移到其他RAG项目如文档问答、客服系统实用性极强实战细节拉满教程不仅提供代码框架还包含日志配置、配置文件管理、索引保存/加载、异常处理等工程化细节完全贴合生产级落地需求避免新手只学“玩具代码”前后知识衔接紧密项目的每个模块都对应前面章节的核心知识点数据准备→ 第二章、索引构建→ 第三章、检索优化→ 第四章、生成集成→ 第五章学完能快速巩固前面的知识形成完整的技术链项目场景贴近生活选择美食菜谱作为实战场景数据易获取、需求明确用户能直观感受到RAG系统的价值学习动力更强代码注释详细教程中的代码包含详细的注释解释核心逻辑和参数含义新手能快速理解每个部分的作用降低上手难度。小建议补充API调用的降级方案教程中使用kimi API部分用户可能无法访问或没有API密钥建议补充开源LLM的本地部署方案如Llama.cpp部署Qwen-1.8B让没有API权限的用户也能完成实战增加模块单元测试代码教程中的代码是完整的系统级代码新手排查问题时较困难建议补充每个模块的单元测试代码如数据准备模块的分块测试、检索模块的检索效果测试方便定位问题补充知识库扩充方法教程中使用现成的食谱文档建议补充如何批量爬取食谱数据如爬取美食网站的菜谱或如何手动添加自定义菜谱让用户能快速扩充自己的知识库增加前端交互界面教程中的交互是命令行形式用户体验较单一建议补充简单的Web前端界面如用Streamlit快速搭建让项目更直观、更适合展示补充性能优化的进阶技巧教程中的项目适合中小型知识库建议补充大数据量下的性能优化技巧如分库分表、缓存策略、异步检索让用户能应对更大规模的场景。六、后续学习计划完善项目功能为项目添加Web前端界面用Streamlit实现可视化交互添加知识库上传功能支持用户手动上传自己的菜谱文档优化检索和生成效果替换混合检索的权重配置针对美食场景做精细化调优优化Prompt设计让生成的菜谱步骤更详细、更贴合用户需求如根据用户的烹饪工具推荐菜谱集成高级功能将第四章的Text2SQL功能集成到项目中支持用户查询“热量低于300大卡的菜谱”等结构化查询集成多模态检索支持用户上传美食图片检索对应的菜谱学习项目优化第九章继续学习第九章的Graph RAG优化将知识图谱与当前项目结合构建菜谱的食材关联、步骤关联提升检索精准度部署上线用Docker打包项目部署到云服务器如阿里云、腾讯云实现公网访问完成从开发到部署的完整流程打造一个可实际使用的RAG产品。