A robotok a felhasználókkal való interakció egyik fő módja, hogy kérdéseket tehetnek fel az információk gyűjtésére. A párbeszédpanel-kódtár olyan hasznos beépített funkciókat biztosít, mint például a parancssori osztályok, amelyek megkönnyítik a kérdések feltevését, és ellenőrzik a választ annak ellenőrzéséhez, hogy megfelel-e egy adott adattípusnak, vagy megfelelnek-e az egyéni érvényesítési szabályoknak.
A párbeszédtár használatával lineáris és összetettebb beszélgetési folyamatokat is kezelhet. Lineáris interakció esetén a robot rögzített lépések sorozatán fut végig, és a beszélgetés befejeződik. A párbeszédpanel akkor hasznos, ha a robotnak adatokat kell gyűjtenie a felhasználótól.
Ez a cikk bemutatja, hogyan valósíthatja meg a lineáris beszélgetési folyamatot a parancssorok létrehozásával és egy vízesés párbeszédpanelről való meghívásával.
A saját kérések párbeszédpanel-kódtár használata nélküli írására vonatkozó példákért tekintse meg a Saját kérések létrehozása a felhasználói beviteli cikk gyűjtéséhez című témakört.
A többfordulós kérések minta egy vízesés párbeszédpanelt, néhány kérdést és egy összetevő párbeszédpanelt használ egy lineáris interakció létrehozásához, amely kérdések sorozatát kérdezi fel a felhasználótól. A kód egy párbeszédpanel használatával lépeget az alábbi lépéseken:
Végül, ha igennel válaszoltak, jelenítse meg az összegyűjtött információkat; ellenkező esetben közölje a felhasználóval, hogy az adatai nem lesznek megőrzve.
A párbeszédpanelek használatához telepítse a Microsoft.Bot.Builder.Dialogs NuGet csomagot.
A robot a felhasználóval a használatával UserProfileDialog
kommunikál. A robot osztályának DialogBot
létrehozásakor a UserProfileDialog
robot a fő párbeszédpanelként van beállítva. A robot ezután egy Run
segédmetódussal éri el a párbeszédpanelt.
Párbeszédpanelek\UserProfileDialog.cs
Először hozza létre az UserProfileDialog
osztályból ComponentDialog
származót, és hét lépésből áll.
A konstruktorban UserProfileDialog
hozza létre a vízesés lépéseit, a parancssorokat és a vízesés párbeszédpanelt, és adja hozzá őket a párbeszédpanel-készlethez. A kéréseknek ugyanabban a párbeszédpanelen kell lenniük, amelyben használják őket.
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);
}
Ezután adja hozzá azokat a lépéseket, amelyeket a párbeszédpanel a bemenet kéréséhez használ. A parancssor használatához hívja meg a párbeszédpanel egyik lépéséből, és kérje le a következő lépésben kapott eredményt a következő lépésben stepContext.Result
. A színfalak mögött a kérések kétlépéses párbeszédpanelek. Először a kérés bemenetet kér. Ezután visszaadja az érvényes értéket, vagy az elejétől kezdve újrakezdi a hibát, amíg érvényes bemenetet nem kap.
A vízeséslépésből mindig null értékű DialogTurnResult
értéket kell visszaadnia. Ha nem, előfordulhat, hogy a párbeszédpanel nem a tervezett módon működik. Az alábbiakban a vízesés párbeszédpanel implementációja NameStepAsync
látható.
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);
}
Ebben AgeStepAsync
a mezőben adjon meg egy újrapróbálkozáshoz szükséges kérést arra az esetben, ha a felhasználó bemenete nem érvényesíthető, vagy mert olyan formátumban van, amelyet a parancssor nem tud elemezni, vagy a bemenet nem tud érvényesítési feltételeket megadni. Ebben az esetben, ha nem adott meg újrapróbálkozási kérést, a parancssor a kezdeti parancssori szöveg használatával fogja újrakényelmesíteni a felhasználót a bemenethez.
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
A rendszer az osztály egy példányában menti a felhasználó szállítási módját, nevét és életkorát UserProfile
.
public class UserProfile
{
public string Transport { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Attachment Picture { get; set; }
}
Párbeszédpanelek\UserProfileDialog.cs
Az utolsó lépésben ellenőrizze az stepContext.Result
előző vízesés lépésben hívott párbeszédpanel által visszaadott hibát. Ha a visszatérési érték igaz, a felhasználói profil tartozéka lekéri és frissíti a felhasználói profilt. A felhasználói profil lekéréséhez hívja megGetAsync
, majd állítsa be a , userProfile.Name
userProfile.Age
és userProfile.Picture
a userProfile.Transport
tulajdonságok értékeit. Végül foglalja össze a felhasználó adatait a hívás EndDialogAsync
előtt, amely véget vet a párbeszédpanelnek. A párbeszédpanel befejezése kiugrik a párbeszédpanel vereméből, és opcionális eredményt ad vissza a párbeszédpanel szülőjének. A szülő az a párbeszédpanel vagy metódus, amely elindította az imént befejezett párbeszédpanelt.
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);
A párbeszédpanelek használatához a projektnek telepítenie kell a botbuilder-dialogs npm csomagot.
A robot egy UserProfileDialog
. A robot létrehozásakor a UserProfileDialog
robot DialogBot
a fő párbeszédpanelként van beállítva. A robot ezután egy run
segédmetódussal éri el a párbeszédpanelt.
párbeszédpanelek/userProfileDialog.js
Először hozza létre az UserProfileDialog
osztályból ComponentDialog
származót, és hét lépésből áll.
A konstruktorban UserProfileDialog
hozza létre a vízesés lépéseit, a parancssorokat és a vízesés párbeszédpanelt, és adja hozzá őket a párbeszédpanel-készlethez. A kéréseknek ugyanabban a párbeszédpanelen kell lenniük, amelyben használják őket.
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;
}
Ezután adja hozzá azokat a lépéseket, amelyeket a párbeszédpanel a bemenet kéréséhez használ. Ha parancssort szeretne használni, hívja meg a párbeszédpanel egyik lépéséből, és kérje le a parancssor eredményét a következő lépésben a lépéskörnyezetből, ebben az esetben a használatával step.result
. A színfalak mögött a kérések kétlépéses párbeszédpanelek. Először a kérés bemenetet kér. Ezután visszaadja az érvényes értéket, vagy az elejétől kezdve újrakezdi a hibát, amíg érvényes bemenetet nem kap.
A vízeséslépésből mindig null értékű DialogTurnResult
értéket kell visszaadnia. Ha nem, előfordulhat, hogy a párbeszédpanel nem a tervezett módon működik. Az alábbiakban a vízesés párbeszédpanel implementációja nameStep
látható.
async nameStep(step) {
step.values.transport = step.result.value;
return await step.prompt(NAME_PROMPT, 'Please enter your name.');
}
Ebben ageStep
a mezőben adjon meg egy újrapróbálkozásos kérést arra az esetben, ha a felhasználó bemenete nem érvényesíthető, mert olyan formátumban van, amelyet a parancssor nem tud elemezni, vagy a bemenet a fenti konstruktorban megadott érvényesítési feltételeket meghiúsul. Ebben az esetben, ha nem adott meg újrapróbálkozási kérést, a parancssor a kezdeti parancssori szöveg használatával fogja újrakényelmesíteni a felhasználót a bemenethez.
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
A rendszer az osztály egy példányában menti a felhasználó szállítási módját, nevét és életkorát UserProfile
.
class UserProfile {
constructor(transport, name, age, picture) {
this.transport = transport;
this.name = name;
this.age = age;
this.picture = picture;
}
}
párbeszédpanelek/userProfileDialog.js
Az utolsó lépésben ellenőrizze az step.result
előző vízesés lépésben hívott párbeszédpanel által visszaadott hibát. Ha a visszatérési érték igaz, a felhasználói profil tartozéka lekéri és frissíti a felhasználói profilt. A felhasználói profil lekéréséhez hívja megget
, majd állítsa be a , userProfile.name
userProfile.age
és userProfile.picture
a tulajdonságok értékeituserProfile.transport
. Végül foglalja össze a felhasználó adatait a hívás endDialog
előtt, amely véget vet a párbeszédpanelnek. A párbeszédpanel befejezése kiugrik a párbeszédpanel vereméből, és opcionális eredményt ad vissza a párbeszédpanel szülőjének. A szülő az a párbeszédpanel vagy metódus, amely elindította az imént befejezett párbeszédpanelt.
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.');
}
A vízesés párbeszédpanel futtatásához hozza létre a bővítménymetódust
A run
párbeszédpanel-környezet létrehozásához és eléréséhez egy belül definiált userProfileDialog
segédmetódus szolgál. accessor
Itt található a párbeszédpanel állapottulajdonságának állapottulajdonság-tartozéka, és this
a felhasználói profil összetevő párbeszédpanelje. Mivel az összetevők párbeszédpanelei belső párbeszédpanelkészletet határoznak meg, létre kell hozni egy külső párbeszédpanel-csoportot, amely látható az üzenetkezelő kódjában, és egy párbeszédpanel-környezet létrehozásához használható.
A párbeszédpanel-környezet a createContext
metódus meghívásával jön létre, és a robot turn handlerén belülről használja a párbeszédpanelkészlettel való interakcióra. A párbeszédpanel környezete tartalmazza az aktuális turn környezetet, a szülő párbeszédpanelt és a párbeszédpanel állapotát, amely módot biztosít a párbeszédpanelen belüli információk megőrzésére.
A párbeszédpanel-környezet lehetővé teszi egy párbeszédpanel indítását a sztringazonosítóval, vagy folytathatja az aktuális párbeszédpanelt (például egy több lépést tartalmazó vízesés párbeszédpanelt). A párbeszédpanel-környezet át lesz adva a robot összes párbeszédpaneljén és vízeséslépésén.
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);
}
}
A robot a felhasználóval a használatával UserProfileDialog
kommunikál. A robot osztályának DialogBot
létrehozásakor a UserProfileDialog
robot a fő párbeszédpanelként van beállítva. A robot ezután egy Run
segédmetódussal éri el a párbeszédpanelt.
UserProfileDialog.java
Először hozza létre az UserProfileDialog
osztályból ComponentDialog
származót, és hét lépésből áll.
A konstruktorban UserProfileDialog
hozza létre a vízesés lépéseit, a parancssorokat és a vízesés párbeszédpanelt, és adja hozzá őket a párbeszédpanel-készlethez. A kéréseknek ugyanabban a párbeszédpanelen kell lenniük, amelyben használják őket.
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");
}
Ezután adja hozzá azokat a lépéseket, amelyeket a párbeszédpanel a bemenet kéréséhez használ. A parancssor használatához hívja meg a párbeszédpanel egyik lépéséből, és kérje le a következő lépésben kapott eredményt a következő lépésben stepContext.getResult()
. A színfalak mögött a kérések kétlépéses párbeszédpanelek. Először a kérés bemenetet kér. Ezután visszaadja az érvényes értéket, vagy az elejétől kezdve újrakezdi a hibát, amíg érvényes bemenetet nem kap.
A vízeséslépésből mindig null értékű DialogTurnResult
értéket kell visszaadnia. Ha nem, előfordulhat, hogy a párbeszédpanel nem a tervezett módon működik. Az alábbiakban a vízesés párbeszédpanel implementációja nameStep
látható.
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);
}
Ebben ageStep
a mezőben adjon meg egy újrapróbálkozáshoz szükséges kérést arra az esetben, ha a felhasználó bemenete nem érvényesíthető, vagy mert olyan formátumban van, amelyet a parancssor nem tud elemezni, vagy a bemenet nem tud érvényesítési feltételeket megadni. Ebben az esetben, ha nem adott meg újrapróbálkozási kérést, a parancssor a kezdeti parancssori szöveg használatával fogja újrakényelmesíteni a felhasználót a bemenethez.
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
A rendszer az osztály egy példányában menti a felhasználó szállítási módját, nevét és életkorát UserProfile
.
// 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
Az utolsó lépésben ellenőrizze az stepContext.Result
előző vízesés lépésben hívott párbeszédpanel által visszaadott hibát. Ha a visszatérési érték igaz, a felhasználói profil tartozéka lekéri és frissíti a felhasználói profilt. A felhasználói profil lekéréséhez hívja megget
, majd állítsa be a , userProfile.Name
userProfile.Age
és userProfile.Picture
a userProfile.Transport
tulajdonságok értékeit. Végül foglalja össze a felhasználó adatait a hívás endDialog
előtt, amely véget vet a párbeszédpanelnek. A párbeszédpanel befejezése kiugrik a párbeszédpanel vereméből, és opcionális eredményt ad vissza a párbeszédpanel szülőjének. A szülő az a párbeszédpanel vagy metódus, amely elindította az imént befejezett párbeszédpanelt.
// 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);
}
}
}
A párbeszédpanelek használatához telepítse a botbuilder-dialogs és botbuilder-aiPyPI csomagokat egy terminál futtatásával pip install botbuilder-dialogs
és pip install botbuilder-ai
használatával.
A robot a felhasználóval a használatával UserProfileDialog
kommunikál. A robot osztályának DialogBot
létrehozásakor a UserProfileDialog
robot a fő párbeszédpanelként van beállítva. A robot ezután egy run_dialog
segédmetódussal éri el a párbeszédpanelt.
párbeszédpanelek\user_profile_dialog.py
Először hozza létre az UserProfileDialog
osztályból ComponentDialog
származót, és hét lépésből áll.
A konstruktorban UserProfileDialog
hozza létre a vízesés lépéseit, a parancssorokat és a vízesés párbeszédpanelt, és adja hozzá őket a párbeszédpanel-készlethez. A kéréseknek ugyanabban a párbeszédpanelen kell lenniük, amelyben használják őket.
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__
Ezután adja hozzá azokat a lépéseket, amelyeket a párbeszédpanel a bemenet kéréséhez használ. A parancssor használatához hívja meg a párbeszédpanel egyik lépéséből, és kérje le a következő lépésben kapott eredményt a következő lépésben step_context.result
. A színfalak mögött a kérések kétlépéses párbeszédpanelek. Először a kérés bemenetet kér. Ezután visszaadja az érvényes értéket, vagy az elejétől kezdve újrakezdi a hibát, amíg érvényes bemenetet nem kap.
A vízeséslépésből mindig null értékű DialogTurnResult
értéket kell visszaadnia. Ha nem, előfordulhat, hogy a párbeszédpanel nem a tervezett módon működik. Itt a vízesés párbeszédpanelen láthatja a name_step
megvalósítást.
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.")),
)
Ebben age_step
a mezőben adjon meg egy újrapróbálkozásos kérést arra az esetben, ha a felhasználó bemenete nem érvényesíthető, mert olyan formátumban van, amelyet a parancssor nem tud elemezni, vagy a bemenet a fenti konstruktorban megadott érvényesítési feltételeket meghiúsul. Ebben az esetben, ha nem lett megadva újrapróbálkozási kérés, a parancssor a kezdeti parancssori szöveg használatával fogja újrapróbálkozni a felhasználót a bemenethez
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
A rendszer az osztály egy példányában menti a felhasználó szállítási módját, nevét és életkorát UserProfile
.
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
párbeszédpanelek\user_profile_dialog.py
Az utolsó lépésben ellenőrizze az step_context.result
előző vízesés lépésben hívott párbeszédpanel által visszaadott hibát. Ha a visszatérési érték igaz, a felhasználói profil tartozéka lekéri és frissíti a felhasználói profilt. A felhasználói profil lekéréséhez hívja megget
, majd állítsa be a , user_profile.name
és user_profile.age
a tulajdonságok értékeituser_profile.transport
. Végül foglalja össze a felhasználó adatait a hívás end_dialog
előtt, amely véget vet a párbeszédpanelnek. A párbeszédpanel befejezése kiugrik a párbeszédpanel vereméből, és opcionális eredményt ad vissza a párbeszédpanel szülőjének. A szülő az a párbeszédpanel vagy metódus, amely elindította az imént befejezett párbeszédpanelt.
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__,
A vízesés párbeszédpanel futtatásához hozza létre a bővítménymetódust
A run_dialog()
súgómetódus a súgók\ dialog_helper.py van definiálva, amely a párbeszédpanel-környezet létrehozásához és eléréséhez használható. accessor
Itt található a párbeszédpanel állapottulajdonságának állapottulajdonság-tartozéka, és dialog
a felhasználói profil összetevő párbeszédpanelje. Mivel az összetevők párbeszédpanelei belső párbeszédpanelkészletet határoznak meg, létre kell hozni egy külső párbeszédpanel-csoportot, amely látható az üzenetkezelő kódjában, és ezt használva hozzon létre egy párbeszédpanel-környezetet.
A párbeszédpanel-környezet létrehozásához hívja meg a create_context
párbeszédpanelt, amely a robot turn handlerén belülről használja a párbeszédpanelkészlettel való interakciót. A párbeszédpanel környezete tartalmazza az aktuális turn környezetet, a szülő párbeszédpanelt és a párbeszédpanel állapotát, amely módot biztosít a párbeszédpanelen belüli információk megőrzésére.
A párbeszédpanel-környezet lehetővé teszi egy párbeszédpanel indítását a sztringazonosítóval, vagy folytathatja az aktuális párbeszédpanelt (például egy több lépést tartalmazó vízesés párbeszédpanelt). A párbeszédpanel-környezet át lesz adva a robot összes párbeszédpaneljén és vízeséslépésén.
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)
Ez a minta frissíti a felhasználói profil állapotát a párbeszédpanelen. Ez a gyakorlat egyes robotok esetében működhet, de nem fog működni, ha több roboton szeretné újra használni a párbeszédpaneleket.
A párbeszédpanel lépéseinek és a robot állapotának elkülönítésére különböző lehetőségek állnak rendelkezésre. Ha például a párbeszédpanel teljes információkat gyűjt, a következőt teheti: