You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ITranscriptLogger与TranscriptMiddleware在Cosmos DB存聊天记录遇阻求助

解决使用ITranscriptLogger和TranscriptMiddleware将聊天历史存储到Cosmos DB的问题

我帮你梳理了代码里的几个关键问题,并且给出了修正后的实现方案,应该能解决聊天记录无法存储、容器不生成的问题:

关键问题分析

  1. 文档ID格式不合法:activity.Id可能包含Cosmos DB禁止的字符(比如/、\、?),直接用作文档ID会导致存储失败
  2. 容器自动创建未启用:默认配置下CosmosDbStorage不会自动创建容器,需要显式开启
  3. 中间件添加方式有误:之前的代码在AddBot中添加中间件,可能没有正确注入到Adapter的处理管道
  4. 异常未捕获:没有错误日志,很难排查存储失败的具体原因

修正后的完整代码

CosmosTranscriptStore.cs

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Schema;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class CosmosTranscriptStore : ITranscriptLogger
{
    private readonly CosmosDbStorage _storage;

    public CosmosTranscriptStore(CosmosDbStorageOptions config)
    {
        // 开启自动创建容器(如果不存在)
        config.CreateIfNotExists = true;
        // 设置分区键,优化查询性能
        config.PartitionKeyPath = "/ConversationId";
        _storage = new CosmosDbStorage(config);
    }

    public async Task LogActivityAsync(IActivity activity)
    {
        if (activity == null)
        {
            throw new ArgumentNullException(nameof(activity));
        }

        // 仅处理消息类型的活动(如需记录其他活动可移除此判断)
        var messageActivity = activity.AsMessageActivity();
        if (messageActivity == null)
        {
            return;
        }

        // 生成符合Cosmos DB要求的文档ID:替换非法字符
        var safeDocumentId = activity.Id.Replace("/", "_")
                                         .Replace("\\", "_")
                                         .Replace("?", "_");

        // 构造强类型的聊天记录实体
        var transcriptEntry = new TranscriptEntry
        {
            Id = safeDocumentId,
            ConversationId = activity.Conversation.Id,
            FromId = activity.From.Id,
            FromName = activity.From.Name,
            ToId = activity.Recipient.Id,
            ToName = activity.Recipient.Name,
            Text = messageActivity.Text,
            Timestamp = activity.Timestamp ?? DateTime.UtcNow,
            ChannelId = activity.ChannelId
        };

        var documents = new Dictionary<string, object>
        {
            { safeDocumentId, transcriptEntry }
        };

        try
        {
            await _storage.WriteAsync(documents, CancellationToken.None);
        }
        catch (Exception ex)
        {
            // 记录异常日志(可替换为你的日志框架)
            Console.WriteLine($"存储聊天记录失败: {ex.Message}");
            throw; // 如需静默处理可移除throw
        }
    }

    // 强类型实体,便于后续查询和维护
    public class TranscriptEntry
    {
        public string Id { get; set; }
        public string ConversationId { get; set; }
        public string FromId { get; set; }
        public string FromName { get; set; }
        public string ToId { get; set; }
        public string ToName { get; set; }
        public string Text { get; set; }
        public DateTime Timestamp { get; set; }
        public string ChannelId { get; set; }
    }
}

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    private const string CosmosServiceEndpoint = "MyCosmosServiceEndpoint";
    private const string CosmosDBKey = "MyCosmosDBKey";
    private const string CosmosDBDatabaseName = "MyCosmosDBDatabaseName";
    private const string CosmosDBCollectionName = "Transcript-storage";

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        // 配置Cosmos DB存储选项
        var cosmosOptions = new CosmosDbStorageOptions
        {
            AuthKey = CosmosDBKey,
            CollectionId = CosmosDBCollectionName,
            CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
            DatabaseId = CosmosDBDatabaseName,
            CreateIfNotExists = true
        };

        // 注册自定义转录日志存储
        services.AddSingleton<ITranscriptLogger>(new CosmosTranscriptStore(cosmosOptions));

        // 配置Bot Adapter并注入转录中间件
        services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(sp =>
        {
            var adapter = new AdapterWithErrorHandler(sp.GetService<IConfiguration>());
            var transcriptLogger = sp.GetService<ITranscriptLogger>();
            // 将转录中间件添加到Adapter管道
            adapter.Use(new TranscriptLoggerMiddleware(transcriptLogger));
            return adapter;
        });

        // 注册Bot相关服务
        services.AddSingleton<MainDialog>();
        services.AddTransient<IBot, WelcomeBot<MainDialog>>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseMvc();
    }
}

验证步骤

  1. 确保你使用的是Cosmos DB账户的Primary Key(不是资源令牌),并且Cosmos DB的网络访问设置允许你的Bot服务IP访问
  2. 运行Bot并发送几条测试消息,等待1-2分钟(Cosmos DB写入有轻微延迟)
  3. 登录Azure Portal,进入你的Cosmos DB账户,查看指定的数据库和容器,应该能看到存储的聊天记录文档
  4. 如果仍然没有数据,检查Bot的日志(比如Application Insights),查看是否有存储相关的异常信息

内容的提问来源于stack exchange,提问作者Lanna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:48:43