Tento článek ukazuje, jak vytvořit dovednost, která podporuje více akcí. Podporuje tyto akce pomocí dialogových oken. Hlavní dialog obdrží počáteční vstup od příjemce dovednosti a pak zahájí příslušnou akci. Informace o implementaci příjemce dovedností pro přidružený vzorový kód najdete v tématu využití dovednosti pomocí dialogových oken.
Tento článek předpokládá, že už máte zkušenosti s vytvářením dovedností.
Pokud chcete vytvořit robota dovedností obecně, podívejte se , jak implementovat dovednost.
Poznámka:
Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.
Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.
Konverzační jazyk understanding (CLU), funkce jazyka Azure AI, je aktualizovaná verze služby LUIS.
Další informace o podpoře porozumění jazyku v sadě SDK služby Bot Framework najdete v tématu Principy přirozeného jazyka.
O této ukázce
Ukázka dovednostíDialog dovedností zahrnuje projekty pro dva roboty:
The dialog root bot, which uses a skill dialog class to consume a skill.
Robot dovedností dialogového okna, který používá dialogové okno pro zpracování aktivit pocházejících od příjemců dovedností. Tato dovednost je adaptací základní ukázky robota . (Další informace o základním robotovi najdete v článku o tom, jak do robota přidat porozumění přirozenému jazyku.)
Tento článek se zaměřuje na použití dialogového okna v robotovi dovedností ke správě více akcí.
U nasazených robotů vyžaduje ověřování robota k robotovi platnou identitu každého zúčastněného robota.
Pomocí bot Framework Emulatoru ale můžete testovat dovednosti a uživatele dovedností místně bez informací o identitě.
Volitelně může robot dovedností používat model služby LUIS rezervace letů. Pokud chcete tento model použít, použijte k vytvoření, trénování a publikování modelu LUIS soubor CognitiveModels/FlightBooking.json.
Konfigurace aplikací
Volitelně můžete do konfiguračního souboru dovednosti přidat informace o identitě dovednosti.
(Pokud dovednost nebo příjemce dovednosti specifikuje identitu, musí obě možnosti.)
Pokud používáte model LUIS, přidejte ID aplikace LUIS, klíč rozhraní API a název hostitele rozhraní API.
{
"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": [ "*" ]
}
MicrosoftAppId=
MicrosoftAppPassword=
server.port=39783
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=*
dialog-skill-bot/config.py
PORT = 39783
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
APP_TYPE = os.environ.get("MicrosoftAppType", "MultiTenant")
APP_TENANTID = os.environ.get("MicrosoftAppTenantId", "")
# Callers to only those specified, '*' allows any caller.
# Example: os.environ.get("AllowedCallers", ["aaaaaaaa-1111-aaaa-aaaa-aaaaaaaa"])
ALLOWED_CALLERS = os.environ.get("AllowedCallers", ["*"])
LUIS_APP_ID = os.environ.get("LuisAppId", "")
LUIS_API_KEY = os.environ.get("LuisAPIKey", "")
Logika směrování aktivit
Dovednost podporuje několik různých funkcí. Může si rezervovat let nebo získat počasí pro město. Kromě toho, pokud obdrží zprávu mimo některý z těchto kontextů, může použít službu LUIS k pokusu o interpretaci zprávy.
Manifest dovednosti popisuje tyto akce, jejich vstupní a výstupní parametry a koncové body dovednosti.
Nezapomeňte, že dovednost dokáže zpracovat událost "BookFlight" nebo "GetWeather". Může také zpracovávat aktivity zpráv.
Dovednost definuje dialogové okno směrování aktivity, které používá k výběru akce, která se má zahájit, na základě počáteční příchozí aktivity od příjemce dovednosti.
Pokud je k dispozici, model LUIS dokáže rozpoznat záměry book-flight a get-weather v počáteční zprávě.
Akce book-flight je vícekrokový proces, implementovaný jako samostatný dialog. Po zahájení akce se příchozí aktivity zpracovávají tímto dialogem. Akce get-weather má zástupnou logiku, která by byla nahrazena plně implementovaným robotem.
Dialogové okno směrování aktivit obsahuje kód pro:
Dialogy použité v dovednosti dědí z třídy dialogového okna komponenty. Další informace o dialogových oknech komponent najdete v tématu správa složitosti dialogového okna.
Inicializace dialogového okna
Dialogové okno směrování aktivit obsahuje podřízené dialogové okno pro rezervaci letu. Hlavní vodopádový dialog obsahuje jeden krok, který spustí akci na základě počáteční přijaté aktivity.
Přijímá také rozpoznávání luis. Pokud je tento rozpoznač inicializován, dialogové okno ho použije k interpretaci záměru počáteční aktivity zprávy.
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.js
constructor(conversationState, luisRecognizer = undefined) {
super(ACTIVITY_ROUTER_DIALOG);
if (!conversationState) throw new Error('[MainDialog]: Missing parameter \'conversationState\' is required');
this.luisRecognizer = luisRecognizer;
// Define the main dialog and its related components.
// This is a sample "book a flight" dialog.
this.addDialog(new BookingDialog())
.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
this.processActivity.bind(this)
]));
this.initialDialogId = WATERFALL_DIALOG;
}
DialogSkillBot\Dialogs\ActivityRouterDialog.java
private final DialogSkillBotRecognizer luisRecognizer;
public ActivityRouterDialog(DialogSkillBotRecognizer luisRecognizer) {
super("ActivityRouterDialog");
this.luisRecognizer = luisRecognizer;
addDialog(new BookingDialog());
List<WaterfallStep> stepList = new ArrayList<WaterfallStep>();
stepList.add(this::processActivity);
addDialog(new WaterfallDialog("WaterfallDialog", stepList));
// The initial child Dialog to run.
setInitialDialogId("WaterfallDialog");
}
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);
}
}
dialogSkillBot/dialogs/activityRouterDialog.js
async processActivity(stepContext) {
// A skill can send trace activities, if needed.
const traceActivity = {
type: ActivityTypes.Trace,
timestamp: new Date(),
text: 'ActivityRouterDialog.processActivity()',
label: `Got activityType: ${ stepContext.context.activity.type }`
};
await stepContext.context.sendActivity(traceActivity);
switch (stepContext.context.activity.type) {
case ActivityTypes.Event:
return await this.onEventActivity(stepContext);
case ActivityTypes.Message:
return await this.onMessageActivity(stepContext);
default:
// Catch all for unhandled intents.
await stepContext.context.sendActivity(
`Unrecognized ActivityType: "${ stepContext.context.activity.type }".`,
undefined,
InputHints.IgnoringInput
);
return { status: DialogTurnStatus.complete };
}
}
/**
* This method performs different tasks based on event name.
*/
async onEventActivity(stepContext) {
const activity = stepContext.context.activity;
const traceActivity = {
type: ActivityTypes.Trace,
timestamp: new Date(),
text: 'ActivityRouterDialog.onEventActivity()',
label: `Name: ${ activity.name }, Value: ${ JSON.stringify(activity.value) }`
};
await stepContext.context.sendActivity(traceActivity);
// Resolve what to execute based on the event name.
switch (activity.name) {
case 'BookFlight':
return await this.beginBookFlight(stepContext);
case 'GetWeather':
return await this.beginGetWeather(stepContext);
default:
// We didn't get an event name we can handle.
await stepContext.context.sendActivity(
`Unrecognized EventName: "${ stepContext.context.activity.name }".`,
undefined,
InputHints.IgnoringInput
);
return { status: DialogTurnStatus.complete };
}
}
DialogSkillBot\Dialogs\ActivityRouterDialog.java
private CompletableFuture<DialogTurnResult> processActivity(WaterfallStepContext stepContext) {
// A skill can send trace activities, if needed.
TurnContext.traceActivity(
stepContext.getContext(),
String.format(
"{%s}.processActivity() Got ActivityType: %s",
this.getClass().getName(),
stepContext.getContext().getActivity().getType()
)
);
switch (stepContext.getContext().getActivity().getType()) {
case ActivityTypes.EVENT:
return onEventActivity(stepContext);
case ActivityTypes.MESSAGE:
return onMessageActivity(stepContext);
default:
String defaultMessage = String
.format("Unrecognized ActivityType: \"%s\".", stepContext.getContext().getActivity().getType());
// We didn't get an activity type we can handle.
return stepContext.getContext()
.sendActivity(MessageFactory.text(defaultMessage, defaultMessage, InputHints.IGNORING_INPUT))
.thenCompose(result -> {
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.COMPLETE));
});
}
}
// This method performs different tasks super. on the event name.
private CompletableFuture<DialogTurnResult> onEventActivity(WaterfallStepContext stepContext) {
Activity activity = stepContext.getContext().getActivity();
TurnContext.traceActivity(
stepContext.getContext(),
String.format(
"%s.onEventActivity(), label: %s, Value: %s",
this.getClass().getName(),
activity.getName(),
GetObjectAsJsonString(activity.getValue())
)
);
// Resolve what to execute super. on the event name.
switch (activity.getName()) {
case "BookFlight":
return beginBookFlight(stepContext);
case "GetWeather":
return beginGetWeather(stepContext);
default:
String message = String.format("Unrecognized EventName: \"%s\".", activity.getName());
// We didn't get an event name we can handle.
stepContext.getContext().sendActivity(MessageFactory.text(message, message, InputHints.IGNORING_INPUT));
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.COMPLETE));
}
}
async def process_activity(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
current_activity_type = step_context.context.activity.type
# A skill can send trace activities, if needed.
await step_context.context.send_trace_activity(
f"{ActivityRouterDialog.__name__}.process_activity()",
label=f"Got ActivityType: {current_activity_type}",
)
if current_activity_type == ActivityTypes.event:
return await self._on_event_activity(step_context)
if current_activity_type == ActivityTypes.message:
return await self._on_message_activity(step_context)
else:
# We didn't get an activity type we can handle.
await step_context.context.send_activity(
MessageFactory.text(
f'Unrecognized ActivityType: "{current_activity_type}".',
input_hint=InputHints.ignoring_input,
)
)
return DialogTurnResult(DialogTurnStatus.Complete)
async def _on_event_activity(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
"""
This method performs different tasks based on the event name.
"""
activity = step_context.context.activity
# Resolve what to execute based on the event name.
if activity.name == "BookFlight":
return await self._begin_book_flight(step_context)
if activity.name == "GetWeather":
return await self._begin_get_weather(step_context)
# We didn't get an activity name we can handle.
await step_context.context.send_activity(
MessageFactory.text(
f'Unrecognized ActivityName: "{activity.name}".',
input_hint=InputHints.ignoring_input,
)
)
return DialogTurnResult(DialogTurnStatus.Complete)
Zpracování aktivit zpráv
Pokud je služba rozpoznávání LUIS nakonfigurovaná, dovednost volá službu LUIS a pak zahájí akci na základě záměru.
Pokud rozpoznávání luis není nakonfigurované nebo záměr není podporovaný, dovednost odešle chybovou zprávu a končí.
// 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.js
/**
* This method just gets a message activity and runs it through LUIS.
*/
async onMessageActivity(stepContext) {
const activity = stepContext.context.activity;
const traceActivity = {
type: ActivityTypes.Trace,
timestamp: new Date(),
text: 'ActivityRouterDialog.onMessageActivity()',
label: `Text: ${ activity.text }, Value: ${ JSON.stringify(activity.value) }`
};
await stepContext.context.sendActivity(traceActivity);
if (!this.luisRecognizer || !this.luisRecognizer.isConfigured) {
await stepContext.context.sendActivity(
'NOTE: LUIS is not configured. To enable all capabilities, please add \'LuisAppId\', \'LuisAPIKey\' and \'LuisAPIHostName\' to the appsettings.json file.',
undefined,
InputHints.IgnoringInput
);
} else {
// Call LUIS with the utterance.
const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context);
const topIntent = LuisRecognizer.topIntent(luisResult);
// Create a message showing the LUIS result.
let resultString = '';
resultString += `LUIS results for "${ activity.text }":\n`;
resultString += `Intent: "${ topIntent }", Score: ${ luisResult.intents[topIntent].score }\n`;
await stepContext.context.sendActivity(resultString, undefined, InputHints.IgnoringInput);
switch (topIntent.intent) {
case 'BookFlight':
return await this.beginBookFlight(stepContext);
case 'GetWeather':
return await this.beginGetWeather(stepContext);
default: {
// Catch all for unhandled intents.
const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ topIntent.intent })`;
await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
break;
}
}
}
return { status: DialogTurnStatus.complete };
}
DialogSkillBot\Dialogs\ActivityRouterDialog.java
// This method just gets a message activity and runs it through LUS.
private CompletableFuture<DialogTurnResult> onMessageActivity(WaterfallStepContext stepContext) {
Activity activity = stepContext.getContext().getActivity();
TurnContext.traceActivity(
stepContext.getContext(),
String.format(
"%s.onMessageActivity(), label: %s, Value: %s",
this.getClass().getName(),
activity.getName(),
GetObjectAsJsonString(activity.getValue())
)
);
if (!luisRecognizer.getIsConfigured()) {
String message = "NOTE: LUIS instanceof not configured. To enable all capabilities, add 'LuisAppId',"
+ " 'LuisAPKey' and 'LuisAPHostName' to the appsettings.json file.";
return stepContext.getContext()
.sendActivity(MessageFactory.text(message, message, InputHints.IGNORING_INPUT))
.thenCompose(
result -> CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.COMPLETE))
);
} else {
// Call LUIS with the utterance.
return luisRecognizer.recognize(stepContext.getContext(), RecognizerResult.class)
.thenCompose(luisResult -> {
// Create a message showing the LUS results.
StringBuilder sb = new StringBuilder();
sb.append(String.format("LUIS results for \"%s\":", activity.getText()));
sb.append(
String.format(
"Intent: \"%s\" Score: %s",
luisResult.getTopScoringIntent().intent,
luisResult.getTopScoringIntent().score
)
);
return stepContext.getContext()
.sendActivity(MessageFactory.text(sb.toString(), sb.toString(), InputHints.IGNORING_INPUT))
.thenCompose(result -> {
switch (luisResult.getTopScoringIntent().intent.toLowerCase()) {
case "bookflight":
return beginBookFlight(stepContext);
case "getweather":
return beginGetWeather(stepContext);
default:
// Catch all for unhandled intents.
String didntUnderstandMessageText = String.format(
"Sorry, I didn't get that. Please try asking in a different "
+ "way (intent was %s)",
luisResult.getTopScoringIntent().intent
);
Activity didntUnderstandMessage = MessageFactory.text(
didntUnderstandMessageText,
didntUnderstandMessageText,
InputHints.IGNORING_INPUT
);
return stepContext.getContext()
.sendActivity(didntUnderstandMessage)
.thenCompose(
stepResult -> CompletableFuture
.completedFuture(new DialogTurnResult(DialogTurnStatus.COMPLETE))
);
}
});
// Start a dialog if we recognize the intent.
});
}
}
async def _on_message_activity(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
"""
This method just gets a message activity and runs it through LUIS.
"""
activity = step_context.context.activity
if not self._luis_recognizer.is_configured:
await step_context.context.send_activity(
MessageFactory.text(
"NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and"
" 'LuisAPIHostName' to the config.py file.",
input_hint=InputHints.ignoring_input,
)
)
else:
# Call LUIS with the utterance.
luis_result = await self._luis_recognizer.recognize(step_context.context)
message = f'LUIS results for "{activity.Text}":\n'
intent, intent_score = None, None
if luis_result.intents:
max_value_key = max(
luis_result.intents, key=lambda key: luis_result.intents[key]
)
intent, intent_score = max_value_key, luis_result.intents[max_value_key]
message += f'Intent: "{intent}" Score: {intent_score}\n'
await step_context.context.send_activity(
MessageFactory.text(message, input_hint=InputHints.ignoring_input,)
)
# Start a dialog if we recognize the intent.
top_intent = luis_result.get_top_scoring_intent().intent
if top_intent == "BookFlight":
return await self._begin_book_flight(step_context)
if top_intent == "GetWeather":
return await self._begin_get_weather(step_context)
# Catch all for unhandled intents.
didnt_understand_message_text = f"Sorry, I didn't get that. Please try asking in a different way (intent was {top_intent})"
await step_context.context.send_activity(
MessageFactory.text(
didnt_understand_message_text,
didnt_understand_message_text,
input_hint=InputHints.ignoring_input,
)
)
return DialogTurnResult(DialogTurnStatus.Complete)
Zahájení akce s více kroky
Akce rezervace spustí vícekrokové dialogové okno pro získání podrobností rezervace od uživatele.
Akce get-weather není implementována. V současné době odesílá zástupnou zprávu a končí.
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);
}
async beginGetWeather(stepContext) {
const activity = stepContext.context.activity;
const location = activity.value || {};
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
const getWeatherMessageText = `TODO: get weather for here (lat: ${ location.latitude }, long: ${ location.longitude })`;
await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
return { status: DialogTurnStatus.complete };
}
DialogSkillBot\Dialogs\ActivityRouterDialog.java
private CompletableFuture<DialogTurnResult> beginBookFlight(WaterfallStepContext stepContext) {
Activity activity = stepContext.getContext().getActivity();
BookingDetails bookingDetails = new BookingDetails();
if (activity.getValue() != null) {
try {
bookingDetails = Serialization.safeGetAs(activity.getValue(), BookingDetails.class);
} catch (JsonProcessingException e) {
// we already initialized bookingDetails above, so the flow will run as if
// no details were sent.
}
}
// Start the booking dialog.
Dialog bookingDialog = findDialog("BookingDialog");
return stepContext.beginDialog(bookingDialog.getId(), bookingDetails);
}
private static CompletableFuture<DialogTurnResult> beginGetWeather(WaterfallStepContext stepContext) {
Activity activity = stepContext.getContext().getActivity();
Location location = new Location();
if (activity.getValue() != null) {
try {
location = Serialization.safeGetAs(activity.getValue(), Location.class);
} catch (JsonProcessingException e) {
// something went wrong, so we create an empty Location so we won't get a null
// reference below when we acess location.
location = new Location();
}
}
// We haven't implemented the GetWeatherDialog so we just display a TODO
// message.
String getWeatherMessageText = String
.format("TODO: get weather for here (lat: %s, long: %s)", location.getLatitude(), location.getLongitude());
Activity getWeatherMessage =
MessageFactory.text(getWeatherMessageText, getWeatherMessageText, InputHints.IGNORING_INPUT);
return stepContext.getContext().sendActivity(getWeatherMessage).thenCompose(result -> {
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.COMPLETE));
});
}
async def _begin_get_weather(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
activity = step_context.context.activity
location = Location()
if activity.value:
location.from_json(activity.value)
# We haven't implemented the GetWeatherDialog so we just display a TODO message.
get_weather_message = f"TODO: get weather for here (lat: {location.latitude}, long: {location.longitude}"
await step_context.context.send_activity(
MessageFactory.text(
get_weather_message, get_weather_message, InputHints.ignoring_input,
)
)
return DialogTurnResult(DialogTurnStatus.Complete)
Vrácení výsledku
Dovednost zahájí dialogové okno rezervace pro akci book-flight. Vzhledem k tomu, že dialogové okno směrování aktivit má jenom jeden krok, končí dialogové okno směrování aktivit a výsledek dialogového okna rezervace se stane výsledkem dialogového okna pro směrování aktivit.
Akce get-weather jednoduše skončí bez nastavení návratové hodnoty.
Zrušení akce s více kroky
Dialogové okno rezervace a jeho podřízený dialog pro datum-překladač jsou odvozeny z dialogového okna základní zrušení a nápovědy, které kontroluje zprávy od uživatele.
Na kartě "nápověda" nebo "?"se zobrazí zpráva nápovědy a pak pokračuje v toku konverzace na následujícím turnu.
Při "zrušení" nebo "ukončit" zruší všechna dialogová okna, která ukončí dovednost.
Služby potřebné pro tuto dovednost jsou stejné jako služby potřebné pro robota dovedností obecně.
Podívejte se , jak implementovat dovednost pro diskuzi o požadovaných službách.
Manifest dovednosti
Manifest dovednosti je soubor JSON, který popisuje aktivity, které může dovednost provádět, její vstupní a výstupní parametry a koncové body dovednosti.
Manifest obsahuje informace, které potřebujete pro přístup ke dovednosti z jiného robota.
{
"$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"
}
}
}
}
}
Schéma manifestu dovedností je soubor JSON, který popisuje schéma manifestu dovednosti.
Nejnovější verze schématu je verze 2.1.
Otestování robota dovedností
Dovednosti můžete otestovat v emulátoru s příjemcem dovedností. K tomu musíte současně spouštět dovednosti i roboty uživatelů. Podívejte se, jak pomocí dialogového okna využívat dovednosti pro informace o konfiguraci dovednosti.
Spusťte robota dovednosti dialogového okna a kořenového robota dialogového okna místně na svém počítači. Pokud potřebujete pokyny, projděte si ukázkový README soubor pro C#, JavaScript, Javu nebo Python.
K otestování robota použijte emulátor.
Když se k konverzaci poprvé připojíte, robot zobrazí uvítací zprávu a zeptá se, jakou dovednost byste chtěli volat. Robot dovedností pro tuto ukázku má jen jednu dovednost.
Vyberte DialogSkillBot.
V dalším kroku vás robot požádá o výběr akce pro dovednost. Zvolte "BookFlight".
Dovednost začíná svou kniholetní akcí; odpovězte na výzvy.
Po dokončení dovednosti zobrazí kořenový robot podrobnosti rezervace před opětovnou výzvou k zadání dovednosti, kterou chcete zavolat.
Znovu vyberte DialogSkillBot a BookFlight.
Odpovězte na první výzvu a pak akci zrušte zadáním "zrušit".
Robot dovedností končí bez dokončení akce a uživatel vyzve ke zručnosti, kterou chcete zavolat.
Další informace o ladění
Vzhledem k tomu, že se ověřuje provoz mezi dovednostmi a uživateli dovedností, při ladění takových robotů existují další kroky.
Dovednost konzument a všechny dovednosti, které využívá, přímo nebo nepřímo, musí být spuštěné.
Pokud jsou roboti spuštěni místně a pokud některý z robotů má ID a heslo aplikace, musí mít všichni roboti platné ID a hesla.