Bot 混淆了并发用户的提示

Bot is mixing up prompts from concurrent users

我遇到了一个问题,多个用户同时访问同一个对话框时,他们的提示值混淆了。有问题的对话框是订单状态对话框,其中提示订单号等。我看到用户 1 查询订单 A,用户 2 查询订单 B(几乎同时)的情况,然后两个用户都收到订单 B 的信息。

这是一个复杂的对话框,分享完整的代码不会有帮助,但我认为以下几点可能相关:

return await step.prompt(TEXT_PROMPT, {
    prompt: `Please provide your ${step.values.orderTypeText} number.`,
    retryPrompt: `Please enter a valid ${step.values.orderTypeText} number.`,
});

就是这样,除非有某种方式不是提示值而是整个消息被发送给错误的用户(但这似乎不太可能,而且在这个例子中它会同时发送给两个用户).

我考虑过不使用 this.queryData 将信息存储在 conversationState 中,但我不想重做代码,除非我可以确认这是我的实现的问题使用,特别是 this.queryData 而不是其他问题。

作为参考,这里是 class 定义,一直到构造函数的末尾:

class viewOrderDialog extends ComponentDialog {
    constructor(dialogId, userDialogStateAccessor, userState, appInsightsClient, dialogState, conversationState) {
        super(dialogId);

        this.addDialog(new ChoicePrompt(CRITERIA_PROMPT));
        this.addDialog(new TextPrompt(TEXT_PROMPT));
        this.addDialog(new TextPrompt(LINE_PROMPT, this.validateLineNumber));
        this.addDialog(new TextPrompt(EMAIL_PROMPT,this.validateEmail));
        this.addDialog(new TextPrompt(ZIP_PROMPT,this.validateZip));
        this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT));
        this.addDialog(new ChoicePrompt(CHOICE_PROMPT));
        this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
            this.requestOrderNumber.bind(this),
            this.selectOrderType.bind(this), // This is being bypassed by current Filtration store implementation
            this.selectSearchCriteria.bind(this),
            this.getQueryData.bind(this),
            this.confirmBillingZip.bind(this),
            this.confirmBillingEmail.bind(this),
            this.displayLineStatus.bind(this),
            this.getEmailAddress.bind(this),
            this.setFollowUp.bind(this),
            this.loopStep.bind(this)
        ]));

        this.initialDialogId = WATERFALL_DIALOG;

        this.queryData = {};

        // State accessors
        this.userDialogStateAccessor = userDialogStateAccessor;
        this.userState = userState;
        this.dialogState = dialogState;
        this.conversationState = conversationState;

        this.appInsightsClient = appInsightsClient;

        this.addDialog(new PaginateDialog(PAGINATE_DIALOG, this.userDialogStateAccessor, userState, this.appInsightsClient));

        // Luis Recognizer
        this.luisRecognizer = new LuisRecognizer({
            applicationId: process.env.LuisAppId,
            endpointKey: process.env.LuisAPIKey,
            endpoint: `https://${ process.env.LuisAPIHostName }`
        }, {
            includeAllIntents: true,
            includeInstanceData: true,
            spellCheck: true,
            bingSpellCheckSubscriptionKey: process.env.bingAPIKey
        }, true);

    } // End constructor

我习惯在.net上使用Botframework,但我认为整体机制与节点不会有太大差异。

在 BotBuilder 的 .net 实现中,正如您所说,所有对话框实例都存储在应用程序启动时的单个实例中,以避免加载对话框实例 运行 几乎相同的数据。

为避免对话框实例之间的信息混合,您有一些选择:

  • 使用状态管理来存储上下文数据,例如 conversationData,用于对话范围的域信息并在所有对话之间共享数据;
  • 或者您可以使用 stepContext.options 存储对话域信息,当对话保留在堆栈中时,它可以在任何步骤中恢复(在您到达最后一步之前,调用 endDialogreplaceDialog).

这里有一些关于如何存储数据的信息:
https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-storage?view=azure-bot-service-4.0&tabs=javascript

@Arie Rodrigues 让我朝着正确的方向前进,但解决方案并不完全正确。 Bot Framework 实际上似乎确实在使用对话框的单个实例,它由 conversation/dialog 状态控制。所以构造函数中定义的 this.queryData 对象实际上被所有对话框共享。在正确的时机,提示输入从一个用户的对话框传递到另一个用户的对话框。

这可以通过保存这些值的任何方法来解决non-globally。对话状态会是一个很好的状态,但我不需要或不想在对话期间保存值,也不需要在每一步检索和保存状态时搞得一团糟。相反,我使用 step.values 代替 this.queryData。当对话框通过 replaceDialog() 在某个步骤循环时,我的代码实际上依赖于 this.queryData 的某些值,因此我必须确保在该方法的选项中传递这些值。这似乎解决了这个问题,因为我有 4 名测试人员同时使用不同的提示值点击相同的对话框而没有问题。