【问题标题】:Retrieving Chat Bot conversation data in Azure在 Azure 中检索聊天机器人对话数据
【发布时间】:2018-09-27 08:03:24
【问题描述】:

首先简要介绍一下背景:Bot 框架将对话数据存储在 Azure 表/Cosmos DB(在我的例子中为 Azure 表)的存储中。对于每个对话,都会在 Azure 表中创建一个条目,其中包含时间戳、用户 ID、对话消息和其他详细信息。

我正在尝试使用 C# 中的自定义代码从 Azure 表存储中检索对话详细信息,如下所示。

Microsoft.WindowsAzure.Storage.CloudStorageAccount storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse(connectionString);
Microsoft.WindowsAzure.Storage.Table.CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("botdata");
TableQuery<DynamicTableEntity> projectionQuery = new TableQuery<DynamicTableEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "directline:user"));
var dataRow = table.ExecuteQuery(projectionQuery).Where(q => q.RowKey == "souvik").FirstOrDefault();
var conversation = Encoding.UTF8.GetString(dataRow.Properties["Data"].BinaryValue);
Console.WriteLine(conversation);

当我检索以二进制格式存储在 Azure 表存储中的数据时,我收到了一些乱码,类似于 \u001f�\b\0\0\0\0\0\u0004\0��\u0005\0C���\u0002\0\0\0。 Azure 存储资源管理器中的数据如下所示-

我已经对每个对话进行了尝试和测试,我发现每次我输入数据并将数据发送到机器人并获得响应时,新记录都会以正确的时间戳添加到 Azure 表中。这是检索数据的正确方法还是我做错了什么?让我知道是否可以添加更多详细信息。

更新 1:

我已经提到了这个-How to retrieve Saved Conversation Data in Azure (Tablelogger),但我的问题没有得到太多帮助。

Update-2

根据下面 Fei 的回答,我注意到当我使用 PartitionKey 作为 directline:userdirectline:conversation 时,我得到的反序列化值为 {} 但如果我使用 directline:private,我会得到这样的结果-

{"ResumptionContext":{"locale":"en-US","isTrustedServiceUrl":true}, "DialogState":"H4sIAAAAAAAEAO1a328iVRRmYGYKbTfbdZtNzLq7GM26DWS2FGi7MVUptUrcVi1VYxrSvcCFjh1mmrl3quyjb74YEx+N/hW+uzFxE/8KX9fsH6HnzAxQ7A8GhHpZoemBDs O953znO+d+99KQFAqF/oIHPuNjNgzm8y29YlvMqnFt3YJfRzeq1E7GP6U20y1zLa2llrQlLZOM5x2DOzZdM6nDbWIk4x85ZUOvfECbu9YhhTtT5Vp6NbtMqunlDE1nFZznF+nM8bWCyaltEoNpm 3oZpvKeHqX29s6+f0MnhlVnJz7nXdkl7DAZH1kMpVIEglAYJ5VDtWaTBmXKl0TnLCLLT8LFJuO0oeUtw6AVDjMx7T1qUluvaA91xs+P5lT0hU0cW8DwRzo4CYhOESAlhv6YIMaJIrdsWjAhJ2aF rjd3m0e06Ni2VSecJj4sfwGp2KE1alN4Gxl+ObPgbwzJEpPRKGBk/HPCkmFUoLqvA4ZM2Wf6YxrdP/aGBaR/DdpfhMVur4TUiUZjqs8i5I8iXwpxsUPLeEfkjs/ST0ydN7sGfN/CWV16T4GRFN /FWBTMFDodkvxL8pOxz4YbZwzNNJgrYRlj7gUOMlTeIJzE3DsQ0NkcY7RRNprbEKcUkqLqDNz0NPBqiPBskgrksikgSphsdXak6kFy6YXzXAHzW1DgPoPFWWzcYoibEutLGwlcLHKD2Icyai JZvpyV9odwH1x4tCQaask9v5t4oSXjDVaxbEMvd0bPaIv403vw8soKyVayy6kH6QxdXH3gpcTtYVfRzGFzxkbmdrRraF7CS1dbPfw6cnFuAmpvUGWbMi6blFZlOTBYO/CZ/yNYiYD4bAOcbcb OIxefw/60n0+j9Jg6JoZD9/dD0bAvRhSk+tNxp3T32HnLNCkubFphizJG6jQHu4lj0Bv/9KJ95xC7/An2B8ZVVPYLhOtAhXIDWf4nFMpz/zBHwqZ+rf3Ws5NvXW/JqJdDfeyWth3DcMtCPNHR 0VDz/axbopJxlK3Y3Zdw0FMqJ3adcrVB+YFVjcg9t3s/nUOUi3BK7B445iHg7HhiddSLzH1/AhCH/sGKtgVbLmoXzJp13hb2JppX0NxCCt3oZ50QlUKC9LPB2fb7ENgmCAgDE/M2mjto4ti2b 7ba9quTJndxK+isB1jWojcuXJNV251K/qaHr55HuRr3vhi5lG3ja1jEtwYgMZJVxsOurpOvWN4gjLmvinrdJOjWdPvV0rQ3JG755/wz8Zxdh6BNziR8hCLRK74jeNNeSX0d50GeuzlX745BztU 3wMmvI604DmxKqrpZ19AP5lrwIOjBqa/K+o+4VIojbPcCn9D6X8gNMNM5zWSQBnSGYwVAEfzqyhhMeQrePJ4nGYbLUTc1C+o9SMQz5eJE9JsJwZrtaL+LGYxCYwLRcPk50pYSiO54gj4t3W4piY WJ1h2azOuIDpRsL5B+PalPfu5PnwgSQVvKJFBHo5hGOaMmu3WD9mKlTb0P8fw4kRjDbeFBkxtQfSxCjr5XJ+pjoj4uQX0I0piCCxUFt54CbYkHP876tkcQ+QOim4mHlnXkXRH2sDSFZglNGsxsBs zax2YOYur4lQrql+kYhoJr8njKisHpcLfbryJpHBlU83DUdiyLe4FnBs1SFs0ymhVUPKmW3l8Vq546khkZJVqNoO6dzkE3rdAqNbncR9LCLfyldEtsvglmxseWNc2KpznXBIxbfQuc+k66QJTET8 RxztrbXrm8F1B5nH7Fe65zOfyvIVI26KnTnAX1bXDrj//erZFi72/Qs62CfWdsm2OntrEOAtYO3BnC+pFWWmWzDmbe93wHKlE/plU3wWr+Xy896kZwz9R33UxcxL6zvBwdDQNvRBbUTdxkhMVxXRSKesU28zeNVmRMBTMAAA=="}

感觉上面的JSON中的DialogState数据需要解密?

对于我输入的每条消息和收到的响应,Azure 表中都会插入三个记录,每个记录都有不同的 PartitionKey,它们是 - directline:privatedirectline:userdirectline:conversation

【问题讨论】:

  • 删除一个问题,因为大量的downvoting,然后立即再次询问在这个网站上是不受欢迎的。
  • @FraukeNonnenmacher 我知道,我只收到了反对票,没有人告诉我做错了什么。看到这个问题,和我删除的一样。根据 Stackoverflow 指南,您是否发现有任何问题?
  • 你没有提供一个最小的、完整的和可验证的例子,你甚至没有给出你的预期结果(尽管你被要求提供它)
  • @FraukeNonnenmacher 我现在已经编辑并更新了这个问题。如果一开始有人指出,会这样做。谢谢

标签: c# azure-storage botframework azure-table-storage azure-cognitive-services


【解决方案1】:

Azure Table Storage 中的二进制数据存储为Base64 encoded string。您需要做的是先将此字符串转换为字节,然后从这些字节中获取字符串。

类似:

var conversation = Encoding.UTF8.GetString(Convert.FromBase64String(dataRow.Properties["Data"].BinaryValue));

【讨论】:

  • 首先Convert.FromBase64String 将只接受string 值。在这里,我将值作为Byte 数组存储在BinaryValue 中。我也试过这个-byte[] data = Convert.FromBase64String("H4sIAAAAAAAEAKuuBQBDv6ajAgAAAA=="); string decodedString = Encoding.UTF8.GetString(data); 但这也没有用
  • 我的错....我没有运行代码。对于那个很抱歉!让我实际将一些二进制数据保存在一个表中并检索它。
  • 所以我尝试了上面的代码,我能够正确地恢复字符串。你是正确的 EntityProperty.BinaryValue 返回一个字节数组。我尝试将字节数组转换为字符串并且它有效。我还从存储资源管理器中复制了 base64 编码字符串并运行了上面的代码,这也有效。您能否编辑您的问题并包括如何在 Azure 表中插入数据?这可能会提供一个线索,说明为什么反序列化在您的情况下不起作用。
  • 这是棘手的部分。我在 Azure 上创建了内部使用 bot 框架的 Web App bot。查看代码,我找不到任何痕迹,否则我可能会发现问题。不知道有什么办法可以查到吗?
  • 从元数据来看,它看起来像一个 JSON 属性-[JsonProperty(PropertyName = "data")] public object Data { get; set; }
【解决方案2】:

如果您想使用WindowsAzure.Storage client library从表存储中检索实体并从Data属性中提取数据,可以参考以下代码。

CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName={your_account_name};AccountKey={your_account_key};EndpointSuffix=core.windows.net");

CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

CloudTable table = tableClient.GetTableReference("botdata");

TableQuery<MessageEntity> query = new TableQuery<MessageEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "emulator:user"));

foreach (MessageEntity entity in table.ExecuteQuery(query))
{
    string mydata = "";
    using (var msi = new MemoryStream(entity.Data))
    using (var mso = new MemoryStream())
    {
        using (var gs = new GZipStream(msi, CompressionMode.Decompress))
        {
            gs.CopyTo(mso);
        }
        mydata = Encoding.UTF8.GetString(mso.ToArray());
    }

    object data = JsonConvert.DeserializeObject(mydata);

    //.....
}

MessageEntity:

public class MessageEntity : TableEntity
{
    public MessageEntity(string pk, string rk)
    {
        this.PartitionKey = pk;
        this.RowKey = rk;
    }

    public MessageEntity() { }

    public string BotId { get; set; }
    public string ChannelId { get; set; }
    public string ConversationId { get; set; }
    public byte[] Data { get; set; }
    public string UserId { get; set; }
}

测试结果:

注意:

  • source code 中,您可以在BotDataEntity 类中找到定义为byte[]Data 属性。
  • 在 Jason Sowers 的回复中,他分享了有关Data 属性的序列化和反序列化的信息。

【讨论】:

  • 谢谢飞。我已经用 Update-2 下标记的更多详细信息更新了问题。你能看一下吗?
【解决方案3】:

我假设您使用的是 botbuilder-azure 包

不要在对话框中执行此操作,因为您只能访问 context 对象中的相同数据。它恰好是我写代码的地方

其实就这么简单:

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var activity = await result as Activity;

    IBotDataStore<BotData> table = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);

//use the type of state data you need
    var userData = await table.LoadAsync(Address.FromActivity(activity), BotStoreType.BotUserData, CancellationToken.None );
    var privateConvoData = await table.LoadAsync(Address.FromActivity(activity), BotStoreType.BotPrivateConversationData, CancellationToken.None );
    var convoData = await table.LoadAsync(Address.FromActivity(activity), BotStoreType.BotConversationData, CancellationToken.None);

//in this case I am just replying with the data, but do what you need with it here
    var reply = activity.CreateReply(userData.Data.ToString());
    var reply2 = activity.CreateReply(privateConvoData.Data.ToString());
    var reply3 = activity.CreateReply(convoData.Data.ToString());

    await context.PostAsync(reply);
    await context.PostAsync(reply2);
    await context.PostAsync(reply3);

    context.Wait(MessageReceivedAsync);
}

如果您注意到botbuilder-azure 的源代码中有一个serialize 和反序列化方法。发生这种情况的地方:

private byte[] Serialize(object data)
{
    using (var cmpStream = new MemoryStream())
    using (var stream = new GZipStream(cmpStream, CompressionMode.Compress))
    using (var streamWriter = new StreamWriter(stream))
    {
        var serializedJSon = JsonConvert.SerializeObject(data, serializationSettings);
        streamWriter.Write(serializedJSon);
        streamWriter.Close();
        stream.Close();
        return cmpStream.ToArray();
    }
}

因此,您需要的数据被压缩成您所看到的“乱码”。当通过LoadAsync 方法访问数据时,它也会像这里一样被解压:

private object Deserialize(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    using (var gz = new GZipStream(stream, CompressionMode.Decompress))
    using (var streamReader = new StreamReader(gz))
    {
        return JsonConvert.DeserializeObject(streamReader.ReadToEnd());
    }
}

return statement return new BotData(entity.ETag, entity.GetData());LoadAsync 方法中调用反序列化,它的GetData() 方法如下:

internal ObjectT GetData<ObjectT>()
{
    return ((JObject)Deserialize(this.Data)).ToObject<ObjectT>();
}

【讨论】:

  • 感谢杰森提供的详细信息。我使用 Microsoft QnA Maker API 作为服务,并在 Azure 上创建了 Web App Bot,无需自定义编码。我假设您详细介绍的代码在我创建机器人时已经被抽象化了。现在,我在另一个应用程序中需要记录在表中的数据。那么如何检索已经存储的数据(对话)?
  • 另外,我尝试解压缩您在上面代码中提到的字节。做streamReader.ReadToEnd()时我没有得到任何数据。
  • 要使用此方法,您的其他项目必须包含botbuilder-azure NuGet 包,并且您必须以某种方式将Activity 对象传递给您的其他应用程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-07
  • 2016-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多