Delen via


Voorbeeld: Een aangepaste vaardigheid maken met behulp van de Bing Entiteiten zoeken-API

In dit voorbeeld leert u hoe u een aangepaste vaardigheid voor een web-API maakt. Deze vaardigheid accepteert locaties, publieke cijfers en organisaties en retourbeschrijvingen voor hen. In het voorbeeld wordt een Azure-functie gebruikt om de Bing Entiteiten zoeken-API te verpakken, zodat de aangepaste vaardigheidsinterface wordt geïmplementeerd.

Vereisten

  • Lees meer over het artikel over de interface voor aangepaste vaardigheden als u niet bekend bent met de invoer-/uitvoerinterface die een aangepaste vaardigheid moet implementeren.

  • Maak een Bing Search-resource via Azure Portal. Er is een gratis laag beschikbaar en voldoende voor dit voorbeeld.

  • Installeer Visual Studio of hoger.

Een Azure-functie maken

Hoewel in dit voorbeeld een Azure-functie wordt gebruikt om een web-API te hosten, is dit niet vereist. Zolang u voldoet aan de interfacevereisten voor een cognitieve vaardigheid, is de aanpak die u neemt immateriële. Met Azure Functions kunt u echter eenvoudig een aangepaste vaardigheid maken.

Een project maken

  1. Selecteer nieuw>project in Visual Studio in het menu Bestand.

  2. Kies Azure Functions als sjabloon en selecteer Volgende. Typ een naam voor uw project en selecteer Maken. De naam van de functie-app moet geldig zijn als een C#-naamruimte, dus gebruik geen onderstrepingstekens, afbreekstreepjes of andere niet-alfanumerieke tekens.

  3. Selecteer een framework met langetermijnondersteuning.

  4. Kies HTTP-trigger voor het type functie dat u wilt toevoegen aan het project.

  5. Kies Functie voor het autorisatieniveau.

  6. Selecteer Maken om het functieproject en de door HTTP geactiveerde functie te maken.

Code toevoegen om de Bing Entity-API aan te roepen

Visual Studio maakt een project met standaardcode voor het gekozen functietype. Met het kenmerk FunctionName in de methode wordt de naam van de functie ingesteld. Met het kenmerk HttpTrigger wordt aangegeven dat de functie wordt geactiveerd door een HTTP-aanvraag.

Vervang de inhoud van Function1.cs door de volgende code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace SampleSkills
{
    /// <summary>
    /// Sample custom skill that wraps the Bing entity search API to connect it with a 
    /// AI enrichment pipeline.
    /// </summary>
    public static class BingEntitySearch
    {
        #region Credentials
        // IMPORTANT: Make sure to enter your credential and to verify the API endpoint matches yours.
        static readonly string bingApiEndpoint = "https://api.bing.microsoft.com/v7.0/entities";
        static readonly string key = "<enter your api key here>";  
        #endregion

        #region Class used to deserialize the request
        private class InputRecord
        {
            public class InputRecordData
            {
                public string Name { get; set; }
            }

            public string RecordId { get; set; }
            public InputRecordData Data { get; set; }
        }

        private class WebApiRequest
        {
            public List<InputRecord> Values { get; set; }
        }
        #endregion

        #region Classes used to serialize the response

        private class OutputRecord
        {
            public class OutputRecordData
            {
                public string Name { get; set; } = "";
                public string Description { get; set; } = "";
                public string Source { get; set; } = "";
                public string SourceUrl { get; set; } = "";
                public string LicenseAttribution { get; set; } = "";
                public string LicenseUrl { get; set; } = "";
            }

            public class OutputRecordMessage
            {
                public string Message { get; set; }
            }

            public string RecordId { get; set; }
            public OutputRecordData Data { get; set; }
            public List<OutputRecordMessage> Errors { get; set; }
            public List<OutputRecordMessage> Warnings { get; set; }
        }

        private class WebApiResponse
        {
            public List<OutputRecord> Values { get; set; }
        }
        #endregion

        #region Classes used to interact with the Bing API
        private class BingResponse
        {
            public BingEntities Entities { get; set; }
        }
        private class BingEntities
        {
            public BingEntity[] Value { get; set; }
        }

        private class BingEntity
        {
            public class EntityPresentationinfo
            {
                public string[] EntityTypeHints { get; set; }
            }

            public class License
            {
                public string Url { get; set; }
            }

            public class ContractualRule
            {
                public string _type { get; set; }
                public License License { get; set; }
                public string LicenseNotice { get; set; }
                public string Text { get; set; }
                public string Url { get; set; }
            }

            public ContractualRule[] ContractualRules { get; set; }
            public string Description { get; set; }
            public string Name { get; set; }
            public EntityPresentationinfo EntityPresentationInfo { get; set; }
        }
        #endregion

        #region The Azure Function definition

        [FunctionName("EntitySearch")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("Entity Search function: C# HTTP trigger function processed a request.");

            var response = new WebApiResponse
            {
                Values = new List<OutputRecord>()
            };

            string requestBody = new StreamReader(req.Body).ReadToEnd();
            var data = JsonConvert.DeserializeObject<WebApiRequest>(requestBody);

            // Do some schema validation
            if (data == null)
            {
                return new BadRequestObjectResult("The request schema does not match expected schema.");
            }
            if (data.Values == null)
            {
                return new BadRequestObjectResult("The request schema does not match expected schema. Could not find values array.");
            }

            // Calculate the response for each value.
            foreach (var record in data.Values)
            {
                if (record == null || record.RecordId == null) continue;

                OutputRecord responseRecord = new OutputRecord
                {
                    RecordId = record.RecordId
                };

                try
                {
                    responseRecord.Data = GetEntityMetadata(record.Data.Name).Result;
                }
                catch (Exception e)
                {
                    // Something bad happened, log the issue.
                    var error = new OutputRecord.OutputRecordMessage
                    {
                        Message = e.Message
                    };

                    responseRecord.Errors = new List<OutputRecord.OutputRecordMessage>
                    {
                        error
                    };
                }
                finally
                {
                    response.Values.Add(responseRecord);
                }
            }

            return (ActionResult)new OkObjectResult(response);
        }

        #endregion

        #region Methods to call the Bing API
        /// <summary>
        /// Gets metadata for a particular entity based on its name using Bing Entity Search
        /// </summary>
        /// <param name="entityName">The name of the entity to extract data for.</param>
        /// <returns>Asynchronous task that returns entity data. </returns>
        private async static Task<OutputRecord.OutputRecordData> GetEntityMetadata(string entityName)
        {
            var uri = bingApiEndpoint + "?q=" + entityName + "&mkt=en-us&count=10&offset=0&safesearch=Moderate";
            var result = new OutputRecord.OutputRecordData();

            using (var client = new HttpClient())
            using (var request = new HttpRequestMessage {
                Method = HttpMethod.Get,
                RequestUri = new Uri(uri)
            })
            {
                request.Headers.Add("Ocp-Apim-Subscription-Key", key);

                HttpResponseMessage response = await client.SendAsync(request);
                string responseBody = await response?.Content?.ReadAsStringAsync();

                BingResponse bingResult = JsonConvert.DeserializeObject<BingResponse>(responseBody);
                if (bingResult != null)
                {
                    // In addition to the list of entities that could match the name, for simplicity let's return information
                    // for the top match as additional metadata at the root object.
                    return AddTopEntityMetadata(bingResult.Entities?.Value);
                }
            }

            return result;
        }

        private static OutputRecord.OutputRecordData AddTopEntityMetadata(BingEntity[] entities)
        {
            if (entities != null)
            {
                foreach (BingEntity entity in entities.Where(
                    entity => entity?.EntityPresentationInfo?.EntityTypeHints != null
                        && (entity.EntityPresentationInfo.EntityTypeHints[0] == "Person"
                            || entity.EntityPresentationInfo.EntityTypeHints[0] == "Organization"
                            || entity.EntityPresentationInfo.EntityTypeHints[0] == "Location")
                        && !String.IsNullOrEmpty(entity.Description)))
                {
                    var rootObject = new OutputRecord.OutputRecordData
                    {
                        Description = entity.Description,
                        Name = entity.Name
                    };

                    if (entity.ContractualRules != null)
                    {
                        foreach (var rule in entity.ContractualRules)
                        {
                            switch (rule._type)
                            {
                                case "ContractualRules/LicenseAttribution":
                                    rootObject.LicenseAttribution = rule.LicenseNotice;
                                    rootObject.LicenseUrl = rule.License.Url;
                                    break;
                                case "ContractualRules/LinkAttribution":
                                    rootObject.Source = rule.Text;
                                    rootObject.SourceUrl = rule.Url;
                                    break;
                            }
                        }
                    }

                    return rootObject;
                }
            }

            return new OutputRecord.OutputRecordData();
        }
        #endregion
    }
}

Zorg ervoor dat u uw eigen sleutelwaarde invoert in de key constante op basis van de sleutel die u hebt gekregen bij het registreren voor de Bing entiteiten zoeken-API.

De functie testen vanuit Visual Studio

Druk op F5 om het programma uit te voeren en het gedrag van de functie te testen. In dit geval gebruiken we de onderstaande functie om twee entiteiten op te zoeken. Gebruik een REST-client om een aanroep uit te voeren, zoals hieronder wordt weergegeven:

POST https://localhost:7071/api/EntitySearch

Aanvraagtekst

{
    "values": [
        {
            "recordId": "e1",
            "data":
            {
                "name":  "Pablo Picasso"
            }
        },
        {
            "recordId": "e2",
            "data":
            {
                "name":  "Microsoft"
            }
        }
    ]
}

De respons

Als het goed is, ziet u een antwoord dat lijkt op het volgende voorbeeld:

{
    "values": [
        {
            "recordId": "e1",
            "data": {
                "name": "Pablo Picasso",
                "description": "Pablo Ruiz Picasso was a Spanish painter [...]",
                "source": "Wikipedia",
                "sourceUrl": "http://en.wikipedia.org/wiki/Pablo_Picasso",
                "licenseAttribution": "Text under CC-BY-SA license",
                "licenseUrl": "http://creativecommons.org/licenses/by-sa/3.0/"
            },
            "errors": null,
            "warnings": null
        },
        "..."
    ]
}

De functie publiceren in Azure

Wanneer u tevreden bent met het gedrag van de functie, kunt u deze publiceren.

  1. Klik in Solution Explorer met de rechtermuisknop op het project en selecteer Publiceren. Kies Nieuwe>publicatie maken.

  2. Als u Visual Studio nog niet aan uw Azure-account hebt gekoppeld, selecteert u Een account toevoegen....

  3. Volg de aanwijzingen op het scherm. U wordt gevraagd een unieke naam op te geven voor uw app-service, het Azure-abonnement, de resourcegroep, het hostingabonnement en het opslagaccount dat u wilt gebruiken. U kunt een nieuwe resourcegroep, een nieuw hostingabonnement en een opslagaccount maken als u deze nog niet hebt. Wanneer u klaar bent, selecteert u Maken

  4. Nadat de implementatie is voltooid, ziet u de SITE-URL. Dit is het adres van uw functie-app in Azure.

  5. Navigeer in Azure Portal naar de resourcegroep en zoek naar de EntitySearch functie die u hebt gepubliceerd. Onder de sectie Beheren ziet u Hostsleutels. Selecteer het pictogram Kopiëren voor de standaardhostsleutel .

De functie testen in Azure

Nu u de standaardhostsleutel hebt, test u uw functie als volgt:

POST https://[your-entity-search-app-name].azurewebsites.net/api/EntitySearch?code=[enter default host key here]

Aanvraagbody

{
    "values": [
        {
            "recordId": "e1",
            "data":
            {
                "name":  "Pablo Picasso"
            }
        },
        {
            "recordId": "e2",
            "data":
            {
                "name":  "Microsoft"
            }
        }
    ]
}

In dit voorbeeld moet hetzelfde resultaat worden geproduceerd dat u eerder hebt gezien bij het uitvoeren van de functie in de lokale omgeving.

Verbinding maken met uw pijplijn

Nu u een nieuwe aangepaste vaardigheid hebt, kunt u deze toevoegen aan uw vaardighedenset. In het onderstaande voorbeeld ziet u hoe u de vaardigheid aanroept om beschrijvingen toe te voegen aan organisaties in het document (dit kan worden uitgebreid om ook te werken aan locaties en personen). Vervang door [your-entity-search-app-name] de naam van uw app.

{
    "skills": [
      "[... your existing skills remain here]",  
      {
        "@odata.type": "#Microsoft.Skills.Custom.WebApiSkill",
        "description": "Our new Bing entity search custom skill",
        "uri": "https://[your-entity-search-app-name].azurewebsites.net/api/EntitySearch?code=[enter default host key here]",
          "context": "/document/merged_content/organizations/*",
          "inputs": [
            {
              "name": "name",
              "source": "/document/merged_content/organizations/*"
            }
          ],
          "outputs": [
            {
              "name": "description",
              "targetName": "description"
            }
          ]
      }
  ]
}

Hier rekenen we op de ingebouwde vaardigheid voor entiteitsherkenning om aanwezig te zijn in de vaardighedenset en om het document te hebben verrijkt met de lijst met organisaties. Ter referentie: hier volgt een configuratie van vaardigheden voor entiteitextractie die voldoende is voor het genereren van de gegevens die we nodig hebben:

{
    "@odata.type": "#Microsoft.Skills.Text.V3.EntityRecognitionSkill",
    "name": "#1",
    "description": "Organization name extraction",
    "context": "/document/merged_content",
    "categories": [ "Organization" ],
    "defaultLanguageCode": "en",
    "inputs": [
        {
            "name": "text",
            "source": "/document/merged_content"
        },
        {
            "name": "languageCode",
            "source": "/document/language"
        }
    ],
    "outputs": [
        {
            "name": "organizations",
            "targetName": "organizations"
        }
    ]
},

Volgende stappen

Gefeliciteerd U hebt uw eerste aangepaste vaardigheid gemaakt. U kunt nu hetzelfde patroon volgen om uw eigen aangepaste functionaliteit toe te voegen. Klik op de volgende koppelingen voor meer informatie.