适用于:SDK v4

可以通过组件对话创建独立的对话来处理特定的方案,将大型对话集分解成更易于管理的片段。 其中的每个片段有自身的对话集,可避免与其外的对话集发生名称冲突。 组件对话可重复使用,因为它们可以:

  • 添加到机器人中的其他 ComponentDialog DialogSet
  • 作为包的一部分导出。
  • 在其他机器人中使用。
  • Bot Framework JavaScript 和 C# SDK 将继续受支持,但是 Python 和 Java SDK 即将停用,最终的长期支持将于 2023 年 11 月结束。 仅执行此存储库中的关键安全性和 bug 修复。

    使用这些 SDK 构建的现有机器人将继续正常运行。

    对于新的机器人生成,请考虑使用 Power Virtual Agents 并阅读如何 选择合适的聊天机器人解决方案

    有关详细信息,请参阅 机器人构建的未来

  • 了解 机器人基础知识 对话库 、以及如何 管理聊天
  • C# JavaScript Java Python 编写的“多轮次提示”示例的副本。
  • 关于本示例

    在多轮次提示示例中,我们使用瀑布对话、一些提示和组件对话来创建向用户询问一系列问题的交互。 代码使用对话来循环执行以下步骤:

    最后,如果他们回答“是”,则显示收集的信息:否则,请告知用户不会保留其信息。

    实现组件对话框

    在多轮次提示示例中,我们使用 瀑布对话 、一些 提示 组件对话 来创建向用户询问一系列问题的交互。

    组件对话可封装一个或多个对话。 组件对话有一个内部对话集,而添加到此内部对话集的对话和提示有其自己的 ID,这些 ID 只能在组件对话内部查看。

    JavaScript Python

    若要使用对话,请安装 Microsoft.Bot.Builder.Dialogs NuGet 包。

    Dialogs\UserProfileDialog.cs

    在这里, UserProfileDialog 类派生自 ComponentDialog 类。

    public class UserProfileDialog : ComponentDialog
    

    在构造函数中,AddDialog 方法会将对话和提示添加到组件对话。 使用此方法添加的第一个项设置为初始对话。 可以通过显式设置 InitialDialogId 属性来更改初始对话。 启动组件对话框时,它会启动其 initial dialog

    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,
            ConfirmStepAsync,
            SummaryStepAsync,
        // 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);
    

    以下代码表示瀑布对话的第一步。

    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);
    

    若要详细了解如何实现瀑布对话,请参阅如何实现顺序聊天流

    若要使用对话,项目需要安装 botbuilder-dialogs npm 包。

    dialogs/userProfileDialog.js

    在这里,UserProfileDialog 类扩展了 ComponentDialog

    class UserProfileDialog extends ComponentDialog {
    

    在构造函数中,AddDialog 方法会将对话和提示添加到组件对话。 使用此方法添加的第一个项设置为初始对话。 可以通过显式设置 InitialDialogId 属性来更改初始对话。 启动组件对话框时,它会启动其 initial dialog

    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.confirmStep.bind(this),
            this.summaryStep.bind(this)
        this.initialDialogId = WATERFALL_DIALOG;
    

    以下代码表示瀑布对话的第一步。

    async transportStep(step) {
        // 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.
        return await step.prompt(CHOICE_PROMPT, {
            prompt: 'Please enter your mode of transport.',
            choices: ChoiceFactory.toChoices(['Car', 'Bus', 'Bicycle'])
    

    若要详细了解如何实现瀑布对话,请参阅如何实现顺序聊天流

    UserProfileDialog.java

    在这里,UserProfileDialog 类派生自 ComponentDialog 类。

    public class UserProfileDialog extends ComponentDialog {
    

    在构造函数中,addDialog 方法会将对话和提示添加到组件对话。 使用此方法添加的第一个项设置为初始对话。 可以通过调用 setInitialDialogId 方法更改初始对话并提供初始对话的名称。 启动组件对话框时,它会启动其 initial dialog

    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> 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);
    

    若要详细了解如何实现瀑布对话,请参阅如何实现顺序聊天流

    若要使用对话,请通过从终端运行 pip install botbuilder-dialogspip install botbuilder-ai 来安装 botbuilder-dialogs 和 botbuilder-ai PyPI 包 。

    dialogs/user_profile_dialog.py

    在这里,UserProfileDialog 类扩展了 ComponentDialog

    class UserProfileDialog(ComponentDialog):
    

    在构造函数中,add_dialog 方法会将对话和提示添加到组件对话。 使用此方法添加的第一个项设置为初始对话。 可以通过显式设置 initial_dialog_id 属性来更改初始对话。 启动组件对话框时,它会启动其 initial dialog

    class UserProfileDialog(ComponentDialog):
        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.confirm_step,
                        self.summary_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__
    

    以下代码表示瀑布对话的第一步。

    async def transport_step(
        self, step_context: WaterfallStepContext
    ) -> DialogTurnResult:
        # 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 users response is received.
        return await step_context.prompt(
            ChoicePrompt.__name__,
            PromptOptions(
                prompt=MessageFactory.text("Please enter your mode of transport."),
                choices=[Choice("Car"), Choice("Bus"), Choice("Bicycle")],
    

    若要详细了解如何实现瀑布对话,请参阅如何实现顺序聊天流

    在运行时,组件对话保留其自己的对话堆栈。 启动组件对话时:

  • 会创建一个实例并将其添加到外部对话堆栈
  • 它会创建一个内部对话堆栈并将其添加到状态中
  • 它会启动初始对话并将其添加到内部对话堆栈。
  • 父上下文将组件视为活动对话。 但是,对于组件内的上下文,初始对话似乎是活动对话。

    从机器人调用对话框

    在外部对话集中(添加组件对话框的对话框)中,组件对话具有创建它的 ID。 在外部集中,组件看起来像单个对话,与提示特别相似。

    若要使用组件对话框,请将它的实例添加到机器人的对话集中。

    JavaScript Python

    Bots\DialogBot.cs

    在该示例中,这是使用从机器人的 OnMessageActivityAsync 方法调用的 RunAsync 方法完成的。

    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);
    

    dialogs/userProfileDialog.js

    在示例中,我们向用户配置文件对话添加了 run 方法。

    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);
    

    bots/dialogBot.js

    run 方法从机器人的 onMessage 方法中调用。

    this.onMessage(async (context, next) => {
        console.log('Running dialog with Message Activity.');
        // Run the Dialog with the new message Activity.
        await this.dialog.run(context, this.dialogState);
        await next();
    

    DialogBot.java

    在该示例中,这是使用从机器人的 onMessageActivity 方法调用的 run 方法完成的。

    @Override
    protected CompletableFuture<Void> onMessageActivity(
        TurnContext turnContext
        LoggerFactory.getLogger(DialogBot.class).info("Running dialog with Message Activity.");
        // Run the Dialog with the new message Activity.
        return Dialog.run(dialog, turnContext, conversationState.createProperty("DialogState"));
    

    helpers/dialog_helper.py

    在示例中,我们向用户配置文件对话添加了 run_dialog 方法。

    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)
    

    从机器人的 on_message_activity 方法中调用的 run_dialog 方法。

    bots/dialog_bot.py

    async def on_message_activity(self, turn_context: TurnContext):
        await DialogHelper.run_dialog(
            self.dialog,
            turn_context,
            self.conversation_state.create_property("DialogState"),
    

    测试机器人

  • 安装 Bot Framework Emulator(如果尚未安装)。
  • 在计算机本地运行示例。
  • 按如下所示启动模拟器,连接到机器人,然后发送消息。
  • 如何针对组件对话执行取消操作

    如果从组件对话的上下文调用“取消所有对话”,组件对话会取消其内部堆栈上的所有对话,然后结束,将控制返回给外部堆栈上的下一对话。

    如果从外部上下文调用 cancel 所有对话 ,则会取消组件,以及外部上下文上的其余对话。

    了解如何创建可创建分支和可循环的复杂聊天。

    处理用户中断