你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。

在 Azure AI 搜索 中为 Markdown blob 和文件编制索引

注意

Azure AI 搜索可通过Azure门户、REST API 和Azure SDK获取。 它也是 Foundry IQ 的基础;Foundry IQ 是一个托管式知识层,可将企业内容转化为供 Microsoft Foundry 门户中的智能体使用的、可复用且具备权限感知能力的知识库。

Important

这些特性和功能支持与其他Microsoft 服务和第三方服务的连接。 使用这些服务受其各自的条款的约束,可能会导致数据处理或存储超出Azure符合性边界,以及流入Azure符合性边界的数据。

您有责任管理您的数据是否会流出您组织的合规和地理边界之外及其任何相关影响,并确保已配置适当的权限、边界和审批。

你负责仔细查看和测试在特定用例上下文中生成的应用程序,并做出所有适当的决策和自定义。 这包括实施自己的负责任的 AI 缓解措施,例如元系统、内容筛选器或其他安全系统,并确保应用程序满足适当的质量、可靠性、安全性和可信度标准。 有关详细信息,请参阅 Azure AI 搜索 透明度说明。

在 Azure AI 搜索 中,适用于 Azure Blob 存储、Azure 文件存储 和 Microsoft OneLake 的索引器支持 Markdown 文件的 markdown 分析模式。 Markdown 文件可以通过两种方式编制索引:

  • 一对多解析模式,为每个 Markdown 文件创建多个搜索文档。
  • 一对一解析模式,会为每个 Markdown 文件创建一个搜索文档。

提示

查看本文后,请继续阅读《教程:从 Azure Blob 存储 搜索 Markdown 数据》。

先决条件

Markdown 解析模式参数

创建或更新索引器时,分析模式参数在索引器定义中指定。

POST https://[service name].search.windows.net/indexers?api-version=2026-04-01
Content-Type: application/json
api-key: [admin key]

{
  "name": "my-markdown-indexer",
  "dataSourceName": "my-blob-datasource",
  "targetIndexName": "my-target-index",
  "parameters": {
    "configuration": {
      "parsingMode": "markdown",
      "markdownParsingSubmode": "oneToMany",
      "markdownHeaderDepth": "h6"
    }
  },
}

Blob 索引器提供一个 submode 参数来确定搜索文档的输出结构。 Markdown 分析模式提供以下子模式选项:

解析模式 子模式 搜索文档 描述
markdown oneToMany 每个 Blob 有多个 (默认值)将 Markdown 分解为多个搜索文档,每个文档都表示 Markdown 文件的内容(非头)部分。 可以省略子模式,除非你想要一对一分析。
markdown oneToOne 每个 Blob 一个 将 Markdown 分析为一个搜索文档,其中部分映射到 Markdown 文件中的特定标头。

对于 oneToMany 子模式,你应该查看“为单个 Blob 编制索引以生成多个搜索文档”,以了解 Blob 索引器如何处理从同一 Blob 生成的多个搜索文档的文档键消歧义。

后面的部分更详细地描述了每个子模式。 如果不熟悉索引器客户端和概念,请参阅 创建搜索索引器。 还应熟悉 基本 Blob 索引器配置的详细信息,此处不会重复这些配置。

可选 Markdown 解析参数

参数区分大小写。

参数名称 允许的值 描述
markdownHeaderDepth h1, h2, h3, h4, h5, h6 (default) 此参数确定分析时考虑的最深标头级别,从而允许灵活处理文档结构(例如,设置为时markdownHeaderDepthh1,分析程序仅识别以“#”开头的顶级标头,并且所有较低级别的标头都被视为纯文本)。 如果未指定,则默认为 h6.

创建索引器后,可以更改此设置。 但是,生成的搜索文档的结构可能会根据 Markdown 内容而更改。

支持的 Markdown 元素

Markdown 分析仅基于标头拆分内容。 所有其他元素(如列表、代码块和表)都被视为纯文本并传递到内容字段。

Markdown 内容示例

以下 Markdown 内容用于本页上的示例:

# Section 1
Content for section 1.

## Subsection 1.1
Content for subsection 1.1.

# Section 2
Content for section 2.

使用一对多分析模式

一对多解析模式将 Markdown 文件解析为多个搜索文档,每个文档对应于 Markdown 文件的一个特定内容部分,该内容部分基于文档中的标头元数据。 Markdown 根据标题分析为搜索文档,包含以下内容:

  • content:一个字符串,包含基于文档中该点的标头元数据在特定位置找到的原始 Markdown。

  • sections:一个对象,包含直到所需标头级别的标头元数据的子字段。 例如,当设置为markdownHeaderDepth时h3,包含字符串字段h1,h2以及h3。 这些字段通过在索引中镜像此结构,或通过格式为 /sections/h1、/sections/h2 等的字段映射进行索引。 在以下示例中查阅有关上下文的索引和索引器配置实例。 包含的子字段包括:

    • h1 - 包含 h1 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
    • (可选) h2- 包含 h2 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
    • (可选) h3- 包含 h3 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
    • (可选) h4- 包含 h4 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
    • (可选) h5- 包含 h5 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
    • (可选) h6- 包含 h6 标头值的字符串。 如果在文档中此时未设置,则为空字符串。
  • ordinal_position:一个整数值,指示节在文档层次结构中的位置。 此字段用于根据文档中出现的原始顺序对各节进行排序,从序号1开始,对每个标题依次递增。

用于一对多分析的索引架构

示例索引配置可能如下所示:

{
  "name": "my-markdown-index",
  "fields": [
  {
    "name": "id",
    "type": "Edm.String",
    "key": true
  },
  {
    "name": "content",
    "type": "Edm.String",
  },
  {
    "name": "ordinal_position",
    "type": "Edm.Int32"
  },
  {
    "name": "sections",
    "type": "Edm.ComplexType",
    "fields": [
    {
      "name": "h1",
      "type": "Edm.String"
    },
    {
      "name": "h2",
      "type": "Edm.String"
    }]
  }]
}

一对多分析的索引器定义

如果字段名称和数据类型保持一致,Blob 索引器可以在请求中没有显式字段映射的情况下推断映射,因此与提供的索引配置对应的索引器配置可能如下所示:

POST https://[service name].search.windows.net/indexers?api-version=2026-04-01
Content-Type: application/json
api-key: [admin key]

{
  "name": "my-markdown-indexer",
  "dataSourceName": "my-blob-datasource",
  "targetIndexName": "my-target-index",
  "parameters": {
    "configuration": { "parsingMode": "markdown" }
  },
}

注意

不需要显式设置 submode,因为 oneToMany 是默认值。

用于一对多分析的索引器输出

由于三个内容部分,此 Markdown 文件在编制索引后将导致三个搜索文档。 由提供的 Markdown 文档的第一个内容部分生成的搜索文档将包含以下值content:、sections和h1h2:

{
  {
    "content": "Content for section 1.\r\n",
    "sections": {
      "h1": "Section 1",
      "h2": ""
    },
    "ordinal_position": 1
  },
  {
    "content": "Content for subsection 1.1.\r\n",
    "sections": {
      "h1": "Section 1",
      "h2": "Subsection 1.1"
    },
    "ordinal_position": 2
  },
  {
    "content": "Content for section 2.\r\n",
    "sections": {
      "h1": "Section 2",
      "h2": ""
    },
    "ordinal_position": 3
  }
}

在搜索索引中映射一对多字段

在字段名称和类型不完全相同的情况下,字段映射会将源字段与目标字段相关联。 但是,字段映射还可用于匹配 Markdown 文档的某些部分,并将它们“提升”到搜索文档的顶级字段。

以下示例演示了此方案。 有关字段映射的详细信息,请参阅 字段映射。

假设搜索索引具有以下字段:raw_content类型为Edm.String、h1_header类型为Edm.String以及h2_header类型为Edm.String。 若要将 Markdown 映射到所需形状,请使用以下字段映射:

"fieldMappings" : [
    { "sourceFieldName" : "/content", "targetFieldName" : "raw_content" },
    { "sourceFieldName" : "/sections/h1", "targetFieldName" : "h1_header" },
    { "sourceFieldName" : "/sections/h2", "targetFieldName" : "h2_header" },
  ]

索引中生成的搜索文档如下所示:

{
  {
    "raw_content": "Content for section 1.\r\n",
    "h1_header": "Section 1",
    "h2_header": "",
  },
  {
    "raw_content": "Content for section 1.1.\r\n",
    "h1_header": "Section 1",
    "h2_header": "Subsection 1.1",
  },
  {
    "raw_content": "Content for section 2.\r\n",
    "h1_header": "Section 2",
    "h2_header": "",
  }
}

使用一对一分析模式

在一对一分析模式下,整个 Markdown 文档索引为单个搜索文档,保留原始内容的层次结构和结构。 当要编制索引的文件共享通用结构时,此模式最有用,因此可以在索引中使用此通用结构使相关字段可搜索。

在索引器定义中,将 parsingMode 设置为 "markdown",并使用可选的 markdownHeaderDepth 参数来定义分块的最大标题深度。 如果未指定,它将默认设置为 h6,以捕获所有可能的标头深度。

Markdown 根据标题分析为搜索文档,包含以下内容:

  • document_content:包含完整 Markdown 文本作为单个字符串。 此字段充当输入文档的原始表示形式。

  • sections:一个对象数组,其中包含 Markdown 文档中各节的分层表示形式。 在这个数组中,每个节都作为一个对象来表示,并以嵌套的方式捕获与标头及其相应内容对应的文档结构。 这些字段可以通过引用路径(例如 /sections/content)通过字段映射访问。 此数组中的对象具有以下属性:

    • header_level:一个字符串,指示 Markdown 语法中标头(h1、h2h3等)的级别。 此字段有助于了解内容的层次结构和结构。

    • header_name:一个字符串,其中包含在 Markdown 文档中出现的标题文本。 此字段提供分区的标签或标题。

    • content:一个字符串,包含从当前标头到下一个标头之间的文本内容。 此字段捕获与标头关联的详细信息或说明。 如果没有直接在标头下的内容,则值为空字符串。

    • ordinal_position:一个整数值,指示节在文档层次结构中的位置。 此字段用于在文档中显示的原始序列中对节进行排序,从序号位置 1 开始,并按顺序递增每个内容块。

    • sections:一个数组,包含表示嵌套在当前节下的子节的对象。 此数组遵循与顶级 sections 数组相同的结构,允许表示多个级别的嵌套内容。 每个子节对象还包括header_level、header_namecontent属性和ordinal_position属性,从而启用表示 Markdown 内容的层次结构的递归结构。

下面是用于解释围绕每个分析模式设计的索引架构的示例 Markdown。

# Section 1
Content for section 1.

## Subsection 1.1
Content for subsection 1.1.

# Section 2
Content for section 2.

用于一对一分析的索引架构

如果不使用字段映射,索引的形状应反映 Markdown 内容的形状。 鉴于示例 Markdown 的结构及其两个部分和单个子节,索引应类似于以下示例:

{
  "name": "my-markdown-index",
  "fields": [
  {
    "name": "id",
    "type": "Edm.String",
    "key": true
  },
  {
    "name": "document_content",
    "type": "Edm.String"
  },
  {
    "name": "sections",
    "type": "Collection(Edm.ComplexType)",
    "fields": [
    {
      "name": "header_level",
      "type": "Edm.String"
    },
    {
      "name": "header_name",
      "type": "Edm.String"
    },
    {
      "name": "content",
      "type": "Edm.String"
    },
    {
      "name": "ordinal_position",
      "type": "Edm.Int32"
    },
    {
      "name": "sections",
      "type": "Collection(Edm.ComplexType)",
      "fields": [
      {
        "name": "header_level",
        "type": "Edm.String"
      },
      {
        "name": "header_name",
        "type": "Edm.String"
      },
      {
        "name": "content",
        "type": "Edm.String"
      },
      {
        "name": "ordinal_position",
        "type": "Edm.Int32"
      }]
    }]
  }]
}

一对一分析的索引器定义

POST https://[service name].search.windows.net/indexers?api-version=2026-04-01
Content-Type: application/json
api-key: [admin key]

{
  "name": "my-markdown-indexer",
  "dataSourceName": "my-blob-datasource",
  "targetIndexName": "my-target-index",
  "parameters": {
    "configuration": {
      "parsingMode": "markdown",
      "markdownParsingSubmode": "oneToOne",
    }
  }
}

一对一分析的索引器输出

因为我们想要索引的 Markdown 仅深入到 h2 级别 ("##"),所以我们需要嵌套到深度 2 的 sections 字段来匹配它。 此配置将导致索引中的以下数据:

  "document_content": "# Section 1\r\nContent for section 1.\r\n## Subsection 1.1\r\nContent for subsection 1.1.\r\n# Section 2\r\nContent for section 2.\r\n",
  "sections": [
    {
      "header_level": "h1",
      "header_name": "Section 1",
      "content": "Content for section 1.",
      "ordinal_position": 1,
      "sections": [
        {
          "header_level": "h2",
          "header_name": "Subsection 1.1",
          "content": "Content for subsection 1.1.",
          "ordinal_position": 2,
        }]
    }],
    {
      "header_level": "h1",
      "header_name": "Section 2",
      "content": "Content for section 2.",
      "ordinal_position": 3,
      "sections": []
    }]
  }

如你所见,序号位置根据内容在文档中的位置递增。

如果内容中跳过了标头级别,生成的文档结构会反映 Markdown 内容中存在的标头,不一定包含从 h1 到 h6 的连续嵌套部分。 例如,当文档开始 h2时,顶级节数组中的第一个元素是 h2。

在搜索索引中映射一对一字段

若要从文档中提取具有自定义名称的字段,可以使用字段映射。 使用与之前相同的 Markdown 示例,请考虑以下索引配置:

{
  "name": "my-markdown-index",
  "fields": [
    {
      "name": "document_content",
      "type": "Edm.String",
    },
    {
      "name": "document_title",
      "type": "Edm.String",
    },
    {
      "name": "opening_subsection_title",
      "type": "Edm.String"
    },
    {
      "name": "summary_content",
      "type": "Edm.String",
    }
  ]
}

从解析出的 Markdown 中提取特定字段的方式与在 outputFieldMappings 中处理文档路径的方式相似,只不过路径以 /sections 开头,而不是 /document。 例如, /sections/0/content 将映射到节数组中位置为 0 的项下的内容。

一个强用例的示例可能如下所示:所有 Markdown 文件在第一个 h1 中有一个文档标题,在第一个 h2 中有一个子节标题,并在最后一个 h1 下面的最后一段内容中有一个摘要。 可以使用以下字段映射来仅为该内容编制索引:

"fieldMappings" : [
  { "sourceFieldName" : "/content", "targetFieldName" : "raw_content" },
  { "sourceFieldName" : "/sections/0/header_name", "targetFieldName" : "document_title" },
  { "sourceFieldName" : "/sections/0/sections/header_name", "targetFieldName" : "opening_subsection_title" },
  { "sourceFieldName" : "/sections/1/content", "targetFieldName" : "summary_content" },
]

在这里,你只会从该文档中提取相关部分。 为了更有效地使用此功能,你计划编制索引的文档应共享相同的分层标头结构。

索引中生成的搜索文档如下所示:

{
  "content": "Content for section 1.\r\n",
  "document_title": "Section 1",
  "opening_subsection_title": "Subsection 1.1",
  "summary_content": "Content for section 2."
}

注意

这些示例明确说明了如何在完全有或没有字段映射的情况下使用这些解析模式,但如果符合需求,也可以在一个场景中同时应用这两种模式。

在 Markdown 重新编制索引过程中管理过时的文档

当使用一对多解析模式时,如果删除其中的某些部分,重新索引已修改的 Markdown 文件可能会导致文档过时或重复。 此行为特定于一对多模式,不适用于一对一分析。

行为概述

一对多分析模式

在 oneToMany 模式下,每个 Markdown 节(基于标题)都作为单独的搜索文档编制索引。 重新编制文件索引时:

  • 无自动删除:索引器使用新文档覆盖现有文档,但不会删除不再对应于更新文件中的任何内容的文档。
  • 潜在的重复项:此问题仅在索引运行之间删除的部分多于插入的部分时才会发生。 在这种情况下,旧版本中的剩余文档仍保留在索引中,导致不再反映源文件的当前状态的过时条目。

一对一分析模式

在 oneToOne 模式下,整个 Markdown 文件作为单个搜索文档编制索引。 重新编制文件索引时:

  • 覆盖行为:现有文档将完全替换为新版本。
  • 没有过时的部分:重新编制文件索引后,现有文档将替换为更新的版本,并且不再包含已删除的内容。 唯一的例外是文件路径或 Blob URI 发生更改,这可能会导致新文档与旧文档一起创建。

解决方法选项

若要确保索引反映 Markdown 文件的当前状态,请考虑以下方法之一:

选项 1. 使用元数据进行软删除

此方法使用软删除来删除与特定 Blob 关联的文档。 有关详细信息,请参阅 在 Azure AI 搜索 中使用索引器对 Azure 存储 进行变更和删除检测。

步骤:

  1. 通过设置元数据字段将 Blob 标记为已删除。
  2. 让索引器运行。 它会删除与该 Blob 关联的索引中的所有文档。
  3. 删除软删除标记并重新为文件编制索引。

选项 2. 使用删除API

在重新为修改后的 Markdown 文件编制索引之前,请使用 删除 API 显式删除与该文件关联的现有文档。 您可以选择以下任一选项:

  • 通过标识要删除的索引中的重复项,手动标识各个过时文档。 对于小规模且易于理解的更改,这可能是可行的,但可能很耗时。
  • (推荐)在重新编制索引之前删除从同一父文件生成的所有文档,确保避免不一致。

步骤:

  1. 标识与文件关联的文档的 ID。 使用类似于以下示例的查询检索绑定到特定文件的所有文档的文档密钥 ID(例如或 idchunk_id)。 将 metadata_storage_path 替换为索引中映射到文件路径或 Blob URI 的相应字段。 此字段必须是键。

    GET https://[service name].search.windows.net/indexes/[index name]/docs?api-version=2026-04-01
    Content-Type: application/json
    api-key: [admin key]
    
    
      {
          "filter": "metadata_storage_path eq 'https://<storage-account>.blob.core.windows.net/<container-name>/<file-name>.md'",
          "select": "id"
      }
    
  2. 对具有已标识密钥的文档发出删除请求。

    POST https://[service name].search.windows.net/indexes/[index name]/docs/index?api-version=2026-04-01
    Content-Type: application/json
    api-key: [admin key]
    
    {
      "value": [
        {
          "@search.action": "delete",
          "id": "aHR0c...jI1"
        },
        {
          "@search.action": "delete",
          "id": "aHR0...MQ2"
        }
      ]
    }
    
  3. 重新为更新的文件编制索引。

后续步骤