實作循序對話流程
適用於: SDK v4
藉由提出問題來收集資訊是 Bot 與用戶互動的主要方式之一。 對話框連結庫提供實用的內建功能,例如 提示 類別,可讓您輕鬆地提出問題並驗證回應,以確保其符合特定數據類型或符合自定義驗證規則。
您可以使用對話框連結庫來管理線性和更複雜的交談流程。 在線性互動中,Bot 會透過固定的步驟順序執行,而交談會完成。 當 Bot 需要從使用者收集資訊時,對話方塊會很有用。
本文說明如何藉由建立提示,並從瀑布式對話呼叫它們,以實作線性對話流程。 如需如何在不使用對話框連結庫的情況下撰寫自己的提示的範例,請參閱 建立您自己的提示以收集使用者輸入 一文。
注意
Bot Framework JavaScript、C# 和 Python SDK 將會繼續受到支援,不過,Java SDK 即將淘汰,最終長期支援將於 2023 年 11 月結束。
使用 Java SDK 建置的現有 Bot 將繼續運作。
針對新的 Bot 建置,請考慮使用 Microsoft Copilot Studio ,並閱讀 選擇正確的 Copilot 解決方案。
如需詳細資訊,請參閱 Bot 建置的未來。
必要條件
關於此範例
多回合提示範例會使用瀑布式對話、一些提示和元件對話來建立線性互動,詢問使用者一系列問題。 程式代碼會使用對話框來循環執行下列步驟:
步驟 | 提示類型 |
---|---|
詢問使用者其運輸模式 | 選擇提示 |
詢問使用者的名字 | 文字提示 |
詢問使用者是否要提供年齡 | 確認提示 |
如果他們回答是,詢問他們的年齡 | 數位提示,驗證只接受大於0且小於150的年齡 |
如果他們不使用 Microsoft Teams,請求他們提供頭像。 | 具有可允許缺少附件的驗證功能的附件提示消息 |
詢問收集到的資訊是否為「確定」 | 重用確認提示 |
最後,如果他們回答是,則顯示收集到的資訊;否則,請告知使用者不會保留其資訊。
建立主要對話框
若要使用對話框,請安裝 Microsoft.Bot.Builder.Dialogs NuGet 套件。
Bot 會透過 UserProfileDialog
與用戶互動。 建立 Bot 的 DialogBot
類別時,UserProfileDialog
被設定為其主要對話。 接著,Bot 會使用 Run
協助程式方法來存取對話方塊。
Dialogs\UserProfileDialog.cs
首先,建立從ComponentDialog
類別衍生且具有七個步驟的UserProfileDialog
。
在建構函式中 UserProfileDialog
,建立瀑布步驟、提示和瀑布對話,並將其新增至對話集。 提示必須位於所使用的相同對話集中。
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);
}
接下來,加入對話過程中用來提示輸入的步驟。 若要使用提示,請從對話框中的步驟呼叫它,並使用 stepContext.Result
擷取下列步驟中的提示結果。 在幕後,這些提示是兩步驟的對話過程。 首先,提示會要求輸入。 然後,它會傳回有效的值,或從開頭重新開始,反覆提示直到收到有效的輸入為止。
您應該一律從瀑布式步驟傳回非空的DialogTurnResult
。 如果您未這麼做,您的對話方塊可能無法如設計般運作。 如下所示是瀑布式對話框中 的 NameStepAsync
實作。
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);
}
請在 AgeStepAsync
中,指定當使用者輸入無法通過驗證時的重試提示,可能是因為提示無法解析輸入格式,或是輸入不符合驗證標準。 在此情況下,如果未提供重試提示,提示會使用初始提示文字來重新提示用戶輸入。
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
用戶的交通方式、名稱和年齡儲存在 UserProfile
類別的實例中。
public class UserProfile
{
public string Transport { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Attachment Picture { get; set; }
}
Dialogs\UserProfileDialog.cs
在最後一步中,檢查在上一個瀑布式步驟中調用的對話框所返回的stepContext.Result
。 如果傳回值為 true,使用者設定檔存取子會取得並更新使用者配置檔。 若要取得使用者配置檔,請先呼叫 GetAsync
,然後設定 userProfile.Transport
、userProfile.Name
、userProfile.Age
和 userProfile.Picture
屬性的值。 最後,在呼叫 EndDialogAsync
之前,摘要說明用戶的資訊,這會結束對話。 結束對話框會將其從對話堆疊中彈出,並將選擇性結果傳回給對話框的父層。 父物件是啟動剛剛結束的對話框的對話框或方法。
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);
執行對話框
Bots\DialogBot.cs
處理程式 OnMessageActivityAsync
會使用 RunAsync
方法來啟動或繼續對話。
OnTurnAsync
會使用 Bot 的狀態管理物件來保存記憶體的任何狀態變更。 方法 ActivityHandler.OnTurnAsync
會呼叫各種活動處理程式方法,例如 OnMessageActivityAsync
。 如此一來,狀態就會儲存在訊息處理程式完成之後,但在回合本身完成之前。
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any state changes that might have occurred during the turn.
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");
// Run the Dialog with the new message Activity.
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
註冊 Bot 的服務
此 Bot 會使用下列服務:
- Bot 的基本服務:認證提供者、配接器和 Bot 實作。
- 用於管理狀態的服務:記憶體、用戶狀態和交談狀態。
- Bot 將使用的對話框。
Startup.cs
在 Startup
中註冊 Bot 的服務。 這些服務可透過相依性插入,供程序代碼的其他部分使用。
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient().AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
});
// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// Create the Bot Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// Create the User state. (Used in this bot's Dialog implementation.)
services.AddSingleton<UserState>();
// Create the Conversation state. (Used by the Dialog system itself.)
services.AddSingleton<ConversationState>();
注意
記憶儲存僅供測試之用,不適用於生產環境。 請務必針對生產用的機器人使用持久性類型的儲存裝置。
測試您的機器人
- 如果您尚未這麼做,請安裝 Bot Framework 模擬器。
- 在本機電腦上運行範例。
- 啟動模擬器、連線至 Bot,並傳送訊息,如下所示。
與多回合提示機器人交談的範例文字記錄。
其他資訊
關於對話框和 Bot 狀態
在此 Bot 中,會定義兩個狀態屬性存取子:
- 在對話狀態屬性中的交談狀態內創建的一個。 對話框狀態會追蹤使用者位於對話集對話內的位置,而且對話內容會更新它,例如呼叫開始對話或繼續對話方法時。
- 在使用者狀態中為使用者配置檔屬性建立的一個。 Bot 會使用此項目來追蹤使用者的相關信息,而且您必須在對話程式碼中明確管理此狀態。
狀態屬性存取子的 get 和 set 方法負責從狀態管理物件的快取中取得和設定屬性的值。 快取在第一次循環請求狀態屬性的值時會填入,但必須明確地保存。 為了保存這兩個狀態屬性的變更,會執行對應狀態管理對象的儲存變更方法呼叫。
此範例會從對話框內更新使用者配置文件狀態。 這種做法適用於某些 Bot,但如果您想要跨多個 Bot 共用對話,則無法運作。
有各種選項可將對話步驟和 Bot 狀態分開。 例如,一旦對話框收集完整的資訊,您就可以:
- 使用結束對話方法,將收集的數據當作返回值回傳給父層級。 這可以是機器人的回合處理程序或對話框堆疊上先前的作用中對話方塊,這也說明了提示類別的設計方式。
- 產生對適當服務的要求。 如果您的 Bot 作為較大型服務的前端,這可能會運作良好。
提示驗證程式方法的定義
UserProfileDialog.cs
以下是 AgePromptValidatorAsync
方法定義的驗證碼範例。
promptContext.Recognized.Value
包含剖析的值,這是數字提示的整數。
promptContext.Recognized.Succeeded
指出提示是否能夠剖析用戶的輸入。 驗證程式應該傳回 false,表示未接受該值,而且提示對話框應該重新提示使用者;否則,傳回 true 以接受輸入,並從提示對話框傳回。 您可以根據您的案例變更驗證程式中的值。
}
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Is this ok?") }, cancellationToken);
}