多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

all-in-rag 食谱知识库实战:以「椒盐玉米」为例解析结构化菜谱文档的加载、分块与检索生成全流程

all-in-rag 食谱知识库实战:以「椒盐玉米」为例解析结构化菜谱文档的加载、分块与检索生成全流程 all-in-rag 食谱知识库实战以「椒盐玉米」为例解析结构化菜谱文档的加载、分块与检索生成全流程【免费下载链接】all-in-rag大模型应用开发实战一RAG 技术全栈指南在线阅读地址https://datawhalechina.github.io/all-in-rag/项目地址: https://gitcode.com/datawhalechina/all-in-rag本文以 all-in-rag 开源项目中「尝尝咸淡」RAG 食谱问答系统code/C8为背景选用其知识库中的 椒盐玉米 菜谱文档作为贯穿全文的实例完整讲解一份结构化 Markdown 菜谱是如何被数据准备模块加载、元数据增强、按标题层级分块再进入 FAISS 向量索引与 BM25 关键词检索最终由大模型生成分步烹饪指导的。读完本文你将掌握小块检索、大块生成父子文本块架构在真实数据上的落地方式并能对照仓库源码独立理解任意一份菜谱文档在 RAG 流水线中的处理细节。一、菜谱文档RAG 系统中的结构化数据样本在 code/C8 项目中知识库数据存放于 data/C8/cook 目录按荤菜、素菜、汤品、甜品等分类组织成子目录。椒盐玉米.md位于素菜分类vegetable_dish下是极具代表性的结构化菜谱样本其内容由四个标准章节构成一级标题 难度评级# 椒盐玉米的做法与预估烹饪难度★★★必备原料和工具玉米粒、椒盐、芝麻粒、油、淀粉、两个塑料簸箕、若干吸油纸计算给出按标准分量换算的用料配比玉米粒 350g、淀粉 40-70g、椒盐粉 10g、芝麻粒 10g操作从解冻、吸油纸吸水、裹淀粉到煎制出锅的完整步骤序列附加内容关于玉米粒购买与解冻的经验性提示。这种标题层级清晰、章节边界明确的文档结构正是第 2 章所讲 Markdown 结构分块 的理想输入。由于每个章节篇幅较短几百字以内不会出现单块超出模型上下文窗口的问题可以直接按标题分块而不必与递归字符分割器组合使用。椒盐玉米这份文档的全部核心要素——原料清单、用量配比、操作步骤、附加技巧——在后续的数据准备与生成阶段都会被完整继承。提示本文引用的菜谱文档与运行代码均位于仓库 code/C8 对应的食谱问答项目中读者可按 环境配置与项目架构 一节创建 Python 3.12.7 虚拟环境并安装 requirements.txt 中的依赖后运行 main.py 复现全流程。二、数据准备菜谱文档如何进入知识库2.1 递归加载 Markdown 文件并生成父文档数据准备模块的入口是 data_preparation.py 中的DataPreparationModule.load_documents()。它使用Path.rglob(*.md)递归扫描data_path下的全部菜谱文件逐份读取原始 Markdown 内容并为每个文件生成一个父文档doc_type 标记为parent。其核心代码如下for md_file in data_path_obj.rglob(*.md): with open(md_file, r, encodingutf-8) as f: content f.read() parent_id hashlib.md5(relative_path.encode(utf-8)).hexdigest() doc Document( page_contentcontent, metadata{source: str(md_file), parent_id: parent_id, doc_type: parent} )以椒盐玉米为例加载后其source指向data/C8/cook/dishes/vegetable_dish/椒盐玉米/椒盐玉米.mdpage_content是完整的菜谱正文parent_id则由相对路径的 MD5 哈希唯一确定——注意这与文档中早期的uuid.uuid4()方案不同当前仓库实现data_preparation.py采用确定性哈希保证同一路径每次加载生成的父文档 ID 稳定便于索引缓存复用。2.2 元数据增强从路径与内容自动抽取分类、菜名、难度加载完成后_enhance_metadata()会为每个父文档补充三层关键元数据菜品分类从文件路径片段匹配CATEGORY_MAPPINGvegetable_dish→ 素菜另有荤菜、汤品、甜品、早餐、主食、水产、调料、饮品等映射菜品名称直接取文件名dish_name 椒盐玉米难度等级用正则re.search(r★, content)统计连续星号数量映射到五档难度——5 星非常困难、4 星困难、3 星中等、2 星简单、1 星非常简单。因此椒盐玉米文档会被自动标注为category素菜、dish_name椒盐玉米、difficulty中等。这些元数据正是后续 main.py 中_extract_filters_from_query()实现元数据过滤检索的依据——当用户问推荐个简单的素菜时系统能据此精确限定category与difficulty。2.3 Markdown 标题分块一份菜谱拆成五个子块chunk_documents()调用_markdown_header_split()通过 LangChain 的MarkdownHeaderTextSplitter按#、##、###三级标题分割headers_to_split_on [ (#, 主标题), # 菜品名称 (##, 二级标题), # 必备原料、计算、操作等 (###, 三级标题) # 简易版本、复杂版本等 ] markdown_splitter MarkdownHeaderTextSplitter( headers_to_split_onheaders_to_split_on, strip_headersFalse # 保留标题便于理解上下文 )strip_headersFalse意味着分块后仍保留标题行使每个子块自带语义上下文。对椒盐玉米文档分割结果对应 数据准备模块实现 中描述的父子映射父文档椒盐玉米.md ├── 子块1# 椒盐玉米的做法 简介 难度评级 ├── 子块2## 必备原料和工具 食材清单 ├── 子块3## 计算 用量配比 ├── 子块4## 操作 详细制作步骤 └── 子块5## 附加内容每个子块都会继承父文档元数据并额外打上chunk_id、parent_id、doc_typechild、chunk_index、chunk_size等标记同时把child_id → parent_id关系写入parent_child_map为小块检索、大块生成奠定基础。2.4 智能去重多个子块归并为一个完整菜谱当用户询问椒盐玉米怎么做时检索阶段可能同时命中子块 2、3、4。get_parent_documents()会统计每个父文档被子块命中的次数作为相关性分数按分数降序去重输出父文档避免 LLM 收到重复内容。从源码结构看data_preparation.py 对应方法命中子块越多的菜谱在最终结果中排序越靠前。三、索引构建与检索玉米粒配方如何被精确召回3.1 向量索引与索引缓存IndexConstructionModule位于 index_construction.py使用配置指定的嵌入模型BAAI/bge-small-zh-v1.5将子块向量化并写入 FAISS 向量库索引持久化到index_save_path默认./vector_index。main.py 启动时优先尝试加载已保存索引命中缓存则秒级启动未命中则走加载文档 → 分块 → 建索引 → 保存全流程。3.2 混合检索与 RRF 重排RetrievalOptimizationModule 同时维护两路检索器向量检索器vectorstore.as_retriever(search_typesimilarity, search_kwargs{k: 5})捕捉语义相似度BM25 检索器BM25Retriever.from_documents(chunks, k5)捕捉关键词精确匹配。hybrid_search()将两路结果通过RRFReciprocal Rank Fusion融合对每个文档按1 / (k rank 1)仓库实现中 RRF 常数 k60见 retrieval_optimization.py累加两个榜单的排名分数按总分降序取top_k配置默认 3。这种设计让椒盐玉米这类既包含语义怎么做又包含术语淀粉、簸箕、椒盐的查询无论走语义还是关键词通道都能被稳定召回。metadata_filtered_search()则先在混合检索结果上放大 3 倍候选再按category/difficulty等过滤条件筛选最终返回top_k条供推荐个简单的素菜这类带约束的查询使用。四、生成让大模型基于菜谱产出分步烹饪指导检索到椒盐玉米的相关子块后main.py 依次完成查询路由、查询重写与生成查询路由query_router()将问题分类为list推荐列表、detail详细做法、general一般问题查询重写detail与general类查询交由query_rewrite()判断是否需要补充烹饪术语后重写list类保持原样上下文组装_build_context()将去重后的父文档连同dish_name、category、difficulty元数据拼装为上下文字符串并限制在max_length2000字符内见 generation_integration.py分步回答generate_step_by_step_answer()通过 generation_integration.py 中的提示词模板引导 Moonshot 模型输出菜品介绍 / 所需食材 / 制作步骤 / 制作技巧四段式回答同时要求不强行填充无关内容。当用户问椒盐玉米怎么做时系统实际召回的是操作子块精确匹配但生成时送入 LLM 的是完整父文档——椒盐玉米的原料玉米粒 350g、淀粉 40-70g、椒盐粉 10g、芝麻粒 10g、工具簸箕、吸油纸与中火先煎 30s 不翻炒、再轻微翻炒 3 分钟的关键火候全部在场回答自然完整可用。这正是小块检索保证精度、大块生成保证完整性的直观体现详细设计动机可参考 环境配置与项目架构 中关于父子文本块的论述。五、配置项速查让椒盐玉米这类文档跑起来的关键参数config.py 中的RAGConfig数据类集中管理全系统配置下表是与菜谱文档处理直接相关的核心参数配置项默认值作用与影响data_path../../data/C8/cook知识库根目录rglob(*.md)递归扫描的起点embedding_modelBAAI/bge-small-zh-v1.5子块向量化所用嵌入模型llm_modelkimi-k2-0711-preview生成回答所用大模型需配置MOONSHOT_API_KEY环境变量top_k3混合检索最终返回的子块数量temperature0.1生成温度值越低回答越稳定、越忠实于菜谱原文max_tokens2048单次生成的最大 token 数足够覆盖一份完整菜谱的分步指导index_save_path./vector_indexFAISS 索引持久化路径存在时跳过重建实现秒级启动修改data_path指向包含任何结构化 Markdown 菜谱的目录如继续在素菜目录下新增菜谱文件系统会自动完成加载、元数据增强与分块无需改动代码。六、从一份菜谱到一套知识库实践要点小结通过椒盐玉米这一实例可以总结出在 all-in-rag 食谱 RAG 项目中处理结构化文档的三条经验让数据结构说话菜谱文档统一的标题层级#/##/###天然适配 Markdown 结构分块配合strip_headersFalse保留语义上下文是小块精确检索的前提元数据是检索的杠杆分类、菜名、难度这些从路径与内容自动抽取的字段支撑了推荐简单素菜这类带约束查询的元数据过滤极大提升召回精准度检索与生成的粒度可以不同小粒度子块负责命中大粒度父文档负责供给 LLM 完整上下文配合 RRF 混合重排与智能去重最终生成既全面又不重复的烹饪回答。如需深入源码细节可继续阅读 数据准备模块实现父子文本块与元数据增强、索引与检索FAISS/BM25 混合检索与 生成系统提示词与路由设计并对照 椒盐玉米.md 原始文档逐行验证整条流水线的处理结果。【免费下载链接】all-in-rag大模型应用开发实战一RAG 技术全栈指南在线阅读地址https://datawhalechina.github.io/all-in-rag/项目地址: https://gitcode.com/datawhalechina/all-in-rag创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表