news 2026/9/24 3:45:08

all-in-rag 食谱知识库实战:以一份简易红烧肉菜谱为例的数据准备全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
all-in-rag 食谱知识库实战:以一份简易红烧肉菜谱为例的数据准备全流程解析
  • 教程
  • 人工智能
  • 大模型
  • RAG

【免费下载链接】all-in-rag

🔍大模型应用开发实战一:RAG 技术全栈指南,在线阅读地址:https://datawhalechina.github.io/all-in-rag/

项目地址:https://gitcode.com/datawhalechina/all-in-rag
点击查看免费下载

在 Datawhale 的 all-in-rag 项目中,第 8 章基于 HowToCook 风格的菜谱数据构建了一个名为"尝尝咸淡"的食谱问答 RAG 系统,而data/C8/cook/dishes/meat_dish/红烧肉/简易红烧肉.md正是该系统知识库中的一份典型数据样本。本篇文章以这份菜谱为绝对主体,先完整讲解其菜谱内容,再结合仓库源码(data_preparation.py 等)深入剖析它从一份普通 Markdown 文件,一步步转化为可精确检索、可完整生成的知识单元的全过程。读完你将掌握:结构化菜谱文档如何被加载与元数据增强、如何按 Markdown 标题层级分块、父子文本块架构如何保证"小块检索、大块生成",以及该系统如何运行验证。

一、这份菜谱文档的完整内容

简易红烧肉.md是知识库中"荤菜"分类下的一份菜谱,全文采用规范的 Markdown 结构:一级标题为菜品名,正文包含简介与难度评级,随后是"必备原料和工具"、"计算"、"操作"、"附加内容"四个二级章节。这种高度规整的结构,正是第 8 章数据准备模块能够直接按标题分块的前提(详见 02_data_preparation.md)。

1.1 菜品简介与难度

这份红烧肉教程是一道新手不败的菜谱。配着米饭好吃的停不下来,香糯无敌棒色泽诱人肥而不腻。建议搭配米饭食用。

文档中标注"预估烹饪难度:★★★"。这一星级标记并非普通文本,它会被数据准备模块的正则逻辑自动解析为结构化的难度元数据(详见下文 2.2 节)。

1.2 必备原料和工具

类别原料/工具
主料大肉、鸡蛋(可选)、豆皮(可选)
辅料生姜、冰糖、生抽、老抽、料酒、香叶、八角、盐、水、葱(记得要开水)
工具刀(原文档特别提示:如果有可能,请尽量把刀磨得锋利一些)

1.3 计算(用量配比)

原文档要求"每次制作前需要确定计划做几份",一份正好够 2~3 人吃;如果只有 1 人食用,可以考虑食材减半。具体用量如下:

  • 猪五花肉:约 3~4 斤
  • 姜:6 片
  • 冰糖:15 克(约 7 块)
  • 生抽:10ml
  • 老抽:15ml
  • 料酒:5ml
  • 开水:没过食材的量,需要 600ml~900ml
  • 香叶:3 片
  • 八角:2 个
  • 鹌鹑蛋(可选,没有鹌鹑蛋可以用同等重量的鸡蛋代替):0~2 个
  • 豆皮(可选):0~80g
  • 盐:2~3g

1.4 操作步骤

原材料准备:

  • 猪五花肉切大块(约 4.5cm,冷冻半小时至一小时更好切)
  • 豆皮切 2cm 的宽度
  • 生姜切片(每片厚度约 3mm)
  • 水烧开
  • 鹌鹑蛋煮熟并用叉子/牙签扎孔(尽量多些好入味)
  • 大葱取白色的部分(葱白)

开始制作:

  1. 冷水锅中放入切好的猪五花肉,加入料酒与葱姜,煮 15 分钟去掉血腥;
  2. 锅中放入两片生姜提味;
  3. 开中小火后直接加入五花肉,不需要放入食用油,每块五花肉六个面都煎一下,煎至出油即可;
  4. 将煎出的油倒出备用,并将五花肉推至一边,加入 15g 冰糖,翻炒至冰糖融化;
  5. 融化后将五花肉与冰糖炒至融合上色,加入生抽 10ml、老抽 15ml、料酒 5ml,翻炒至上色;
  6. 加入烧好的开水炖煮 40 分钟(刀工差的同学切的过大请自觉延长炖煮时间),并放入生姜 2 片、香叶 3 片、八角 2 个;
  7. 盖上锅盖煮至沸腾后,加入煮好扎好孔的鹌鹑蛋和豆皮,开中小火,等待 40 分钟(中途可适当翻搅防止粘锅);
  8. 打开锅盖,待汤汁快没有的时候开大火收汁(切记不可收干);
  9. 加入 2~3g 盐,翻炒一下,就可以出锅了。

原文档末尾的"附加内容"章节为空,这并不影响其作为知识库样本的价值——生成模块的提示词设计(见 generation_integration.py)明确要求"如果原文的附加内容与烹饪无关或为空,可以基于制作步骤总结关键要点,或者完全省略此部分",保证回答不会强行填充无关内容。

二、这份菜谱在 RAG 系统中的定位

2.1 数据源路径与目录约定

系统的数据根路径在 config.py 中配置为../../data/C8/cook(相对于code/C8目录),而这份红烧肉菜谱恰好位于该路径下的dishes/meat_dish/红烧肉/子目录。meat_dish这个目录名不是随意取的,它是元数据增强时推断菜品分类的关键依据。

2.2 加载与元数据增强:一份菜谱如何获得"标签"

在 data_preparation.py 中,load_documents()使用Path.rglob("*.md")递归扫描数据目录下的所有 Markdown 文件,读取原始内容并创建Document对象。每个父文档的parent_id由相对路径经 MD5 哈希得到(确定性 ID,重复构建不产生新 ID),doc_type标记为"parent"

随后_enhance_metadata()为每份菜谱补充三类核心元数据(data_preparation.py):

  • 菜品分类(category):通过CATEGORY_MAPPING字典在文件路径中匹配目录名,meat_dish → 荤菜vegetable_dish → 素菜soup → 汤品等九类。因此简易红烧肉.md会被自动打上荤菜标签;
  • 菜品名称(dish_name):直接取文件名(不含扩展名)file_path.stem,即"简易红烧肉";
  • 难度等级(difficulty):使用正则re.search(r'★+', content)精确匹配连续星号数量,再经映射表{5: '非常困难', 4: '困难', 3: '中等', 2: '简单', 1: '非常简单'}转换。本菜谱的三颗星会被解析为中等

这些元数据不仅用于展示,还直接服务于检索阶段的过滤能力:main.py_extract_filters_from_query()会从用户问题中抽取分类与难度关键词(如"简单的荤菜"),命中后调用metadata_filtered_search()缩小检索范围(见 main.py)。

2.3 Markdown 结构感知分块

分块逻辑位于chunk_documents()_markdown_header_split()(data_preparation.py),它使用 LangChain 的MarkdownHeaderTextSplitter,按三级标题进行分割:

headers_to_split_on = [ ("#", "主标题"), # 菜品名称 ("##", "二级标题"), # 必备原料、计算、操作等 ("###", "三级标题") # 简易版本、复杂版本等 ] markdown_splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on, strip_headers=False # 保留标题,便于理解上下文 )

对于本菜谱,分块结果如下(对应 02_data_preparation.md 中"分块效果示例"描述的父子文本块映射):

父文档:简易红烧肉.md(完整菜谱) ├── 子块1:# 简易红烧肉的做法 + 简介 + 难度评级 ├── 子块2:## 必备原料和工具 + 食材清单 ├── 子块3:## 计算 + 用量配比 ├── 子块4:## 操作 + 详细制作步骤 └── 子块5:## 附加内容(空章节)

每个子块都会继承父文档的全部元数据,并额外获得chunk_id(UUID)、doc_type="child"chunk_index(在父文档中的位置)、batch_indexchunk_size;同时self.parent_child_map[child_id] = parent_id建立起子块到父文档的映射关系。strip_headers=False意味着标题文本被保留在子块内容中,便于 LLM 理解上下文。

2.4 父子文本块:为什么"小块检索、大块生成"

这是本项目数据准备设计的核心思想。如果只问"红烧肉需要什么食材",整份菜谱文档中只有"必备原料和工具"和"计算"两个章节与该问题高度相关,直接向量化整篇文档会导致该部分被稀释、排名靠后;而按标题切成小块后,子块 2/3 能精确命中。

但反过来,如果只把命中的小块喂给 LLM,又会丢失完整上下文(如缺少操作步骤)。因此检索阶段使用小块精确匹配,生成阶段则通过get_parent_documents()(data_preparation.py)把命中的多个子块聚合回完整父文档再传给 LLM:

  1. 统计每个父文档被匹配的子块数量(相关性指标);
  2. 按命中次数降序排序,命中子块越多的菜谱排名越靠前;
  3. 每个父文档只输出一次,实现智能去重,避免同一道菜被重复拼接。

这也正是"用户问宫保鸡丁怎么做时,可能同时检索到操作、原料等多个子块,最终合并为一个完整菜谱"的去重机制。

三、从分块到检索与生成:整条调用链

这份菜谱生成的子块会继续流入后续模块,形成完整的 RAG 链路:

  1. 索引构建IndexConstructionModule使用BAAI/bge-small-zh-v1.5嵌入模型(CPU 推理、向量归一化)将子块向量化,构建 FAISS 向量索引并持久化到./vector_index(见 index_construction.py)。第二次启动时可直接加载已保存索引,实现秒级启动。
  2. 混合检索与 RRF 重排RetrievalOptimizationModule同时使用向量检索(语义)与 BM25 检索(关键词),再用 RRF(Reciprocal Rank Fusion)公式1/(k+rank+1)(k=60)融合两个榜单的排名(见 retrieval_optimization.py),最终按config.top_k=3截取结果。
  3. 查询路由与重写GenerationIntegrationModule先将问题路由为list(推荐列表)/detail(详细做法)/general(一般问题)三类;detail类走分步指导提示词,general类走基础回答提示词,均支持流式输出(见 generation_integration.py)。
  4. 生成:默认使用kimi-k2-0711-preview模型(通过 MoonshotChat 接入,需设置MOONSHOT_API_KEY环境变量),temperature=0.1max_tokens=2048(见 config.py)。上下文构建器_build_context()会把每个父文档的dish_namecategorydifficulty元数据连同全文一起格式化后送入提示词。

例如用户问"红烧肉怎么做",系统会命中该菜谱的多个子块,去重聚合回完整父文档,最终以"菜品介绍 → 所需食材 → 制作步骤 → 制作技巧"的分步结构输出回答。

四、运行与验证

按第 8 章环境配置(01_env_architecture.md)操作:

conda create -n cook-rag-1 python=3.12.7 conda activate cook-rag-1 cd code/C8 pip install -r requirements.txt export MOONSHOT_API_KEY=你的API密钥 # 或在 .env 中配置 python main.py

依赖清单见 requirements.txt,核心包括langchain==0.3.26langchain-community==0.3.27langchain-huggingface==0.3.1faiss-cpurank_bm25等。程序启动后会先构建/加载知识库并打印统计信息(文档总数、文本块数、分类与难度分布),随后进入交互式问答,输入"红烧肉需要什么食材""推荐几个简单的荤菜"等即可验证本菜谱在系统中的检索与生成效果。

小结

一份看似普通的简易红烧肉.md,在 all-in-rag 第 8 章的数据准备模块中经历了"递归加载 → 元数据增强 → 三级标题分块 → 父子关系建立 → 智能去重"的完整流水线:目录名meat_dish变成荤菜标签、三颗星变成中等难度、四个章节变成五个可精确检索的子块,最终在混合检索与 RRF 重排的配合下,为"小块检索、大块生成"的问答体验提供了坚实的数据底座。理解这一过程,你就掌握了如何把任何高度结构化的 Markdown 语料,转化为高质量 RAG 知识库的通用方法论。

  • 教程
  • 人工智能
  • 大模型
  • RAG

【免费下载链接】all-in-rag

🔍大模型应用开发实战一:RAG 技术全栈指南,在线阅读地址:https://datawhalechina.github.io/all-in-rag/

项目地址:https://gitcode.com/datawhalechina/all-in-rag
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 3:39:35

DC-DC电源纹波与噪声测量:示波器接地方式决定测试结果可信度

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 3:33:18

OpenAI、Anthropic同日模型大战,“是兄弟就砍一刀”

刀刀见骨,AI巨头为自己画过的大饼“填窟窿”文|魏琳华编|刘俊宏9月23日凌晨,OpenAI和Anthropic像约好了一样,前后脚各自放出新模型:OpenAI端出了GPT-6 Sol和GPT-6 Luna两款模型,把旗舰GPT-6 Ast…

作者头像 李华
网站建设 2026/9/24 3:32:25

AirPods Pro在Win11延迟高?五种实测方案从280ms降到75ms

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 3:30:14

PADS Layout模块复用实战:从网络继承到EMC合规的工程化流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华