共用方式為

为父子索引编制定义索引投影

如果要对 RAG 模式或矢量化内容进行分块,则可以指定 索引投影 来控制 一对多索引,其中源内容(一个)投影到一个或多个索引(多个)。 索引投影的目的是控制是否显示父文档中的元素(如文件名或创建日期):

  • 在单个索引中对每个子项(区块)重复
  • 作为独立搜索文档索引在同一索引中
  • 或者,导入到单独的索引中

建议在单个索引中重复父字段,因为将不同的文档形状或将内容拆分为两个索引可能很难查询,尤其是在不支持索引联接的经典搜索中。

在 Azure AI 搜索中,分块由技能执行,因此取决于索引器。 若要定义索引投影,请在 技能集中指定它。

先决条件

该技能组包含索引器投影,投影对数据进行整形以便进行一对多索引编制。 如果你的场景包含集成矢量化,则技能组还可以包含其他技能,例如嵌入技能(如 AzureOpenAIEmbedding)。

选择方法

索引映射为每个“父”文档生成“子”文档(块)。 选择如何管理父内容:

方法 Description 配置
单个索引,重复父字段 (建议) 每个区块的父字段重复。 所有文档都具有统一的形状。 将索引器和 targetIndexName 索引投影 targetIndexName 都设置为同一索引。 将 projectionMode 设置为 skipIndexingParentDocuments
单个索引、混合文档形状 父文档和区块文档共存。 父文档具有空区块字段。 将这两个 targetIndexName 值设置为同一索引。 设置为 projectionModeincludeIndexingParentDocuments (或省略,因为它是默认值)。
两个或多个单独的索引 元数据查找的父索引、用于搜索的子索引。 无查询时联接。 将索引器 targetIndexName 设置为父索引。 将索引投影 targetIndexName 设置为子索引。 数组 selectors 确定子索引的数量和构成。

对于大多数 RAG 方案,请使用第一种方法。 请参阅 经典 RAG 示例

  1. 创建 专为区块设计的索引,其中包含父字段。
  2. 创建具有分块技能的技能集indexProjections
  3. 创建一个索引器并指向您的支持的数据源。

如果数据源支持更改跟踪,索引器会自动同步更改。

为一对多索引编制创建索引

无论是为重复父值的区块创建一个索引,还是为父子字段布局创建单独的索引,用于搜索的主索引都是围绕数据区块设计的。 索引架构必须具有以下字段:

  • 唯一标识每个文档的文档键字段。 它必须随 Edm.String 分析器定义为类型 keyword

  • 一个将每个区块与其父级关联的字段。 该对象的类型必须是 Edm.String。 它不能是文档键字段,并且必须将 filterable 设置为 true。 它在示例中被称为 parent_id,在本文中称为投影键值

  • 内容的其他字段,例如文本或矢量化区块字段。

在创建技能组或运行索引器之前,搜索服务上必须存在索引。 在技能集中定义的selectors应包括这些字段。

包含父字段和子字段的单索引架构

单一索引围绕区块设计,每个块都有重复的父内容,这是 RAG 和矢量搜索场景的主要模式。 通过索引投影,可以将正确的父内容与每个区块相关联。

以下架构是满足索引投影要求的一个示例。 在本示例中:

  • 父字段是parent_id和标题,它们针对每个区块重复
  • 子字段是矢量和非矢量区块。 chunk_id 是此索引的文档 ID。

可以使用 Azure 门户、REST API 或 Azure SDK 来创建索引

使用 REST 客户端或 Azure 门户中的添加索引操作及 JSON 选项来创建索引。

{
    "name": "my_consolidated_index",
    "fields": [
        {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
        {"name": "parent_id", "type": "Edm.String", "filterable": true},
        {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
        {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
        {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
    ],
    "vectorSearch": {
        "algorithms": [{"name": "hnsw", "kind": "hnsw", "hnswParameters": {}}],
        "profiles": [{"name": "hnsw", "algorithm": "hnsw"}]
    }
}

向技能组中添加索引投影

索引投影是在技能组定义中定义的,主要定义为 selectors 的数组,其中每个选择器对应于搜索服务上的一个不同的目标索引。 本部分首先提供了上下文的语法和示例,然后提供了参数参考信息

索引投影已正式发布。 建议使用最新的稳定 API:

下面是索引投影定义的示例有效负载,你可以使用它将文本拆分技能的各个页面输出投影为搜索索引中其自己的文档。

"indexProjections": {
    "selectors": [
        {
            "targetIndexName": "my_consolidated_index",
            "parentKeyFieldName": "parent_id",
            "sourceContext": "/document/pages/*",
            "mappings": [
                {
                    "name": "chunk",
                    "source": "/document/pages/*",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "chunk_vector",
                    "source": "/document/pages/*/chunk_vector",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "title",
                    "source": "/document/title",
                    "sourceContext": null,
                    "inputs": []
                }
            ]
        }
    ],
    "parameters": {
        "projectionMode": "skipIndexingParentDocuments"
    }
}

参数参考

索引投影参数 定义
selectors 包含主搜索库参数的数组,通常是围绕区块设计的索引。 可以通过指定多个选择器将内容发送到多个子索引。 在运行索引器之前,搜索服务必须存在索引架构。
parameters 关于索引投影的特定配置属性的参数字典。

参数具有以下元素作为其定义的一部分。

参数 定义
parameters.projectionMode 一个可选参数,用于向索引器提供指令。 有效值包括 includeIndexingParentDocumentsskipIndexingParentDocuments

此参数 skipIndexingParentDocuments的最佳值为 . 分块文档是主要搜索目标时,应使用它。

如果未设置skipIndexingParentDocumentsprojectionMode,则会自动获得includeIndexingParentDocuments,因为它是默认值。 它会在索引中添加一些额外的搜索文档,这些文档对于数据块内容为空,但是填充了与父级相关的内容。 例如,如果五个 PDF 为索引贡献 100 个区块,则索引中的文档数为 105。 为父字段创建的五个文档在块(子)字段中拥有空值,这使得它们与索引中的大多数文档有显著差异。 因此,我们建议 projectionMode 设置为 skipIndexingParentDocuments.

选择器具有以下元素作为其定义的一部分。

选择器 定义
selectors.targetIndexName 索引数据投影到的索引的名称。 它是具有重复父字段的单个分块索引,如果为父子内容使用单独的索引,则它是子索引。
selectors.parentKeyFieldName 提供父文档的键的字段名称。
selectors.sourceContext 定义将数据映射到各个搜索文档的粒度的扩充注释。 有关详细信息,请参阅技能上下文和输入注释语言
selectors.mappings 扩充数据到搜索索引中的字段的映射数组。 每个映射都包含:
name:应在其中对数据编制索引的搜索索引中的字段的名称。
source:应从中拉取数据的扩充注释路径。

每个 mapping 还可以递归方式使用可选的 sourceContextinputs 字段定义数据,类似于知识存储整形程序技能。 根据你的应用程序,这些参数允许你将数据整形为搜索索引中的 Edm.ComplexType 类型的字段。 某些 LLM 不接受搜索结果中的复杂类型,因此你使用的 LLM 决定了复杂类型映射是否有用。

mappings 参数非常重要。 你必须显式映射子索引中的每个字段,但 ID 字段除外,例如文档键和父 ID。

此要求与 Azure AI 搜索中的其他字段映射约定形成鲜明对比。 对于某些数据源类型,索引器可以基于相似名称或已知特征来隐式映射字段(例如,blob 索引器使用唯一的元数据存储路径作为默认文档键)。 但是,对于索引器投影,你必须在关系的“多”端显式指定每个字段映射。

重要

不要为父键字段创建字段映射。 这样做会中断更改跟踪和同步的数据刷新。

检查字段映射

索引器与三种不同类型的字段映射相关联。 在运行索引器之前,请检查你的字段映射并了解何时使用每种类型。

字段映射是在索引器中定义的,用于将源字段映射到索引字段。 字段映射用作数据路径,它们从源中提取数据并将其传入以进行索引编制,无需使用中间技能处理步骤。 通常,索引器可以自动映射具有相同名称和类型的字段。 仅当存在差异时,才需要使用显式字段映射。 在目前为止讨论的一对多索引编制和模式中,可能不需要字段映射。

输出字段映射是在索引器中定义的,用于将技能组生成的扩充内容映射到主索引中。 由于通过一种技能(文本分割技术)创建,因此块被视为丰富内容,但不需要为块或通过选择器映射定义的索引投影设置输出字段映射。

Selectors.mappings 在技能集中定义,并映射到子索引中的字段。 如果子索引还包括父字段(例如在合并索引解决方案中),则应为每个包含内容的字段设置字段映射,包括父级 title 字段(假设你希望 title 显示在每个分块文档中)。 如果使用 单独的父索引和子索引,则选择器应仅具有子级字段的字段映射。

注意

输出字段映射和选择器映射都接受扩充的文档树节点作为源输入。 了解如何指定每个节点的路径对于设置数据路径至关重要。 若要了解有关路径语法的详细信息,请参考引用扩充节点的路径技能组定义作为示例。

运行索引器

创建数据源、索引和技能组后,即可创建并运行索引器。 此步骤将执行管道。

你可以在处理结束后查询你的搜索索引以测试你的解决方案。

内容生命周期

根据底层数据源的不同,索引器通常可以提供持续的更改跟踪和删除检测。 本部分解释了一对多索引的内容生命周期,因为它与数据刷新有关。

对于提供更改跟踪和删除检测的数据源,索引器进程可以获取源数据中的更改。 每次运行索引器和技能组时,如果技能组或基础源数据已更改,索引投影就会更新。 索引器选取的任何更改都将通过扩充过程传播到索引中的投影,从而确保投影数据是原始数据源中内容的当前表示形式。 数据刷新活动被捕获在每个区块的投影键值中。 当基础数据发生更改时,此值将更新。

注意

虽然可以使用索引推送 API 手动编辑投影文档中的数据,但应避免这样做。 假设源数据中的文档已更新,并且数据源已启用更改跟踪或删除检测,则对索引的手动更新将在下一次管道调用时被覆盖。

更新的内容

如果你向数据源中添加新内容,则新区块或子文档将在下次运行索引器时添加到索引中。

如果你修改了数据源中的现有内容,并且你使用的数据源支持更改跟踪和删除检测,则区块将在搜索索引中以增量方式更新。 例如,如果文档中的单词或句子发生更改,则在下一个索引器运行时更新包含该单词或句子的目标索引中的区块。 现有字段不支持其他类型的更新,例如更改字段类型和某些属性。 有关允许的更新的详细信息,请参阅更新索引架构

某些数据源(例如 Azure 存储)默认支持基于时间戳的更改和删除跟踪。 其他数据源(例如 Azure SQLAzure Cosmos DB)必须进行配置才能进行更改跟踪。

删除的内容

如果源内容不再存在(例如,如果文本被缩短为具有更少的区块),则搜索索引中的相应子文档将被删除。 其余子文档还会更新其键以包括新的哈希值,即使其内容不更改也是如此。

如果父文档从数据源中完全删除,则仅当数据源定义上定义的 dataDeletionDetectionPolicy 检测到删除时,才会删除相应的子文档。 如果没有 dataDeletionDetectionPolicy 配置并且需要从数据源中删除父文档,则应 在不再需要子文档时手动删除子文档

投影的键值

为了确保已更新和删除的内容的数据完整性,数据刷新在一对多索引中依赖于“多”端的投影键值。 如果使用集成矢量化或 导入数据(新) 向导,则投影的键值是 parent_id 索引的分块或“多”端的字段。

投影键值是索引器为每个文档生成的唯一标识符。 它确保唯一性并使得更改和删除跟踪正常工作。 该键包含以下各段:

  • 用于保证唯一性的随机哈希。 如果在后续的索引器运行之间更新了父文档,则此哈希会更改。
  • 父文档的键。
  • 标识所生成文档的上下文的扩充批注路径。

例如,如果将具有键值“aa1b22c33”的父文档拆分为四页,则会通过索引投影将其中每个页面投影为其自己的文档:

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

如果源数据中的父文档被更新,可能会导致更多的分块页面,随机哈希值会发生更改,会添加更多的页面,并且每个区块的内容都会被更新以匹配源文档中的任何内容。

单独的父子索引的示例

本部分显示了单独的父索引和子索引的示例。 这是一种不常见的模式,但使用这种方法可能最能满足你的应用程序需求。 在此场景中,你将父子内容投影到两个单独的索引中。

  1. 创建两个索引架构。

    每个架构都有其特定粒度的字段,父 ID 字段是两个索引通用的,用于查找查询中。 主要搜索库是子索引,但可以发出查找查询来检索结果中每个匹配项的父字段。 Azure AI 搜索在查询时不支持联接,因此你的应用程序代码或业务流程层需要合并或整理可传递给应用或进程的结果。

    父索引具有 parent_id 字段和 title。 parent_id 是文档键。 除非你希望在父文档级别对字段进行矢量化,否则不需要对搜索配置进行矢量化。

    {
        "name": "my-parent-index",
        "fields": [
    
            {"name": "parent_id", "type": "Edm.String", "key":true, "filterable": true},
            {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true}
        ]
    }
    

    子索引具有分块字段,以及 parent_id 字段。 如果你使用集成矢量化、计分配置文件、语义排序器或分析器,则你将在子索引中设置它们。

    {
        "name": "my-child-index",
        "fields": [
            {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
            {"name": "parent_id", "type": "Edm.String", "filterable": true},
             {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
            {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
        ],
        "vectorSearch": {
            "algorithms": [{"name": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
            "profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
        },
        "scoringProfiles": [],
        "semanticConfiguration": [],
        "analyzers": []
    }
    
  2. 更新索引器以指定父索引作为目标。

    索引器定义指定管道的组件。 在索引器定义中,要提供的索引名称是父索引。 如果你需要父级字段的字段映射,请在 outputFieldMappings 中定义它们。 对于使用单独索引的一对多索引编制,索引器定义可能如以下示例所示。

    {
      "name": "my-indexer",
      "dataSourceName": "my-ds",
      "targetIndexName": "my-parent-index",
      "skillsetName" : "my-skillset",
      "parameters": { },
      "fieldMappings": (optional) Maps fields in the underlying data source to fields in an index,
      "outputFieldMappings" : (required) Maps skill outputs to fields in an index,
    }
    
  3. 在技能集中添加 indexProjections

    下面是索引投影定义的一个示例,它指定索引器用来为内容编制索引的数据路径。 它在索引投影定义中指定子索引名称,并指定每个子级或区块级字段的映射。 这是指定子索引名称的唯一位置。

    请注意, parameters 为 null 且正在使用默认值 includeIndexingParentDocuments。 索引器填充父索引。 该 selectors 数组用于将区块文档投影到子索引。

    "indexProjections": {
        "selectors": [
            {
                "targetIndexName": "my-child-index",
                "parentKeyFieldName": "parent_id",
                "sourceContext": "/document/pages/*",
                "mappings": [
                    {
                        "name": "chunk",
                        "source": "/document/pages/*",
                        "sourceContext": null,
                        "inputs": []
                    },
                    {
                        "name": "chunk_vector",
                        "source": "/document/pages/*/chunk_vector",
                        "sourceContext": null,
                        "inputs": []
                    }
                ]
            }
        ],
        "parameters": {}
    }
    
  4. 运行索引器。 如果以前运行过索引器,请记得先重置它。

    应有两个索引填充相应的内容。 在搜索资源管理器中查询索引,以验证每个索引是否具有正确的内容。

下一步

数据分块和一对多索引是 Azure AI 搜索中经典 RAG 模式的一部分。 继续学习以下教程和代码示例,了解有关它的详细信息。