Azure AI 搜索中的技能组概念

本文面向需要更深入了解技能组概念和组合的开发人员,并假设这些开发人员熟悉 AI 扩充的基本概念。

技能组是 Azure AI 搜索中附加到索引器的可重用资源。 它包含一项或多项技能,用于调用内置 AI 或对从外部数据源检索的文档进行外部自定义处理。

下图演示了技能组执行的基本数据流。

显示技能组数据流的示意图,其中突出显示了输入、输出和映射。

从技能组处理的开始到结束,技能对扩充文档进行读取和写入操作。 扩充文档最初是从数据源中提取的原始内容(表达为 "/document" 根节点)。 随着每项技能的执行,扩充文档获得了结构和内容,因为技能将其输出作为图中的节点写入。

完成技能组执行后,扩充文档的输出会通过输出字段映射编入索引。 任何需要从源到索引完整传输的原始内容都是通过字段映射来定义的。

若要配置扩充,需在技能组和索引器中指定设置。

技能集定义

技能组包含一系列执行扩充的一项或多项技能,例如翻译文本或对图像文件执行 OCR。 技能可以是 Microsoft 的内置技能,也可以是用于处理外部托管的逻辑的自定义技能。 技能组生成在编制索引或投影到知识存储期间使用的扩充文档。

技能包含上下文、输入和输出:

显示技能组的哪些属性建立数据路径的示意图。

  • 上下文是指操作的范围,可以是对每个文档调用一次,也可以是对集合中每个项调用一次。

  • 输入源自扩充文档中的节点,其中“源”和“名称”标识给定节点。

  • 输出作为新节点发送回扩充文档。 值是节点“名称”和节点内容。 如果节点名称重复,可设置一个目标名称消除歧义。

技能上下文

每项技能都有上下文,该上下文可以是整个文档 (/document) 或树 (/document/countries/*) 下的节点。 上下文确定:

  • 技能对单个值执行的次数(每个字段、每个文档一次),或针对集合类型的上下文值执行的次数,其中添加 /* 会导致对集合中的每个实例调用一次技能。

  • 输出声明,或在扩充树中添加技能输出的位置。 输出始终作为上下文节点的子级添加到树中。

  • 输入的形状。 对于多级别集合,将上下文设置为父集合会影响技能输入的形状。 例如,如果某个扩充树包含国家/地区列表,其中的每个国家/地区已使用包含邮政编码列表的省/自治区/直辖市列表进行扩充,则设置上下文的方式将决定输出的表现形式。

    上下文 输入 输入的形状 技能调用
    /document/countries/* /document/countries/*/states/*/zipcodes/* 国家/地区中所有邮政编码的列表 对每个国家/地区调用一次
    /document/countries/*/states/* /document/countries/*/states/*/zipcodes/* 省/自治区/直辖市中邮政编码的列表 对每个国家/地区和州/省的组合调用一次

技能依赖项

技能可以独立和并行执行,也可以按顺序执行(如果将一个技能的输出馈送到另一个技能中)。 以下示例演示了两个按顺序执行的内置技能

  • 技能 #1 是一种文本拆分技能,它可以接受“reviews_text”源字段的内容作为输入,并将该内容拆分为 5000 个字符的“页”作为输出。 将大文本拆分为较小的区块可以为情绪检测等技能生成更好的结果。

  • 技能 #2 是一个情绪检测技能,它接受“页面”作为输入,并生成一个名为“Sentiment”的新字段作为输出,该输出包含情绪分析结果。

注意第一个技能的输出(“pages”)是如何用于情绪分析的,其中“/document/reviews_text/pages/*”既是上下文又是输入。 有关路径表述的详细信息,请参阅如何引用扩充

{
    "skills": [
        {
            "@odata.type": "#Microsoft.Skills.Text.SplitSkill",
            "name": "#1",
            "description": null,
            "context": "/document/reviews_text",
            "defaultLanguageCode": "en",
            "textSplitMode": "pages",
            "maximumPageLength": 5000,
            "inputs": [
                {
                    "name": "text",
                    "source": "/document/reviews_text"
                }
            ],
            "outputs": [
                {
                    "name": "textItems",
                    "targetName": "pages"
                }
            ]
        },
        {
            "@odata.type": "#Microsoft.Skills.Text.SentimentSkill",
            "name": "#2",
            "description": null,
            "context": "/document/reviews_text/pages/*",
            "defaultLanguageCode": "en",
            "inputs": [
                {
                    "name": "text",
                    "source": "/document/reviews_text/pages/*",
                }
            ],
            "outputs": [
                {
                    "name": "sentiment",
                    "targetName": "sentiment"
                },
                {
                    "name": "confidenceScores",
                    "targetName": "confidenceScores"
                },
                {
                    "name": "sentences",
                    "targetName": "sentences"
                }
            ]
        }
      . . .
  ]
}

扩充树

扩充文档是在技能组执行过程中创建的临时类树形数据结构,用于收集通过技能引入的所有更改。 总体而言,扩充表示为可寻址节点的层次结构。 节点还包括从外部数据源逐字传递的任何未扩充字段。

扩充文档在技能组执行期间存在,但可以将其缓存或发送到知识存储中。

最初,扩充文档只是文档破解期间从数据源提取的内容,其中文本和图像从源提取,并可用于语言或图像分析。

初始内容是元数据和根节点 (document/content)。 根节点通常是整个文档或在文档破解期间从数据源提取的规范化映像。 它在扩充树中的表达方式因数据源类型而异。 下表显示了进入多个受支持数据源的扩展管道的文档的状态:

数据源\分析模式 默认值 JSON、JSON 行 和 CSV
Blob 存储 /document/content
/document/normalized_images/*
/document/{key1}
/document/{key2}
Azure SQL /document/{column1}
/document/{column2}
不可用
Azure Cosmos DB /document/{key1}
/document/{key2}
空值

随着技能的执行,输出将作为新节点添加到扩充树中。 如果技能执行遍及整个文档,则会在根节点下第一级添加节点。

节点可用作下游技能的输入。 例如,创建内容的技能(如翻译后的字符串)可能成为识别实体或提取关键短语的技能的输入。

从扩充树中进行读取和写入的技能

尽管可以通过调试会话可视化编辑器来可视化和使用扩充树,但它主要是一种内部结构。

扩充是不可变的:创建节点后无法对节点进行编辑。 随着技能集变得越来越复杂,扩充树也会更加复杂,但是,并非扩充树中的所有节点都需要将扩充保存到索引或知识存储中。

可以有选择地只保留部分扩充输出,以便只保留要使用的内容。 索引器定义中的输出字段映射将确定搜索索引中实际引入的内容。 同样,如果要创建知识存储,可以将输出映射到分配给投影的形状

注意

扩充树格式甚至能够使扩充管道将元数据附加到基元数据类型。 元数据将不是有效的 JSON 对象,但它可以在知识存储的投影定义中投影为有效的 JSON 格式。 有关详细信息,请参阅整形程序技能

索引器定义

索引器具有用于配置索引器执行的属性和参数。 这些属性中包括设置搜索索引中字段的数据路径的映射。

显示索引器的哪些属性建立索引中字段的数据路径的示意图。

有两组映射:

“sourceFieldName”属性指定数据源中的字段或扩充树中的节点。 “targetFieldName”属性指定接收内容的索引中的搜索字段。

扩充示例

本示例使用酒店评价技能组作为参考点,说明如何使用概念图通过技能执行来发展扩充树

本示例还演示了如何执行以下操作:

  • 技能的上下文和输入如何共同确定技能的执行次数
  • 哪种输入形状基于上下文

在本示例中,CSV 文件中的源字段包括客户对酒店的评价(“reviews_text”)和评级(“reviews_rating”)。 索引器将从 Blob 存储中添加元数据字段,并且技能将添加翻译文本、情绪评分和关键短语检测。

在酒店评价示例中,扩充流程中的“文档”表示单条酒店评价。

提示

可以在 Azure 门户中或通过 REST API 为此数据创建搜索索引和知识存储。 还可以使用调试会话来深入了解扩充树的技能组组合、依赖关系和效果。 本文中的映像是从调试会话中提取的。

从概念上讲,初始的扩充树如下所示:

完成文档破解之后的扩充树

所有扩充的根节点是 "/document"。 使用 Blob 索引器时,"/document" 节点会包含 "/document/content""/document/normalized_images" 的子节点。 数据为 CSV 时(如本示例所示),列名称将映射到 "/document" 下的节点。

技能 #1:拆分技能

当源内容由大块文本组成时,将其分解成较小的部分会很有帮助,可以提高语言、情绪和关键短语检测的准确度。 有两个可用的粒度:页面和句子。 一个页面包含大约 5000 个字符。

文本拆分技能通常是技能组中的第一个技能。

"@odata.type": "#Microsoft.Skills.Text.SplitSkill",
"name": "#1",
"description": null,
"context": "/document/reviews_text",
"defaultLanguageCode": "en",
"textSplitMode": "pages",
"maximumPageLength": 5000,
"inputs": [
{
    "name": "text",
    "source": "/document/reviews_text"
}
],
"outputs": [
{
    "name": "textItems",
    "targetName": "pages"
}

此拆分技能使用 "/document/reviews_text" 的技能上下文对 reviews_text 执行一次。 技能输出是一个列表,其中的 reviews_text 分块成包含 5000 个字符的段。 拆分技能的输出名为 pages,将添加到扩充树中。 使用 targetName 功能可以在将技能输出添加到扩充树之前对其重命名。

现在,扩充树的技能上下文下包含一个新节点。 此节点可用于任何技能、投影或输出字段映射。

执行技能 #1 后的扩充树

若要访问由技能添加到节点的任何扩充,需要使用扩充的完整路径。 例如,若要使用 pages 节点中的文本作为另一技能的输入,需将该节点指定为 "/document/reviews_text/pages/*"。 有关路径的详细信息,请参阅引用扩充

技能 #2:语言检测

酒店评论文档包含以多种语言表达的客户反馈。 语言检测技能确定使用的是哪种语言。 然后,考虑到语言,在检测情绪和短语时,结果将传递给关键短语提取和情绪检测(未显示)。

尽管语言检测技能是技能组中定义的第三个技能(技能 #3),但它是下一个要执行的技能。 它不需要任何输入,因此它与前一个技能并行执行。 与前面的拆分技能一样,语言检测技能也只对每个文档调用一次。 扩充树现在包含新的语言节点。

执行技能 #2 后的扩充树

技能 #3 和 #4(情绪分析和关键短语检测)

客户反馈反映了一系列正面和负面的体验。 情绪分析技能将分析反馈,并在一个从负到正的连续区间上打分,如果情绪不确定,则指定为中性。 在进行情绪分析的同时,关键短语检测识别并提取看似重要的单词和短短语。

根据上下文 pages,系统将对 /document/reviews_text/pages/* 集合中的每个项目调用一次情绪分析和关键短语技能。 该技能的输出是关联的页元素下的一个节点。

现在,你应该可以查看技能集中的其他技能,并直观地了解扩充树如何随着每个技能的执行而不断扩大。 某些技能(例如合并技能和整形程序技能)也会创建新的节点,但只使用现有节点中的数据,而不会创建全新的扩充。

执行所有技能后的扩充树

上述树中的连接器颜色指示扩充由不同的技能创建,节点需要单独寻址,并且不会成为选择父节点时返回的对象的一部分。

技能 #5 整形程序技能

如果输出中包含知识存储,则添加一个整形程序技能作为最后一步。 整形程序技能使用扩充树中的节点创建数据形状。 例如,你可能想要将多个节点合并为一个形状。 然后,便可以将此形状投影为一个表(节点成为表中的列),并按名称将形状传递到表投影。

可以轻松地使用整形程序技能,因为它侧重于在一种技能下进行塑造。 或者,你可以选择在单个投影内进行内联成型。 整形程序技能不会增加或减少扩充树,因此没有对它进行可视化。 相反,可以将整形程序技能视为重新表述已有扩充树的方式。 从概念上讲,这类似于在数据库中使用表创建视图。

{
  "@odata.type": "#Microsoft.Skills.Util.ShaperSkill",
  "name": "#5",
  "description": null,
  "context": "/document",
  "inputs": [
    {
      "name": "name",
      "source": "/document/name"
    },
    {
      "name": "reviews_date",
      "source": "/document/reviews_date"
    },
    {
      "name": "reviews_rating",
      "source": "/document/reviews_rating"
    },
    {
      "name": "reviews_text",
      "source": "/document/reviews_text"
    },
    {
      "name": "reviews_title",
      "source": "/document/reviews_title"
    },
    {
      "name": "AzureSearch_DocumentKey",
      "source": "/document/AzureSearch_DocumentKey"
    },
    {
      "name": "pages",
      "sourceContext": "/document/reviews_text/pages/*",
      "inputs": [
        {
          "name": "Sentiment",
          "source": "/document/reviews_text/pages/*/Sentiment"
        },
        {
          "name": "LanguageCode",
          "source": "/document/Language"
        },
        {
          "name": "Page",
          "source": "/document/reviews_text/pages/*"
        },
        {
          "name": "keyphrase",
          "sourceContext": "/document/reviews_text/pages/*/Keyphrases/*",
          "inputs": [
            {
              "name": "Keyphrases",
              "source": "/document/reviews_text/pages/*/Keyphrases/*"
            }
          ]
        }
      ]
    }
  ],
  "outputs": [
    {
      "name": "output",
      "targetName": "tableprojection"
    }
  ]
}

后续步骤

借助介绍和示例,尝试使用内置技能创建第一个技能组。