Botframework-solutions: [TypeScript] "SkillMiddleware" 类应该接受三个参数而不是两个。

创建于 2019-10-17  ·  13评论  ·  资料来源: microsoft/botframework-solutions

什么项目受到影响?

Microsoft botframework V4,自定义技能。

这是什么语言?

打字稿

发生什么了?

技能无法通过 SkillContext 接收到虚拟助手传递过来的信息

重现此问题的步骤是什么?

第 1 步:使用“ https://botbuilder.myget.org/feed/aitemplates/package/npm/botbuilder-solutions ”生成 VA
第 2 步:使用技能生成器命令生成技能
https://botbuilder.myget.org/feed/aitemplates/package/npm/botbuilder-skills
第 3 步:添加带有 slot 参数的技能清单文件
第 4 步:使用 slot 参数值设置 SkillContext。
第 5 步:尝试访问 Skill 处的 SkillContext 值。

你期待发生什么?

如果“slot”属性匹配,则技能应该可以访问在 SkillContext 中设置的对象。
但是技能是无法获取到SkillContext的。
“SkillMiddleware”应该支持skillContextAccessor 并用slot 参数填充SkillContext。

你能分享任何日志、错误输出等吗?

在做了一些工作后发现 CustomSkillAdapter 填充了“skillContextAccessor”值。

当前代码:
导出类 CustomSkillAdapter 扩展 SkillHttpBotAdapter {
构造函数(
遥测客户端:遥测客户端,
对话状态:对话状态,
SkillContextAccessor:StatePropertyAccessor,
dialogStateAccessor:StatePropertyAccessor,
...
){
超级(遥测客户端);
[...]
this.use(new SkillMiddleware(conversationState, dialogStateAccessor));
[...]
}
}

根据此“ https://microsoft.github.io/botframework-solutions/howto/skills/skillenablingav4bot/ ”的预期代码

导出类 CustomSkillAdapter 扩展 SkillHttpBotAdapter {
构造函数(
遥测客户端:遥测客户端,
对话状态:对话状态,
SkillContextAccessor:StatePropertyAccessor,
dialogStateAccessor:StatePropertyAccessor,
...
){
超级(遥测客户端);
[...]
this.use(new SkillMiddleware(conversationState, SkillContextAccessor, dialogStateAccessor));
[...]
}
}

谢谢,
爱德华

最有用的评论

听到这个真棒! 很高兴能帮到你😊

所有13条评论

@Edwardfelix08 ,对于答案的延迟表示歉意。
我们查看了将 V4 Bot 作为技能启用

按照您的重现步骤,我们了解到您有一些关于您的虚拟助手的信息,您将这些信息保存在虚拟助手的SkillContext中,当技能收到消息时,您希望从SkillContext这样对吗?

嗨@dfavretto。
是的,它是正确的。

@Edwardfelix08 ,我们一直在测试这个场景,并创建了一个关于如何使这个工作的指南。 考虑到示例助手尚未准备好在这种情况下开箱即用,因此我们提供了三个步骤来完成此操作所需的一些小修改:

  1. 【虚拟助手】在SkillContext中保存信息
  2. [botbuilder-skills] SkillContext信息过滤保存到Activity的SemanticAction
  3. [Skill] 使用 Activity 的 SemanticAction 信息填充 SkillState

【虚拟助手】在SkillContext中保存信息

首先,您必须将所需信息保存在技能上下文中,以便以后检索。 这必须将您的消息转发到技能

  1. mainDialog 中,您必须使用SkillContextAccessor来检索与您的上下文对应的SkillContext 。 这可以通过以下几行来完成:

    const sc: SkillContext = await this.skillContextAccessor.get(dc.context, new SkillContext());
    const skillContext: SkillContext = Object.assign(new SkillContext(), sc);
    
  2. 现在您已将SkillContext保存在变量中,您可以获取或设置任何值。 不要忘记SkillContext中的值是一个键值对,因此如果您不想覆盖您的值,请确保您的键是唯一的。 现在您可以保存任何内容,即位置值:

    skillContext.setObj('location', locationObj);
    
  3. 最后,设置使用skillContextAccessorSkillContext所以这个信息被保存在您的userState:

    await this.skillContextAccessor.set(dc.context, skillContext);
    

[botbuilder-skills] SkillContext信息过滤保存到Activity的SemanticAction

保存在SkillContext的信息后,它的优良将消息转发到技能,但在此之前的消息到达技能,在SkillDialog里面botbuilder-skills期间将处理SkillContext onBeginDialog方法,这是唯一的一次是当执行首次调用该技能。

该方法将自动执行以下步骤:

  1. 以与我们在第一步中所做的相同的方式获取
  2. 使用SkillManifest来获取插槽,然后将此信息发送到matchSkillContextToSlots方法
  3. matchSkillContextToSlots方法中,会评估SkillContext以查找与Slots键匹配的任何键。 每个匹配的键都用于将该SkillContext的键值对保存到SemanticAction 中
  4. 带有SemanticAction (从与Slots匹配的SkillContext检索的信息)的Activity被转发到相应的 Skill。

[Skill] 使用 Activity 的 SemanticAction 信息填充 SkillState

第三步也是最后一步是使用从虚拟助手发送的信息填充技能状态,步骤如下:

  1. 在 Skill 中的mainDialog 中,有一个名为populateStateFromSemanticAction的方法。 这个方法被评论了,有点过时了。 我们创建了 [PR #2669](已经合并)来更新这个方法。
  2. 确保更新您的SkillState ,添加必要的属性。
  3. 根据需要修改populateStateFromSemanticAction以从SemanticAction获取所有属性并将它们保存在SkillState 中。 现在,该方法正在寻找一个“位置”属性作为示例,这可以更改为您想要访问的任何属性:
    typescript if (semanticAction !== undefined && Object.keys(semanticAction.entities) .find((key: string) => key === "location"))
  4. 现在,您可以从任何地方访问您的 Skill 中的SkillState

希望这些步骤有用😊

嘿@lauren-mills 你能检查一下这种方法是否正确吗? 😁

谢谢@dfavretto会尝试看看它是否有效。

@dfavretto我已经尝试了上述解决方案,但没有奏效。 我仍然看到“CustomSkillAdapter”类中没有更新或代码更改

@ Edwardfelix08你能不能跟我们分享什么是你所面临的问题? 此外,这里有一些指导性问题,可以更好地确定我们如何为您提供帮助:

  1. 您能否设法将信息保存在虚拟助手SkillContext中?
  2. 您的SkillManifest更新了必要的插槽
  3. Slot的键是否与用于保存SkillContext的信息的键匹配?
  4. 你的Skill里面的方法populateStateFromSemanticAction是不是没有注释掉?

此外,关于CustomSkillAdapter ,对于这种情况,无需更改该类。

@dfavretto
下面是使用的代码

  1. 你能设法在你的虚拟助手的 SkillContext 中保存信息吗?

    VA 主对话框。
    const sk: SkillContext = await this.skillContextAccessor.get(dc.context, new SkillContext());
    const SkillContext: SkillContext = Object.assign(new SkillContext(), sk);
    SkillContext.setObj('userState', userProfile);
    等待 this.skillContextAccessor.set(dc.context, SkillContext);

  2. 您的技能清单是否更新了必要的插槽?
    VA 中的 Skill.json
    “插槽”:[{
    "name": "userState",
    “类型”:[“IUserState”]
    }],

    技能清单
    “插槽”:[{
    "name": "userState",
    “类型”:[“IUserState”]
    }],

  3. Slot 的键是否与用于在 SkillContext 中保存信息的键匹配?
    populateStateFromSemanticAction 中的代码
    if (skillContext.ContainsKey('userState')) {
    ......
    ......
    }

  4. 您的 Skill 中的 populateStateFromSemanticAction 方法是否未注释?
    是的,我在 botSkill 中取消了代码表 populateStateFromSemanticAction 方法的注释。

我们应该使用的机器人框架版本是什么?
我们正在使用以下软件包。

    "botbuilder": "^4.5.3",
    "botbuilder-ai": "^4.5.3",
    "botbuilder-applicationinsights": "^4.5.3",
    "botbuilder-azure": "^4.5.3",
    "botbuilder-core": "^4.5.3",
    "botbuilder-dialogs": "^4.5.3",
    "botbuilder-skills": "^4.4.6",
    "botbuilder-solutions": "^4.4.6",
    "botframework-config": "^4.5.3",
    "botframework-connector": "^4.5.3",
    "botframework-schema": "^4.5.3",

@Edwardfelix08 ,我看到您正试图从skillContext方法中的populateStateFromSemanticAction获取值userState populateStateFromSemanticAction 。 我想指出的是,您在Virtual Assistant 中填写的skillContext不再是您在Skill 中填写的那个。

为了利用这种情况,来自虚拟助手的skillContext被复制到活动的semanticAction 。 Activity 是从Virtual Assistant发送到Skill的唯一元素,然后可以对其进行评估以获取此信息。

以下是populateStateFromSemanticAction的示例,它将从活动的semanticAction获取您的userState值:

protected async populateStateFromSemanticAction(context: TurnContext): Promise<void> {
        // Example of populating local state with data passed through semanticAction out of Activity
        const activity: Activity = context.activity;
        const semanticAction: SemanticAction | undefined = activity.semanticAction;
        if (semanticAction !== undefined && Object.keys(semanticAction.entities).find((key: string) => key === "userState")) {

            const userState: any = semanticAction.entities["userState"];
            const userStateObj = userState.properties["userState"];
            const state: SkillState = await this.stateAccessor.get(context, new SkillState());
            state.userState = userStateObj !== undefined ? userStateObj : userState;
            await this.stateAccessor.set(context, state);
        }
    }

@dfavretto抱歉回复晚了。
我已经尝试过同样的方法,但仍然无法获得价值。
activity.semanticAction值未定义。

@Edwardfelix08 ,我们进行了一些验证,但无法重现您的场景,因为SemanticAction始终默认为某个值,因此不应未定义它。

image

只是为了确认,在您的虚拟助手中,您是否按以下方式调用您的技能?

image

@darrenj - 我们是否应该添加支持团队,以便他们可以从另一个角度帮助解决这个问题? 由于它似乎与 TypeScript 实现本身没有任何关系,但它是在此处实现的特定场景。

@dfavretto
抱歉回复晚了。 我们能够让这个工作,谢谢你。 我们指的是框架的旧版本,一旦更新并与您的代码交叉检查,并且“populateStateFromSemanticAction”存在差异,将代码更新为建议的代码,它开始像魅力一样工作。

感谢您在这方面帮助我们。

听到这个真棒! 很高兴能帮到你😊

此页面是否有帮助?
0 / 5 - 0 等级