Questo articolo illustra come creare una competenza che supporta più azioni. Queste azioni sono rese possibili tramite dialoghi. Il dialogo principale riceve l'input iniziale dal consumer di competenze e quindi avvia l'azione appropriata. Per informazioni sull'implementazione del consumer di competenze per il codice di esempio associato, vedere come utilizzare una competenza con i dialoghi.
Questo articolo presuppone che tu abbia già familiarità con la creazione di competenze.
Per informazioni generali su come creare un bot competenza, vedere come implementare una competenza.
Nota
Gli SDK JavaScript, C# e Python di Bot Framework continueranno a essere supportati, ma Java SDK verrà ritirato con il supporto finale a lungo termine che termina a novembre 2023.
I bot esistenti creati con Java SDK continueranno a funzionare.
Language Understanding (LUIS) verrà ritirato il 1° ottobre 2025.
A partire dal 1° aprile 2023, non sarà possibile creare nuove risorse LUIS.
Una versione più recente di Language Understanding è ora disponibile come parte del linguaggio di intelligenza artificiale di Azure.
CLU (Conversational Language Understanding), una funzionalità del linguaggio di intelligenza artificiale di Azure, è la versione aggiornata di LUIS.
Per altre informazioni sul supporto per la comprensione del linguaggio in Bot Framework SDK, vedere Comprensione del linguaggio naturale.
Informazioni sull'esempio
L'esempio di skillDialog di competenze include i progetti per due bot:
Il bot radice del dialogo, che usa una classe SkillDialog per usare una competenza.
Il bot competenza del dialogo, che usa un dialogo per gestire le attività dei consumer di competenze. Questa competenza è un adattamento dell'esempio di bot di base. Per altre informazioni, vedere come aggiungere la comprensione del linguaggio naturale al bot.
Questo articolo è incentrato su come usare un dialogo all'interno di un bot di competenze per gestire più azioni.
Per i bot distribuiti, l'autenticazione da bot a bot richiede che ogni bot partecipante abbia un'identità valida.
Tuttavia, è possibile testare competenze e consumer di competenze in locale con Bot Framework Emulator senza informazioni sull'identità.
Per rendere disponibile la competenza per i bot rivolti agli utenti, registrare la competenza con Azure. Per altre informazioni, vedere come registrare un bot con Azure AI servizio Bot.
Facoltativamente, il bot competenza può usare un modello LUIS di prenotazione di voli. Per usare questo modello, usare il file CognitiveModels/FlightBooking.json per creare, eseguire il training e pubblicare il modello LUIS.
Configurazione dell'applicazione
Facoltativamente, aggiungere le informazioni sull'identità della competenza al file di configurazione della competenza.
Se la competenza o il consumer di competenze specifica un'identità, entrambe devono.
Se si usa il modello LUIS, aggiungere l'ID app LUIS, la chiave API e il nome host dell'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", "")
Logica di routing delle attività
La competenza supporta due diverse funzionalità. Può prenotare un volo o recuperare le previsioni meteo di una città. Inoltre, se riceve un messaggio al di fuori di uno di questi contesti, può usare LUIS per provare a interpretarlo.
Il manifesto della competenza descrive queste azioni, i relativi parametri di input e di output e gli endpoint della competenza.
Si noti che la competenza può gestire un evento "BookFlight" o "GetWeather". Può anche gestire le attività dei messaggi.
La competenza definisce un dialogo di routing delle attività che usa per selezionare l'azione da avviare, in base all'attività iniziale in ingresso proveniente dal consumer di competenze.
Se specificato, il modello LUIS è in grado di riconoscere finalità di prenotazione di voli e recupero di previsioni meteo in un messaggio iniziale.
L'azione di prenotazione di voli è un processo in più passaggi, implementato come dialogo separato. Una volta avviata l'azione, le attività in ingresso vengono gestite da tale dialogo. L'azione di recupero di previsioni meteo ha una logica di segnaposto che verrebbe sostituita in un bot completamente implementato.
Il dialogo di routing delle attività include il codice per:
I dialoghi usati nella competenza ereditano dalla classe ComponentDialog. Per altre informazioni sui dialoghi componenti, vedere la procedura per gestire la complessità dei dialoghi.
Inizializzare il dialogo
Il dialogo di routing delle attività include un dialogo figlio per la prenotazione di un volo. Il dialogo a cascata principale prevede un passaggio che avvierà un'azione in base all'attività iniziale ricevuta.
Accetta anche un riconoscitore LUIS. Se il riconoscitore viene inizializzato, il dialogo lo userà per interpretare la finalità di un'attività di messaggi iniziale.
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)
Gestire le attività dei messaggi
Se il riconoscitore LUIS è configurato, la competenza chiama LUIS e quindi avvia un'azione in base alla finalità.
Se il sistema di riconoscimento LUIS non è configurato o la finalità non è supportata, la competenza invia un messaggio di errore e termina.
// 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)
Avviare un'azione in più passaggi
L'azione d prenotazione di voli avvia un dialogo in più passaggi per ottenere i dettagli della prenotazione dall'utente.
L'azione get-weather non è implementata. Attualmente, invia un messaggio segnaposto e quindi termina.
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)
Restituire un risultato
La competenza avvia un dialogo di prenotazione per l'azione di prenotazione di voli. Poiché il dialogo di routing delle attività prevede un unico passaggio, termina quando termina il dialogo di prenotazione e il risultato del dialogo di prenotazione diventa il risultato del dialogo di routing delle attività.
L'azione di recupero di previsioni meteo termina semplicemente senza impostare un valore restituito.
Annullamento di un'azione in più passaggi
Il dialogo di prenotazione e il relativo dialogo figlio di risoluzione della data derivano dal dialogo di base "cancel" e "help", che controlla i messaggi provenienti dall'utente.
Per "help" o "?", visualizza un messaggio della Guida e quindi continua il flusso di conversazione al turno successivo.
Per "cancel" o "quit", annulla tutti i dialoghi, terminando la competenza.
I servizi necessari per questa competenza sono gli stessi di quelli necessari per un bot competenza in generale.
Per una descrizione dei servizi necessari, vedere come implementare una competenza.
Manifesto della competenza
Un manifesto della competenza è un file JSON che descrive le attività eseguibili con la competenza, i relativi parametri di input e output e gli endpoint della competenza.
Il manifesto contiene le informazioni necessarie per accedere alla competenza da un altro bot.
{
"$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"
}
}
}
}
}
Lo schema del manifesto della competenza è un file JSON che descrive lo schema del manifesto della competenza.
La versione più recente dello schema è v2.1.
Testare il bot competenza
È possibile testare la competenza nell'emulatore con il consumer di competenze. A questo scopo, è necessario eseguire il bot competenza e il bot del consumer di competenze contemporaneamente. Per informazioni su come configurare la competenza, vedere come usare i dialoghi per una competenza.
Eseguire il bot competenza del dialogo e il bot radice del dialogo in locale nel computer. Se sono necessarie istruzioni, vedere il file di README esempio per C#, JavaScript, Java o Python.
Usare l'emulatore per testare il bot.
Quando si partecipa per la prima volta alla conversazione, il bot visualizza un messaggio di benvenuto e chiede quale competenza si vuole chiamare. Il bot competenza per questo esempio ha solo una competenza.
Selezionare DialogSkillBot.
Successivamente, il bot chiede di scegliere un'azione per la competenza. Scegliere "BookFlight".
La competenza inizia l'azione di prenotazione di voli; rispondere alle richieste.
Quando la competenza viene completata, il bot radice visualizza i dettagli della prenotazione prima di chiedere nuovamente quale competenza si vuole chiamare.
Selezionare di nuovo DialogSkillBot e "BookFlight".
Rispondere alla prima richiesta, quindi immettere "cancel" per annullare l'azione.
Il bot competenza termina senza completare l'azione e il consumer chiede quale competenza si vuole chiamare.
Altre informazioni sul debug
Poiché il traffico tra competenze e consumer di competenze viene autenticato, è necessario eseguire passaggi aggiuntivi durante il debug di tali bot.
Il consumer di competenze e tutte le competenze utilizzate, direttamente o indirettamente, devono essere in esecuzione.
Se i bot vengono eseguiti localmente e se uno dei bot ha un ID app e una password, tutti i bot devono avere ID e password validi.
In caso contrario, è possibile eseguire il debug di un consumer di competenze o di una competenza in modo molto simile al debug di altri bot. Per altre informazioni, vedere Debug di un bot e Debug con Bot Framework Emulator.