79.高级RAG-md文档元素分割器

内容参考于:图灵AI大模型全栈

Markdown元素处理器

元素处理器它把文档看做成元素的集合,会抽取表格、代码这种特殊内容,然后进行分割,也只把这种特殊内容进行索引生成

之前51节中也写了Markdown文档处理,它的处理是通过标题来进行分割,这次的元素分割器是按照特殊内容(表格、代码)来分割

也就是说这次的元素处理器,它是专门用来处理表格的,它会把表格专门抽取出来

如下效果图:

分割逻辑

它处理的逻辑是把表格取出来进行拆分,如下图红框 标题三 是一个标题才对,它直接分割到文本中了

如下图红框,表格的metadata信息,它还会给表格写总结

如下图红框,拆分后的文本,所以它并没有按照标题来分割,它是按照标签也就是表格分割,按照表格分割完,然后再使用之前的句子分割

它是先把表格独立出来然后再进行分割,如下图红框独立出来的表格是一个IndexNode(表格标签)

内容

json 复制代码
{
// 当前索引节点的唯一 ID(UUID)
"id_": "c57188c9-2c3b-48c2-ae0e-890dd99af1ab",

// 该节点的向量嵌入,null 表示尚未生成;后续可由嵌入模型填充
"embedding": null,

// ========== metadata: 节点携带的元数据 ==========
"metadata": {
// 原始文件路径(Windows 风格)
"file_path": "data_file\\test.md",
// 文件名
"file_name": "test.md",
// 文件 MIME 类型
"file_type": "text/markdown",
// 文件大小(字节)
"file_size": 5270,
// 文件创建日期(可能来自系统)
"creation_date": "2026-07-24",
// 文件最后修改日期
"last_modified_date": "2026-07-10",
// 列模式,这里为空字符串
"col_schema": "",

// *** 以下两个字段是该节点特有的,来自表格解析结果 ***

// table_df: 表格数据的 pandas DataFrame 的字典表示。
// 包含 ' 姓名 '、' 年龄 '、' 工作岗位 ' 三列,以及一列未命名的空列(Unnamed: 3,全为 NaN)。
// 注意:这里的姓名和年龄可能是虚构/示例数据(例如 888 岁)。
"table_df": "{' 姓名 ': {0: ' 张三 ', 1: ' 李四 ', 2: ' 王五 '}, ' 年龄 ': {0: 888, 1: 999, 2: 777}, ' 工作岗位 ': {0: ' 魔法开发 ', 1: ' 物理开发 ', 2: ' 科技开发 '}, 'Unnamed: 3': {0: nan, 1: nan, 2: nan}}",

// table_summary: 对表格内容的自然语言摘要,也是这个索引节点的文本表示(text 字段也用到了它)。
// 结尾的 ",\nwith the following columns:\n" 看起来是拼接痕迹,实际摘要就是前面的句子。
"table_summary": "包含张三(888岁,魔法开发)、李四(999岁,物理开发)、王五(777岁,科技开发)的姓名、年龄与工作岗位信息。,\nwith the following columns:\n"
},

// ========== 元数据过滤策略 ==========
// 生成向量嵌入时,排除这些元数据键(不参与 embedding 计算)
"excluded_embed_metadata_keys": [
"file_name",
"file_type",
"file_size",
"creation_date",
"last_modified_date",
"last_accessed_date"
],
// 发送给 LLM 时,排除这些元数据键(避免无关信息干扰大模型)
"excluded_llm_metadata_keys": [
"file_name",
"file_type",
"file_size",
"creation_date",
"last_modified_date",
"last_accessed_date"
],

// ========== relationships: 与其他节点的关系图 ==========
"relationships": {
// 关系 1:指向源文档节点(node_type = 4,通常代表 DOCUMENT 类型)
"1": {
 "node_id": "4f18172a-5760-47e9-8124-636ff1707dfa",  // 源文档节点的 ID
 "node_type": "4",                                   // 节点类型:4 一般对应 Document/根节点
 "metadata": { /* 与上面相同的基础文件元数据 */ },
 "hash": "29dc8e51...",  // 该文档节点的内容哈希,用于去重/比较
 "class_name": "RelatedNodeInfo"
},

// 关系 2:指向同级或相关的另一个文本节点(node_type = 1,TEXT 类型)
"2": {
 "node_id": "d5523ab1-af34-46aa-9344-5eedf34fcda7",
 "node_type": "1",
 "metadata": { /* 基础文件元数据 */ },
 "hash": "544aed85...",
 "class_name": "RelatedNodeInfo"
},

// 关系 3:指向该索引节点所对应的"详细内容文本节点"(也是 TEXT 类型)。
// 注意:这个文本节点包含了实际的表格数据(table_df)和摘要,而当前索引节点只保存摘要文本。
"3": {
 "node_id": "3174a7eb-f781-4682-a4a5-221a7d201695",  // 与当前节点的 id_ 和 index_id 相同
 "node_type": "1",                                   // 文本节点
 "metadata": {
   // 详细的表格 DataFrame 字符串
   "table_df": "{' 姓名 ': {0: ' 张三 ', 1: ' 李四 ', 2: ' 王五 '}, ' 年龄 ': {0: 888, 1: 999, 2: 777}, ' 工作岗位 ': {0: ' 魔法开发 ', 1: ' 物理开发 ', 2: ' 科技开发 '}, 'Unnamed: 3': {0: nan, 1: nan, 2: nan}}",
   // 表格摘要
   "table_summary": "包含张三(888岁,魔法开发)、李四(999岁,物理开发)、王五(777岁,科技开发)的姓名、年龄与工作岗位信息。,\nwith the following columns:\n",
   /* 基础文件元数据 */
 },
 "hash": "5a243c2b...",
 "class_name": "RelatedNodeInfo"
}
},

// 元数据模板:用于格式化元数据字符串,如 "{key}: {value}",换行分隔
"metadata_template": "{key}: {value}",
"metadata_separator": "\n",

// ========== 文本内容:该索引节点的核心文本,即表格摘要 ==========
"text": "包含张三(888岁,魔法开发)、李四(999岁,物理开发)、王五(777岁,科技开发)的姓名、年龄与工作岗位信息。,\nwith the following columns:\n",

// MIME 类型
"mimetype": "text/plain",

// 该节点文本在原文档中的起始字符位置
"start_char_idx": 11,
// 结束字符位置
"end_char_idx": 124,

// 文本模板:用于将元数据与正文拼在一起的格式(如 "{metadata_str}\n\n{content}")
"text_template": "{metadata_str}\n\n{content}",

// 该索引节点所指向的详细内容节点的 ID,等同于关系 3 中的 node_id。
// 检索时通过这个 id 可以获取到包含表格真实数据的数据节点。
"index_id": "3174a7eb-f781-4682-a4a5-221a7d201695",

// 预留的对象引用,当前为空
"obj": null,

// 类名,表明这是一个 IndexNode
"class_name": "IndexNode"
}

上方的json可以理解是针对表格做的一个简介,通过3找到的下一节点才是它真正的表格数据,如下真正的表格数据

python 复制代码
{
  // 当前文本节点的唯一 ID。此 ID 被上游的 IndexNode.index_id 所引用。
  "id_": "3174a7eb-f781-4682-a4a5-221a7d201695",
  
  // 向量嵌入,null 表示待生成。
  "embedding": null,
  
  // ========== metadata: 节点携带的元数据 ==========
  "metadata": {
    // 原始文件路径
    "file_path": "data_file\\test.md",
    // 文件名
    "file_name": "test.md",
    // MIME 类型
    "file_type": "text/markdown",
    // 文件大小(字节)
    "file_size": 5270,
    // 文件创建日期
    "creation_date": "2026-07-24",
    // 最后修改日期
    "last_modified_date": "2026-07-10",
    
    // *** 与 IndexNode 相比,这里没有 col_schema 字段,而是直接包含表格数据 ***
    
    // table_df: 表格数据的 pandas DataFrame 的字典表示。
    // 包含 ' 姓名 '、' 年龄 '、' 工作岗位 ' 以及一个无用的 'Unnamed: 3' 列(全为 NaN)。
    "table_df": "{' 姓名 ': {0: ' 张三 ', 1: ' 李四 ', 2: ' 王五 '}, ' 年龄 ': {0: 888, 1: 999, 2: 777}, ' 工作岗位 ': {0: ' 魔法开发 ', 1: ' 物理开发 ', 2: ' 科技开发 '}, 'Unnamed: 3': {0: nan, 1: nan, 2: nan}}",
    
    // table_summary: 表格内容的自然语言摘要,与 IndexNode 中的完全一致。
    "table_summary": "包含张三(888岁,魔法开发)、李四(999岁,物理开发)、王五(777岁,科技开发)的姓名、年龄与工作岗位信息。,\nwith the following columns:\n"
  },
  
  // ========== 元数据过滤策略 ==========
  // 生成向量嵌入时,排除以下元数据键(不参与 embedding 计算)
  "excluded_embed_metadata_keys": [
    "file_name",
    "file_type",
    "file_size",
    "creation_date",
    "last_modified_date",
    "last_accessed_date"
  ],
  // 发送给 LLM 时,排除以下元数据键
  "excluded_llm_metadata_keys": [
    "file_name",
    "file_type",
    "file_size",
    "creation_date",
    "last_modified_date",
    "last_accessed_date"
  ],
  
  // ========== relationships: 与其他节点的关系图 ==========
  "relationships": {
    // 关系 1:指向源文档节点(node_type = 4,DOCUMENT 类型)
    "1": {
      "node_id": "4f18172a-5760-47e9-8124-636ff1707dfa",  // 根文档节点 ID
      "node_type": "4",                                   // 文档节点
      "metadata": {
        "file_path": "data_file\\test.md",
        "file_name": "test.md",
        "file_type": "text/markdown",
        "file_size": 5270,
        "creation_date": "2026-07-24",
        "last_modified_date": "2026-07-10"
      },
      "hash": "29dc8e51bfabcb621c1c66cbd429e9d62d2419531d530428b0d10a8750e30488",
      "class_name": "RelatedNodeInfo"
    },
    
    // 关系 2:指向配对的索引节点(node_type = 3,INDEX 类型)
    "2": {
      "node_id": "c57188c9-2c3b-48c2-ae0e-890dd99af1ab",  // 这就是上一个 IndexNode 的 id
      "node_type": "3",                                   // 索引节点
      "metadata": {
        "file_path": "data_file\\test.md",
        "file_name": "test.md",
        "file_type": "text/markdown",
        "file_size": 5270,
        "creation_date": "2026-07-24",
        "last_modified_date": "2026-07-10",
        "col_schema": ""                                  // 索引节点才有 col_schema
      },
      "hash": "8be5eb07ec22a359bc244395b52b68e25645260b67468e4579f0c5e7d3002d2a",
      "class_name": "RelatedNodeInfo"
    },
    
    // 关系 3:指向另一个索引节点(可能是同一表格的另一个处理版本,或另一表格)
    "3": {
      "node_id": "20996213-921e-4578-b778-8cfb269b9a92",
      "node_type": "3",
      "metadata": {
        "col_schema": "",
        "file_path": "data_file\\test.md",
        "file_name": "test.md",
        "file_type": "text/markdown",
        "file_size": 5270,
        "creation_date": "2026-07-24",
        "last_modified_date": "2026-07-10"
      },
      "hash": "a9aa21cec2f10a73bf06d5b417f7fce283ab86a874ac5332c8b00872ad84b2f3",
      "class_name": "RelatedNodeInfo"
    }
  },
  
  // 元数据展示模板,格式为 "key: value",换行分隔
  "metadata_template": "{key}: {value}",
  "metadata_separator": "\n",
  
  // ========== 文本内容:表格的 Markdown 表示 + 摘要 ==========
  // 这里是实际提供给检索和 LLM 的完整文本,包含表格的 Markdown 源码。
  "text": "包含张三(888岁,魔法开发)、李四(999岁,物理开发)、王五(777岁,科技开发)的姓名、年龄与工作岗位信息。,\nwith the following columns:\n\n| 姓名 | 年龄 | 工作岗位 |Unnamed: 3|\n|---|---|---|---|\n| 张三 |888| 魔法开发 |nan|\n| 李四 |999| 物理开发 |nan|\n| 王五 |777| 科技开发 |nan|\n",
  
  // MIME 类型
  "mimetype": "text/plain",
  
  // 原文档中该表格的起始字符位置(与 IndexNode 一致)
  "start_char_idx": 11,
  // 结束字符位置
  "end_char_idx": 124,
  
  // 文本模板:将元数据和正文拼接的格式
  "text_template": "{metadata_str}\n\n{content}",
  
  // 注意:TextNode 没有 index_id 字段,因为它本身是"详细数据"的终点。
  // 但 IndexNode 通过 index_id 指向了本节点的 id。
  
  // 类名:标识这是一个 TextNode
  "class_name": "TextNode"
}

上方的表格信息的样子都是通过提示词来实现的,如下图红框,我们的提示词是让大模型把Markdown文档中的表格和内容做极简的摘要

使用LLamaIndex提供的Markdown元素分割器的缺点就是它丢失了标题,它只关注表格了,不关注标题,所以我们需要考虑如何处理这个标题,可以在进行元素分割之前,先使用标题分割一次,把标题的内容放到元数据中,下一节会处理这个问题


相关推荐
xian_wwq2 小时前
【学习笔记】RAG 只是 Context Engineering 的一小块-5/16
笔记·学习·rag
染指11106 小时前
80.高级RAG-LLamaIndex实际应用-金融助手
人工智能·rag·llama_index·llamaindex
阿图灵6 小时前
Agentic AI 架构入门(三):Agent 的七大组件与 PRAL 循环
人工智能·架构·llm·rag·ai agent·智能体·agentic ai
VipSoft7 小时前
LangChain — RAG 知识库(实操)
langchain·rag
染指11102 天前
76.高级RAG-后检索器(时间排序)
人工智能·python·llama_index·llamaindex
Tbisnic2 天前
LangChain的 六大核心组件与 RAG 知识库构建
人工智能·python·ai·langchain·rag·langgraph
赵大仁2 天前
浏览器端 RAG:Transformers.js + WebGPU 本地检索入门
前端·ai·react·webgpu·rag
VipSoft3 天前
LangChain — RAG 构建知识库(理论)
rag
梦想三三3 天前
Qwen Function Calling实战:重构电商客服AI Agent
人工智能·python·langchain·大模型·rag