使用 REST 创建知识存储

在 Azure AI 搜索中,知识存储AI 生成的内容的存储库,用于非搜索场景。 你可以使用索引器和技能组创建知识存储,并指定用于存储输出的 Azure 存储。 填充知识存储后,请使用存储资源管理器Power BI 等工具来浏览内容。

在本文中,你要使用 REST API 在知识存储中引入、扩充和浏览一组关于酒店住宿的客户评论。 该知识存储包含从源提取的原始文本内容,以及 AI 生成的内容,其中包括情绪评分、关键短语提取、语言检测和非英语客户评论的文本翻译。

为了获得初始数据集,首先要将这些酒店评论导入到 Azure Blob 存储中。 处理完以后,结果将作为知识存储保存在 Azure 表存储中。

提示

本文使用 REST 提供每个步骤的详细说明。 如果只想运行命令,请下载 REST 文件。 或者,还可以在 Azure 门户中创建知识存储

先决条件

此示例中的技能组使用 Azure AI 服务进行扩充。 由于工作负载很小,因此,Azure AI 服务在后台会抽调一部分算力来免费处理事务(每天最多 20 个)。 负载很小意味着可以跳过创建或附加 Azure AI 多服务资源的过程。

将数据上传到 Azure 存储并获取连接字符串

  1. 下载 HotelReviews_Free.csv。 此 CSV 包含了关于一家酒店的 19 条客户反馈(源自 Kaggle.com)。

  2. 在 Azure 门户中,找到你的存储帐户并使用存储浏览器创建一个名为 hotel-reviews 的 blob 容器

  3. 选择页面顶部的“上传”,以加载从上一步骤下载的 HotelReviews-Free.csv 文件 。

    包含上传的文件和左侧导航窗格的存储浏览器的屏幕截图

  4. 在左侧,依次选择“访问密钥”和“显示密钥”,然后复制 key1 或 key2 的连接字符串。 完整的访问连接字符串具有以下格式:

"knowledgeStore": {
    "storageConnectionString": "DefaultEndpointsProtocol=https;AccountName=<YOUR-ACCOUNT-NAME>;AccountKey=<YOUR-ACCOUNT-KEY>;EndpointSuffix=core.chinacloudapi.cn;"
}

注意

如果不想在连接字符串上提供敏感数据,请参阅使用托管标识进行连接

复制密钥和 URL

在此示例中,REST 调用需要搜索服务终结点,并在每个请求上使用 API 密钥。 可以从 Azure 门户获取这些值。

  1. 登录到 Azure 门户,导航到概述页,并复制 URL。 示例终结点可能类似于 https://mydemo.search.azure.cn

  2. 在“设置”>“密钥”下,复制管理密钥。 管理密钥用于添加、修改和删除对象。 有两个可互换的管理密钥。 复制其中任意一个。

    Azure 门户中 URL 和 API 密钥的屏幕截图。

具有有效的 API 密钥可以在发送请求的应用程序与处理请求的搜索服务之间建立信任关系,这种信任关系以每个请求为基础。

创建索引

创建索引 (REST) 将在搜索服务中创建搜索索引。 搜索索引与知识存储不相关,但索引器需要一个知识存储。 搜索索引包含与知识存储相同的内容,可以通过发送查询请求来浏览这些内容。

  1. 在 Visual Studio Code 中打开新的文本文件。

  2. 将变量设置为之前收集的搜索终结点和 API 密钥。

    @baseUrl = PUT-YOUR-SEARCH-SERVICE-URL-HERE
    @apiKey = PUT-YOUR-ADMIN-API-KEY-HERE
    @storageConnection = PUT-YOUR-STORAGE-CONNECTION-STRING-HERE
    @blobContainer = PUT-YOUR-CONTAINER-NAME-HERE (hotel-reviews)
    
  3. 使用 .rest 文件扩展名保存文件。

  4. 粘贴到以下示例以创建索引请求。

    ### Create a new index
    POST {{baseUrl}}/indexes?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-idx",  
            "fields": [
                { "name": "name", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_date", "type": "Edm.DateTimeOffset", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_rating", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_text", "type": "Edm.String", "filterable": false,  "sortable": false, "facetable": false },
                { "name": "reviews_title", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_username", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "AzureSearch_DocumentKey", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false, "key": true },
                { "name": "language", "type": "Edm.String", "filterable": true, "sortable": false, "facetable": true },
                { "name": "translated_text", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
                { "name": "sentiment", "type": "Collection(Edm.String)", "searchable": false, "filterable": true, "retrievable": true, "sortable": false, "facetable": true },
                { "name": "keyphrases", "type": "Collection(Edm.String)", "filterable": true, "sortable": false, "facetable": true }
            ]
        }
    
  5. 选择“发送请求”。 你应该会有一个 HTTP/1.1 201 Created 响应,并且响应正文应该会包含索引架构的 JSON 表示形式。

创建数据源

创建数据源 在 Azure AI 搜索上创建数据源连接。

  1. 粘贴到以下示例以创建数据源。

    ### Create a data source
    POST {{baseUrl}}/datasources?api-version=2023-11-01  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-ds",
            "description": null,
            "type": "azureblob",
            "subtype": null,
            "credentials": {
                "connectionString": "{{storageConnectionString}}"
            },
            "container": {
                "name": "{{blobContainer}}",
                "query": null
            },
            "dataChangeDetectionPolicy": null,
            "dataDeletionDetectionPolicy": null
        }
    
  2. 选择“发送请求”。

创建技能集

技能组定义扩充(技能)和知识存储。 创建技能组在搜索服务上创建对象。

  1. 粘贴到以下示例以创建技能组。

    ### Create a skillset
    POST {{baseUrl}}/skillsets?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-ss",
            "description": "Skillset to detect language, translate text, extract key phrases, and score sentiment",
            "skills": [ 
                {
                    "@odata.type": "#Microsoft.Skills.Text.SplitSkill", 
                    "context": "/document/reviews_text", "textSplitMode": "pages", "maximumPageLength": 5000,
                    "inputs": [ 
                        { "name": "text", "source": "/document/reviews_text" }
                    ],
                    "outputs": [
                        { "name": "textItems", "targetName": "pages" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.V3.SentimentSkill",
                    "context": "/document/reviews_text/pages/*",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text/pages/*" },
                        { "name": "languageCode", "source": "/document/language" }
                    ],
                    "outputs": [
                        { "name": "sentiment", "targetName": "sentiment" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.LanguageDetectionSkill",
                    "context": "/document",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text" }
                    ],
                    "outputs": [
                        { "name": "languageCode", "targetName": "language" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.TranslationSkill",
                    "context": "/document/reviews_text/pages/*",
                    "defaultFromLanguageCode": null,
                    "defaultToLanguageCode": "en",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text/pages/*" }
                    ],
                    "outputs": [
                        { "name": "translatedText", "targetName": "translated_text" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.KeyPhraseExtractionSkill",
                    "context": "/document/reviews_text/pages/*",
                    "inputs": [
                        { "name": "text",  "source": "/document/reviews_text/pages/*" },
                        { "name": "languageCode",  "source": "/document/language" }
                    ],
                    "outputs": [
                        { "name": "keyPhrases" , "targetName": "keyphrases" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Util.ShaperSkill",
                    "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": "reviews_username",  "source": "/document/reviews_username" },
                        { "name": "AzureSearch_DocumentKey",  "source": "/document/AzureSearch_DocumentKey" },
                        {
                        "name": "pages",
                        "sourceContext": "/document/reviews_text/pages/*",
                        "inputs": [
                            {
                            "name": "languageCode",
                            "source": "/document/language"
                            },
                            {
                            "name": "translatedText",
                            "source": "/document/reviews_text/pages/*/translated_text"
                            },
                            { 
                            "name": "sentiment",
                            "source": "/document/reviews_text/pages/*/sentiment"
                            },
                            {
                            "name": "keyPhrases",
                            "source": "/document/reviews_text/pages/*/keyphrases/*"
                            },
                            {
                            "name": "Page",
                            "source": "/document/reviews_text/pages/*"
                            }
                        ]
                        }
                    ],
                    "outputs": [
                        { "name": "output" , "targetName": "tableprojection" }
                    ]
                }
            ],
            "knowledgeStore": {
                "storageConnectionString": "{{storageConnectionString}}",
                "projections": [
                    {
                        "tables": [
                            { "tableName": "hotelReviews1Document", "generatedKeyName": "Documentid", "source": "/document/tableprojection" },
                            { "tableName": "hotelReviews2Pages", "generatedKeyName": "Pagesid", "source": "/document/tableprojection/pages/*" },
                            { "tableName": "hotelReviews3KeyPhrases", "generatedKeyName": "KeyPhrasesid", "source": "/document/tableprojection/pages/*/keyPhrases/*" }
                        ],
                        "objects": []
                    },
                    {
                        "tables": [
                            { 
                                "tableName": "hotelReviews4InlineProjectionDocument", "generatedKeyName": "Documentid", "sourceContext": "/document",
                                "inputs": [
                                    { "name": "name", "source": "/document/name"},
                                    { "name": "reviews_date", "source": "/document/reviews_date"},
                                    { "name": "reviews_rating", "source": "/document/reviews_rating"},
                                    { "name": "reviews_username", "source": "/document/reviews_username"},
                                    { "name": "reviews_title", "source": "/document/reviews_title"},
                                    { "name": "reviews_text", "source": "/document/reviews_text"},
                                    { "name": "AzureSearch_DocumentKey", "source": "/document/AzureSearch_DocumentKey" }
                                ]
                            },
                            { 
                                "tableName": "hotelReviews5InlineProjectionPages", "generatedKeyName": "Pagesid", "sourceContext": "/document/reviews_text/pages/*",
                                "inputs": [
                                    { "name": "Sentiment", "source": "/document/reviews_text/pages/*/sentiment"},
                                    { "name": "LanguageCode", "source": "/document/language"},
                                    { "name": "Keyphrases", "source": "/document/reviews_text/pages/*/keyphrases"},
                                    { "name": "TranslatedText", "source": "/document/reviews_text/pages/*/translated_text"},
                                    { "name": "Page", "source": "/document/reviews_text/pages/*" }
                                ]
                            },
                            { 
                                "tableName": "hotelReviews6InlineProjectionKeyPhrases", "generatedKeyName": "kpidv2", "sourceContext": "/document/reviews_text/pages/*/keyphrases/*",
                                "inputs": [
                                    { "name": "Keyphrases", "source": "/document/reviews_text/pages/*/keyphrases/*" }
                                ]
                            }
                        ],
                        "objects": []
                    }
                ]
            }
        }
    

要点

  • 整形程序技能对于知识存储定义非常重要。 它指定数据流入知识存储表的方式。 输入是要存储的扩充文档的组成部分。 输出是将节点合并到单个结构中。

  • 投影指定知识存储的表、对象和 Blob。 每个投影项指定要在 Azure 存储中创建的列或字段的 "name""source" 指定将整形程序输出的哪个部分分配给该字段或列。

创建索引器

创建索引器将创建并运行索引器。 索引器执行首先破解文档、提取文本和图像以及初始化技能组。 索引器将检查所创建的其他对象:数据源、索引和技能组。

  1. 粘贴到以下示例以创建索引器。

    ### Create indexer
    POST {{baseUrl}}/indexers?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-idxr",
            "dataSourceName": "hotel-reviews-kstore-ds",
            "skillsetName": "hotel-reviews-kstore-ss",
            "targetIndexName": "hotel-reviews-kstore-idx",
            "parameters": {
                "configuration": {
                    "dataToExtract": "contentAndMetadata",
                    "parsingMode": "delimitedText",
                    "firstLineContainsHeaders": true,
                    "delimitedTextDelimiter": ","
        }
    },
    "fieldMappings": [
        {
            "sourceFieldName": "AzureSearch_DocumentKey",
            "targetFieldName": "AzureSearch_DocumentKey",
            "mappingFunction": { "name": "base64Encode" }
        }
    ],
    "outputFieldMappings": [
        { "sourceFieldName": "/document/reviews_text/pages/*/Keyphrases/*", "targetFieldName": "Keyphrases" },
        { "sourceFieldName": "/document/Language", "targetFieldName": "Language" },
        { "sourceFieldName": "/document/reviews_text/pages/*/Sentiment", "targetFieldName": "Sentiment" }
        ]
    }
    
  2. 选择“发送请求”以创建并运行索引器。 完成此步骤需要几分钟时间。

要点

  • parameters/configuration 对象控制索引器引入数据的方式。 在这种情况下,输入数据在一个具有标题行和逗号分隔值的 CSV 文件中。

  • 字段映射创建的“AzureSearch_DocumentKey”是 blob 索引器生成的(基于元数据存储路径)每个文档的唯一标识符。

  • 输出字段映射指定如何将已扩充的字段映射到搜索索引中的字段。 输出字段映射不用于知识存储(知识存储使用形状和投影来表达物理数据结构)。

查看状态

发送每个请求后,搜索服务应以 201 成功消息进行响应。

### Get Indexer Status (wait several minutes for the indexer to complete)
GET {{baseUrl}}/indexers/hotel-reviews-kstore-idxr/status?api-version=2023-11-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}

几分钟后,可以查询索引以检查内容。 即使未使用索引,此步骤也是确认技能组生成预期输出的简便方法。

### Query the index (indexer status must be "success" before querying the index)
POST {{baseUrl}}/indexes/hotel-reviews-kstore-idxr/docs/search?api-version=2023-11-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}

  {
    "search": "*",
    "select": "reviews_title, reviews_username, language, translated_text, sentiment",
    "count": true
  }

检查 Azure 门户中的表

在 Azure 门户中,切换到 Azure 存储帐户并使用存储浏览器查看新的表。 应看到六个表,每个表对应于技能组中定义的每个投影。

每个表都是使用在查询中交叉链接表所需的 ID 生成的。 打开表时,滚动浏览这些字段可查看管道添加的内容字段。

存储浏览器中知识存储表的屏幕截图

在本演练中,知识存储由各种表组成,显示了形成和构建表的不同方式。 表 1 到表 3 使用整形程序技能的输出来确定列和行。 表 4 到 6 基于嵌入在投影本身的内联整形指令创建。 可以使用这两种方法获得相同的结果。

说明
hotelReviews1Document 包含从 CSV 结转的字段,例如 reviews_date 和 reviews_text。
hotelReviews2Pages 包含由技能集创建的扩充字段,例如情绪评分和已翻译的文本。
hotelReviews3KeyPhrases 包含仅包含关键短语的长列表。
hotelReviews4InlineProjectionDocument 第一个表的替代项,使用内联整形而不是整形程序技能来为投影的数据绘制数据。
hotelReviews5InlineProjectionPages 第二个表的替代项,使用内联整形。
hotelreviews6InlineProjectionKeyPhrases 第三个表的替代项,使用内联整形。

清理

在自己的订阅中操作时,最好在项目结束时确定是否仍需要已创建的资源。 持续运行资源可能会产生费用。 可以逐个删除资源,也可以删除资源组以删除整个资源集。

可以使用左侧导航窗格中的“所有资源”或“资源组”链接 ,在门户中查找和管理资源。

后续步骤

使用 Azure AI 服务扩充数据并将结果投影到知识存储后,接下来可以使用存储资源管理器或其他应用来浏览扩充的数据集。