Példa: Egyéni képesség létrehozása a Bing Entity Search API használatával

Ebben a példában megtudhatja, hogyan hozhat létre egyéni webes API-képességet. Ez a képesség helyeket, közszereplőket és szervezeteket fogad el, és leírásokat ad vissza hozzájuk. A példa egy Azure-függvény használatával burkolja be a Bing Entity Search API-t, hogy megvalósítsa az egyéni készségfelületet.

Előfeltételek

  • Ha nem ismeri az egyéni képesség által implementálandó bemeneti/kimeneti felületet, olvassa el az egyéni készség felületéről szóló cikket.

  • Bing Search-erőforrás létrehozása az Azure Portalon. Ehhez a példához ingyenes szint érhető el és elegendő.

  • Telepítse a Visual Studiót vagy újabb verziót.

Azure-függvény létrehozása

Bár ez a példa egy Azure-függvényt használ egy webes API üzemeltetéséhez, nem szükséges. Mindaddig, amíg megfelel a kognitív képességek felületi követelményeinek, az ön által használt megközelítés nem lényegtelen. Az Azure Functions azonban megkönnyíti az egyéni képességek létrehozását.

Projekt létrehozása

  1. A Visual Studióban válassza az Új>projekt lehetőséget a Fájl menüből.

  2. Válassza az Azure Functionst sablonként, és válassza a Tovább gombot. Írja be a projekt nevét, és válassza a Létrehozás lehetőséget. A függvényalkalmazás nevének C#-névtérként kell érvényesnek lennie, ezért ne használjon aláhúzásjeleket, kötőjeleket vagy más nem alfanumerikus karaktereket.

  3. Válasszon ki egy hosszú távú támogatást igénylő keretrendszert.

  4. Válassza a HTTP-eseményindítót a projekthez hozzáadni kívánt függvény típusához.

  5. Válassza a függvényt az engedélyezési szinthez.

  6. Válassza a Létrehozás lehetőséget a függvényprojekt és a HTTP által aktivált függvény létrehozásához.

Kód hozzáadása a Bing Entity API meghívásához

A Visual Studio létrehoz egy projektet a kiválasztott függvénytípushoz tartozó sablonkóddal. A metódus FunctionName attribútuma adja meg a függvény nevét. A HttpTrigger attribútum adja meg, hogy a függvényt egy HTTP-kérelem aktiválja.

Cserélje le a Function1.cs tartalmát a következő kódra:

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

A Bing entitáskeresési API-ra való regisztrációkor kapott kulcs alapján mindenképpen adja meg a saját kulcsértékét az állandóban key .

A függvény tesztelése a Visual Studióból

Nyomja le az F5 billentyűt a program futtatásához és a függvény viselkedésének teszteléséhez. Ebben az esetben az alábbi függvény használatával keresünk két entitást. A REST-ügyfél használatával az alábbihoz hasonló hívást kezdeményezhet:

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

Kérés törzse

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

Válasz

A következő példához hasonló választ kell látnia:

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

A függvény közzététele az Azure-ban

Ha elégedett a függvény viselkedésével, közzéteheti.

  1. A Megoldáskezelőben kattintson a jobb gombbal a projektre, és válassza a Publish (Közzététel) lehetőséget. Válassza az Új>közzététel létrehozása lehetőséget.

  2. Ha még nem csatlakoztatta a Visual Studiót az Azure-fiókjához, válassza a Fiók hozzáadása... lehetőséget.

  3. Kövesse a képernyőn megjelenő utasításokat. A rendszer arra kéri, hogy adjon meg egy egyedi nevet az app service-nek, az Azure-előfizetésnek, az erőforráscsoportnak, az üzemeltetési csomagnak és a használni kívánt tárfióknak. Ha még nem rendelkezik ezekkel, létrehozhat egy új erőforráscsoportot, egy új üzemeltetési tervet és egy tárfiókot. Ha végzett, válassza a Létrehozás lehetőséget

  4. Az üzembe helyezés befejezése után figyelje meg a webhely URL-címét. Ez az Azure-beli függvényalkalmazás címe.

  5. Az Azure Portalon keresse meg az erőforráscsoportot, és keresse meg a EntitySearch közzétett függvényt. A Kezelés szakaszban meg kell jelennie a gazdagépkulcsoknak. Válassza az alapértelmezett gazdagépkulcs Másolás ikonjának kiválasztását.

A függvény tesztelése az Azure-ban

Most, hogy már rendelkezik az alapértelmezett gazdagépkulcspel, tesztelje a függvényt az alábbiak szerint:

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

Kérelem törzse

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

Ennek a példának ugyanazt az eredményt kell eredményeznie, amit korábban a függvény helyi környezetben való futtatásakor látott.

Csatlakozás a folyamathoz

Most, hogy új egyéni képességekkel rendelkezik, hozzáadhatja azt a készségkészlethez. Az alábbi példa bemutatja, hogyan hívhatja meg a készséget, hogy leírásokat adjon hozzá a dokumentumban található szervezetekhez (ez kiterjeszthető a helyeken és személyeken való munkavégzésre is). Cserélje le [your-entity-search-app-name] az alkalmazás nevére.

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

Itt arra számítunk, hogy a beépített entitásfelismerő képesség jelen lesz a képességkészletben, és a dokumentumot a szervezetek listájával bővítjük. Az alábbiakban egy entitás-kinyerési képesség konfigurációját tekintjük át, amely elegendő lenne a szükséges adatok létrehozásához:

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

Következő lépések

Gratulálunk! Létrehozta az első egyéni készségét. Most már ugyanezt a mintát követve hozzáadhatja saját egyéni funkcióit. További információért kattintson az alábbi hivatkozásokra.