在 Azure AI 搜尋服務 中為 Markdown blob 和檔案建立索引

註

Azure AI 搜尋服務 可透過 Azure 入口網站、REST API 及 Azure SDK 取得。 它同時也是 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資料。

先決條件

  • 一個支援的資料來源:Azure Blob 儲存體、Azure File Storage、Microsoft OneLake。

    對於 OneLake,務必符合 OneLake 索引器的所有要求。

    Azure 儲存體 for blob indexers and file indexers(預覽版)是一個標準效能(通用 v2)實例,支援熱層級與冷層存取。

標記語法解析模式參數

解析模式參數會在建立或更新索引器時,在索引器的定義中指定。

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) 此參數決定解析時考慮的最深標頭層級,允許靈活處理文件結構(例如,當 markdownHeaderDepth 設為 h1時,解析器僅識別以「#」開頭的頂層標頭,所有較低階標頭視為純文字)。 若未指定,則預設為 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、h1 以及 h2。

{
  {
    "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 to "markdown" ,並使用可選 markdownHeaderDepth 參數定義分區的最大標題深度。 若未指定,則預設為 h6,捕捉所有可能的標頭深度。

Markdown 會根據標題來被剖析成搜尋文件,其中包含以下內容:

  • document_content: 包含完整的 Markdown 文字為單一字串。 此欄位作為輸入文件的原始表示。

  • sections:一個包含 Markdown 文件中各區段階層表示的物件陣列。 每個區段在此陣列中以物件形式表示,並以巢狀方式捕捉文件結構,對應標頭及其相應內容。 這些場可透過場映射來存取,方法是參考路徑,例如 /sections/content。 此陣列中的物件具有以下特性:

    • header_level: 一個字串,表示標頭(h1、h2h3等)在 Markdown 語法中的位置。 此領域有助於理解內容的層級結構與結構。

    • header_name:包含 Markdown 文件中顯示的標頭文字的文字串。 此欄位提供該區段的標籤或標題。

    • content: 字串包含緊接標頭後至下一個標頭的文字內容。 此欄位捕捉與標頭相關的詳細資訊或描述。 如果標頭下方沒有直接內容,該值就是空字串。

    • ordinal_position:一個整數值,表示該區段在文件階層中的位置。 此欄位用於將章節按照它們在文件中出現的原始順序排列,從序號 1 開始,並為每個內容區塊依序遞增。

    • sections:一個陣列,包含代表巢狀子區段的物件,這些物件位於當前區段下方。 此陣列遵循與頂層 sections 陣列相同的結構,允許表示多層巢狀內容。 每個子區段物件也包含 header_level、 header_name、 content和 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 (“##”),因此需要 sections 巢狀的欄位深度為 2 來匹配。 此配置會產生以下索引資料:

  "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 檔案的當前狀態,請考慮以下方法之一:

選項一。 含元資料的軟刪除

此方法使用軟刪除來刪除與特定 blob 相關的文件。 欲了解更多資訊,請參閱Azure AI 搜尋服務 中使用索引器進行 Azure 儲存體的變更與刪除偵測。

步驟:

  1. 透過設定元資料欄位,將 blob 標記為已刪除。
  2. 讓索引器運行。 它會刪除與該 blob 相關的索引中的所有文件。
  3. 移除軟刪除標記並重新索引檔案。

選項二。 使用刪除 API

在重新索引修改過的 Markdown 檔案前,請明確使用 delete API 刪除與該檔案相關的現有文件。 你可以選擇:

  • 手動辨識過時文件,透過識別索引中重複的檔案來刪除。 這對於小且熟悉的變更可能可行,但可能耗時。
  • (推薦)在重新索引前,請移除所有從同一父檔案產生的文件,以避免不一致。

步驟:

  1. 識別與檔案相關的文件編號。 使用類似以下範例的查詢,取得所有綁定於特定檔案的文件的金鑰 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. 重新索引更新後的檔案。

下一步