Het verzamelen van informatie door vragen te stellen is een van de belangrijkste manieren waarop een bot met gebruikers communiceert. De dialoogvensterbibliotheek biedt handige ingebouwde functies zoals promptklassen waarmee u eenvoudig vragen kunt stellen en het antwoord kunt valideren om ervoor te zorgen dat het overeenkomt met een specifiek gegevenstype of voldoet aan aangepaste validatieregels.
U kunt lineaire en complexere gespreksstromen beheren met behulp van de dialoogvensterbibliotheek. In een lineaire interactie doorloopt de bot een vaste reeks stappen en wordt het gesprek voltooid. Een dialoogvenster is handig wanneer de bot informatie van de gebruiker moet verzamelen.
In dit artikel wordt beschreven hoe u een lineaire gespreksstroom implementeert door prompts te maken en aan te roepen vanuit een watervaldialoogvenster.
Zie het artikel Uw eigen invoer voor gebruikersinvoer maken voor voorbeelden van het schrijven van uw eigen prompts zonder de dialoogvensterbibliotheek te gebruiken.
In het voorbeeld met meerdere prompts wordt een watervaldialoogvenster, een paar prompts en een onderdeeldialoogvenster gebruikt om een lineaire interactie te maken waarin de gebruiker een reeks vragen wordt gesteld. In de code wordt een dialoogvenster gebruikt om deze stappen te doorlopen:
Ten slotte, als ze ja hebben beantwoord, geeft u de verzamelde informatie weer; laat de gebruiker anders weten dat de gegevens niet worden bewaard.
Als u dialoogvensters wilt gebruiken, installeert u het NuGet-pakket Microsoft.Bot.Builder.Dialogs .
De bot communiceert met de gebruiker via UserProfileDialog
. Wanneer u de klasse van DialogBot
de bot maakt, wordt de UserProfileDialog
klasse ingesteld als het hoofddialoogvenster. De bot gebruikt vervolgens een Run
helpermethode voor toegang tot het dialoogvenster.
Dialoogvensters\UserProfileDialog.cs
Begin met het maken van de UserProfileDialog
die is afgeleid van de ComponentDialog
klasse en heeft zeven stappen.
Maak in de UserProfileDialog
constructor de watervalstappen, prompts en het watervaldialoogvenster en voeg deze toe aan de dialoogvensterset. De prompts moeten zich in dezelfde dialoogvensterset bevinden waarin ze worden gebruikt.
public UserProfileDialog(UserState userState)
: base(nameof(UserProfileDialog))
{
_userProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile");
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[]
{
TransportStepAsync,
NameStepAsync,
NameConfirmStepAsync,
AgeStepAsync,
PictureStepAsync,
SummaryStepAsync,
ConfirmStepAsync,
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>), AgePromptValidatorAsync));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new AttachmentPrompt(nameof(AttachmentPrompt), PicturePromptValidatorAsync));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
Voeg vervolgens de stappen toe die in het dialoogvenster worden gebruikt om om invoer te vragen. Als u een prompt wilt gebruiken, roept u deze aan vanuit een stap in het dialoogvenster en haalt u het promptresultaat op in de volgende stap met behulp van stepContext.Result
. Achter de schermen zijn prompts een dialoogvenster in twee stappen. Eerst vraagt de prompt om invoer. Vervolgens wordt de geldige waarde geretourneerd, of begint opnieuw vanaf het begin met een reprompt totdat er een geldige invoer wordt ontvangen.
U moet altijd een niet-null-waarde DialogTurnResult
retourneren uit een watervalstap. Als u dat niet doet, werkt het dialoogvenster mogelijk niet zoals ontworpen. Hieronder ziet u de implementatie voor NameStepAsync
in het watervaldialoogvenster.
private static async Task<DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["transport"] = ((FoundChoice)stepContext.Result).Value;
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") }, cancellationToken);
}
Geef AgeStepAsync
in, geef een prompt voor opnieuw proberen op wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren of de invoer mislukt een validatiecriteria. Als er in dit geval geen prompt voor opnieuw proberen is opgegeven, wordt de eerste prompttekst gebruikt om de gebruiker opnieuw in te stellen voor invoer.
private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
// User said "yes" so we will be prompting for the age.
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
var promptOptions = new PromptOptions
{
Prompt = MessageFactory.Text("Please enter your age."),
RetryPrompt = MessageFactory.Text("The value entered must be greater than 0 and less than 150."),
};
return await stepContext.PromptAsync(nameof(NumberPrompt<int>), promptOptions, cancellationToken);
}
else
{
// User said "no" so we will skip the next step. Give -1 as the age.
return await stepContext.NextAsync(-1, cancellationToken);
}
}
UserProfile.cs
De manier van transport, naam en leeftijd van de gebruiker worden opgeslagen in een instantie van de UserProfile
klasse.
public class UserProfile
{
public string Transport { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Attachment Picture { get; set; }
}
Dialoogvensters\UserProfileDialog.cs
Controleer in de laatste stap het stepContext.Result
geretourneerde dialoogvenster dat is aangeroepen in de vorige watervalstap. Als de retourwaarde waar is, wordt het gebruikersprofiel opgehaald en bijgewerkt door de gebruikersprofieltoegangsfunctie. Als u het gebruikersprofiel wilt ophalen, roept GetAsync
u de waarden van de userProfile.Transport
, userProfile.Name
userProfile.Age
en userProfile.Picture
eigenschappen aan en stelt u deze in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept EndDialogAsync
, waardoor het dialoogvenster wordt beëindigd. Als u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack weergegeven en wordt een optioneel resultaat geretourneerd naar het bovenliggende dialoogvenster. Het bovenliggende item is het dialoogvenster of de methode waarmee het dialoogvenster is gestart dat net is beëindigd.
else
{
msg += $" Your profile will not be kept.";
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["picture"] = ((IList<Attachment>)stepContext.Result)?.FirstOrDefault();
// Get the current profile object from user state.
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);
userProfile.Transport = (string)stepContext.Values["transport"];
userProfile.Name = (string)stepContext.Values["name"];
userProfile.Age = (int)stepContext.Values["age"];
userProfile.Picture = (Attachment)stepContext.Values["picture"];
var msg = $"I have your mode of transport as {userProfile.Transport} and your name as {userProfile.Name}";
if (userProfile.Age != -1)
{
msg += $" and your age as {userProfile.Age}";
}
msg += ".";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);
if (userProfile.Picture != null)
{
try
{
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(userProfile.Picture, "This is your profile picture."), cancellationToken);
}
catch
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("A profile picture was saved but could not be displayed here."), cancellationToken);
Als u dialoogvensters wilt gebruiken, moet uw project het npm-pakket botbuilder-dialogs installeren.
De bot communiceert met de gebruiker via een UserProfileDialog
. Wanneer u de bot DialogBot
maakt, wordt deze UserProfileDialog
ingesteld als het hoofddialoogvenster. De bot gebruikt vervolgens een run
helpermethode voor toegang tot het dialoogvenster.
dialoogvensters/userProfileDialog.js
Begin met het maken van de UserProfileDialog
die is afgeleid van de ComponentDialog
klasse en heeft zeven stappen.
Maak in de UserProfileDialog
constructor de watervalstappen, prompts en het watervaldialoogvenster en voeg deze toe aan de dialoogvensterset. De prompts moeten zich in dezelfde dialoogvensterset bevinden waarin ze worden gebruikt.
constructor(userState) {
super('userProfileDialog');
this.userProfile = userState.createProperty(USER_PROFILE);
this.addDialog(new TextPrompt(NAME_PROMPT));
this.addDialog(new ChoicePrompt(CHOICE_PROMPT));
this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT));
this.addDialog(new NumberPrompt(NUMBER_PROMPT, this.agePromptValidator));
this.addDialog(new AttachmentPrompt(ATTACHMENT_PROMPT, this.picturePromptValidator));
this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
this.transportStep.bind(this),
this.nameStep.bind(this),
this.nameConfirmStep.bind(this),
this.ageStep.bind(this),
this.pictureStep.bind(this),
this.summaryStep.bind(this),
this.confirmStep.bind(this)
]));
this.initialDialogId = WATERFALL_DIALOG;
}
Voeg vervolgens de stappen toe die in het dialoogvenster worden gebruikt om om invoer te vragen. Als u een prompt wilt gebruiken, roept u deze aan vanuit een stap in het dialoogvenster en haalt u het promptresultaat op in de volgende stap in de stapcontext, in dit geval met behulp van step.result
. Achter de schermen zijn prompts een dialoogvenster in twee stappen. Eerst vraagt de prompt om invoer. Vervolgens wordt de geldige waarde geretourneerd, of begint opnieuw vanaf het begin met een reprompt totdat er een geldige invoer wordt ontvangen.
U moet altijd een niet-null-waarde DialogTurnResult
retourneren uit een watervalstap. Als u dat niet doet, werkt het dialoogvenster mogelijk niet zoals ontworpen. Hieronder ziet u de implementatie voor het nameStep
dialoogvenster waterval.
async nameStep(step) {
step.values.transport = step.result.value;
return await step.prompt(NAME_PROMPT, 'Please enter your name.');
}
Geef ageStep
in, geef een prompt voor opnieuw proberen op wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren, of de invoer mislukt een validatiecriteria, die is opgegeven in de bovenstaande constructor. Als er in dit geval geen prompt voor opnieuw proberen is opgegeven, wordt de eerste prompttekst gebruikt om de gebruiker opnieuw in te stellen voor invoer.
async ageStep(step) {
if (step.result) {
// User said "yes" so we will be prompting for the age.
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
const promptOptions = { prompt: 'Please enter your age.', retryPrompt: 'The value entered must be greater than 0 and less than 150.' };
return await step.prompt(NUMBER_PROMPT, promptOptions);
} else {
// User said "no" so we will skip the next step. Give -1 as the age.
return await step.next(-1);
}
}
userProfile.js
De manier van transport, naam en leeftijd van de gebruiker worden opgeslagen in een instantie van de UserProfile
klasse.
class UserProfile {
constructor(transport, name, age, picture) {
this.transport = transport;
this.name = name;
this.age = age;
this.picture = picture;
}
}
dialoogvensters/userProfileDialog.js
Controleer in de laatste stap het step.result
geretourneerde dialoogvenster dat is aangeroepen in de vorige watervalstap. Als de retourwaarde waar is, wordt het gebruikersprofiel opgehaald en bijgewerkt door de gebruikersprofieltoegangsfunctie. Als u het gebruikersprofiel wilt ophalen, roept get
u aan en stelt u vervolgens de waarden van de userProfile.transport
, userProfile.name
userProfile.age
en userProfile.picture
eigenschappen in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept endDialog
, waardoor het dialoogvenster wordt beëindigd. Als u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack weergegeven en wordt een optioneel resultaat geretourneerd naar het bovenliggende dialoogvenster. Het bovenliggende item is het dialoogvenster of de methode waarmee het dialoogvenster is gestart dat net is beëindigd.
await step.context.sendActivity(msg);
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await step.endDialog();
}
async summaryStep(step) {
step.values.picture = step.result && step.result[0];
// Get the current profile object from user state.
const userProfile = await this.userProfile.get(step.context, new UserProfile());
userProfile.transport = step.values.transport;
userProfile.name = step.values.name;
userProfile.age = step.values.age;
userProfile.picture = step.values.picture;
let msg = `I have your mode of transport as ${ userProfile.transport } and your name as ${ userProfile.name }`;
if (userProfile.age !== -1) {
msg += ` and your age as ${ userProfile.age }`;
}
msg += '.';
await step.context.sendActivity(msg);
if (userProfile.picture) {
try {
await step.context.sendActivity(MessageFactory.attachment(userProfile.picture, 'This is your profile picture.'));
} catch {
await step.context.sendActivity('A profile picture was saved but could not be displayed here.');
}
De extensiemethode maken om het watervaldialoogvenster uit te voeren
Een run
helpermethode, gedefinieerd in userProfileDialog
, wordt gebruikt om de dialoogvenstercontext te maken en te openen. accessor
Hier ziet u de toegangsrechten voor de eigenschap status van de status van het dialoogvenster en this
is het dialoogvenster gebruikersprofielonderdeel. Aangezien onderdeeldialoogvensters een interne dialoogvensterset definiëren, moet er een buitenste dialoogvensterset worden gemaakt die zichtbaar is voor de berichthandlercode en wordt gebruikt om een dialoogvenstercontext te maken.
De dialoogvenstercontext wordt gemaakt door de createContext
methode aan te roepen en wordt gebruikt om te communiceren met de dialoogvensterset vanuit de draaihandler van de bot. De dialoogvenstercontext bevat de huidige beurtcontext, het bovenliggende dialoogvenster en de dialoogvensterstatus, die een methode biedt voor het behouden van informatie in het dialoogvenster.
Met de dialoogvenstercontext kunt u een dialoogvenster met de tekenreeks-id starten of doorgaan met het huidige dialoogvenster (zoals een watervaldialoogvenster met meerdere stappen). De dialoogvenstercontext wordt doorgegeven aan alle dialoogvensters en watervalstappen van de bot.
async run(turnContext, accessor) {
const dialogSet = new DialogSet(accessor);
dialogSet.add(this);
const dialogContext = await dialogSet.createContext(turnContext);
const results = await dialogContext.continueDialog();
if (results.status === DialogTurnStatus.empty) {
await dialogContext.beginDialog(this.id);
}
}
De bot communiceert met de gebruiker via UserProfileDialog
. Wanneer u de klasse van DialogBot
de bot maakt, wordt de UserProfileDialog
klasse ingesteld als het hoofddialoogvenster. De bot gebruikt vervolgens een Run
helpermethode voor toegang tot het dialoogvenster.
UserProfileDialog.java
Begin met het maken van de UserProfileDialog
die is afgeleid van de ComponentDialog
klasse en heeft zeven stappen.
Maak in de UserProfileDialog
constructor de watervalstappen, prompts en het watervaldialoogvenster en voeg deze toe aan de dialoogvensterset. De prompts moeten zich in dezelfde dialoogvensterset bevinden waarin ze worden gebruikt.
public UserProfileDialog(UserState withUserState) {
super("UserProfileDialog");
userProfileAccessor = withUserState.createProperty("UserProfile");
WaterfallStep[] waterfallSteps = {
UserProfileDialog::transportStep,
UserProfileDialog::nameStep,
this::nameConfirmStep,
this::ageStep,
UserProfileDialog::pictureStep,
this::confirmStep,
this::summaryStep
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
addDialog(new TextPrompt("TextPrompt"));
addDialog(new NumberPrompt<Integer>("NumberPrompt", UserProfileDialog::agePromptValidator, Integer.class));
addDialog(new ChoicePrompt("ChoicePrompt"));
addDialog(new ConfirmPrompt("ConfirmPrompt"));
addDialog(new AttachmentPrompt("AttachmentPrompt", UserProfileDialog::picturePromptValidator));
// The initial child Dialog to run.
setInitialDialogId("WaterfallDialog");
}
Voeg vervolgens de stappen toe die in het dialoogvenster worden gebruikt om om invoer te vragen. Als u een prompt wilt gebruiken, roept u deze aan vanuit een stap in het dialoogvenster en haalt u het promptresultaat op in de volgende stap met behulp van stepContext.getResult()
. Achter de schermen zijn prompts een dialoogvenster in twee stappen. Eerst vraagt de prompt om invoer. Vervolgens wordt de geldige waarde geretourneerd, of begint opnieuw vanaf het begin met een reprompt totdat er een geldige invoer wordt ontvangen.
U moet altijd een niet-null-waarde DialogTurnResult
retourneren uit een watervalstap. Als u dat niet doet, werkt het dialoogvenster mogelijk niet zoals ontworpen. Hieronder ziet u de implementatie voor nameStep
in het watervaldialoogvenster.
private static CompletableFuture<DialogTurnResult> nameStep(WaterfallStepContext stepContext) {
stepContext.getValues().put("transport", ((FoundChoice) stepContext.getResult()).getValue());
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your name."));
return stepContext.prompt("TextPrompt", promptOptions);
}
Geef ageStep
in, geef een prompt voor opnieuw proberen op wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren of de invoer mislukt een validatiecriteria. Als er in dit geval geen prompt voor opnieuw proberen is opgegeven, wordt de eerste prompttekst gebruikt om de gebruiker opnieuw in te stellen voor invoer.
private CompletableFuture<DialogTurnResult> ageStep(WaterfallStepContext stepContext) {
if ((Boolean)stepContext.getResult()) {
// User said "yes" so we will be prompting for the age.
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your age."));
promptOptions.setRetryPrompt(MessageFactory.text("The value entered must be greater than 0 and less than 150."));
return stepContext.prompt("NumberPrompt", promptOptions);
}
// User said "no" so we will skip the next step. Give -1 as the age.
return stepContext.next(-1);
}
UserProfile.java
De manier van transport, naam en leeftijd van de gebruiker worden opgeslagen in een instantie van de UserProfile
klasse.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.sample.multiturnprompt;
import com.microsoft.bot.schema.Attachment;
/**
* This is our application state.
*/
public class UserProfile {
public String transport;
public String name;
public Integer age;
public Attachment picture;
}
UserProfileDialog.java
Controleer in de laatste stap het stepContext.Result
geretourneerde dialoogvenster dat is aangeroepen in de vorige watervalstap. Als de retourwaarde waar is, wordt het gebruikersprofiel opgehaald en bijgewerkt door de gebruikersprofieltoegangsfunctie. Als u het gebruikersprofiel wilt ophalen, roept get
u de waarden van de userProfile.Transport
, userProfile.Name
userProfile.Age
en userProfile.Picture
eigenschappen aan en stelt u deze in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept endDialog
, waardoor het dialoogvenster wordt beëindigd. Als u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack weergegeven en wordt een optioneel resultaat geretourneerd naar het bovenliggende dialoogvenster. Het bovenliggende item is het dialoogvenster of de methode waarmee het dialoogvenster is gestart dat net is beëindigd.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.sample.multiturnprompt;
import com.microsoft.bot.builder.MessageFactory;
import com.microsoft.bot.builder.StatePropertyAccessor;
import com.microsoft.bot.builder.UserState;
import com.microsoft.bot.connector.Channels;
import com.microsoft.bot.dialogs.ComponentDialog;
import com.microsoft.bot.dialogs.DialogTurnResult;
import com.microsoft.bot.dialogs.WaterfallDialog;
import com.microsoft.bot.dialogs.WaterfallStep;
import com.microsoft.bot.dialogs.WaterfallStepContext;
import com.microsoft.bot.dialogs.choices.ChoiceFactory;
import com.microsoft.bot.dialogs.choices.FoundChoice;
import com.microsoft.bot.dialogs.prompts.AttachmentPrompt;
import com.microsoft.bot.dialogs.prompts.ChoicePrompt;
import com.microsoft.bot.dialogs.prompts.ConfirmPrompt;
import com.microsoft.bot.dialogs.prompts.NumberPrompt;
import com.microsoft.bot.dialogs.prompts.PromptOptions;
import com.microsoft.bot.dialogs.prompts.PromptValidatorContext;
import com.microsoft.bot.dialogs.prompts.TextPrompt;
import com.microsoft.bot.schema.Attachment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.StringUtils;
public class UserProfileDialog extends ComponentDialog {
private final StatePropertyAccessor<UserProfile> userProfileAccessor;
public UserProfileDialog(UserState withUserState) {
super("UserProfileDialog");
userProfileAccessor = withUserState.createProperty("UserProfile");
WaterfallStep[] waterfallSteps = {
UserProfileDialog::transportStep,
UserProfileDialog::nameStep,
this::nameConfirmStep,
this::ageStep,
UserProfileDialog::pictureStep,
this::confirmStep,
this::summaryStep
};
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
addDialog(new TextPrompt("TextPrompt"));
addDialog(new NumberPrompt<Integer>("NumberPrompt", UserProfileDialog::agePromptValidator, Integer.class));
addDialog(new ChoicePrompt("ChoicePrompt"));
addDialog(new ConfirmPrompt("ConfirmPrompt"));
addDialog(new AttachmentPrompt("AttachmentPrompt", UserProfileDialog::picturePromptValidator));
// The initial child Dialog to run.
setInitialDialogId("WaterfallDialog");
}
private static CompletableFuture<DialogTurnResult> transportStep(WaterfallStepContext stepContext) {
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
// Running a prompt here means the next WaterfallStep will be run when the user's response is received.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your mode of transport."));
promptOptions.setChoices(ChoiceFactory.toChoices("Car", "Bus", "Bicycle"));
return stepContext.prompt("ChoicePrompt", promptOptions);
}
private static CompletableFuture<DialogTurnResult> nameStep(WaterfallStepContext stepContext) {
stepContext.getValues().put("transport", ((FoundChoice) stepContext.getResult()).getValue());
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your name."));
return stepContext.prompt("TextPrompt", promptOptions);
}
private CompletableFuture<DialogTurnResult> nameConfirmStep(WaterfallStepContext stepContext) {
stepContext.getValues().put("name", stepContext.getResult());
// We can send messages to the user at any point in the WaterfallStep.
return stepContext.getContext().sendActivity(MessageFactory.text(String.format("Thanks %s.", stepContext.getResult())))
.thenCompose(resourceResponse -> {
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Would you like to give your age?"));
return stepContext.prompt("ConfirmPrompt", promptOptions);
});
}
private CompletableFuture<DialogTurnResult> ageStep(WaterfallStepContext stepContext) {
if ((Boolean)stepContext.getResult()) {
// User said "yes" so we will be prompting for the age.
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please enter your age."));
promptOptions.setRetryPrompt(MessageFactory.text("The value entered must be greater than 0 and less than 150."));
return stepContext.prompt("NumberPrompt", promptOptions);
}
// User said "no" so we will skip the next step. Give -1 as the age.
return stepContext.next(-1);
}
private static CompletableFuture<DialogTurnResult> pictureStep(WaterfallStepContext stepContext) {
stepContext.getValues().put("age", (Integer) stepContext.getResult());
String msg = (Integer)stepContext.getValues().get("age") == -1
? "No age given."
: String.format("I have your age as %d.", (Integer)stepContext.getValues().get("age"));
// We can send messages to the user at any point in the WaterfallStep.
return stepContext.getContext().sendActivity(MessageFactory.text(msg))
.thenCompose(resourceResponse -> {
if (StringUtils.equals(stepContext.getContext().getActivity().getChannelId(), Channels.MSTEAMS)) {
// This attachment prompt example is not designed to work for Teams attachments, so skip it in this case
return stepContext.getContext().sendActivity(MessageFactory.text("Skipping attachment prompt in Teams channel..."))
.thenCompose(resourceResponse1 -> stepContext.next(null));
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Please attach a profile picture (or type any message to skip)."));
promptOptions.setRetryPrompt(MessageFactory.text("The attachment must be a jpeg/png image file."));
return stepContext.prompt("AttachmentPrompt", promptOptions);
});
}
private CompletableFuture<DialogTurnResult> confirmStep(WaterfallStepContext stepContext) {
List<Attachment> attachments = (List<Attachment>)stepContext.getResult();
stepContext.getValues().put("picture", attachments == null ? null : attachments.get(0));
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
PromptOptions promptOptions = new PromptOptions();
promptOptions.setPrompt(MessageFactory.text("Is this ok?"));
return stepContext.prompt("ConfirmPrompt", promptOptions);
}
private CompletableFuture<DialogTurnResult> summaryStep(WaterfallStepContext stepContext) {
if ((Boolean)stepContext.getResult()) {
// Get the current profile object from user state.
return userProfileAccessor.get(stepContext.getContext(), () -> new UserProfile())
.thenCompose(userProfile -> {
userProfile.transport = (String) stepContext.getValues().get("transport");
userProfile.name = (String) stepContext.getValues().get("name");
userProfile.age = (Integer) stepContext.getValues().get("age");
userProfile.picture = (Attachment) stepContext.getValues().get("picture");
String msg = String.format(
"I have your mode of transport as %s and your name as %s",
userProfile.transport, userProfile.name
);
if (userProfile.age != -1) {
msg += String.format(" and your age as %s", userProfile.age);
}
msg += ".";
return stepContext.getContext().sendActivity(MessageFactory.text(msg))
.thenApply(resourceResponse -> userProfile);
})
.thenCompose(userProfile -> {
if (userProfile.picture != null) {
try {
return stepContext.getContext().sendActivity(
MessageFactory.attachment(userProfile.picture,
"This is your profile picture."
));
} catch(Exception ex) {
return stepContext.getContext().sendActivity(
MessageFactory.text(
"A profile picture was saved but could not be displayed here."
));
}
}
return stepContext.getContext().sendActivity(
MessageFactory.text("A profile picture wasn't attached.")
);
})
.thenCompose(resourceResponse -> stepContext.endDialog());
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
return stepContext.getContext().sendActivity(MessageFactory.text("Thanks. Your profile will not be kept."))
.thenCompose(resourceResponse -> stepContext.endDialog());
}
private static CompletableFuture<Boolean> agePromptValidator(
PromptValidatorContext<Integer> promptContext
) {
// This condition is our validation rule. You can also change the value at this point.
return CompletableFuture.completedFuture(
promptContext.getRecognized().getSucceeded()
&& promptContext.getRecognized().getValue() > 0
&& promptContext.getRecognized().getValue() < 150);
}
private static CompletableFuture<Boolean> picturePromptValidator(
PromptValidatorContext<List<Attachment>> promptContext
) {
if (promptContext.getRecognized().getSucceeded()) {
List<Attachment> attachments = promptContext.getRecognized().getValue();
List<Attachment> validImages = new ArrayList<>();
for (Attachment attachment : attachments) {
if (StringUtils.equals(
attachment.getContentType(), "image/jpeg") || StringUtils.equals(attachment.getContentType(), "image/png")
) {
validImages.add(attachment);
}
}
promptContext.getRecognized().setValue(validImages);
// If none of the attachments are valid images, the retry prompt should be sent.
return CompletableFuture.completedFuture(!validImages.isEmpty());
}
else {
// We can return true from a validator function even if Recognized.Succeeded is false.
return promptContext.getContext().sendActivity("No attachments received. Proceeding without a profile picture...")
.thenApply(resourceResponse -> true);
}
}
}
Als u dialoogvensters wilt gebruiken, installeert u de botbuilder-dialogs en botbuilder-ai PyPI-pakketten door deze uit te voeren pip install botbuilder-dialogs
en pip install botbuilder-ai
vanuit een terminal.
De bot communiceert met de gebruiker via UserProfileDialog
. Wanneer de klasse van DialogBot
de bot wordt gemaakt, wordt de UserProfileDialog
klasse ingesteld als het hoofddialoogvenster. De bot gebruikt vervolgens een run_dialog
helpermethode voor toegang tot het dialoogvenster.
dialoogvensters\user_profile_dialog.py
Begin met het maken van de UserProfileDialog
die is afgeleid van de ComponentDialog
klasse en heeft zeven stappen.
Maak in de UserProfileDialog
constructor de watervalstappen, prompts en het watervaldialoogvenster en voeg deze toe aan de dialoogvensterset. De prompts moeten zich in dezelfde dialoogvensterset bevinden waarin ze worden gebruikt.
def __init__(self, user_state: UserState):
super(UserProfileDialog, self).__init__(UserProfileDialog.__name__)
self.user_profile_accessor = user_state.create_property("UserProfile")
self.add_dialog(
WaterfallDialog(
WaterfallDialog.__name__,
[
self.transport_step,
self.name_step,
self.name_confirm_step,
self.age_step,
self.picture_step,
self.summary_step,
self.confirm_step,
],
)
)
self.add_dialog(TextPrompt(TextPrompt.__name__))
self.add_dialog(
NumberPrompt(NumberPrompt.__name__, UserProfileDialog.age_prompt_validator)
)
self.add_dialog(ChoicePrompt(ChoicePrompt.__name__))
self.add_dialog(ConfirmPrompt(ConfirmPrompt.__name__))
self.add_dialog(
AttachmentPrompt(
AttachmentPrompt.__name__, UserProfileDialog.picture_prompt_validator
)
)
self.initial_dialog_id = WaterfallDialog.__name__
Voeg vervolgens de stappen toe die in het dialoogvenster worden gebruikt om om invoer te vragen. Als u een prompt wilt gebruiken, roept u deze aan vanuit een stap in het dialoogvenster en haalt u het promptresultaat op in de volgende stap met behulp van step_context.result
. Achter de schermen zijn prompts een dialoogvenster in twee stappen. Eerst vraagt de prompt om invoer. Vervolgens wordt de geldige waarde geretourneerd, of begint opnieuw vanaf het begin met een reprompt totdat er een geldige invoer wordt ontvangen.
U moet altijd een niet-null-waarde DialogTurnResult
retourneren uit een watervalstap. Als u dat niet doet, werkt het dialoogvenster mogelijk niet zoals ontworpen. Hier ziet u de implementatie voor het name_step
in het watervaldialoogvenster.
async def name_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
step_context.values["transport"] = step_context.result.value
return await step_context.prompt(
TextPrompt.__name__,
PromptOptions(prompt=MessageFactory.text("Please enter your name.")),
)
Geef age_step
in, geef een prompt voor opnieuw proberen op wanneer de invoer van de gebruiker niet kan worden gevalideerd, omdat deze een indeling heeft die de prompt niet kan parseren, of de invoer mislukt een validatiecriteria, die is opgegeven in de bovenstaande constructor. Als er in dit geval geen prompt voor opnieuw proberen is opgegeven, gebruikt de prompt de eerste prompttekst om de gebruiker opnieuw in te stellen voor invoer
async def age_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
if step_context.result:
# User said "yes" so we will be prompting for the age.
# WaterfallStep always finishes with the end of the Waterfall or with another dialog,
# here it is a Prompt Dialog.
return await step_context.prompt(
NumberPrompt.__name__,
PromptOptions(
prompt=MessageFactory.text("Please enter your age."),
retry_prompt=MessageFactory.text(
"The value entered must be greater than 0 and less than 150."
),
),
)
# User said "no" so we will skip the next step. Give -1 as the age.
return await step_context.next(-1)
data_models\user_profile.py
De manier van transport, naam en leeftijd van de gebruiker worden opgeslagen in een instantie van de UserProfile
klasse.
class UserProfile:
"""
This is our application state. Just a regular serializable Python class.
"""
def __init__(self, name: str = None, transport: str = None, age: int = 0, picture: Attachment = None):
self.name = name
self.transport = transport
self.age = age
self.picture = picture
dialoogvensters\user_profile_dialog.py
Controleer in de laatste stap het step_context.result
geretourneerde dialoogvenster dat is aangeroepen in de vorige watervalstap. Als de retourwaarde waar is, wordt het gebruikersprofiel opgehaald en bijgewerkt door de gebruikersprofieltoegangsfunctie. Als u het gebruikersprofiel wilt ophalen, roept get
u aan en stelt u vervolgens de waarden van de user_profile.transport
, user_profile.name
en user_profile.age
eigenschappen in. Ten slotte kunt u de informatie voor de gebruiker samenvatten voordat u aanroept end_dialog
, waardoor het dialoogvenster wordt beëindigd. Als u het dialoogvenster beëindigt, wordt het uit de dialoogvensterstack weergegeven en wordt een optioneel resultaat geretourneerd naar het bovenliggende dialoogvenster. Het bovenliggende item is het dialoogvenster of de methode waarmee het dialoogvenster is gestart dat net is beëindigd.
async def summary_step(
self, step_context: WaterfallStepContext
) -> DialogTurnResult:
step_context.values["picture"] = (
None if not step_context.result else step_context.result[0]
)
# Get the current profile object from user state. Changes to it
# will saved during Bot.on_turn.
user_profile = await self.user_profile_accessor.get(
step_context.context, UserProfile
)
user_profile.transport = step_context.values["transport"]
user_profile.name = step_context.values["name"]
user_profile.age = step_context.values["age"]
user_profile.picture = step_context.values["picture"]
msg = f"I have your mode of transport as {user_profile.transport} and your name as {user_profile.name}."
if user_profile.age != -1:
msg += f" And age as {user_profile.age}."
await step_context.context.send_activity(MessageFactory.text(msg))
if user_profile.picture:
await step_context.context.send_activity(
MessageFactory.attachment(
user_profile.picture, "This is your profile picture."
)
)
else:
await step_context.context.send_activity(
"A profile picture was saved but could not be displayed here."
)
# WaterfallStep always finishes with the end of the Waterfall or with another
# dialog, here it is the end.
return await step_context.prompt(
ConfirmPrompt.__name__,
De extensiemethode maken om het watervaldialoogvenster uit te voeren
Er wordt een run_dialog()
helpermethode gedefinieerd in helpers\dialog_helper.py die wordt gebruikt om de context van het dialoogvenster te maken en te openen. accessor
Hier ziet u de toegangsrechten voor de eigenschap status van de status van het dialoogvenster en dialog
is het dialoogvenster gebruikersprofielonderdeel. Aangezien onderdeeldialoogvensters een interne dialoogvensterset definiëren, moet er een buitenste dialoogvensterset worden gemaakt die zichtbaar is voor de berichthandlercode en die gebruiken om een dialoogvenstercontext te maken.
Maak de dialoogvenstercontext door het create_context
aan te roepen, dat wordt gebruikt om te communiceren met de dialoogvensterset vanuit de draaihandler van de bot. De dialoogvenstercontext bevat de huidige beurtcontext, het bovenliggende dialoogvenster en de dialoogvensterstatus, die een methode biedt voor het behouden van informatie in het dialoogvenster.
Met de dialoogvenstercontext kunt u een dialoogvenster met de tekenreeks-id starten of doorgaan met het huidige dialoogvenster (zoals een watervaldialoogvenster met meerdere stappen). De dialoogvenstercontext wordt doorgegeven aan alle dialoogvensters en watervalstappen van de bot.
class DialogHelper:
@staticmethod
async def run_dialog(
dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor
):
dialog_set = DialogSet(accessor)
dialog_set.add(dialog)
dialog_context = await dialog_set.create_context(turn_context)
results = await dialog_context.continue_dialog()
if results.status == DialogTurnStatus.Empty:
await dialog_context.begin_dialog(dialog.id)
In dit voorbeeld wordt de status van het gebruikersprofiel bijgewerkt vanuit het dialoogvenster. Deze procedure kan voor sommige bots werken, maar dit werkt niet als u een dialoogvenster tussen bots opnieuw wilt gebruiken.
Er zijn verschillende opties voor het scheiden van dialoogvensterstappen en botstatus. Zodra uw dialoogvenster bijvoorbeeld volledige informatie verzamelt, kunt u het volgende doen: