如何在Microsoft Botbuilder SDK中分离触发动作与提示对话(Node.js ES6 Babel)
这个场景我之前开发Bot时也碰到过,核心就是要实现对话的中断与无缝恢复——让Bot处理完临时的无关请求后,能精准回到主菜单原来的等待状态,继续等待用户的业务操作输入。下面给你适配Node.js+ES6+Babel+Botbuilder SDK的具体实现方案:
核心思路
在/MainMenu对话中加入前置拦截逻辑:每次用户发送消息时,先判断是否是触发无关动作的关键词(比如“你好吗?”),如果是,先启动对应的临时对话处理请求,等临时对话结束后,自动回到/MainMenu的原状态,继续之前的流程。
具体代码实现
1. 定义临时无关对话(比如处理“你好吗?”的对话)
先写一个简单的对话类来处理这类闲聊请求,处理完后主动结束对话,把控制权交回主菜单:
import { ComponentDialog, WaterfallDialog, TextPrompt } from 'botbuilder-dialogs'; class HowAreYouDialog extends ComponentDialog { constructor(id) { super(id); // 添加必要的提示组件 this.addDialog(new TextPrompt('textPrompt')); // 用瀑布流定义对话流程 this.addDialog(new WaterfallDialog('mainFlow', [ async (step) => { // 回复用户的问题 await step.context.sendActivity('我很好呀,谢谢你的关心😊!'); // 结束当前对话,回到上一层对话(也就是MainMenu) return await step.endDialog(); } ])); this.initialDialogId = 'mainFlow'; } } export default HowAreYouDialog;
2. 修改/MainMenu对话,加入中断拦截逻辑
重写MainMenuDialog的continueDialog方法——这个方法会在对话处于等待用户输入的状态时(比如你用step.prompt等待回复时)优先触发,正好用来做前置拦截:
import { ComponentDialog, WaterfallDialog, TextPrompt, Dialog } from 'botbuilder-dialogs'; class MainMenuDialog extends ComponentDialog { constructor(id) { super(id); this.addDialog(new TextPrompt('mainMenuPrompt')); // 主菜单的核心流程 this.addDialog(new WaterfallDialog('mainMenuFlow', [ async (step) => { // 提示用户输入业务操作 return await step.prompt('mainMenuPrompt', '欢迎来到主菜单,请输入您需要的操作(比如"查询订单"),或者随便聊点什么~'); }, async (step) => { // 处理用户的正常业务操作请求 const userInput = step.result.trim(); if (userInput.includes('订单')) { return await step.beginDialog('/OrderDialog'); // 跳转到订单对话 } else if (userInput.includes('帮助')) { return await step.beginDialog('/HelpDialog'); // 跳转到帮助对话 } else { // 不理解的业务输入,重新提示 await step.context.sendActivity('我不太明白您的需求,请再试一次?'); return await step.replaceDialog(this.id); } } ])); this.initialDialogId = 'mainMenuFlow'; } // 重写continueDialog,加入中断逻辑 async continueDialog(dc) { const userMessage = dc.context.activity.text.toLowerCase(); // 判断是否是触发无关对话的关键词 if (userMessage.includes('你好吗') || userMessage.includes('怎么样') || userMessage.includes('过得好')) { // 启动闲聊对话 await dc.beginDialog('/HowAreYou'); // 返回EndOfTurn,告诉Bot当前对话暂时中断,等子对话结束后再继续 return Dialog.EndOfTurn; } // 如果不是无关消息,走原有的对话逻辑 return await super.continueDialog(dc); } } export default MainMenuDialog;
3. 在Bot入口注册所有对话
确保把主菜单和临时对话都注册到DialogSet里:
import { DialogSet, MemoryStorage, ConversationState } from 'botbuilder'; import MainMenuDialog from './dialogs/MainMenuDialog'; import HowAreYouDialog from './dialogs/HowAreYouDialog'; // 其他业务对话导入... // 初始化对话状态存储 const memoryStorage = new MemoryStorage(); const conversationState = new ConversationState(memoryStorage); const dialogState = conversationState.createProperty('dialogState'); // 注册所有对话 const dialogSet = new DialogSet(dialogState); dialogSet.add(new MainMenuDialog('/MainMenu')); dialogSet.add(new HowAreYouDialog('/HowAreYou')); // 注册其他业务对话...
关键逻辑说明
continueDialog的作用:当/MainMenu处于等待用户输入的状态(比如step.prompt之后),用户发送的消息会先进入这个方法,我们在这里做前置判断,实现对话中断。Dialog.EndOfTurn的意义:返回这个值后,Bot会暂停当前/MainMenu的流程,先处理启动的临时对话;等临时对话调用step.endDialog()结束后,Bot会自动回到/MainMenu的原状态,继续等待用户的业务操作输入。- 子对话的结束:一定要在临时对话的最后调用
step.endDialog(),这样才能把控制权交回父对话,确保主菜单能恢复到之前的状态。
额外优化建议
- 可以把判断无关关键词的逻辑抽成一个单独的工具函数,比如
isInterruptMessage(text),方便后续扩展更多闲聊关键词。 - 如果有多个不同的无关动作(比如“讲个笑话”“今天天气怎么样”),都可以在
continueDialog里添加判断,分别启动对应的子对话。
内容的提问来源于stack exchange,提问作者gpsugy
相关产品推荐
相关产品推荐

