【问题标题】:Propagate states to dialogs in MS BotFramework将状态传播到 MS BotFramework 中的对话框
【发布时间】:2020-11-27 03:39:50
【问题描述】:

在 Microsoft BotFramework v4 中,您通常将状态(UserStateConversationStatePrivateConversationState)传播到对话框通过将它们作为参数传递给它的构造函数。

这边:

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        IStorage storage = new MemoryStorage(); // For testing only !
        services.AddSingleton(new UserState(storage));
        services.AddSingleton(new ConversationState(storage));
        // ...
        services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
        services.AddSingleton<GoviiBaseDialog>(x => new RootDialog(
            x.GetRequiredService<UserState>(),
            x.GetRequiredService<ConversationState>()
        );
        services.AddTransient<IBot, Bot<RootDialog>>();
    }
}

Bot.cs

public class Bot<T> : ActivityHandler where T : Dialog
{
    T _dialog;
    BotState _userState, _conversationState;

    public Bot(T dialog, UserState userState, ConversationState conversationState,)
    {
        _userState = userState;
        _conversationState = conversationState;
        _dialog = dialog;
    }

    public override async Task OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default)
    {
        await base.OnTurnAsync(context, cancellationToken);
        await _userState.SaveChangesAsync(context);
        await _conversationState.SaveChangesAsync(context);
    }

    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> context, CancellationToken cancellationToken)
    {
        await _dialog.RunAsync(context, _conversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
    }
}

RootDialog.cs

public class RootDialog : ComponentDialog
{
    UserState _userState;
    ConversationState _conversationState;

    public RootDialog(UserState uState, ConversationState cState) : base("id")
    {
        _userState = uState;
        _conversationState = cState;

        // Add some dialogs and pass states as parameters
        AddDialog(new CustomDialog_1(uState, cState));
        AddDialog(new CustomDialog_2(uState, cState));
        // ...
        AddDialog(new CustomDialog_N(uState, cState));
    }
}

现在让我们假设那些 CustomDialogs 再次使用其他一些需要访问状态的 CustomDialogs。状态必须一次又一次地作为参数传递给构造函数。

问题是:是否有另一种方法来访问状态以避免一次又一次地作为参数传递?

【问题讨论】:

  • 这可能是一个 XY 问题,所以你能解释一下为什么你想将机器人状态传递给你的对话框吗?我认为将特定的机器人状态访问器传递给对话框而不是机器人状态本身是有意义的,但我知道这不会改变您问题的性质。
  • 我认为传递 BotState 而不是访问器的原因是因为我还是 BF v4 的新手,对它不是很熟悉。我将在接下来的几天重构我的代码并更新问题。但是,是的,这不会改变我的问题的性质。
  • 我还发现this说通过状态是可以的。那么传递整个状态而不是访问器有缺点吗?
  • 没有客观的缺点,只有潜在的主观缺点取决于你喜欢如何设计你的机器人。但这并不重要。一旦出现在对话框中,我仍然需要知道您想要使用状态来跟踪什么。
  • 我使用状态来跟踪识别的实体和用户输入的位置等。我需要将这些东西保存在某个地方以供以后处理。

标签: botframework


【解决方案1】:

如何在对话框中访问状态取决于状态的范围。

如果状态的范围是对话框,那么您应该使用对话框状态。更具体地说,您应该使用对话框相关对话框实例的 state 属性。在最近的回答中对此进行了一些讨论:Dialogs keep their variable values in new conversation in MS BotFramework v4

(阅读对话框实例here)。您希望对话框跟踪的任何内容都应放入相关对话框实例的状态对象中,查看如何执行此操作的示例的最佳位置是 SDK 源代码本身。例如,您可以看到 waterfall dialog 如何跟踪其自定义值以及它所处的步骤:

// Update persisted step index
var state = dc.ActiveDialog.State;
state[StepIndex] = index;

如果状态的范围大于一个对话框的一个实例,您可以将机器人状态对象传递给您的对话框,就像您一直在做的那样。如果您将对话框置于依赖注入中,这可能会变得更容易,以便您的机器人状态可以自动注入到它们的构造函数中。如果您的对话框访问在对话框之外也使用的状态属性,那么为对话框提供状态属性访问器而不是状态本身是有意义的,从而减少冗余并分离关注点。

如果您想确保在任何有转弯上下文的地方都可以访问您的机器人状态,那么实际上有一种内置方法可以自动将您的机器人状态添加到每次转弯的转弯状态。这是UseBotState 扩展方法:

adapter.UseBotState(userState, conversationState);

然后您可以像这样检索状态:

var userState = turnContext.TurnState.Get<UserState>();
var conversationState = turnContext.TurnState.Get<ConversationState>();

【讨论】:

    猜你喜欢
    • 2021-02-24
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 2016-02-17
    相关资源
    最近更新 更多