如何在Bot Framework全对话中触发LUIS并保留上下文与认证参数
实现方案
第一步:存储authHeaders到Bot State
你无法跨对话访问authHeaders是因为仅将其作为对话启动参数传递,参数只能在当前对话及其子对话的options中读取,将其存储到UserState后,可在任意能拿到TurnContext的位置读取和修改。
// 主Bot文件中初始化State const { UserState, MemoryStorage } = require('botbuilder'); const memoryStorage = new MemoryStorage(); const userState = new UserState(memoryStorage); const AUTH_HEADERS_PROPERTY = 'authHeaders'; this.authHeadersAccessor = userState.createProperty(AUTH_HEADERS_PROPERTY); // 在AUTH_DIALOG的showUserInfo方法中,拿到authHeaders后存入State const authHeadersAccessor = stepContext.context.turnState.get('authHeadersAccessor'); await authHeadersAccessor.set(stepContext.context, authHeaders); await userState.saveChanges(stepContext.context);
第二步:实现全局LUIS触发
两种可选方案,可根据项目结构选择:
方案1:子对话统一继承带中断逻辑的基类(推荐)
适合对话数量不多、需要灵活控制中断规则的场景,无需修改全局逻辑。
const { ComponentDialog, Dialog } = require('botbuilder-dialogs'); const { LuisRecognizer } = require('botbuilder-ai'); class BaseDialog extends ComponentDialog { constructor(dialogId) { super(dialogId); } // 每次收到新消息先触发中断逻辑 async onContinueDialog(innerDc) { const interruptResult = await this.interrupt(innerDc); if (interruptResult) { return Dialog.EndOfTurn; } return await super.onContinueDialog(innerDc); } async interrupt(innerDc) { const luisRecognizer = new LuisRecogniser(luisConfig, innerDc.dialogs.telemetryClient); if (!luisRecognizer.isConfigured) return false; // 执行LUIS识别 const luisResult = await luisRecognizer.executeLuisQuery(innerDc.context); const topIntent = LuisRecognizer.topIntent(luisResult); // 读取存储的authHeaders const authHeadersAccessor = innerDc.context.turnState.get('authHeadersAccessor'); let authHeaders = await authHeadersAccessor.get(innerDc.context, {}); // 匹配意图跳转对应对话 switch(topIntent) { case 'Intent1': authHeaders.header1 = luisResult.entities; await authHeadersAccessor.set(innerDc.context, authHeaders); await userState.saveChanges(innerDc.context); // 清空当前对话栈跳转新对话 await innerDc.cancelAllDialogs(); await innerDc.beginDialog(DIALOG_A, authHeaders); return true; case 'Intent2': authHeaders.header2 = luisResult.entities; await authHeadersAccessor.set(innerDc.context, authHeaders); await userState.saveChanges(innerDc.context); await innerDc.cancelAllDialogs(); await innerDc.beginDialog(DIALOG_B, authHeaders); return true; // 其他意图同理 default: // 意图匹配当前对话则不中断,继续执行 return false; } } }
后续所有子对话(包括FLOW_DIALOG、DIALOG_A、DIALOG_B)统一继承BaseDialog即可实现全局LUIS触发。
方案2:onMessage阶段统一处理
适合对话数量多、不想修改每个子对话继承关系的场景。
this.onMessage(async (context, next) => { // 读取authHeaders const authHeaders = await this.authHeadersAccessor.get(context, {}); // 执行LUIS识别 const luisRecognizer = new LuisRecogniser(luisConfig, this.telemetryClient); const luisResult = await luisRecognizer.executeLuisQuery(context); const topIntent = LuisRecognizer.topIntent(luisResult); // 创建DialogContext const dc = await this.dialogs.createContext(context); switch(topIntent) { case 'Intent1': authHeaders.header1 = luisResult.entities; await this.authHeadersAccessor.set(context, authHeaders); await this.userState.saveChanges(context); await dc.cancelAllDialogs(); await dc.beginDialog(DIALOG_A, authHeaders); break; case 'Intent2': authHeaders.header2 = luisResult.entities; await this.authHeadersAccessor.set(context, authHeaders); await this.userState.saveChanges(context); await dc.cancelAllDialogs(); await dc.beginDialog(DIALOG_B, authHeaders); break; // 其他意图同理 default: // 无匹配跳转意图则继续当前对话 await dc.continueDialog(); if (!dc.activeDialog) { await dc.beginDialog(FLOW_DIALOG, authHeaders); } } await next(); });
注意事项
- 生产环境请将
MemoryStorage替换为持久化存储(如Azure Cosmos DB、Blob存储),避免服务重启后用户认证信息丢失 - 可根据业务需求在中断逻辑中添加例外规则,比如用户正在输入表单信息时禁止意图跳转
- 切换对话前调用
cancelAllDialogs()可以清空当前对话栈,避免残留的对话逻辑干扰新流程
内容的提问来源于stack exchange,提问作者AJ31
相关产品推荐
相关产品推荐

