Tutorial: Verwenden von .NET und KI zum Generieren von durchsuchbarem Inhalt über Azure-Blobs

Wenn Sie in Azure Blob Storage über unstrukturierten Text oder Bilder verfügen, können Sie mithilfe einer KI-Anreicherungspipeline Informationen extrahieren und neue Inhalte erstellen, die für die Volltextsuche oder in Knowledge Mining-Szenarios verwendet werden können.

In diesem Tutorial lernen Sie Folgendes:

  • Einrichten einer Entwicklungsumgebung
  • Definieren einer Pipeline mit optischer Zeichenerkennung und Sprachenerkennung sowie mit Entitäts- und Schlüsselworterkennung
  • Ausführen der Pipeline zum Aufrufen von Transformationen sowie zum Erstellen und Laden eines Suchindexes
  • Erkunden Sie Ergebnisse per Volltextsuche und umfangreicher Abfragesyntax.

Sollten Sie über kein Azure-Abonnement verfügen, können Sie ein kostenloses Konto erstellen, bevor Sie beginnen.

Übersicht

In diesem Tutorial werden C# und die Clientbibliothek Azure.Search.Documents verwendet, um eine Datenquelle, einen Index, einen Indexer und ein Skillset zu erstellen.

Der Indexer stellt eine Verbindung mit einem Blobcontainer her, der im Datenquellenobjekt angegeben ist, und sendet alle indizierten Inhalte an einen bereits vorhandenen Suchindex.

Das Skillset wird an den Indexer angefügt. Es werden integrierte Skills von Microsoft verwendet, um Informationen zu suchen und zu extrahieren. Die Schritte in der Pipeline umfassen die optische Zeichenerkennung (OCR) für Bilder, die Spracherkennung für Text sowie die Schlüsselwortextraktion und die Entitätserkennung (Organisationen). Neue, durch die Pipeline erstellte Informationen werden in neuen Feldern in einem Index gespeichert. Nachdem der Index aufgefüllt wurde, können die Felder in Abfragen, Facetten und Filtern verwendet werden.

Voraussetzungen

Hinweis

In diesem Tutorial können Sie den kostenlosen Suchdienst verwenden. Der kostenlose Suchdienst ist auf drei Indizes, drei Indexer und drei Datenquellen beschränkt. In diesem Tutorial wird davon jeweils eine Instanz erstellt. Vergewissern Sie sich zunächst, dass Ihr Dienst über genügend freie Kapazität für die neuen Ressourcen verfügt.

Herunterladen von Beispieldaten

Die Beispieldaten bestehen aus 14 Dateien mit gemischten Inhaltstypen, die Sie in einem späteren Schritt in Azure Blob Storage hochladen.

  1. Öffnen Sie diesen OneDrive-Ordner, und klicken Sie links oben auf Herunterladen, um die Dateien auf Ihren Computer zu kopieren.

  2. Klicken Sie mit der rechten Maustaste auf die ZIP-Datei, und wählen Sie Alle extrahieren aus. Es stehen 14 Dateien verschiedener Art zur Verfügung. In dieser Übung werden sieben davon verwendet.

Sie können auch den Quellcode für dieses Tutorial herunterladen. Der Quellcode befindet sich im Ordner tutorial-ai-enrichment/v11 im Repository azure-search-dotnet-samples.

1. Erstellen der Dienste

In diesem Tutorial werden Azure Cognitive Search für Indizierungsvorgänge und Abfragen, Cognitive Services im Back-End für die KI-Anreicherung und Azure Blob Storage für die Datenbereitstellung verwendet. Dieses Tutorial bleibt unter der kostenlosen Zuteilung von 20 Transaktionen pro Indexer pro Tag für Cognitive Services. Aus diesem Grund müssen Sie nur den Such- und den Speicherdienst erstellen.

Erstellen Sie diese beiden Dienste nach Möglichkeit in derselben Region und Ressourcengruppe, um eine möglichst große Nähe zu erreichen und die Verwaltung zu vereinfachen. In der Praxis kann sich Ihr Azure Storage-Konto in einer beliebigen Region befinden.

Azure Storage

  1. Melden Sie sich beim Azure-Portal an, und klicken Sie auf + Ressource erstellen.

  2. Suchen Sie nach Speicherkonto, und wählen Sie das Speicherkonto-Angebot von Microsoft aus.

    Erstellen eines Speicherkontos

  3. Auf der Registerkarte „Grundlagen“ sind folgende Angaben erforderlich. Übernehmen Sie bei allen anderen Optionen die Standardeinstellungen.

    • Ressourcengruppe. Sie können entweder eine vorhandene Ressourcengruppe auswählen oder eine neue Ressourcengruppe erstellen. Verwenden Sie jedoch für alle Dienste die gleiche Gruppe, um die Dienste gemeinsam verwalten zu können.

    • Speicherkontoname: Falls Sie über mehrere Ressourcen des gleichen Typs verfügen, geben Sie zur Unterscheidung den Typ und die Region an (Beispiel: blobstoragewestus).

    • Standort. Wählen Sie nach Möglichkeit den gleichen Standort aus, der auch für Azure Cognitive Search und Cognitive Services verwendet wird. Bei Verwendung eines einzelnen Standorts fallen keine Bandbreitengebühren an.

    • Kontoart: Verwenden Sie die Standardeinstellung StorageV2 (allgemein, Version 2) .

  4. Klicken Sie zum Erstellen des Diensts auf Überprüfen + erstellen.

  5. Klicken Sie nach der Erstellung auf Go to the resource (Zur Ressource), um die Übersichtsseite zu öffnen.

  6. Klicken Sie auf den Dienst Blobs.

  7. Klicken Sie auf + Container, um einen Container zu erstellen, und nennen Sie ihn cog-search-demo.

  8. Wählen Sie cog-search-demo aus, und klicken Sie auf Hochladen, um den Ordner zu öffnen, in dem Sie die Downloaddateien gespeichert haben. Wählen Sie alle 14 Dateien aus, und klicken Sie auf OK, um den Upload durchzuführen.

    Hochladen der Beispieldateien

  9. Rufen Sie eine Verbindungszeichenfolge ab, bevor Sie Azure Storage verlassen, um in Azure Cognitive Search eine Verbindung herstellen zu können.

    1. Wechseln Sie zurück zur Übersichtsseite Ihres Speicherkontos. (Wir haben blobstoragewestus als Beispiel verwendet.)

    2. Wählen Sie im linken Navigationsbereich die Option Zugriffsschlüssel aus, und kopieren Sie eine der Verbindungszeichenfolgen.

    Die Verbindungszeichenfolge ist eine URL, die in etwa wie folgt aussieht:

    DefaultEndpointsProtocol=https;AccountName=blobstoragewestus;AccountKey=<your account key>;EndpointSuffix=core.windows.net
    
  10. Speichern Sie die Verbindungszeichenfolge im Editor. Sie wird später bei der Einrichtung der Datenquellenverbindung benötigt.

Cognitive Services

Die KI-Anreicherung basiert auf Cognitive Services (einschließlich Textanalyse und maschinellem Sehen für die Verarbeitung von natürlicher Sprache und Bildern). Bei der Erstellung eines echten Prototyps oder Projekts würden Sie an dieser Stelle Cognitive Services bereitstellen (in der gleichen Region wie Azure Cognitive Search), um eine Verknüpfung mit Indizierungsvorgängen zu ermöglichen.

In dieser Übung können Sie die Ressourcenbereitstellung allerdings überspringen, da Azure Cognitive Search im Hintergrund eine Verbindung mit Cognitive Services herstellen kann und 20 kostenlose Transaktionen pro Indexerausführung ermöglicht. Da in diesem Tutorial 14 Transaktionen verwendet werden, ist die kostenlose Zuteilung ausreichend. Planen Sie bei umfangreicheren Projekten die Bereitstellung von Cognitive Services im S0-Tarif (nutzungsbasierte Bezahlung). Weitere Informationen finden Sie unter Anfügen einer Cognitive Services-Ressource an eine Qualifikationsgruppe in Azure Search.

Azure Cognitive Search ist die dritte Komponente, die Sie im Portal erstellen können, oder Sie suchen nach einem vorhandenen Suchdienst in Ihrem Abonnement.

Im Rahmen dieser exemplarischen Vorgehensweise können Sie den Free-Tarif verwenden.

Für die Interaktion mit dem Azure Cognitive Search-Dienst benötigen Sie die Dienst-URL und einen Zugriffsschlüssel. Ein Suchdienst wird mit beidem erstellt. Gehen Sie daher wie folgt vor, um die erforderlichen Informationen zu erhalten, falls Sie Azure Cognitive Search Ihrem Abonnement hinzugefügt haben:

  1. Melden Sie sich beim Azure-Portal an, und rufen Sie auf der Seite Übersicht Ihres Suchdiensts die URL ab. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

  2. Kopieren Sie unter Einstellungen > Schlüssel einen Administratorschlüssel, um Vollzugriff auf den Dienst zu erhalten. Es gibt zwei austauschbare Administratorschlüssel – diese wurden zum Zweck der Geschäftskontinuität bereitgestellt, falls Sie einen Rollover für einen Schlüssel durchführen müssen. Für Anforderungen zum Hinzufügen, Ändern und Löschen von Objekten können Sie den primären oder den sekundären Schlüssel verwenden.

    Rufen Sie auch den Abfrageschlüssel ab. Es empfiehlt sich, Abfrageanforderungen mit schreibgeschütztem Zugriff auszugeben.

    Abrufen des Dienstnamens sowie der Administrator- und Abfrageschlüssel

Ein gültiger Schlüssel stellt anforderungsbasiert eine Vertrauensstellung her zwischen der Anwendung, die die Anforderung versendet, und dem Dienst, der sie verarbeitet.

2. Einrichten Ihrer Umgebung

Öffnen Sie zunächst Visual Studio, und erstellen Sie ein neues Konsolen-App-Projekt, das mit .NET Core ausgeführt werden kann.

Installieren von Azure.Search.Documents

Das .NET SDK für Azure Cognitive Search umfasst eine Clientbibliothek, mit der Sie sowohl Ihre Indizes, Datenquellen, Indexer und Skillsets verwalten als auch Dokumente hochladen und verwalten sowie Abfragen ausführen können, ohne die Details von HTTP und JSON berücksichtigen zu müssen. Diese Clientbibliothek wird als NuGet-Paket bereitgestellt.

Installieren Sie für dieses Projekt Version 11 oder höher von Azure.Search.Documents sowie die neueste Version von Microsoft.Extensions.Configuration.

  1. Klicken Sie in Visual Studio auf Extras > NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten... .

  2. Suchen Sie nach Azure.Search.Document.

  3. Wählen Sie die neueste Version aus, und klicken Sie auf Installieren.

  4. Wiederholen Sie die vorherigen Schritte, um Microsoft.Extensions.Configuration und Microsoft.Extensions.Configuration.Json zu installieren.

Hinzufügen von Informationen zur Dienstverbindung

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr Projekt, und wählen Sie Hinzufügen > Neues Element aus.

  2. Geben Sie der Datei den Namen appsettings.json, und wählen Sie Hinzufügen aus.

  3. Fügen Sie diese Datei in Ihr Ausgabeverzeichnis ein.

    1. Klicken Sie mit der rechten Maustaste auf appsettings.json, und wählen Sie Eigenschaften aus.
    2. Ändern Sie den Wert von In Ausgabeverzeichnis kopieren in Kopieren, wenn neuer.
  4. Kopieren Sie den folgenden JSON-Code in die neue JSON-Datei.

    {
      "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",
    }
    

Fügen Sie Ihren Suchdienst und die Informationen Ihres Blob Storage-Kontos hinzu. Denken Sie daran, dass Sie diese Informationen über die Schritte zur Dienstbereitstellung im vorherigen Abschnitt erhalten können.

Geben Sie für SearchServiceUri die vollständige URL ein.

Hinzufügen von Namespaces

Fügen Sie in Program.cs die folgenden Namespaces hinzu.

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

Erstellen eines Clients

Erstellen Sie unter Main eine Instanz der SearchIndexClient- und der SearchIndexerClient-Klasse.

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

Hinweis

Die Clients stellen eine Verbindung mit Ihrem Suchdienst her. Sie sollten nach Möglichkeit nur eine Instanz in Ihrer Anwendung freigeben, um zu vermeiden, dass zu viele Verbindungen geöffnet werden. Die Methoden sind threadsicher und ermöglichen diese Freigabe.

Hinzufügen einer Funktion zum Beenden des Programms während eines Fehlers

Dieses Tutorial soll die einzelnen Schritte der Indizierungspipeline veranschaulichen. Bei einem kritischen Problem, das verhindert, dass das Programm die Datenquelle, das Skillset, den Index oder den Indexer erstellt, wird eine Fehlermeldung ausgegeben und das Programm beendet, sodass der Fehler ermittelt und behoben werden kann.

Fügen Sie ExitProgram zu Main hinzu, um Szenarien zu behandeln, in denen das Programm beendet werden muss.

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. Erstellen der Pipeline

In Azure Cognitive Search erfolgt die KI-Verarbeitung während der Indizierung (oder Datenerfassung). In diesem Teil der exemplarischen Vorgehensweise werden vier Objekte erstellt: Datenquelle, Indexdefinition, Skillset und Indexer.

Schritt 1: Erstellen einer Datenquelle

SearchIndexerClient verfügt über eine DataSourceName-Eigenschaft, die Sie auf ein SearchIndexerDataSourceConnection-Objekt festlegen können. Dieses Objekt stellt alle Methoden bereit, die Sie benötigen, um Azure Cognitive Search-Datenquellen zu erstellen, aufzulisten, zu aktualisieren oder zu löschen.

Erstellen Sie eine neue SearchIndexerDataSourceConnection-Instanz, indem Sie indexerClient.CreateOrUpdateDataSourceConnection(dataSource) aufrufen. Der folgende Code erstellt eine Datenquelle vom Typ 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;
}

Bei einer erfolgreichen Anforderung gibt die Methode die Datenquelle zurück, die erstellt wurde. Wenn ein Problem mit der Anforderung auftritt, z. B. ein ungültiger Parameter vorhanden ist, löst die Methode eine Ausnahme aus.

Fügen Sie nun unter Main eine Zeile zum Aufrufen der Funktion CreateOrUpdateDataSource hinzu, die Sie gerade hinzugefügt haben.

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

Erstellen Sie das Projekt, und führen Sie es aus. Da es sich hier um Ihre erste Anforderung handelt, überprüfen Sie im Azure-Portal, ob die Datenquelle in Azure Cognitive Search erstellt wurde. Überprüfen Sie auf der Übersichtsseite des Suchdiensts, ob die Liste „Datenquellen“ ein neues Element enthält. Möglicherweise müssen Sie einige Minuten warten, bis die Portalseite aktualisiert wurde.

Datenquellenkachel im Portal

Schritt 2: Erstellen eines Skillsets

In diesem Abschnitt definieren Sie eine Reihe von Anreicherungsschritten, die Sie auf Ihre Daten anwenden möchten. Jeder Anreicherungsschritt wird als Skill und die Anreicherungsschritte zusammen als Skillset bezeichnet. In diesem Tutorial werden für die Qualifikationsgruppe integrierte kognitive Qualifikationen verwendet:

  • Mit der Qualifikation für die optische Zeichenerkennung (OCR) wird gedruckter und handschriftlicher Text in Bilddateien erkannt.

  • Mit der Textzusammenführung wird Text aus einer Sammlung von Feldern in einem einzigen Feld konsolidiert.

  • Spracherkennung, um die Sprache der Inhalte zu bestimmen.

  • Mit der Textaufteilung werden große Inhalte vor dem Aufrufen der Qualifikationen zur Schlüsselbegriffserkennung und Entitätserkennung in kleinere Teile aufgeteilt. Für die Schlüsselbegriffserkennung und Entitätserkennung werden Eingaben von maximal 50.000 Zeichen akzeptiert. Für einige der Beispieldateien ist eine Aufteilung erforderlich, um diesen Grenzwert zu erfüllen.

  • Entitätserkennung, um die Namen von Organisationen aus Inhalten im Blobcontainer zu extrahieren.

  • Schlüsselbegriffserkennung, um die wichtigsten Schlüsselbegriffe herauszuziehen.

Während der ersten Verarbeitung entschlüsselt Azure Cognitive Search jedes Dokument, um die Inhalte aus verschiedenen Dateiformaten zu extrahieren. Aus der Quelldatei stammender Text wird für jedes Dokument jeweils im generierten Feld content gespeichert. Legen Sie daher die Eingabe auf "/document/content" fest, um diesen Text zu verwenden. Bildinhalte werden in einem generierten normalized_images-Feld platziert, das in einem Skillset als /document/normalized_images/* angegeben wird.

Ausgaben können einem Index zugeordnet, als Eingabe einer Downstream-Qualifikation verwendet oder in beider Weise zugleich eingesetzt werden, wie etwa bei Sprachcode. Im Index ist ein Sprachcode zu Filterungszwecken nützlich. Als Eingabe wird ein Sprachcode von Qualifikationen zur Textanalyse verwendet, um die Linguistikregeln über Wörtertrennung zu informieren.

Weitere Informationen zu den Grundlagen von Qualifikationsgruppen finden Sie unter How to define a skillset (Definieren von Qualifikationsgruppen).

OCR-Qualifikation

OcrSkill extrahiert Text aus Bildern. Bei dieser Qualifikation wird davon ausgegangen, dass ein Feld „normalized_images“ vorhanden ist. Um dieses Feld zu generieren, legen Sie später in diesem Tutorial die "imageAction"-Konfiguration in der Indexerdefinition auf "generateNormalizedImages" fest.

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

Qualifikation: Zusammenführung

In diesem Abschnitt erstellen Sie ein MergeSkill-Element, mit dem das Feld mit dem Dokumentinhalt mit dem vom OCR-Skill erstellten Text zusammengeführt wird.

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

Qualifikation „Sprachenerkennung“

LanguageDetectionSkill erkennt die Sprache von Eingabetexten und meldet einen einzigen Sprachcode für jedes Dokument, das mit der Anforderung übermittelt wurde. Wir verwenden die Ausgabe der Qualifikation Sprachenerkennung als Teil der Eingabe für die Qualifikation Textaufteilung.

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

Qualifikation „Textaufteilung“

Das folgende SplitSkill-Element teilt Text nach Seiten und beschränkt die Seitenlänge auf 4.000 Zeichen, die mit String.Length gemessen werden. Der Algorithmus versucht, den Text in Blöcke aufzuteilen, die höchstens maximumPageLength groß sind. Dabei versucht der Algorithmus, Sätze an Satzgrenzen zu teilen, sodass die Größe der Blöcke etwas kleiner als maximumPageLength sein kann.

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

Qualifikation „Entitätserkennung“

Diese EntityRecognitionSkill-Instanz ist auf den Erkennungskategorietyp organization festgelegt. EntityRecognitionSkill kann auch die Kategorietypen person und location erkennen.

Beachten Sie, dass das Feld „context“ mit einem Sternchen auf "/document/pages/*" festgelegt ist, d. h. der Anreicherungsschritt wird für jede Seite unter "/document/pages" aufgerufen.

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

Qualifikation „Schlüsselbegriffserkennung“

Wie die EntityRecognitionSkill-Instanz, die gerade erstellt wurde, wird auch KeyPhraseExtractionSkill für jede Seite des Dokuments aufgerufen.

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

Aufbauen und Erstellen der Qualifikationsgruppe

Erstellen Sie SearchIndexerSkillset mithilfe der von Ihnen erstellten Skills.

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

Fügen Sie Main die folgenden Zeilen hinzu.

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

Schritt 3: Erstellen eines Index

In diesem Abschnitt definieren Sie das Indexschema, indem Sie angeben, welche Felder in den durchsuchbaren Index aufgenommen und mit welchen Suchattributen die einzelnen Felder versehen werden sollen. Felder besitzen einen Typ und können Attribute annehmen, die bestimmen, wie das Feld verwendet wird (durchsuchbar, sortierbar usw.). Feldname in einem Index müssen nicht exakt mit den Feldnamen in der Quelle übereinstimmen. In einem späteren Schritt fügen Sie in einem Indexer Feldzuordnungen hinzu, um die Quell- und Zielfelder zu verbinden. Definieren Sie für diesen Schritt den Index mit Feldbenennungskonventionen, die für Ihre Suchanwendung angemessen sind.

In dieser Übung werden die folgenden Felder und Feldtypen verwendet:

Feldnamen Feldtypen
id Edm.String
content Edm.String
languageCode Edm.String
keyPhrases List<Edm.String>
organizations List<Edm.String>

Erstellen der DemoIndex-Klasse

Die Felder für diesen Index werden mithilfe einer Modellklasse definiert. Jede Eigenschaft der Modellklasse verfügt über Attribute, die das suchbezogene Verhalten des entsprechenden Indexfelds bestimmen.

Wir fügen die Modellklasse einer neuen C#-Datei hinzu. Klicken Sie mit der rechten Maustaste auf Ihr Projekt, und wählen Sie Hinzufügen > Neues Element und dann „Klasse“ aus. Geben Sie der Datei den Namen DemoIndex.cs, und wählen Sie dann Hinzufügen aus.

Geben Sie unbedingt auch an, dass Sie die Typen aus den Namespaces Azure.Search.Documents.Indexes und System.Text.Json.Serialization verwenden möchten.

Fügen Sie DemoIndex.cs die folgende Modellklassendefinition hinzu, und fügen Sie sie in denselben Namespace ein, in dem Sie den Index erstellen.

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

Nach dem Definieren einer Modellklasse können Sie nun in Program.cs ganz problemlos eine Indexdefinition erstellen. Der Name für diesen Index lautet demoindex. Falls bereits ein Index mit diesem Namen vorhanden ist, wird er gelöscht.

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

Während der Tests stellen Sie möglicherweise fest, dass Sie mehr als einmal versuchen, den Index zu erstellen. Aus diesem Grund sollten Sie vor dem Versuch, den Index zu erstellen, überprüfen, ob er nicht bereits vorhanden ist.

Fügen Sie Main die folgenden Zeilen hinzu.

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

Fügen Sie die folgende using-Anweisung hinzu, um den mehrdeutigen Verweis zu korrigieren.

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

Weitere Informationen zu Indexkonzepten finden Sie unter Erstellen eines Index (REST-API).

Schritt 4: Erstellen und Ausführen eines Indexers

Bisher haben Sie eine Datenquelle, eine Qualifikationsgruppe und einen Index erstellt. Diese drei Komponenten werden Teil eines Indexers, der jedes einzelne Stück per Pull in einen einzelnen mehrstufigen Vorgang herunterlädt. Um diese in einem Indexer zusammenzuführen, müssen Sie Feldzuordnungen definieren.

  • fieldMappings werden vor der Qualifikationsgruppe verarbeitet, und die Quellfelder der Datenquelle werden Zielfeldern in einem Index zugeordnet. Wenn die Feldnamen und -typen auf beiden Seiten gleich sind, ist keine Zuordnung erforderlich.

  • outputFieldMappings werden nach der Qualifikationsgruppe verarbeitet. Es wird auf nicht vorhandene sourceFieldNames verwiesen, bis diese per Dokumententschlüsselung oder Anreicherung erstellt werden. targetFieldName ist ein Feld in einem Index.

Neben dem Verknüpfen von Ein- und Ausgaben können Sie auch Feldzuordnungen nutzen, um Datenstrukturen zu vereinfachen. Weitere Informationen finden Sie unter Zuordnen angereicherter Felder zu einem durchsuchbaren 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;
}

Fügen Sie Main die folgenden Zeilen hinzu.

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

Die Indexerverarbeitung nimmt erwartungsgemäß einige Zeit in Anspruch. Das Dataset ist zwar klein, Analysequalifikationen sind aber rechenintensiv. Einige Qualifikationen, wie etwa die Bildanalyse, weisen lange Ausführungszeiten auf.

Tipp

Die Pipeline wird durch Erstellen eines Indexers aufgerufen. Wenn beim Zugriff auf die Daten, dem Zuordnen von Ein- und Ausgaben oder der Reihenfolge der Vorgänge Probleme bestehen, äußern sie sich in dieser Phase.

Untersuchen der Indexererstellung

Der Code legt "maxFailedItems" auf -1 fest, womit die Index-Engine angewiesen wird, Fehler beim Datenimport zu ignorieren. Dies ist nützlich, weil die Demodatenquelle nur wenige Dokumente enthält. Für eine größere Datenquelle sollten Sie den Wert größer als 0 festlegen.

Beachten Sie auch, dass "dataToExtract" auf "contentAndMetadata" festgelegt ist. Diese Anweisung weist den Indexer an, die Inhalte aus verschiedenen Dateiformaten sowie die den einzelnen Dateien zugeordneten Metadaten automatisch zu extrahieren.

Wenn die Inhalte extrahiert werden, können Sie imageAction darauf festlegen, Text aus in der Datenquelle gefundenen Bildern zu extrahieren. Die Festlegung von "imageAction" auf die Konfiguration "generateNormalizedImages" in Kombination mit den Qualifikationen OCR und Textzusammenführung weist den Indexer an, Text aus den Bildern zu extrahieren (beispielsweise das Wort „Stop“ aus einem Stoppschild) und ihn als Teil des Inhaltsfelds einzubetten. Dieses Verhalten betrifft sowohl die in den Dokumenten eingebetteten Bilder (denken Sie etwa an Bilder in PDF-Dateien) als auch die in der Datenquelle gefundenen Bilder, z.B. eine JPG-Datei.

4. Überwachen der Indizierung

Nachdem der Indexer definiert wurde, wird er automatisch ausgeführt, wenn Sie die Anforderung senden. Abhängig von den kognitiven Qualifikationen, die Sie definiert haben, kann die Indizierung länger als erwartet dauern. Um herauszufinden, ob der Indexer noch ausgeführt wird, verwenden Sie die GetStatus-Methode.

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 stellt den aktuellen Status und den Ausführungsverlauf eines Indexers dar.

Warnungen sind bei bestimmten Kombinationen aus Quelldatei und Qualifikation häufig und weisen nicht immer auf ein Problem hin. Im Rahmen dieses Tutorials sind die Warnungen gutartig (z.B. keine Texteingaben aus den JPEG-Dateien).

Fügen Sie Main die folgenden Zeilen hinzu.

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

In den Azure Cognitive Search-Tutorials für Konsolen-Apps wird in der Regel eine Verzögerung von zwei Sekunden vor dem Ausführen von Abfragen hinzugefügt, die Ergebnisse zurückgeben. Da die Anreicherung allerdings einige Minuten dauert, schließen Sie die Konsolen-App und verwenden stattdessen einen anderen Ansatz.

Die einfachste Option ist der Such-Explorer im Portal. Sie können zuerst eine leere Abfrage, die alle Dokumente zurückgibt, oder eine gezieltere Suche durchführen, die neuen, von der Pipeline erstellten Feldinhalt zurückgibt.

  1. Klicken Sie im Azure-Portal auf der Übersichtsseite für die Suche auf Indizes.

  2. Suchen Sie in der Liste nach demoindex . Diese sollte 14 Dokumente enthalten. Wenn die Anzahl der Dokumente 0 (null) ist, wird der Indexer entweder noch ausgeführt, oder die Seite wurde noch nicht aktualisiert.

  3. Klicken Sie auf demoindex (Zur Kasse). Der Such-Explorer ist die erste Registerkarte.

  4. Der Inhalt kann durchsucht werden, sobald das erste Dokument geladen wurde. Führen Sie eine Abfrage ohne Angabe durch, indem Sie auf Suchen klicken, um zu überprüfen, ob Inhalt vorhanden ist. Diese Abfrage gibt alle derzeit indizierten Dokumente zurück und gibt Ihnen so eine Vorstellung davon, was der Index enthält.

  5. Geben Sie als Nächstes die folgende Zeichenfolge ein, um bessere Ergebnisse zu erzielen: search=*&$select=id, languageCode, organizations.

Zurücksetzen und erneut ausführen

In den frühen experimentellen Phasen der Entwicklung besteht der praktikabelste Ansatz für den Übergang von einer Entwurfsphase zur nächsten darin, die Objekte aus Azure Cognitive Search zu löschen und Ihrem Code zu erlauben, sie neu zu erstellen. Ressourcennamen sind eindeutig. Wenn Sie ein Objekt löschen, können Sie es unter dem gleichen Namen neu erstellen.

Im Beispielcode dieses Tutorials wird eine Überprüfung auf vorhandene Objekte durchgeführt. Diese werden dann gelöscht, damit Sie Ihren Code erneut ausführen können. Sie können auch das Portal verwenden, um Indizes, Indexer, Datenquellen und Qualifikationsgruppen zu löschen.

Wesentliche Punkte

Dieses Tutorial veranschaulicht die grundlegenden Schritte zum Erstellen einer erweiterten Indizierungspipeline durch Erstellung von Komponenten: eine Datenquelle, eine Qualifikationsgruppe, ein Index und ein Indexer.

Es wurden integrierte Qualifikationen im Zusammenhang mit der Skillsetdefinition und den Mechanismen zur Verkettung von Qualifikationen mithilfe von Ein- und Ausgängen vorgestellt. Sie haben darüber hinaus erfahren, dass outputFieldMappings in der Indexerdefinition erforderlich ist, um angereicherte Werte aus der Pipeline an einen durchsuchbaren Index in einem Azure Cognitive Search-Dienst weiterzuleiten.

Ferner haben Sie erfahren, wie die Ergebnisse getestet werden und das System für weitere Entwicklungsschritte zurückgesetzt wird. Sie haben gelernt, dass das Ausgeben von Abfragen auf den Index die von der angereicherten Indizierungspipeline erstellte Ausgabe zurückgibt. Darüber hinaus haben Sie die Überprüfung des Indexerstatus und das Löschen von Objekten vor der erneuten Ausführung einer Pipeline gelernt.

Bereinigen von Ressourcen

Wenn Sie in Ihrem eigenen Abonnement arbeiten, ist es ratsam, nach Abschluss eines Projekts die nicht mehr benötigten Ressourcen zu entfernen. Ressourcen, die weiterhin ausgeführt werden, können Sie Geld kosten. Sie können entweder einzelne Ressourcen oder aber die Ressourcengruppe löschen, um den gesamten Ressourcensatz zu entfernen.

Ressourcen können im Portal über den Link „Alle Ressourcen“ oder „Ressourcengruppen“ im linken Navigationsbereich gesucht und verwaltet werden.

Nächste Schritte

Nachdem Sie sich nun mit allen Objekten einer KI-Anreicherungspipeline vertraut gemacht haben, können Sie sich weiter über Skillsetdefinitionen und einzelne Qualifikationen informieren.