مثال: إنشاء مهارة مخصصة باستخدام واجهة برمجة تطبيقات بحث الكيانات Bing
في هذا المثال، تعرف على كيفية إنشاء مهارة مخصصة لواجهة برمجة تطبيقات الويب. ستقبل هذه المهارة المواقع والشخصيات العامة والمنظمات ، وتعيد الأوصاف لهم. يستخدم المثال دالة Azure لالتفاف واجهة برمجة تطبيقات بحث الكيانات Bing بحيث تقوم بتنفيذ واجهة المهارات المخصصة.
المتطلبات الأساسية
اقرأ عن مقالة واجهة المهارات المخصصة إذا لم تكن على دراية بواجهة الإدخال/الإخراج التي يجب أن تنفذها مهارة مخصصة.
قم بإنشاء مورد v7 للبحث Bing من خلال مدخل Azure. تتوفر طبقة مجانية وكافية لهذا المثال.
قم بتثبيت Visual Studio 2019 أو إصدار أحدث، بما في ذلك عبء عمل تطوير Azure.
إنشاء Azure Function
على الرغم من أن هذا المثال يستخدم دالة Azure لاستضافة واجهة برمجة تطبيقات ويب، إلا أنه غير مطلوب. طالما أنك تستوفي متطلبات الواجهة لمهارة إدراكية ، فإن النهج الذي تتبعه غير مهم. ومع ذلك، تسهل وظائف Azure إنشاء مهارة مخصصة.
إنشاء تطبيق وظيفة
في Visual Studio، اختر مشروع جديدةمن> قائمةملف.
في مربع الحوار Project جديد، حدد وظائف Azure كقالب وحدد التالي. اكتب اسما لمشروعك، وحدد إنشاء. يجب أن يكون اسم تطبيق الدالة صالحا كمساحة اسم C#، لذا لا تستخدم الشرطة السفلية أو الواصلات أو أي أحرف أخرى غير أبجدية رقمية.
حدد النوع الذي تريد أن يكون مشغل HTTP
بالنسبة إلى حساب التخزين، يمكنك تحديد بلا، حيث لن تحتاج إلى أي مساحة تخزين لهذه الوظيفة.
حدد إنشاء لإنشاء مشروع الوظيفة ووظيفة تشغيل HTTP.
تعديل التعليمة البرمجية لاستدعاء خدمة البحث عن الكيانات Bing
Visual Studio بإنشاء مشروع وفيه فئة تحتوي على رمز الغلاية لنوع الوظيفة المختارة. تقوم السمة FunctionName الموجودة على الأسلوب بتعيين اسم الدالة. تحدد السمة HttpTrigger أنه يتم تشغيل الدالة بواسطة طلب HTTP.
الآن ، استبدل كافة محتويات الملف Function1.cs بالتعليمة البرمجية التالية:
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
}
}
تأكد من إدخال قيمة المفتاح الخاصة بك في الثابت استنادا إلى المفتاح الذي حصلت عليه عند الاشتراك في key واجهة برمجة تطبيقات البحث عن الكيانات Bing.
يتضمن هذا النموذج كافة التعليمات البرمجية اللازمة في ملف واحد للراحة. يمكنك العثور على نسخة أكثر تنظيما قليلا من نفس المهارة في مستودع مهارات الطاقة.
بالطبع ، يمكنك إعادة تسمية الملف من Function1.cs إلى BingEntitySearch.cs.
اختبار الوظيفة من Visual Studio
اضغط F5 لتشغيل البرنامج واختبار سلوكيات الوظيفة. في هذه الحالة، سنستخدم الدالة أدناه للبحث عن كيانين. استخدم ساعي البريد أو عازف الكمان لإصدار مكالمة مثل تلك الموضحة أدناه:
POST https://localhost:7071/api/EntitySearch
نص الطلب
{
"values": [
{
"recordId": "e1",
"data":
{
"name": "Pablo Picasso"
}
},
{
"recordId": "e2",
"data":
{
"name": "Microsoft"
}
}
]
}
الاستجابة
من المفترض أن تشاهد استجابة مشابهة للمثال التالي:
{
"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
},
"..."
]
}
نشر الدالة على Azure
عندما تكون راضيا عن سلوك الوظيفة، يمكنك نشره.
في Solution Explorer، انقر بزر الماوس الأيمن فوق المشروع، ثم حدد Publish. اختر إنشاء نشر جديد>.
إذا لم تكن قد قمت بالفعل بتوصيل Visual Studio بحساب Azure الخاص بك، فحدد إضافة حساب....
اتبع المطالبات التي تظهر على الشاشة. تتم مطالبتك بتحديد اسم فريد لخدمة التطبيق واشتراك Azure ومجموعة الموارد وخطة الاستضافة وحساب التخزين الذي تريد استخدامه. يمكنك إنشاء مجموعة موارد جديدة وخطة استضافة جديدة وحساب تخزين إذا لم يكن لديك هذه الموارد بالفعل. عند الانتهاء، حدد إنشاء
بعد اكتمال النشر، لاحظ عنوان URL للموقع. إنه عنوان تطبيق الوظائف الخاص بك في Azure.
في مدخل Azure، انتقل إلى مجموعة الموارد، وابحث عن الوظيفة التي
EntitySearchنشرتها. ضمن القسم إدارة ، من المفترض أن ترى مفاتيح المضيف. حدد أيقونة نسخ لمفتاح المضيف الافتراضي .
اختبار الدالة في Azure
الآن بعد أن أصبح لديك مفتاح المضيف الافتراضي ، اختبر الدالة الخاصة بك على النحو التالي:
POST https://[your-entity-search-app-name].azurewebsites.net/api/EntitySearch?code=[enter default host key here]
نص الطلب
{
"values": [
{
"recordId": "e1",
"data":
{
"name": "Pablo Picasso"
}
},
{
"recordId": "e2",
"data":
{
"name": "Microsoft"
}
}
]
}
يجب أن ينتج عن هذا المثال نفس النتيجة التي رأيتها سابقا عند تشغيل الدالة في البيئة المحلية.
الاتصال إلى خط الأنابيب الخاص بك
الآن بعد أن أصبح لديك مهارة مخصصة جديدة ، يمكنك إضافتها إلى مجموعة المهارات الخاصة بك. يوضح لك المثال أدناه كيفية استدعاء مهارة إضافة أوصاف إلى المؤسسات في المستند (يمكن توسيع نطاق ذلك للعمل أيضا على المواقع والأشخاص). استبدل [your-entity-search-app-name] باسم تطبيقك.
{
"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"
}
]
}
]
}
هنا ، نعتمد على مهارة التعرف على الكيانات المضمنة لتكون موجودة في مجموعة المهارات وإثراء المستند بقائمة المنظمات. للرجوع إليها ، إليك تكوين مهارة استخراج الكيانات التي ستكون كافية في توليد البيانات التي نحتاجها:
{
"@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"
}
]
},
الخطوات التالية
تهانينا! لقد أنشأت أول مهارة مخصصة لك. يمكنك الآن اتباع نفس النمط لإضافة وظائفك المخصصة. انقر فوق الروابط التالية لمعرفة المزيد.