教學課程:使用 .NET 和 AI 從 Azure blob 產生可搜尋的內容Tutorial: Use .NET and AI to generate searchable content from Azure blobs

如果您在 Azure Blob 儲存體中有非結構化的文字或影像,您可以利用 AI 擴充管線來擷取資訊,並建立適用於全文檢索搜尋或知識採礦案例的新內容。If you have unstructured text or images in Azure Blob storage, an AI enrichment pipeline can extract information and create new content for full-text search or knowledge mining scenarios.

在本教學課程中,您將學會如何:In this tutorial, you will learn how to:

  • 設定開發環境Set up a development environment
  • 定義使用 OCR、語言偵測和實體和關鍵字組辨識的管線。Define a pipeline that uses OCR, language detection, and entity and key phrase recognition.
  • 執行管線以叫用轉換,以及建立和載入搜尋索引。Execute the pipeline to invoke transformations, and to create and load a search index.
  • 使用全文檢索搜尋和豐富的查詢語法來探索結果。Explore results using full text search and a rich query syntax.

如果您沒有 Azure 訂用帳戶,請在開始前開啟免費帳戶If you don't have an Azure subscription, open a free account before you begin.

總覽Overview

本教學課程使用 c # 和 Azure.Search.Documents 用戶端程式庫,來建立資料來源、索引、索引子和技能集。This tutorial uses C# and the Azure.Search.Documents client library to create a data source, index, indexer, and skillset.

索引子會連接至資料來源物件中所指定的 blob 容器,並將所有已編制索引的內容傳送至現有的搜尋索引。The indexer connects to a blob container that's specified in the data source object, and sends all indexed content to an existing search index.

技能集會附加至索引子。The skillset is attached to the indexer. 它使用 Microsoft 內建的技能來尋找和解壓縮資訊。It uses built-in skills from Microsoft to find and extract information. 管線中的步驟包括影像上的光學字元辨識 (OCR)、文字的語言偵測、關鍵片語擷取和實體辨識 (組織)。Steps in the pipeline include Optical Character Recognition (OCR) on images, language detection on text, key phrase extraction, and entity recognition (organizations). 管線所建立的新資訊會儲存在索引的新欄位中。New information created by the pipeline is stored in new fields in an index. 填入索引後,您就可以使用查詢、facet 和篩選器中的欄位。Once the index is populated, you can use the fields in queries, facets, and filters.

必要條件Prerequisites

注意

您可以使用免費搜尋服務來進行本教學課程。You can use the free search service for this tutorial. 免費的搜尋服務會有限制,您只能使用三個索引、三個索引子和三個資料來源。A free search service limits you to three indexes, three indexers, and three data sources. 本教學課程會各建立一個。This tutorial creates one of each. 開始之前,請確定您的服務有空間可接受新的資源。Before starting, make sure you have room on your service to accept the new resources.

下載範例資料Download sample data

範例資料包含 14 個混合內容類型的檔案,您將在稍後的步驟中上傳至 Azure Blob 儲存體。The sample data consists of 14 files of mixed content type that you will upload to Azure Blob storage in a later step.

  1. 開啟此 OneDrive 資料夾,然後在左上角按一下 [下載],將檔案複製到您的電腦。Open this OneDrive folder and on the top-left corner, click Download to copy the files to your computer.

  2. 以滑鼠右鍵按一下 ZIP 檔案並選取 [全部解壓縮]。Right-click the zip file and select Extract All. 其中有 14 個不同類型的檔案。There are 14 files of various types. 在此練習中,您將使用 7 個檔案。You'll use 7 for this exercise.

您也可以下載本教學課程的原始程式碼。You can also download the source code for this tutorial. 原始程式碼位於 azure-search-dotnet-samples 存放庫的 tutorial-ai-enrichment/v11 資料夾中。Source code is in the tutorial-ai-enrichment/v11 folder in the azure-search-dotnet-samples repository.

1 - 建立服務1 - Create services

本教學課程會使用 Azure 認知搜尋進行索引編製和查詢、在後端將認知服務用於 AI 擴充,以及使用 Azure Blob 儲存體來提供資料。This tutorial uses Azure Cognitive Search for indexing and queries, Cognitive Services on the backend for AI enrichment, and Azure Blob storage to provide the data. 本教學課程在認知服務上使用的資源會保持在每個索引子每日 20 筆交易的免費配置以內,因此您需要建立的服務只有搜尋和儲存體。This tutorial stays under the free allocation of 20 transactions per indexer per day on Cognitive Services, so the only services you need to create are search and storage.

為具備鄰近性和管理方面的優勢,如果可能,請將這兩項服務建立在相同的區域和資源群組中。If possible, create both in the same region and resource group for proximity and manageability. 實際上,您的 Azure 儲存體帳戶可以位在任何區域中。In practice, your Azure Storage account can be in any region.

開始使用 Azure 儲存體Start with Azure Storage

  1. 登入 Azure 入口網站,然後按一下 [+ 建立資源]。Sign in to the Azure portal and click + Create Resource.

  2. 搜尋「儲存體帳戶」,然後選取 Microsoft 的儲存體帳戶供應項目。Search for storage account and select Microsoft's Storage Account offering.

    建立儲存體帳戶Create Storage account

  3. 在 [基本] 索引標籤中,需要下列項目。In the Basics tab, the following items are required. 接受所有其他項目的預設值。Accept the defaults for everything else.

    • 資源群組Resource group. 選取現有群組或建立一個新的群組,但必須對所有服務使用相同的群組,以便您一起管理這些服務。Select an existing one or create a new one, but use the same group for all services so that you can manage them collectively.

    • 儲存體帳戶名稱Storage account name. 如果您認為您可能會有多個相同類型的資源,請透過名稱在類型和區域上做出區別,例如 blobstoragewestus。If you think you might have multiple resources of the same type, use the name to disambiguate by type and region, for example blobstoragewestus.

    • 位置Location. 可能的話,請選擇用於 Azure 認知搜尋和認知服務的相同位置。If possible, choose the same location used for Azure Cognitive Search and Cognitive Services. 單一位置可避免產生頻寬費用。A single location voids bandwidth charges.

    • 帳戶種類Account Kind. 選擇預設值 [StorageV2 (一般用途 v2)]。Choose the default, StorageV2 (general purpose v2).

  4. 按一下 [檢閱 + 建立] 以建立服務。Click Review + Create to create the service.

  5. 建立後,按一下 [移至資源] 以開啟 [概觀] 頁面。Once it's created, click Go to the resource to open the Overview page.

  6. 按一下 [Blob] 服務。Click Blobs service.

  7. 按一下 [+ 容器] 來建立容器,並將其命名為 cog-search-demo。Click + Container to create a container and name it cog-search-demo.

  8. 選取 [cog-search-demo],然後按一下 [上傳] 以開啟其中儲存下載檔案的資料夾。Select cog-search-demo and then click Upload to open the folder where you saved the download files. 將十四個檔案全選,然後按一下 [確定] 加以上傳。Select all fourteen files and click OK to upload.

    上傳範例檔案Upload sample files

  9. 在您離開 Azure 儲存體之前,請取得連接字串,以便在 Azure 認知搜尋中制定連線。Before you leave Azure Storage, get a connection string so that you can formulate a connection in Azure Cognitive Search.

    1. 往回瀏覽到儲存體帳戶的 [概觀] 頁面 (我們使用 blobstoragewestus 作為範例)。Browse back to the Overview page of your storage account (we used blobstoragewestus as an example).

    2. 在左側導覽窗格中,選取 [存取金鑰] 並複製其中一個連接字串。In the left navigation pane, select Access keys and copy one of the connection strings.

    連接字串應為類似於下列範例的 URL:The connection string is a URL similar to the following example:

    DefaultEndpointsProtocol=https;AccountName=blobstoragewestus;AccountKey=<your account key>;EndpointSuffix=core.windows.net
    
  10. 將連接字串儲存到記事本。Save the connection string to Notepad. 您稍後設定資料來源連線時會用到該字串。You'll need it later when setting up the data source connection.

認知服務Cognitive Services

AI 擴充以認知服務為後盾,包括用於自然語言和影像處理的文字分析和電腦視覺。AI enrichment is backed by Cognitive Services, including Text Analytics and Computer Vision for natural language and image processing. 如果您的目標是要完成實際的原型或專案,您應在此時佈建認知服務 (位於 Azure 認知搜尋所在的區域),以便將其連結至索引作業。If your objective was to complete an actual prototype or project, you would at this point provision Cognitive Services (in the same region as Azure Cognitive Search) so that you can attach it to indexing operations.

不過,在此練習中,您可以略過資源佈建,因為 Azure 認知搜尋可以在幕後連線到認知服務,並為每個索引子執行提供 20 筆免費交易。For this exercise, however, you can skip resource provisioning because Azure Cognitive Search can connect to Cognitive Services behind the scenes and give you 20 free transactions per indexer run. 由於本教學課程會使用 14 筆交易,因此使用免費配置就已足夠。Since this tutorial uses 14 transactions, the free allocation is sufficient. 針對較大型的專案,請考慮以隨用隨付 S0 層來佈建認知服務。For larger projects, plan on provisioning Cognitive Services at the pay-as-you-go S0 tier. 如需詳細資訊,請參閱連結認知服務For more information, see Attach Cognitive Services.

第三個元件是 Azure 認知搜尋,您可以在入口網站中建立或在訂用帳戶中尋找現有的搜尋服務The third component is Azure Cognitive Search, which you can create in the portal or find an existing search service in your subscription.

您可以使用免費層來完成此逐步解說。You can use the Free tier to complete this walkthrough.

若要與 Azure 認知搜尋服務互動,您需要服務 URL 和存取金鑰。To interact with your Azure Cognitive Search service you will need the service URL and an access key. 建立搜尋服務時需要這兩項資料,因此如果您將 Azure 認知搜尋新增至您的訂用帳戶,請依照下列步驟來取得必要的資訊:A search service is created with both, so if you added Azure Cognitive Search to your subscription, follow these steps to get the necessary information:

  1. 登入 Azure 入口網站,並在搜尋服務的 [概觀] 頁面上取得 URL。Sign in to the Azure portal, and in your search service Overview page, get the URL. 範例端點看起來會像是 https://mydemo.search.windows.netAn example endpoint might look like https://mydemo.search.windows.net.

  2. 在 [設定] > [金鑰] 中,複製服務上完整權限的管理金鑰。In Settings > Keys, copy an admin key for full rights on the service. 可互換的管理金鑰有兩個,可在您需要變換金鑰時提供商務持續性。There are two interchangeable admin keys, provided for business continuity in case you need to roll one over. 您可以在新增、修改及刪除物件的要求上使用主要或次要金鑰。You can use either the primary or secondary key on requests for adding, modifying, and deleting objects.

    一併取得查詢金鑰。Get the query key as well. 最佳做法是發出具有唯讀存取權的查詢要求。It's a best practice to issue query requests with read-only access.

    取得服務名稱及管理和查詢金鑰

擁有有效的金鑰就能為每個要求在傳送要求之應用程式與處理要求之服務間建立信任。Having a valid key establishes trust, on a per request basis, between the application sending the request and the service that handles it.

2 - 設定您的環境2 - Set up your environment

一開始請開啟 Visual Studio,並建立可在 .NET Core 上執行的新主控台應用程式專案。Begin by opening Visual Studio and creating a new Console App project that can run on .NET Core.

安裝 Azure.Search.DocumentsInstall Azure.Search.Documents

Azure 認知搜尋 .NET SDK 包含用戶端程式庫,可讓您管理索引、資料來源、索引子及技能集,以及上傳和管理文件,還可以執行查詢,而且一律不需要處理 HTTP 和 JSON 的細節。The Azure Cognitive Search .NET SDK consists of a client library that enables you to manage your indexes, data sources, indexers, and skillsets, as well as upload and manage documents and execute queries, all without having to deal with the details of HTTP and JSON. 此用戶端程式庫會以 NuGet 套件的形式散發。This client library is distributed as a NuGet package.

針對此專案,請安裝第 11 版或更新版本的 Azure.Search.Documents 和最新版本的 Microsoft.Extensions.ConfigurationFor this project, install version 11 or later of the Azure.Search.Documents and the latest version of Microsoft.Extensions.Configuration.

  1. 在 Visual Studio 中,依序選取 [工具] > [NuGet 套件管理員] > [管理解決方案的 NuGet 套件]。In Visual Studio, select Tools > NuGet Package Manager > Manage NuGet Packages for Solution...

  2. 瀏覽 Azure.Search.DocumentBrowse for Azure.Search.Document.

  3. 選取最新版本,然後按一下 [安裝]。Select the latest version and then click Install.

  4. 重複先前的步驟以安裝 Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.JsonRepeat the previous steps to install Microsoft.Extensions.Configuration and Microsoft.Extensions.Configuration.Json.

新增服務連線資訊Add service connection information

  1. 在 [方案總管] 中以滑鼠右鍵按一下您的專案,然後選取 [新增] > [新增項目...]。Right-click on your project in the Solution Explorer and select Add > New Item... .

  2. 將檔案命名為 appsettings.json,然後選取 [新增]。Name the file appsettings.json and select Add.

  3. 將此檔案包含在您的輸出目錄中。Include this file in your output directory.

    1. 以滑鼠右鍵按一下 appsettings.json,然後選取 [屬性]。Right-click on appsettings.json and select Properties.
    2. 將 [複製到輸出目錄] 的值變更為 [有更新時才複製]。Change the value of Copy to Output Directory to Copy if newer.
  4. 將下列 JSON 複製到新的 JSON 檔案。Copy the below JSON into your new JSON file.

    {
      "SearchServiceUri": "Put your search service URI here",
      "SearchServiceAdminApiKey": "Put your primary or secondary API key here",
      "SearchServiceQueryApiKey": "Put your query API key here",
      "AzureBlobConnectionString": "Put your Azure Blob connection string here",
    }
    

新增您的搜尋服務與 Blob 儲存體帳戶資訊。Add your search service and blob storage account information. 您應該記得,您可以從上一節所指示的服務佈建步驟中取得這項資訊。Recall that you can get this information from the service provisioning steps indicated in the previous section.

針對 SearchServiceUri,輸入完整的 URL。For SearchServiceUri, enter the full URL.

新增命名空間Add namespaces

Program.cs 中,新增下列命名空間。In Program.cs, add the following namespaces.

using Azure;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EnrichwithAI

建立用戶端Create a client

Main 底下建立 SearchIndexClientSearchIndexerClient 的執行個體。Create an instance of a SearchIndexClient and a SearchIndexerClient under Main.

public static void Main(string[] args)
{
    // Create service client
    IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
    IConfigurationRoot configuration = builder.Build();

    string searchServiceUri = configuration["SearchServiceUri"];
    string adminApiKey = configuration["SearchServiceAdminApiKey"];
    string cognitiveServicesKey = configuration["CognitiveServicesKey"];

    SearchIndexClient indexClient = new SearchIndexClient(new Uri(searchServiceUri), new AzureKeyCredential(adminApiKey));
    SearchIndexerClient indexerClient = new SearchIndexerClient(new Uri(searchServiceUri), new AzureKeyCredential(adminApiKey));
}

注意

用戶端會連線到您的搜尋服務。The clients connect to your search service. 為了避免開啟太多連線,如果可以,您應該嘗試在應用程式中共用單一執行個體。In order to avoid opening too many connections, you should try to share a single instance in your application if possible. 方法是可啟用這類共用的安全執行緒。The methods are thread-safe to enable such sharing.

新增函式以在失敗期間結束程式Add function to exit the program during failure

本教學課程旨在協助您了解編製索引管線的每個步驟。This tutorial is meant to help you understand each step of the indexing pipeline. 如果發生嚴重問題,導致程式無法建立資料來源、技能集、索引或索引子,程式將會輸出錯誤訊息並結束,讓您可以得知並解決問題。If there is a critical issue that prevents the program from creating the data source, skillset, index, or indexer the program will output the error message and exit so that the issue can be understood and addressed.

ExitProgram 新增至 Main,以處理需要結束程式的案例。Add ExitProgram to Main to handle scenarios that require the program to exit.

private static void ExitProgram(string message)
{
    Console.WriteLine("{0}", message);
    Console.WriteLine("Press any key to exit the program...");
    Console.ReadKey();
    Environment.Exit(0);
}

3 - 建立管線3 - Create the pipeline

在 Azure 認知搜尋中,AI 處理會在編製索引 (或資料擷取) 期間進行。In Azure Cognitive Search, AI processing occurs during indexing (or data ingestion). 逐步解說的這個部分會建立四個物件:資料來源、索引定義、技能集、索引子。This part of the walkthrough creates four objects: data source, index definition, skillset, indexer.

步驟 1:建立資料來源Step 1: Create a data source

SearchIndexerClient 具有 DataSourceName 屬性,您可以將其設定為 SearchIndexerDataSourceConnection 物件。SearchIndexerClient has a DataSourceName property that you can set to a SearchIndexerDataSourceConnection object. 此物件會提供您建立、列出、更新或刪除 Azure 認知搜尋資料來源所需的所有方法。This object provides all the methods you need to create, list, update, or delete Azure Cognitive Search data sources.

呼叫 indexerClient.CreateOrUpdateDataSourceConnection(dataSource) 來建立新的 SearchIndexerDataSourceConnection 執行個體。Create a new SearchIndexerDataSourceConnection instance by calling indexerClient.CreateOrUpdateDataSourceConnection(dataSource). 下列程式碼會建立 AzureBlob 類型的資料來源。The following code creates a data source of type AzureBlob.

private static SearchIndexerDataSourceConnection CreateOrUpdateDataSource(SearchIndexerClient indexerClient, IConfigurationRoot configuration)
{
    SearchIndexerDataSourceConnection dataSource = new SearchIndexerDataSourceConnection(
        name: "demodata",
        type: SearchIndexerDataSourceType.AzureBlob,
        connectionString: configuration["AzureBlobConnectionString"],
        container: new SearchIndexerDataContainer("cog-search-demo"))
    {
        Description = "Demo files to demonstrate cognitive search capabilities."
    };

    // The data source does not need to be deleted if it was already created
    // since we are using the CreateOrUpdate method
    try
    {
        indexerClient.CreateOrUpdateDataSourceConnection(dataSource);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed to create or update the data source\n Exception message: {0}\n", ex.Message);
        ExitProgram("Cannot continue without a data source");
    }

    return dataSource;
}

如果要求成功,方法將傳回已建立的資料來源。For a successful request, the method will return the data source that was created. 如果要求發生問題 (例如無效的參數),則方法將擲回例外狀況。If there is a problem with the request, such as an invalid parameter, the method will throw an exception.

現在,在 Main 中新增一行,以呼叫您剛才新增的 CreateOrUpdateDataSource 函式。Now add a line in Main to call the CreateOrUpdateDataSource function that you've just added.

// Create or Update the data source
Console.WriteLine("Creating or updating the data source...");
SearchIndexerDataSourceConnection dataSource = CreateOrUpdateDataSource(indexerClient, configuration);

建置並執行解決方案。Build and run the solution. 由於這是您第一個要求,請查看 Azure 入口網站,以確認已在 Azure 認知搜尋中建立資料來源。Since this is your first request, check the Azure portal to confirm the data source was created in Azure Cognitive Search. 在搜尋服務概觀頁面上,確認 [資料來源] 清單有新的項目。On the search service overview page, verify the Data Sources list has a new item. 您可能需要等候幾分鐘,讓入口網站重新整理頁面。You might need to wait a few minutes for the portal page to refresh.

入口網站中的資料來源圖格Data sources tile in the portal

步驟 2:建立技能集Step 2: Create a skillset

在此節中,您會定義一組要套用至資料的擴充步驟。In this section, you define a set of enrichment steps that you want to apply to your data. 每個擴充步驟均稱為一個「技能」,而一組擴充步驟會稱為一個「技能集」。Each enrichment step is called a skill and the set of enrichment steps, a skillset. 本教學課程會使用為技能集內建的認知技能This tutorial uses built-in cognitive skills for the skillset:

  • 光學字元辨識可辨識影像檔案中的列印和手寫文字。Optical Character Recognition to recognize printed and handwritten text in image files.

  • 文字合併可將一組欄位中的文字合併成單一欄位。Text Merger to consolidate text from a collection of fields into a single field.

  • 語言偵測,用以識別內容的語言。Language Detection to identify the content's language.

  • 文字分割,用以在呼叫關鍵片語擷取技能和實體辨識技能之前,將大型內容分成較小的區塊。Text Split to break large content into smaller chunks before calling the key phrase extraction skill and the entity recognition skill. 關鍵片語擷取和實體辨識接受 50,000 個字元 (含) 以下的輸入。Key phrase extraction and entity recognition accept inputs of 50,000 characters or less. 有些範例檔案需要進行分割,以符合這項限制。A few of the sample files need splitting up to fit within this limit.

  • 實體辨識,用以從 Blob 容器中的內容擷取組織名稱。Entity Recognition for extracting the names of organizations from content in the blob container.

  • 關鍵片語擷取,用以提取最高排名的關鍵片語。Key Phrase Extraction to pull out the top key phrases.

在最初處理期間,Azure 認知搜尋會萃取每份文件,以擷取不同檔案格式的內容。During initial processing, Azure Cognitive Search cracks each document to extract content from different file formats. 來自來源檔案的文字,會將文字放入產生的 content 欄位中,每份文件一個欄位。Text originating in the source file is placed into a generated content field, one for each document. 如前所述,將輸入設定為 "/document/content" 以使用此文字。As such, set the input as "/document/content" to use this text. 影像內容會放入產生的 normalized_images 欄位中,在技能集中指定為 /document/normalized_images/*Image content is placed into a generated normalized_images field, specified in a skillset as /document/normalized_images/*.

輸出可以對應至索引、作為下游技能的輸入,或在使用語言代碼時同時作為對應和輸入。Outputs can be mapped to an index, used as input to a downstream skill, or both as is the case with language code. 在索引中,語言代碼可用於篩選。In the index, a language code is useful for filtering. 作為輸入時,文字分析技能會使用語言代碼指出斷字方面的語言規則。As an input, language code is used by text analysis skills to inform the linguistic rules around word breaking.

如需技能集基本概念的詳細資訊,請參閱如何定義技能集For more information about skillset fundamentals, see How to define a skillset.

OCR 技術OCR skill

OCR 技能可從影像中擷取文字。The OCR skill extracts text from images. 此技能假設 normalized_images 欄位存在。This skill assumes that a normalized_images field exists. 為產生此欄位,我們會在此教學課程稍後,於索引子定義中將 "imageAction" 設定設為 "generateNormalizedImages"To generate this field, later in the tutorial we'll set the "imageAction" configuration in the indexer definition to "generateNormalizedImages".

private static OcrSkill CreateOcrSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("image")
    {
        Source = "/document/normalized_images/*"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("text")
    {
        TargetName = "text"
    });

    OcrSkill ocrSkill = new OcrSkill(inputMappings, outputMappings)
    {
        Description = "Extract text (plain and structured) from image",
        Context = "/document/normalized_images/*",
        DefaultLanguageCode = OcrSkillLanguage.En,
        ShouldDetectOrientation = true
    };

    return ocrSkill;
}

合併技能Merge skill

在此節中,您將建立 合併 技能,來合併文件內容欄位與 OCR 技能所產生的文字。In this section, you'll create a Merge skill that merges the document content field with the text that was produced by the OCR skill.

private static MergeSkill CreateMergeSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("text")
    {
        Source = "/document/content"
    });
    inputMappings.Add(new InputFieldMappingEntry("itemsToInsert")
    {
        Source = "/document/normalized_images/*/text"
    });
    inputMappings.Add(new InputFieldMappingEntry("offsets")
    {
        Source = "/document/normalized_images/*/contentOffset"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("mergedText")
    {
        TargetName = "merged_text"
    });

    MergeSkill mergeSkill = new MergeSkill(inputMappings, outputMappings)
    {
        Description = "Create merged_text which includes all the textual representation of each image inserted at the right location in the content field.",
        Context = "/document",
        InsertPreTag = " ",
        InsertPostTag = " "
    };

    return mergeSkill;
}

語言偵測技能Language detection skill

語言偵測 技能會偵測輸入文字的語言,並針對要求所提交的每份文件回報單一語言代碼。The Language Detection skill detects the language of the input text and reports a single language code for every document submitted on the request. 我們將使用 語言偵測 技能的輸出作為 文字分割 技能輸入的一部分。We'll use the output of the Language Detection skill as part of the input to the Text Split skill.

private static LanguageDetectionSkill CreateLanguageDetectionSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("text")
    {
        Source = "/document/merged_text"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("languageCode")
    {
        TargetName = "languageCode"
    });

    LanguageDetectionSkill languageDetectionSkill = new LanguageDetectionSkill(inputMappings, outputMappings)
    {
        Description = "Detect the language used in the document",
        Context = "/document"
    };

    return languageDetectionSkill;
}

文字分割技能Text split skill

下列 分割 技能將依頁面分割文字,並按照 String.Length 所測量的來將其限制為 4,000 個字元。The below Split skill will split text by pages and limit the page length to 4,000 characters as measured by String.Length. 此演算法會嘗試將文字分割成區塊,且區塊大小最多為 maximumPageLengthThe algorithm will try to split the text into chunks that are at most maximumPageLength in size. 在此案例中,演算法將盡量在例句邊界斷句,好讓區塊大小稍微小於 maximumPageLengthIn this case, the algorithm will do its best to break the sentence on a sentence boundary, so the size of the chunk may be slightly less than maximumPageLength.

private static SplitSkill CreateSplitSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("text")
    {
        Source = "/document/merged_text"
    });
    inputMappings.Add(new InputFieldMappingEntry("languageCode")
    {
        Source = "/document/languageCode"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("textItems")
    {
        TargetName = "pages",
    });

    SplitSkill splitSkill = new SplitSkill(inputMappings, outputMappings)
    {
        Description = "Split content into pages",
        Context = "/document",
        TextSplitMode = TextSplitMode.Pages,
        MaximumPageLength = 4000,
        DefaultLanguageCode = SplitSkillLanguage.En
    };

    return splitSkill;
}

實體辨識技能Entity recognition skill

EntityRecognitionSkill 執行個體會設定來辨識類別類型 organizationThis EntityRecognitionSkill instance is set to recognize category type organization. 實體辨識 技能也可以辨識類別類型 personlocationThe Entity Recognition skill can also recognize category types person and location.

請注意,[內容] 欄位會設定為附有星號的 "/document/pages/*",這表示會在 "/document/pages" 下方針對每個頁面呼叫擴充步驟。Notice that the "context" field is set to "/document/pages/*" with an asterisk, meaning the enrichment step is called for each page under "/document/pages".

private static EntityRecognitionSkill CreateEntityRecognitionSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("text")
    {
        Source = "/document/pages/*"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("organizations")
    {
        TargetName = "organizations"
    });

    EntityRecognitionSkill entityRecognitionSkill = new EntityRecognitionSkill(inputMappings, outputMappings)
    {
        Description = "Recognize organizations",
        Context = "/document/pages/*",
        DefaultLanguageCode = EntityRecognitionSkillLanguage.En
    };
    entityRecognitionSkill.Categories.Add(EntityCategory.Organization);

    return entityRecognitionSkill;
}

關鍵片語擷取技能Key phrase extraction skill

如同剛建立的 EntityRecognitionSkill 執行個體,會針對文件的每個頁面呼叫 關鍵片語擷取 技能。Like the EntityRecognitionSkill instance that was just created, the Key Phrase Extraction skill is called for each page of the document.

private static KeyPhraseExtractionSkill CreateKeyPhraseExtractionSkill()
{
    List<InputFieldMappingEntry> inputMappings = new List<InputFieldMappingEntry>();
    inputMappings.Add(new InputFieldMappingEntry("text")
    {
        Source = "/document/pages/*"
    });
    inputMappings.Add(new InputFieldMappingEntry("languageCode")
    {
        Source = "/document/languageCode"
    });

    List<OutputFieldMappingEntry> outputMappings = new List<OutputFieldMappingEntry>();
    outputMappings.Add(new OutputFieldMappingEntry("keyPhrases")
    {
        TargetName = "keyPhrases"
    });

    KeyPhraseExtractionSkill keyPhraseExtractionSkill = new KeyPhraseExtractionSkill(inputMappings, outputMappings)
    {
        Description = "Extract the key phrases",
        Context = "/document/pages/*",
        DefaultLanguageCode = KeyPhraseExtractionSkillLanguage.En
    };

    return keyPhraseExtractionSkill;
}

建置和建立技能集Build and create the skillset

使用您建立的技能來建置 SkillsetBuild the Skillset using the skills you created.

private static SearchIndexerSkillset CreateOrUpdateDemoSkillSet(SearchIndexerClient indexerClient, IList<SearchIndexerSkill> skills,string cognitiveServicesKey)
{
    SearchIndexerSkillset skillset = new SearchIndexerSkillset("demoskillset", skills)
    {
        Description = "Demo skillset",
        CognitiveServicesAccount = new CognitiveServicesAccountKey(cognitiveServicesKey)
    };

    // Create the skillset in your search service.
    // The skillset does not need to be deleted if it was already created
    // since we are using the CreateOrUpdate method
    try
    {
        indexerClient.CreateOrUpdateSkillset(skillset);
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine("Failed to create the skillset\n Exception message: {0}\n", ex.Message);
        ExitProgram("Cannot continue without a skillset");
    }

    return skillset;
}

將下列數行新增至 MainAdd the following lines to Main.

// Create the skills
Console.WriteLine("Creating the skills...");
OcrSkill ocrSkill = CreateOcrSkill();
MergeSkill mergeSkill = CreateMergeSkill();
EntityRecognitionSkill entityRecognitionSkill = CreateEntityRecognitionSkill();
LanguageDetectionSkill languageDetectionSkill = CreateLanguageDetectionSkill();
SplitSkill splitSkill = CreateSplitSkill();
KeyPhraseExtractionSkill keyPhraseExtractionSkill = CreateKeyPhraseExtractionSkill();

// Create the skillset
Console.WriteLine("Creating or updating the skillset...");
List<SearchIndexerSkill> skills = new List<SearchIndexerSkill>();
skills.Add(ocrSkill);
skills.Add(mergeSkill);
skills.Add(languageDetectionSkill);
skills.Add(splitSkill);
skills.Add(entityRecognitionSkill);
skills.Add(keyPhraseExtractionSkill);

SearchIndexerSkillset skillset = CreateOrUpdateDemoSkillSet(indexerClient, skills, cognitiveServicesKey);

步驟 3:建立索引Step 3: Create an index

在本節中,您會藉由指定要在可搜尋索引中包含哪些欄位以及每個欄位的搜尋屬性,來定義索引結構描述。In this section, you define the index schema by specifying which fields to include in the searchable index, and the search attributes for each field. 欄位具有類型,並且可取用決定如何使用欄位 (可搜尋、可排序等等) 的屬性。Fields have a type and can take attributes that determine how the field is used (searchable, sortable, and so forth). 索引中的欄位名稱不需要完全符合來源中的欄位名稱。Field names in an index are not required to identically match the field names in the source. 在後續步驟中,您可以在索引子中新增欄位對應以連接來源-目的地欄位。In a later step, you add field mappings in an indexer to connect source-destination fields. 針對此步驟,請使用與搜尋應用程式相關的欄位命名慣例來定義索引。For this step, define the index using field naming conventions pertinent to your search application.

此練習會使用下列欄位和欄位類型:This exercise uses the following fields and field types:

欄位名稱Field names 欄位類型Field types
id Edm.StringEdm.String
content Edm.StringEdm.String
languageCode Edm.StringEdm.String
keyPhrases List<Edm.String>List<Edm.String>
organizations List<Edm.String>List<Edm.String>

建立 DemoIndex 類別Create DemoIndex Class

此索引的欄位會使用模型類別來定義。The fields for this index are defined using a model class. 每個模型類別的屬性皆具有屬性,會判斷對應索引欄位的搜尋相關行為。Each property of the model class has attributes which determine the search-related behaviors of the corresponding index field.

我們會將模型類別新增至新的 C# 檔案。We'll add the model class to a new C# file. 以滑鼠右鍵按一下您的專案,然後選取 [新增] > [新增項目]、選取 [類別] 並將檔案命名為 DemoIndex.cs,然後選取 [新增]。Right click on your project and select Add > New Item..., select "Class" and name the file DemoIndex.cs, then select Add.

請務必指出您想要使用來自 Azure.Search.Documents.IndexesSystem.Text.Json.Serialization 命名空間的類型。Make sure to indicate that you want to use types from the Azure.Search.Documents.Indexes and System.Text.Json.Serialization namespaces.

將下列模型類別定義新增至 DemoIndex.cs,然後將它包含於您將建立索引的相同命名空間。Add the below model class definition to DemoIndex.cs and include it in the same namespace where you'll create the index.

using Azure.Search.Documents.Indexes;
using System.Text.Json.Serialization;

namespace EnrichwithAI
{
    // The SerializePropertyNamesAsCamelCase is currently unsupported as of this writing. 
    // Replace it with JsonPropertyName
    public class DemoIndex
    {
        [SearchableField(IsSortable = true, IsKey = true)]
        [JsonPropertyName("id")]
        public string Id { get; set; }

        [SearchableField]
        [JsonPropertyName("content")]
        public string Content { get; set; }

        [SearchableField]
        [JsonPropertyName("languageCode")]
        public string LanguageCode { get; set; }

        [SearchableField]
        [JsonPropertyName("keyPhrases")]
        public string[] KeyPhrases { get; set; }

        [SearchableField]
        [JsonPropertyName("organizations")]
        public string[] Organizations { get; set; }
    }
}

既然您已定義模型類別,請返回 Program.cs,您現在可以非常輕鬆地建立索引定義。Now that you've defined a model class, back in Program.cs you can create an index definition fairly easily. 此索引的名稱將會是 demoindexThe name for this index will be demoindex. 如果已有索引使用該名稱,將會加以刪除。If an index already exists with that name, it will be deleted.

private static SearchIndex CreateDemoIndex(SearchIndexClient indexClient)
{
    FieldBuilder builder = new FieldBuilder();
    var index = new SearchIndex("demoindex")
    {
        Fields = builder.Build(typeof(DemoIndex))
    };

    try
    {
        indexClient.GetIndex(index.Name);
        indexClient.DeleteIndex(index.Name);
    }
    catch (RequestFailedException ex) when (ex.Status == 404)
    {
        //if the specified index not exist, 404 will be thrown.
    }

    try
    {
        indexClient.CreateIndex(index);
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine("Failed to create the index\n Exception message: {0}\n", ex.Message);
        ExitProgram("Cannot continue without an index");
    }

    return index;
}

在測試期間,您可能發現您正嘗試多次建立索引。During testing, you may find that you're attempting to create the index more than once. 基於此緣故,請檢查以查看您即將建立的索引是否已經存在,然後再嘗試建立它。Because of this, check to see if the index that you're about to create already exists before attempting to create it.

將下列數行新增至 MainAdd the following lines to Main.

// Create the index
Console.WriteLine("Creating the index...");
SearchIndex demoIndex = CreateDemoIndex(indexClient);

新增下列 using 陳述式以解析「消除歧義」參考。Add the following using statement to resolve the disambiguate reference.

using Index = Azure.Search.Documents.Indexes.Models;

若要深入了解索引概念,請參閱建立索引 (REST API)To learn more about index concepts, see Create Index (REST API).

步驟 4:建立及執行索引子Step 4: Create and run an indexer

至此,您已建立資料來源、技能集和索引。So far you have created a data source, a skillset, and an index. 這三項元件構成了索引子的一部分,可將各項資料一併提取到多階段的單一作業中。These three components become part of an indexer that pulls each piece together into a single multi-phased operation. 若要在索引子中結合這些項目,您必須定義欄位對應。To tie these together in an indexer, you must define field mappings.

  • fieldMappings 會在技能集之前進行處理,用來將資料來源中的來源欄位對應到索引中的目標欄位。The fieldMappings are processed before the skillset, mapping source fields from the data source to target fields in an index. 如果兩端上的欄位名稱和類型都相同,則不需要任何對應。If field names and types are the same at both ends, no mapping is required.

  • outputFieldMappings 會在技能集之後進行處理,用來參考文件萃取或擴充後才建立的 sourceFieldNames。The outputFieldMappings are processed after the skillset, referencing sourceFieldNames that don't exist until document cracking or enrichment creates them. targetFieldName 是索引中的欄位。The targetFieldName is a field in an index.

除了將輸入連結至輸出,您也可以使用欄位對應來將資料結構壓平合併。In addition to hooking up inputs to outputs, you can also use field mappings to flatten data structures. 如需詳細資訊,請參閱如何將擴充的欄位對應至可搜尋的索引For more information, see How to map enriched fields to a searchable index.

private static SearchIndexer CreateDemoIndexer(SearchIndexerClient indexerClient, SearchIndexerDataSourceConnection dataSource, SearchIndexerSkillset skillSet, SearchIndex index)
{
    IndexingParameters indexingParameters = new IndexingParameters()
    {
        MaxFailedItems = -1,
        MaxFailedItemsPerBatch = -1,
    };
    indexingParameters.Configuration.Add("dataToExtract", "contentAndMetadata");
    indexingParameters.Configuration.Add("imageAction", "generateNormalizedImages");

    SearchIndexer indexer = new SearchIndexer("demoindexer", dataSource.Name, index.Name)
    {
        Description = "Demo Indexer",
        SkillsetName = skillSet.Name,
        Parameters = indexingParameters
    };

    FieldMappingFunction mappingFunction = new FieldMappingFunction("base64Encode");
    mappingFunction.Parameters.Add("useHttpServerUtilityUrlTokenEncode", true);

    indexer.FieldMappings.Add(new FieldMapping("metadata_storage_path")
    {
        TargetFieldName = "id",
        MappingFunction = mappingFunction

    });
    indexer.FieldMappings.Add(new FieldMapping("content")
    {
        TargetFieldName = "content"
    });

    indexer.OutputFieldMappings.Add(new FieldMapping("/document/pages/*/organizations/*")
    {
        TargetFieldName = "organizations"
    });
    indexer.OutputFieldMappings.Add(new FieldMapping("/document/pages/*/keyPhrases/*")
    {
        TargetFieldName = "keyPhrases"
    });
    indexer.OutputFieldMappings.Add(new FieldMapping("/document/languageCode")
    {
        TargetFieldName = "languageCode"
    });

    try
    {
        indexerClient.GetIndexer(indexer.Name);
        indexerClient.DeleteIndexer(indexer.Name);
    }
    catch (RequestFailedException ex) when (ex.Status == 404)
    {
        //if the specified indexer not exist, 404 will be thrown.
    }

    try
    {
        indexerClient.CreateIndexer(indexer);
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine("Failed to create the indexer\n Exception message: {0}\n", ex.Message);
        ExitProgram("Cannot continue without creating an indexer");
    }

    return indexer;
}

將下列數行新增至 MainAdd the following lines to Main.

// Create the indexer, map fields, and execute transformations
Console.WriteLine("Creating the indexer and executing the pipeline...");
SearchIndexer demoIndexer = CreateDemoIndexer(indexerClient, dataSource, skillset, demoIndex);

預期索引子處理需要一些時間才能完成。Expect indexer processing to take some time to complete. 即使資料集很小,分析技能仍需要大量計算。Even though the data set is small, analytical skills are computation-intensive. 某些技能 (例如影像分析) 需要長時間執行。Some skills, such as image analysis, are long-running.

提示

建立索引子時會叫用管線。Creating an indexer invokes the pipeline. 資料的存取、輸入和輸出的對應或作業順序若有問題,都會在這個階段中出現。If there are problems reaching the data, mapping inputs and outputs, or order of operations, they appear at this stage.

探索建立索引子Explore creating the indexer

程式碼會將 "maxFailedItems" 設定為 -1,這會指示索引引擎在資料匯入期間忽略錯誤。The code sets "maxFailedItems" to -1, which instructs the indexing engine to ignore errors during data import. 此設定有其用處,因為示範資料來源中只有少量文件。This is useful because there are so few documents in the demo data source. 若要有較大的資料來源,您應將值設定為大於 0。For a larger data source, you would set the value to greater than 0.

另請注意,"dataToExtract" 會設定為 "contentAndMetadata"Also notice the "dataToExtract" is set to "contentAndMetadata". 此陳述式會指示索引子自動擷取不同檔案格式的內容,以及每個檔案的相關中繼資料。This statement tells the indexer to automatically extract the content from different file formats as well as metadata related to each file.

在擷取內容時,您可以設定 imageAction,以從在資料來源中找到的影像擷取文字。When content is extracted, you can set imageAction to extract text from images found in the data source. 設定為 "generateNormalizedImages" 組態的 "imageAction" 可與 OCR 技能和文字合併技能相結合,以指示索引子從影像中擷取文字 (例如,從「停」交通號誌中擷取「停」這個字),並將其內嵌為內容欄位的一部分。The "imageAction" set to "generateNormalizedImages" configuration, combined with the OCR Skill and Text Merge Skill, tells the indexer to extract text from the images (for example, the word "stop" from a traffic Stop sign), and embed it as part of the content field. 此行為適用於內嵌在文件中的影像 (例如 PDF 內的影像),以及在資料來源中找到的影像 (例如 JPG 檔案)。This behavior applies to both the images embedded in the documents (think of an image inside a PDF), as well as images found in the data source, for instance a JPG file.

4 - 監視編製索引4 - Monitor indexing

索引子經定義後,將會在您提交要求時自動執行。Once the indexer is defined, it runs automatically when you submit the request. 根據您所定義的認知技能,索引編製所需的時間可能會超出您的預期。Depending on which cognitive skills you defined, indexing can take longer than you expect. 若要了解索引子是否仍在執行,請使用 GetStatus 方法。To find out whether the indexer is still running, use the GetStatus method.

private static void CheckIndexerOverallStatus(SearchIndexerClient indexerClient, SearchIndexer indexer)
{
    try
    {
        var demoIndexerExecutionInfo = indexerClient.GetIndexerStatus(indexer.Name);

        switch (demoIndexerExecutionInfo.Value.Status)
        {
            case IndexerStatus.Error:
                ExitProgram("Indexer has error status. Check the Azure Portal to further understand the error.");
                break;
            case IndexerStatus.Running:
                Console.WriteLine("Indexer is running");
                break;
            case IndexerStatus.Unknown:
                Console.WriteLine("Indexer status is unknown");
                break;
            default:
                Console.WriteLine("No indexer information");
                break;
        }
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine("Failed to get indexer overall status\n Exception message: {0}\n", ex.Message);
    }
}

demoIndexerExecutionInfo 表示索引子的目前狀態和執行記錄。demoIndexerExecutionInfo represents the current status and execution history of an indexer.

某些來源檔案和技能的組合常會出現警告,這並不一定表示有問題。Warnings are common with some source file and skill combinations and do not always indicate a problem. 在本教學課程中,警告是良性的 (例如,沒有來自 JPEG 檔案的文字輸入)。In this tutorial, the warnings are benign (for example, no text inputs from the JPEG files).

將下列數行新增至 MainAdd the following lines to Main.

// Check indexer overall status
Console.WriteLine("Check the indexer overall status...");
CheckIndexerOverallStatus(indexerClient, demoIndexer);

在 Azure 認知搜尋教學課程主控台應用程式中,我們通常會在執行傳回結果的查詢之前加上 2 秒的延遲,但是因為擴充需要數分鐘的時間才能完成,所以我們將會關閉主控台應用程式,並改用另一種方法。In Azure Cognitive Search tutorial console apps, we typically add a 2-second delay before running queries that return results, but because enrichment takes several minutes to complete, we'll close the console app and use another approach instead.

最簡單的選項是入口網站中的搜尋總管The easiest option is Search explorer in the portal. 您可以先執行會傳回所有文件的空白查詢,或傳回管線所建立新欄位內容的更加鎖定目標搜尋。You can first run an empty query that returns all documents, or a more targeted search that returns new field content created by the pipeline.

  1. 在 Azure 入口網站的 [搜尋概觀] 頁面中,選取 [索引]。In Azure portal, in the search Overview page, select Indexes.

  2. 在清單中尋找 demoindexFind demoindex in the list. 應該有 14 份文件。It should have 14 documents. 如果文件計數為零,表示索引子仍在執行中,或頁面尚未重新整理。If the document count is zero, the indexer is either still running or the page hasn't been refreshed yet.

  3. 選取 [demoindex]。Select demoindex. 搜尋總管是第一個索引標籤。Search explorer is the first tab.

  4. 只要載入第一份文件,您就可以查詢內容。Content is searchable as soon as the first document is loaded. 若要確認內容是否存在,請按一下 [搜尋] 來執行未指定的查詢。To verify content exists, run an unspecified query by clicking Search. 此查詢會傳回所有目前已編製索引的文件,讓您了解索引所包含的內容。This query returns all currently indexed documents, giving you an idea of what the index contains.

  5. 接下來,貼上下列字串以取得更容易管理的結果:search=*&$select=id, languageCode, organizationsNext, paste in the following string for more manageable results: search=*&$select=id, languageCode, organizations

重設並重新執行Reset and rerun

在開發的早期實驗階段中若要設計反覆項目,最實用的方法是從 Azure 認知搜尋中刪除物件,並讓您的程式碼加以重建。In the early experimental stages of development, the most practical approach for design iteration is to delete the objects from Azure Cognitive Search and allow your code to rebuild them. 資源名稱是唯一的。Resource names are unique. 刪除物件可讓您使用相同的名稱加以重新建立。Deleting an object lets you recreate it using the same name.

本教學課程的範例程式碼會檢查是否有現有的物件,並將其刪除,以便您重新執行程式碼。The sample code for this tutorial checks for existing objects and deletes them so that you can rerun your code. 您也可以使用入口網站來刪除索引、索引子、資料來源和技能集。You can also use the portal to delete indexes, indexers, data sources, and skillsets.

重要心得Takeaways

本教學課程示範了藉由建立下列元件組件來建置擴充索引管線的基本步驟:資料來源、技能集、索引和索引子。This tutorial demonstrated the basic steps for building an enriched indexing pipeline through the creation of component parts: a data source, skillset, index, and indexer.

內建技能已透過輸入和輸出連同技能集定義和鏈結技能的機制一起導入。Built-in skills were introduced, along with skillset definition and the mechanics of chaining skills together through inputs and outputs. 您也已了解在將管線中的擴充值路由至 Azure 認知搜尋服務上的可搜尋索引時,索引子定義中必須要有 outputFieldMappingsYou also learned that outputFieldMappings in the indexer definition is required for routing enriched values from the pipeline into a searchable index on an Azure Cognitive Search service.

最後,您了解到如何測試結果並重設系統,以進行進一步的反覆運算。Finally, you learned how to test results and reset the system for further iterations. 您已了解對索引發出查詢,會傳回由擴充的索引管線建立的輸出。You learned that issuing queries against the index returns the output created by the enriched indexing pipeline. 您也已了解如何檢查索引子狀態,以及在重新執行管線之前應刪除哪些物件。You also learned how to check indexer status, and which objects to delete before rerunning a pipeline.

清除資源Clean up resources

如果您使用自己的訂用帳戶,當專案結束時,建議您移除不再需要的資源。When you're working in your own subscription, at the end of a project, it's a good idea to remove the resources that you no longer need. 讓資源繼續執行可能會產生費用。Resources left running can cost you money. 您可以個別刪除資源,或刪除資源群組以刪除整組資源。You can delete resources individually or delete the resource group to delete the entire set of resources.

您可以使用左導覽窗格中的 [所有資源] 或 [資源群組] 連結,在入口網站中尋找和管理資源。You can find and manage resources in the portal, using the All resources or Resource groups link in the left-navigation pane.

後續步驟Next steps

現在您已熟悉 AI 擴充管線中的所有物件,接下來我們將進一步了解技能集定義和個別技能。Now that you're familiar with all of the objects in an AI enrichment pipeline, let's take a closer look at skillset definitions and individual skills.