Поделиться через


Руководство по C#. Использование наборов навыков для создания содержимого с возможностью поиска в Azure AI

В этом руководстве описано, как использовать пакет SDK Azure для .NET для создания конвейера обогащения ИИ для извлечения содержимого и преобразований во время индексирования.

Наборы навыков добавляют обработку ИИ в необработанное содержимое, что делает его более универсальным и доступным для поиска. После того как вы узнаете, как работают наборы навыков, вы можете поддерживать широкий спектр преобразований: от анализа изображений до обработки естественного языка до настраиваемой обработки, которую вы предоставляете внешне.

В этом руководстве вы узнаете, как:

  • Определите объекты в конвейере обогащения.
  • Создание набора навыков. Вызов OCR, распознавание речи, распознавание сущностей и извлечение ключевых фраз.
  • Выполните конвейер. Создание и загрузка индекса поиска.
  • Проверьте результаты с помощью полнотекстового поиска.

Если у вас еще нет подписки Azure, создайте бесплатную учетную запись Azure, прежде чем начинать работу.

Обзор

В этом руководстве используется C# и клиентская библиотека Azure.Search.Documents для создания источника данных, индекса, индексатора и набора навыков.

Индексатор управляет каждым шагом в конвейере, начиная с извлечения содержимого примеров данных (неструктурированный текст и изображения) в контейнере БОЛЬШИХ двоичных объектов на служба хранилища Azure.

После извлечения содержимого набор навыков выполняет встроенные навыки от Корпорации Майкрософт для поиска и извлечения информации. Эти навыки включают оптическое распознавание символов (OCR) на изображениях, распознавание языка на тексте, извлечении ключевых фраз и распознавании сущностей (организации). Новая информация, созданная набором навыков, отправляется в поля в индексе. После заполнения индекса вы можете использовать поля в запросах, аспектах и фильтрах.

Необходимые компоненты

Примечание.

Вы можете использовать бесплатную службу поиска для этого руководства. Уровень "Бесплатный" ограничивается тремя индексами, тремя индексаторами и тремя источниками данных. В этом руководстве создается по одному объекту из каждой категории. Перед началом работы убедитесь, что у службы есть достаточно места, чтобы принять новые ресурсы.

Загрузка файлов

Скачайте ZIP-файл примера репозитория данных и извлеките его содержимое. Инструкции.

Отправка примеров данных в служба хранилища Azure

  1. В служба хранилища Azure создайте новый контейнер и назовите его cog-search-demo.

  2. Отправьте примеры файлов данных.

  3. Получите строка подключения хранилища, чтобы сформулировать подключение в службе "Поиск ИИ Azure".

    1. В левой части экрана выберите ключи доступа.

    2. Скопируйте строка подключения для одного или двух ключей. Строка подключения аналогичен следующему примеру:

      DefaultEndpointsProtocol=https;AccountName=<your account name>;AccountKey=<your account key>;EndpointSuffix=core.windows.net
      

Службы ИИ Azure

Встроенное обогащение ИИ поддерживается службами ИИ Azure, включая языковую службу и Распознавание ИИ Azure для естественного языка и обработки изображений. Для небольших рабочих нагрузок, таких как это руководство, можно использовать бесплатное выделение 20 транзакций на индексатор. Для более крупных рабочих нагрузок подключите ресурс Служб искусственного интеллекта Azure к набору навыков для оплаты по мере использования.

Копирование URL-адреса службы поиска и ключа API

Для работы с этим руководством для подключений к службе "Поиск ИИ Azure" требуется конечная точка и ключ API. Эти значения можно получить из портал Azure.

  1. Войдите в портал Azure, перейдите на страницу обзора службы поиска и скопируйте URL-адрес. Пример конечной точки может выглядеть так: https://mydemo.search.windows.net.

  2. В разделе "Ключи>параметров" скопируйте ключ администратора. Ключи администратора используются для добавления, изменения и удаления объектов. Существует два взаимозаменяемых ключа администратора. Скопируйте любой из них.

    Снимок экрана: URL-адрес и ключи API в портал Azure.

Настройка среды

Сначала откройте Visual Studio и создайте новый проект консольного приложения, которое будет выполняться на базе .NET Core.

Установка Azure.Search.Documents

Пакет SDK для поиска ИИ Azure состоит из клиентской библиотеки, которая позволяет управлять индексами, источниками данных, индексаторами и наборами навыков, а также отправлять документы и управлять ими, а также выполнять запросы без необходимости работать с подробными сведениями о HTTP и JSON. Эта клиентская библиотека распространяется как пакет NuGet.

Для этого проекта установите Azure.Search.Documents версии 11 или более поздней и Microsoft.Extensions.Configuration самой поздней версии.

  1. В Visual Studio выберите Сервис>Диспетчер пакетов NuGet>Управление пакетами NuGet для решения...

  2. Найдите Azure.Search.Document.

  3. Выберите последнюю версию и нажмите кнопку "Установить".

  4. Повторите предыдущие шаги для установки Microsoft.Extensions.Configuration и Microsoft.Extensions.Configuration.Json.

Добавление сведений о подключении службы

  1. Щелкните правой кнопкой мыши проект в обозревателе решений и последовательно выберите Добавить>Новый элемент....

  2. Присвойте файлу имя appsettings.json и нажмите Добавить.

  3. Добавьте файл в каталог выходных данных.

    1. Щелкните правой кнопкой мыши appsettings.json и выберите пункт Свойства.
    2. Измените значение параметра Копировать в выходной каталог на Копировать более новые.
  4. Скопируйте указанный ниже файл JSON в новый файл JSON.

    {
      "SearchServiceUri": "<YourSearchServiceUri>",
      "SearchServiceAdminApiKey": "<YourSearchServiceAdminApiKey>",
      "SearchServiceQueryApiKey": "<YourSearchServiceQueryApiKey>",
      "AzureAIServicesKey": "<YourMultiRegionAzureAIServicesKey>",
      "AzureBlobConnectionString": "<YourAzureBlobConnectionString>"
    }
    

Укажите службу поиска и добавьте сведения об учетной записи хранения BLOB-объектов. Помните, что эти сведения можно получить во время выполнения действий по подготовке службы из предыдущего раздела.

В поле SearchServiceUri введите полный URL-адрес.

Добавление пространств имен

В файле Program.cs добавьте следующие пространства имен.

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

Создание клиента

Создайте экземпляр класса SearchIndexClient и SearchIndexerClient в разделе 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 azureAiServicesKey = configuration["AzureAIServicesKey"];

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

Примечание.

Клиенты подключатся к службе поиска. Во избежание открытия слишком большого числа подключений в приложении рекомендуется по возможности совместно использовать один экземпляр. Методы поддерживают такое использование, так как являются потокобезопасными.

Добавление функции для выхода из программы во время сбоя

Этот учебник поможет вам разобраться в каждом этапе работы конвейера индексирования. Если возникла критическая проблема, препятствующая созданию программы источника данных, набора навыков, индекса или индексатора, программа выводит сообщение об ошибке и завершает работу, чтобы проблема могла быть понята и устранена.

Добавьте ExitProgram в Main, чтобы обработать сценарии, требующие завершения работы программы.

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

Создание конвейера

В поиске ИИ Azure обработка ИИ выполняется во время индексирования (или приема данных). В этой части пошагового руководства описано, как создать четыре объекта: источник данных, определение индекса, набор навыков и индексатор.

Шаг 1. Создание источника данных

SearchIndexerClient имеет свойство DataSourceName, которое можно задать для объекта SearchIndexerDataSourceConnection. Этот объект предоставляет все методы, необходимые для создания, перечисления, обновления или удаления источников данных поиска ИИ Azure.

Вызовите метод indexerClient.CreateOrUpdateDataSourceConnection(dataSource), чтобы создать экземпляр SearchIndexerDataSourceConnection. Следующий код создает источник данных типа 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 Azure AI 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;
}

Для успешного запроса метод возвращает созданный источник данных. При возникновении проблемы с запросом, например недопустимым параметром, метод вызывает исключение.

Теперь добавьте строку в раздел Main, чтобы вызвать только что добавленную функцию CreateOrUpdateDataSource.

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

Выполните сборку и запуск решения. Так как это первый запрос, проверьте портал Azure, чтобы убедиться, что источник данных был создан в службе "Поиск ИИ Azure". На странице обзора службы поиска проверьте наличие нового элемента в списке "Источники данных". Возможно, потребуется дождаться нескольких минут, пока страница портал Azure будет обновлена.

Плитка источников данных в портал Azure

Шаг 2. Создание набора навыков

В рамках этого раздела вы определите набор требуемых шагов обогащения данных. Каждый шаг обогащения называется навыком, а набор шагов обогащения — набором навыков. В этом руководстве используются встроенные навыки для набора навыков :

  • Оптическое распознавание символов для распознавания печатного и рукописного текста в файлах изображений.

  • Слияние текста для консолидации текста из коллекции полей в одно поле "объединенное содержимое".

  • Распознавание языка для определения языка содержимого.

  • Распознавание сущностей для извлечения названий организаций из содержимого в контейнере больших двоичных объектов.

  • Разделение текста для разбиения объемного содержимого на более мелкие фрагменты перед вызовом навыка извлечения ключевых фраз и навыка распознавания сущностей. Функции извлечения ключевых фраз и распознавания сущностей принимают входные данные объемом не более 50 000 символов. Некоторые примеры файлов следует разделить, чтобы удовлетворить это ограничение.

  • Извлечение ключевых фраз для получения основных ключевых фраз.

Во время первоначальной обработки поиск Azure AI взломает каждый документ, чтобы извлечь содержимое из разных форматов файлов. Текст из исходного файла помещается в созданное поле content, по одному для каждого документа. Поэтому задайте для входных данных "/document/content", чтобы использовать этот текст. Содержимое изображения помещается в созданное поле normalized_images, указанное в наборе навыков как /document/normalized_images/*.

Выходные данные могут быть сопоставлены с индексом, используемым в качестве входных данных для нисходящего навыка, или к обоим, как в случае с кодом языка. В индексе код языка полезен для фильтрации. В качестве входных данных код языка используется навыками анализа текста, чтобы указать лингвистические правила для разбивки слов.

Общие сведения о наборах навыков см. в статье How to create a skillset in an enrichment pipeline (Способ создания набора навыков в конвейере обогащения).

Навык OCR

OcrSkill извлекает текст из изображений. При использовании этого навыка предполагается наличие поля normalized_images. Чтобы создать это поле, далее в руководстве мы задали "imageAction" конфигурацию в определении "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;
}

Навык объединения

В этом разделе описано MergeSkill , как объединить поле содержимого документа с текстом, созданным навыком OCR.

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;
}

Навык распознавания языка

LanguageDetectionSkill определяет язык введенного текста и сообщает один код языка для каждого документа, отправленного по запросу. Мы используем выходные данные навыка распознавания языка в рамках входных данных навыка разделения текста.

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;
}

Навык разделения текста

Приведенный ниже SplitSkill разделяет текст по страницам и ограничивает длину страницы до 4000 символов, как измеряется String.Length. Алгоритм пытается разделить текст на блоки, которые в большинстве случаев maximumPageLength имеют размер. В этом случае алгоритм делает все возможное, чтобы разорвать предложение на границе предложения, поэтому размер блока может быть немного меньше 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;
}

Навык распознавания сущностей

Этот экземпляр EntityRecognitionSkill настроен для распознавания типа категории organization. EntityRecognitionSkill может также распознавать типы категорий person и location.

Обратите внимание, что в поле context задается значение "/document/pages/*" со звездочкой. Это означает, что этап обогащения вызывается для каждой страницы в "/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;
}

Навык извлечения ключевых фраз

Как и созданный только что экземпляр EntityRecognitionSkill, KeyPhraseExtractionSkill вызывается для каждой страницы документа.

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;
}

Сборка и создание наборов навыков

Выполните сборку SearchIndexerSkillset с использованием созданных вами навыков.

private static SearchIndexerSkillset CreateOrUpdateDemoSkillSet(SearchIndexerClient indexerClient, IList<SearchIndexerSkill> skills,string azureAiServicesKey)
{
    SearchIndexerSkillset skillset = new SearchIndexerSkillset("demoskillset", skills)
    {
        // Azure AI services was formerly known as Cognitive Services.
        // The APIs still use the old name, so we need to create a CognitiveServicesAccountKey object.
        Description = "Demo skillset",
        CognitiveServicesAccount = new CognitiveServicesAccountKey(azureAiServicesKey)
    };

    // 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;
}

Добавьте в раздел 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, azureAiServicesKey);

Шаг 3. Создание индекса

В этом разделе вы определите схему индекса, указав поля для включения в индекс поиска, а также атрибуты поиска для каждого поля. Поля имеют тип и могут принимать атрибуты, определяющие их использование (поиск, сортировка и т. д.). Имена полей в индексе не обязательно совпадают с именами полей в источнике. На более позднем этапе вы добавите сопоставления полей в индексаторе для подключения полей "источник — назначение". Для этого шага определите индекс, используя соглашения об именовании полей, относящиеся к вашему поисковому приложению.

В этом упражнении используются следующие поля и типы полей:

Имена полей Типы полей
id Edm.String
content Edm.String
languageCode Edm.String
keyPhrases Список<Edm.String>
organizations Список<Edm.String>

Создание класса DemoIndex

Поля для этого индекса определяются с помощью класса модели. Каждое свойство класса модели имеет атрибуты, которые определяют связанные с поиском характеристики соответствующего поля индекса.

Мы добавим класс модели в новый файл C#. Щелкните проект правой кнопкой мыши и выберите "Добавить>новый элемент", выберите "Класс" и назовите файлDemoIndex.cs, а затем нажмите кнопку "Добавить".

Обязательно укажите, что нужно использовать типы из пространств имен Azure.Search.Documents.Indexes и System.Text.Json.Serialization.

Добавьте приведенное ниже определение DemoIndex.cs класса модели и добавьте его в то же пространство имен, где создается индекс.

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. У этого индекса будет имя demoindex. Если индекс уже существует с таким именем, он удаляется.

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;
}

Во время тестирования вы можете обнаружить, что вы пытаетесь создать индекс более одного раза. Поэтому предварительно проверяйте, не создан ли он.

Добавьте в раздел Main следующие строки.

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

Добавьте следующую инструкцию using для разрешения несообщенной ссылки.

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

Дополнительные сведения об основных понятиях индекса см. в статье Создание индекса (REST API).

Шаг 4. Создание и запуск индексатора

Пока вы создали источник данных, набор навыков и индекс. Эти три компонента становятся частью индексатора, который объединяет каждую часть в единую многофазную операцию. Чтобы связать их вместе в индексаторе, необходимо определить сопоставления полей.

  • Обработка FieldMappings выполняется перед набором навыков. Исходные поля из источника данных сопоставляются с целевыми полями в индексе. Если имена и типы полей одинаковы в исходном и целевом расположениях, сопоставление не требуется.

  • Обработка OutputFieldMappings выполняется после набора навыков. Добавляются ссылки на элементы sourceFieldNames, которые не существуют, пока они не будут созданы при анализе или обогащении документов. TargetFieldName — это поле в индексе.

Помимо привязки входных данных к выходным данным, вы можете использовать сопоставления полей для преобразования структур данных в плоские структуры. Для получения дополнительных сведений см. раздел Сопоставление обогащенных полей с индексом, поддерживающим поиск.

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;
}

Добавьте в раздел 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);

Обработка индексатора займет некоторое время. Несмотря на то что набор данных невелик, аналитические навыки выполняют интенсивные вычисления. Некоторые навыки, такие как анализ изображений, долго выполняются.

Совет

Создание индексатора вызывает конвейер. Если есть проблемы с получением данных, сопоставлением входных и выходных данных или порядком операций, они появятся на этом этапе.

Подробнее о создании индексатора

Код задает "maxFailedItems" значение -1, что указывает подсистеме индексирования игнорировать ошибки во время импорта данных. Это полезно, потому что в демонстрационном источнике данных мало документов. Для большего источника данных необходимо установить значение больше 0.

Также обратите внимание, что для "dataToExtract" задано значение "contentAndMetadata". Этот оператор указывает индексатору автоматически извлекать содержимое из разных форматов файлов, а также метаданные, относящиеся к каждому файлу.

Когда содержимое будет извлечено, вы можете установить imageAction для извлечения текста из изображений, найденных в источнике данных. "imageAction" с заданной конфигурацией "generateNormalizedImages" вместе с навыком распознавания текста и навыком объединения текста инструктирует индексатор извлекать текст из изображений (например слово "стоп" из знака остановки движения) и вставлять его как часть поля содержимого. Это относится как к изображениям, встроенным в документы (например, изображение внутри PDF-файлов), так и к изображениям, найденным в источнике данных, например к файлу JPG.

Мониторинга индексирования

После того как индексатор будет определен, он будет выполняться автоматически при отправке запроса. В зависимости от того, какие навыки определены, индексирование может занять больше времени, чем ожидалось. Определить, запущен ли еще индексатор, можно с помощью метода GetStatus.

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 представляет текущее состояние и журнал выполнения индексатора.

Предупреждения распространены в некоторых сочетаниях исходных файлов и навыков и не всегда указывают на проблему. В этом руководстве предупреждения являются неопасными (например, нет текстовых входных данных из файлов JPEG).

Добавьте в раздел Main следующие строки.

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

В консольных приложениях службы поиска ИИ Azure обычно мы добавим 2-секундную задержку перед выполнением запросов, возвращающих результаты, но так как обогащение занимает несколько минут, мы закрываем консольное приложение и используйте другой подход.

Самый простой вариант — обозреватель поиска в портал Azure. Сначала можно запустить пустой запрос, возвращающий все документы, или более целевой поиск, который возвращает содержимое нового поля, созданного конвейером.

  1. На портале Azure Microsoft на странице "Обзор" выберите Индексы.

  2. Найдите в списке demoindex. Он должен содержать 14 документов. Если число документов равно нулю, то индексатор либо еще выполняется, либо страница пока не обновлялась.

  3. Выберите demoindex. Обозреватель поиска находится на первой вкладке.

  4. Поиск в содержимом можно начать сразу после загрузки первого документа. Чтобы проверить наличие содержимого, выполните неопределенный запрос, нажав кнопку Поиск. Этот запрос возвращает все текущие проиндексированные документы, и вы получаете представление о том, что содержит индекс.

  5. Затем вставьте следующую строку для получения более управляемых результатов: search=*&$select=id, languageCode, organizations

Сброс и повторный запуск

На ранних экспериментальных этапах разработки наиболее практический подход к итерации проектирования заключается в удалении объектов из службы "Поиск ИИ Azure" и их перестроении. Имена ресурсов являются уникальными. Удаление объекта позволяет воссоздать его с использованием того же имени.

Пример кода для этого учебника проверяет имеющиеся объекты и удаляет их, чтобы вы могли повторно выполнить код. Вы также можете использовать портал Azure для удаления индексов, индексаторов, источников данных и наборов навыков.

Общие выводы

В этом руководстве описано, как создать расширенный конвейер индексирования путем создания таких компонентов, как источник данных, набор навыков, индекс и индексатор.

Вы получили сведения о встроенных навыках, а также об определении набора навыков и механизме построения цепочек навыков путем сопоставления входных и выходных данных. Вы также узнали, что outputFieldMappings в определении индексатора требуется маршрутизация обогащенных значений из конвейера в индекс, доступный для поиска в служба ИИ Azure.

Наконец, вы узнали, как тестировать результаты и выполнять сброс системы для дальнейших итераций. Вы узнали, что отправка запросов к индексу возвращает результат, созданный обогащенным конвейером индексирования. Вы также узнали, как проверить состояние индексатора, и какие объекты нужно удалить перед повторным запуском конвейера.

Очистка ресурсов

Если вы работаете в своей подписке, после завершения проекта целесообразно удалить созданные ресурсы, которые вам больше не потребуются. Ресурсы, которые продолжат работать, могут быть платными. Вы можете удалить ресурсы по отдельности либо удалить всю группу ресурсов.

Ресурсы можно найти и управлять ими в портал Azure, используя ссылку "Все ресурсы" или "Группы ресурсов" в области навигации слева.

Следующие шаги

Теперь, когда вы знакомы со всеми объектами в конвейере обогащения с помощью ИИ, давайте более подробно рассмотрим определения набора навыков и отдельные навыки.