Bot Framework V4 MS 团队渠道和 GDPR

Bot Framework V4 MS teams channel and GDPR

正在使用 Teams 聊天机器人 (V4/Node) 并且需要解决 GDPR。

简而言之,聊天机器人的用户需要能够导出或删除聊天机器人存储的个人数据。个人数据是与已识别或可识别的自然人相关的任何信息。状态对象中的用户 ID 也是如此。

我读了一篇关于 GDPR and bots 的博客,但这篇博客并未针对 Teams 频道。而且是关于V3

  1. 用户在对话框中提供的个人数据(由我编写)是 简单的部分。我会写一些对话框来显示和删除它们 (就像比尔在他的回答中所做的那样)。
  2. 实际对话中的内容是 Teams 平台的一部分,will\should在 Teams 本身中解决。

我不知道如何解决的一点是机器人实际 运行(机器人状态等)的数据。如果用户需要删除他或她参与某个对话的事实怎么办。这可能存储在某些状态对象中(在我的例子中是在 Blob 存储中)。但是哪些呢?

我会很感激 ideas\guidance 如何解决这个问题。

免责声明:我不是 GDPR 专家,但我相信以下内容就足够了。

从机器人的角度来看,存储的数据在 Teams 频道中是相同的。您拥有通常(在大多数示例中)使用 Blob 存储设置的对话状态和用户状态数据。我对这些项目使用 conversationStateuserState 命名法。

在我的用例中,我在 userState 中存储帐号,在 conversationState 中存储用户 name/email。请注意,机器人还存储了其他一些东西(我相信尤其是 conversationState)围绕对话框的状态和其他机器人特定的东西,这些东西通常毫无意义,但我不知道它们是否会被视为通用数据保护条例。不管怎样,我们都会把这些整个对象都擦掉。

为此,我创建了一个对话框来管理用户配置文件,其中显示存储的关键信息(我专门访问帐号、用户名和电子邮件),然后提示用户是否要删除信息。在 nodejs 中看起来像这样。

const { ConfirmPrompt, ComponentDialog, WaterfallDialog } = require('botbuilder-dialogs');
const { ActivityTypes } = require('botbuilder');

const WATERFALL_DIALOG = 'waterfallDialog';
const CONFIRM_PROMPT = 'confirmPrompt';

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

        this.dialogs.add(new ConfirmPrompt(CONFIRM_PROMPT));
        this.dialogs.add(new WaterfallDialog(WATERFALL_DIALOG, [
            this.showInfoAndPrompt.bind(this),
            this.confirmDelete.bind(this)
        ]));

        this.initialDialogId = WATERFALL_DIALOG;

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

        this.appInsightsClient = appInsightsClient;

    } // End constructor

    async showInfoAndPrompt(step) {
        this.appInsightsClient.trackEvent({name:'manageProfileDialog', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
        this.appInsightsClient.trackMetric({name: 'showInfoAndPrompt', value: 1});

        const userProfile = await this.userDialogStateAccessor.get(step.context, {});
        const conversationData = await this.dialogState.get(step.context, {});

        if (!userProfile.accountNumber & !conversationData.userEmail & !conversationData.userFullName & !conversationData.orderType) {
            this.appInsightsClient.trackEvent({name:'manageProfileDialogEnd', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
            this.appInsightsClient.trackMetric({name: 'confirmDelete', value: 1});

            await step.context.sendActivity(`I don't have any of your information stored.`);
            return await step.endDialog();
        } else {
            var storedData = '';
            if (userProfile.accountNumber) {
                storedData += `  \n**Account Number:** ${userProfile.accountNumber}`;
            }
            if (conversationData.userFullName) {
                storedData += `  \n**Name:** ${conversationData.userFullName}`;
            }
            if (conversationData.userEmail) {
                storedData += `  \n**Email:** ${conversationData.userEmail}`;
            }
            if (conversationData.orderType) {
                storedData += `  \n**Default order type:** ${conversationData.orderType}`;
            }
            await step.context.sendActivity(`Here is the informaiton I have stored: \n ${storedData} \n\n I will forget everything except your account number after the end of this conversation.`);
            await step.context.sendActivity({ type: ActivityTypes.Typing });
            await new Promise(resolve => setTimeout(resolve, process.env.DIALOG_DELAY));
            return await step.prompt(CONFIRM_PROMPT, `I can clear your information if you don't want me to store it or if you want to reneter it. Would you like me to clear your information now?`,['Yes','No']);
        }
    }

    async confirmDelete(step) {
        this.appInsightsClient.trackEvent({name:'manageProfileDialogEnd', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
        if (step.result) {
            const userProfile = await this.userDialogStateAccessor.delete(step.context, {});
            const conversationData = await this.dialogState.delete(step.context, {});
            await step.context.sendActivity(`OK, I have cleared your information.`);
            return await step.endDialog();
        } else {
            await step.context.sendActivity(`OK, I won't clear your information. You can ask again at any time.`);
            this.appInsightsClient.trackMetric({name: 'confirmDelete', value: 1});
            return await step.endDialog();
        }
    }

}

module.exports.ManageProfileDialog = manageProfileDialog;

关于 GDPR,我不确定的一件事是您是否在 运行 机器人的过程中在其他地方存储成绩单或 activity 数据。例如,我将对话记录存储在 CosmosDB 中,其中可能包括姓名和电子邮件地址之类的内容(如果它们是在对话过程中提供的)。我想清除这些信息也没有很好的办法。此外,我将 LUIS 跟踪和其他信息存储在 Application Insights 中,在许多情况下,这些信息包括 activity,其中可能附加了用户名或 ID 等内容。我什至不确定是否可以从 Application Insights 中删除这些痕迹。我不知道这些是否属于 GDPR 的范畴,因为它们是可操作的,但如果这是一个潜在的问题,请注意您在日志记录 and/or 转录应用程序中存储的内容。