你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
适用于:SDK v4
本文演示如何创建支持多个操作的技能。 它使用对话支持这些操作。 主对话接收来自技能使用者的初始输入,然后启动相应的操作。 有关为关联的示例代码实现技能使用者的信息,请参阅如何通过对话使用技能。
本文假定你已熟悉如何创建技能。 有关如何创建一般技能机器人的信息,请参阅如何实现技能。
注意
若要使用所选的 AI 服务、业务流程和知识生成代理,请考虑使用 Microsoft 365 代理 SDK。 代理 SDK 支持 C#、JavaScript 或 Python。 可以在 aka.ms/agents 了解有关代理 SDK 的详细信息。 如果要查找基于 SaaS 的代理平台,请考虑 Microsoft Copilot Studio。 如果现有的机器人是使用 Bot Framework SDK 生成的,则可以将机器人更新到代理 SDK。 可以查看 Bot Framework SDK 到代理 SDK 迁移指南的核心更改和更新。 自 2025 年 12 月 31 日起,Bot Framework SDK 的支持票证将不再提供服务。
先决条件
- 了解机器人基础知识、技能机器人工作原理以及如何实现技能。
- Azure 订阅(用于部署技能)。 如果没有订阅,请在开始之前创建一个免费帐户。
- (可选)LUIS 帐户。 (有关详细信息,请参阅如何向机器人添加自然语言理解。)
- 以 C#、JavaScript、Java 或 Python 编写的 "技能对话" 示例的副本。
注意
语言理解 (LUIS) 将于 2025 年 10 月 1 日停用。 从 2023 年 4 月 1 日开始,将无法创建新的 LUIS 资源。 语言理解的较新版本现已作为 Azure AI 语言的一部分提供。
对话语言理解(CLU)是 Azure AI 语言的一项功能,是 LUIS 的更新版本。 有关 Bot Framework SDK 中的语言理解支持的详细信息,请参阅自然语言理解。
关于此示例
skills skillDialog 示例包含下面的两个机器人的项目:
- 对话根机器人,通过技能对话类使用技能。
- 对话技能机器人,它通过对话来处理来自技能使用者的活动。 此技能是核心机器人示例的改编版本。 (有关核心机器人的详细信息,请参阅如何向机器人添加自然语言理解。)
本文重点介绍如何在技能机器人中使用对话来管理多个操作。
有关技能使用者机器人的信息,请参阅如何通过对话使用技能。
资源
对于部署的机器人,机器人到机器人身份验证要求每个参与的机器人都有有效的标识。 但是,可以使用 Bot Framework Emulator 在本地测试技能和技能使用者,而无需提供标识信息。
若要使技能可供面向用户的机器人使用,请在 Azure 中注册该技能。 有关详细信息,请参阅如何使用 Azure AI 机器人服务注册机器人。
或者,技能机器人可以使用航班预订 LUIS 模型。 若要使用此模型,请使用 CognitiveModels/FlightBooking.json 文件来创建、训练和发布 LUIS 模型。
应用程序配置
(可选)将技能的标识信息添加到技能的配置文件。 (如果技能或技能使用者指定标识,则两者都必须指定。)
如果使用的是 LUIS 模型,请添加 LUIS 应用 ID、API 密钥和 API 主机名。
DialogSkillBot\appsettings.json
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"ConnectionName": "",
"LuisAppId": "",
"LuisAPIKey": "",
"LuisAPIHostName": "",
// This is a comma separate list with the App IDs that will have access to the skill.
// This setting is used in AllowedCallersClaimsValidator.
// Examples:
// [ "*" ] allows all callers.
// [ "AppId1", "AppId2" ] only allows access to parent bots with "AppId1" and "AppId2".
"AllowedCallers": [ "*" ]
}
活动路由逻辑
该技能支持几种不同的功能。 它可以预订航班或获取城市的天气状况。 此外,如果它收到这些上下文之外的消息,则可以使用 LUIS 来尝试解释消息。 技能清单介绍了这些操作及其输入和输出参数以及技能的终结点。 值得注意的是,该技能可以处理“BookFlight”或“GetWeather”这类事件。 它还可以处理消息活动。
技能定义活动路由对话,该对话用于根据技能使用者中的初始传入活动选择要启动的操作。 如果已提供,LUIS 模型可以识别初始消息中的预订航班和获取天气信息意向。
预订航班操作是一个多步骤过程,作为单独的对话实现。 操作开始后,传入活动由该对话处理。 获取天气信息操作具有要在完全实现的机器人中替换的占位符逻辑。
活动路由对话包括用来执行以下操作的代码:
技能中使用的对话继承自组件对话类。 有关组件对话的详细信息,请参阅如何管理对话复杂性。
初始化对话
活动路线对话框包含一个用于预订航班的子对话框。 主瀑布对话包含一个步骤,该步骤将根据收到的初始活动启动操作。
它还接受 LUIS 识别器。 如果已初始化此识别器,则对话将使用它来解释初始消息活动的意向。
DialogSkillBot\Dialogs\ActivityRouterDialog.cs
private readonly DialogSkillBotRecognizer _luisRecognizer;
public ActivityRouterDialog(DialogSkillBotRecognizer luisRecognizer)
: base(nameof(ActivityRouterDialog))
{
_luisRecognizer = luisRecognizer;
AddDialog(new BookingDialog());
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
处理初始活动
在主瀑布对话的第一个(且仅有一个)步骤中,技能检查传入活动类型。
- 事件活动将转发到事件活动处理程序,该处理程序根据事件的名称启动相应的操作。
- 消息活动将转发到消息活动处理程序,该处理程序在确定要执行的操作之前执行其他处理。
如果技能无法识别传入活动的类型或事件的名称,则将发送错误消息并结束。
DialogSkillBot\Dialogs\ActivityRouterDialog.cs
private async Task<DialogTurnResult> ProcessActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// A skill can send trace activities, if needed.
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.ProcessActivityAsync()", label: $"Got ActivityType: {stepContext.Context.Activity.Type}", cancellationToken: cancellationToken);
switch (stepContext.Context.Activity.Type)
{
case ActivityTypes.Event:
return await OnEventActivityAsync(stepContext, cancellationToken);
case ActivityTypes.Message:
return await OnMessageActivityAsync(stepContext, cancellationToken);
default:
// We didn't get an activity type we can handle.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized ActivityType: \"{stepContext.Context.Activity.Type}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Complete);
}
}
// This method performs different tasks based on the event name.
private async Task<DialogTurnResult> OnEventActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnEventActivityAsync()", label: $"Name: {activity.Name}. Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);
// Resolve what to execute based on the event name.
switch (activity.Name)
{
case "BookFlight":
return await BeginBookFlight(stepContext, cancellationToken);
case "GetWeather":
return await BeginGetWeather(stepContext, cancellationToken);
default:
// We didn't get an event name we can handle.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized EventName: \"{activity.Name}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Complete);
}
}
处理消息活动
如果已配置 LUIS 识别器,则技能将调用 LUIS,然后基于意向启动操作。 如果未配置 LUIS 识别器或意向不受支持,则技能将发送错误消息并结束。
DialogSkillBot\Dialogs\ActivityRouterDialog.cs
// This method just gets a message activity and runs it through LUIS.
private async Task<DialogTurnResult> OnMessageActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnMessageActivityAsync()", label: $"Text: \"{activity.Text}\". Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);
if (!_luisRecognizer.IsConfigured)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
}
else
{
// Call LUIS with the utterance.
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
// Create a message showing the LUIS results.
var sb = new StringBuilder();
sb.AppendLine($"LUIS results for \"{activity.Text}\":");
var (intent, intentScore) = luisResult.Intents.FirstOrDefault(x => x.Value.Equals(luisResult.Intents.Values.Max()));
sb.AppendLine($"Intent: \"{intent}\" Score: {intentScore.Score}");
await stepContext.Context.SendActivityAsync(MessageFactory.Text(sb.ToString(), inputHint: InputHints.IgnoringInput), cancellationToken);
// Start a dialog if we recognize the intent.
switch (luisResult.TopIntent().intent)
{
case FlightBooking.Intent.BookFlight:
return await BeginBookFlight(stepContext, cancellationToken);
case FlightBooking.Intent.GetWeather:
return await BeginGetWeather(stepContext, cancellationToken);
default:
// Catch all for unhandled intents.
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
break;
}
}
return new DialogTurnResult(DialogTurnStatus.Complete);
}
开始多步骤操作
预订航班操作启动多步骤对话,以获取用户的预订详细信息。
获取天气信息的功能尚未实现。 目前,它会发送一条占位符消息,然后结束。
DialogSkillBot\Dialogs\ActivityRouterDialog.cs
private async Task<DialogTurnResult> BeginBookFlight(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
var bookingDetails = new BookingDetails();
if (activity.Value != null)
{
bookingDetails = JsonConvert.DeserializeObject<BookingDetails>(JsonConvert.SerializeObject(activity.Value));
}
// Start the booking dialog.
var bookingDialog = FindDialog(nameof(BookingDialog));
return await stepContext.BeginDialogAsync(bookingDialog.Id, bookingDetails, cancellationToken);
}
private static async Task<DialogTurnResult> BeginGetWeather(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
var ___location = new Location();
if (activity.Value != null)
{
___location = JsonConvert.DeserializeObject<Location>(JsonConvert.SerializeObject(activity.Value));
}
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
var getWeatherMessageText = $"TODO: get weather for here (lat: {___location.Latitude}, long: {___location.Longitude}";
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Complete);
}
返回结果
技能为预订航班操作启动预订对话。 由于活动路由对话只包含一个步骤,因此当预订对话结束时,活动路由对话也会结束,而预订对话的对话结果将成为活动路由对话的对话结果。
无需设置返回值,获取天气信息操作即可结束。
取消多步骤操作
预订对话及其子日期解析程序对话均派生自基本的“取消和帮助”对话,该对话将检查来自用户的消息。
- 单击“帮助”或“?”时,它将显示一条帮助消息,然后在下一轮继续对话流。
- 单击“取消”或“退出”时,它将取消所有对话,这会结束技能。
有关详细信息,请参阅如何处理用户中断。
服务注册
此技能所需的服务与一般技能机器人所需的服务相同。 有关所需服务的讨论,请参阅如何实现技能。
技能清单
技能清单是一个 JSON 文件,用于描述技能可以执行的活动、其输入和输出参数以及技能的终结点。 清单包含从其他机器人访问技能所需的信息。
DialogSkillBot\wwwroot\manifest\dialogchildbot-manifest-1.0.json
{
"$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
"$id": "DialogSkillBot",
"name": "Skill bot with dialogs",
"version": "1.0",
"description": "This is a sample skill definition for multiple activity types.",
"publisherName": "Microsoft",
"privacyUrl": "https://dialogskillbot.contoso.com/privacy.html",
"copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
"license": "",
"iconUrl": "https://dialogskillbot.contoso.com/icon.png",
"tags": [
"sample",
"travel",
"weather",
"luis"
],
"endpoints": [
{
"name": "default",
"protocol": "BotFrameworkV3",
"description": "Default endpoint for the skill.",
"endpointUrl": "https://dialogskillbot.contoso.com/api/messages",
"msAppId": "00000000-0000-0000-0000-000000000000"
}
],
"activities": {
"bookFlight": {
"description": "Books a flight (multi turn).",
"type": "event",
"name": "BookFlight",
"value": {
"$ref": "#/definitions/bookingInfo"
},
"resultValue": {
"$ref": "#/definitions/bookingInfo"
}
},
"getWeather": {
"description": "Retrieves and returns the weather for the user's ___location.",
"type": "event",
"name": "GetWeather",
"value": {
"$ref": "#/definitions/___location"
},
"resultValue": {
"$ref": "#/definitions/weatherReport"
}
},
"passthroughMessage": {
"type": "message",
"description": "Receives the user's utterance and attempts to resolve it using the skill's LUIS models.",
"value": {
"type": "object"
}
}
},
"definitions": {
"bookingInfo": {
"type": "object",
"required": [
"origin"
],
"properties": {
"origin": {
"type": "string",
"description": "This is the origin city for the flight."
},
"destination": {
"type": "string",
"description": "This is the destination city for the flight."
},
"travelDate": {
"type": "string",
"description": "The date for the flight in YYYY-MM-DD format."
}
}
},
"weatherReport": {
"type": "array",
"description": "Array of forecasts for the next week.",
"items": [
{
"type": "string"
}
]
},
"___location": {
"type": "object",
"description": "Location metadata.",
"properties": {
"latitude": {
"type": "number",
"title": "Latitude"
},
"longitude": {
"type": "number",
"title": "Longitude"
},
"postalCode": {
"type": "string",
"title": "Postal code"
}
}
}
}
}
技能清单架构是一个 JSON 文件,用于描述技能清单的架构。 最新架构版本为 v2.1。
测试技能机器人
可以使用技能使用者测试模拟器中的技能。 为此,需要同时运行技能和技能使用者机器人。 有关如何配置技能的信息,请参阅如何通过对话使用技能。
下载并安装最新的 Bot Framework Emulator。
- 在计算机上以本地方式运行对话技能机器人和对话根机器人。 如需说明,请参阅
README
、JavaScript、Java 或 Python 的示例的 文件。 - 使用模拟器测试机器人。
- 第一次加入对话时,机器人会显示欢迎消息,并询问你要调用的技能。 此示例的技能机器人只包含一项技能。
- 选择“DialogSkillBot”。
- 接下来,机器人会要求你为技能选择一项操作。 选择“BookFlight”。
- 技能开始预订航班操作;回答提示。
- 技能完成后,根机器人会显示预订详细信息,然后会再次提示你要调用的技能。
- 再次选择“DialogSkillBot”和“BookFlight”。
- 回答第一个提示,然后输入“cancel”以取消操作。
- 技能机器人结束时未完成此操作,使用者会提示你要调用的技能。
了解更多调试信息
由于技能与技能使用者之间的流量已经过身份验证,因此调试此类机器人时会执行额外的步骤。
- 技能使用者及其直接或间接使用的所有技能都必须处于运行中。
- 如果机器人在本地运行,并且任何机器人有应用 ID 和密码,则所有机器人都必须具有有效的 ID 和密码。
- 如果所有机器人都已部署,请参阅如何使用 devtunnel 从任何通道调试机器人。
- 如果某些机器人在本地运行,并且部署了一些机器人,请参阅如何调试技能或技能使用者。
或者,可以像调试其他机器人一样调试技能使用者或技能。 有关详细信息,请参阅调试机器人和使用 Bot Framework Emulator 执行调试。
其他信息
- 通过对话使用技能介绍如何通过技能对话使用技能。