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

如何将C#开发的MS Bot对话历史保存至Cosmos DB

Hey there! Let's walk through how to save both your full conversation history and the user's form data (name, email, message) to Cosmos DB with your Bot Framework v3 setup in C#. You already have the Bot Data Store connected to track state, which is a solid foundation—now let's extend that to capture everything you need.

First, Understand the Difference

Your current DocumentDbBotDataStore handles state data (like the dialog stack, user session info), but it doesn't automatically save the actual conversation messages (what the user types and what your Bot replies). To capture those, we'll use the IActivityLogger interface built into Bot Framework.

Step 1: Create a Model for Conversation History

First, define a class to represent each message in your conversation history—this will map directly to a document in Cosmos DB:

public class ConversationHistoryItem
{
    public string Id { get; set; } = Guid.NewGuid().ToString();
    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; } = DateTime.UtcNow;
}

Step 2: Build a Custom Activity Logger

Next, create a logger that implements IActivityLogger to save every message activity to Cosmos DB:

public class DocumentDbActivityLogger : IActivityLogger
{
    private readonly DocumentClient _client;
    private readonly string _databaseId;
    private readonly string _collectionId;

    public DocumentDbActivityLogger(Uri serviceEndpoint, string authKey, string databaseId, string collectionId)
    {
        _client = new DocumentClient(serviceEndpoint, authKey);
        _databaseId = databaseId;
        _collectionId = collectionId;
        // Automatically create database/collection if they don't exist
        InitializeDatabaseAndCollectionAsync().Wait();
    }

    private async Task InitializeDatabaseAndCollectionAsync()
    {
        await _client.CreateDatabaseIfNotExistsAsync(new Database { Id = _databaseId });
        await _client.CreateDocumentCollectionIfNotExistsAsync(
            UriFactory.CreateDatabaseUri(_databaseId),
            new DocumentCollection { Id = _collectionId },
            new RequestOptions { OfferThroughput = 400 });
    }

    public async Task LogAsync(IActivity activity)
    {
        // Only log actual message activities (ignore typing indicators, etc.)
        if (activity.Type == ActivityTypes.Message)
        {
            var historyItem = new ConversationHistoryItem
            {
                ConversationId = activity.Conversation.Id,
                FromId = activity.From.Id,
                FromName = activity.From.Name,
                ToId = activity.Recipient.Id,
                ToName = activity.Recipient.Name,
                Text = activity.Text,
                Timestamp = activity.Timestamp.Value.UtcDateTime
            };

            await _client.CreateDocumentAsync(
                UriFactory.CreateDocumentCollectionUri(_databaseId, _collectionId),
                historyItem);
        }
    }
}

Step 3: Register the Logger in global.asax

Update your Application_Start method to register the new logger alongside your existing Bot Data Store. I've added config settings for database/collection names to make it flexible:

public class WebApiApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
        GlobalConfiguration.Configure(WebApiConfig.Register); 

        // Pull config values from Web.config
        var uri = new Uri(ConfigurationManager.AppSettings["DocumentDbUrl"]); 
        var key = ConfigurationManager.AppSettings["DocumentDbKey"]; 
        var databaseId = ConfigurationManager.AppSettings["BotDatabaseId"];
        var historyCollectionId = ConfigurationManager.AppSettings["ConversationHistoryCollectionId"];

        var store = new DocumentDbBotDataStore(uri, key); 
        Conversation.UpdateContainer( 
            builder => 
            { 
                // Register the activity logger
                builder.RegisterType<DocumentDbActivityLogger>()
                    .As<IActivityLogger>()
                    .WithConstructorArguments(uri, key, databaseId, historyCollectionId)
                    .SingleInstance();

                // Your existing Bot Data Store registration
                builder.Register(c => store) 
                    .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore) 
                    .AsSelf() 
                    .SingleInstance(); 
                builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)) 
                    .As<IBotDataStore<BotData>>() 
                    .AsSelf() 
                    .InstancePerLifetimeScope(); 
            }); 
    } 
}

Don't forget to add these settings to your Web.config:

<appSettings>
    <add key="DocumentDbUrl" value="your-cosmos-db-uri"/>
    <add key="DocumentDbKey" value="your-cosmos-db-primary-key"/>
    <add key="BotDatabaseId" value="BotDatabase"/>
    <add key="ConversationHistoryCollectionId" value="ConversationHistory"/>
    <add key="UserDataCollectionId" value="UserData"/>
</appSettings>

Step 4: Save User Form Data (Name, Email, Message)

Now, to save the structured form data from each dialog, you can add code to your dialog callbacks. Let's use a root dialog as an example, which calls your NameDialog and then saves the result:

First, create a model for user form data:

public class UserFormData
{
    public string Id { get; set; }
    public string ConversationId { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Message { get; set; }
    public DateTime SubmittedAt { get; set; } = DateTime.UtcNow;
}

Then, update your root dialog to save the data after each step:

[Serializable]
public class RootDialog : IDialog<object>
{
    private readonly DocumentClient _documentClient;
    private readonly string _databaseId;
    private readonly string _userDataCollectionId;
    private UserFormData _userFormData = new UserFormData();

    public RootDialog(Uri serviceEndpoint, string authKey, string databaseId, string userDataCollectionId)
    {
        _documentClient = new DocumentClient(serviceEndpoint, authKey);
        _databaseId = databaseId;
        _userDataCollectionId = userDataCollectionId;
    }

    public async Task StartAsync(IDialogContext context)
    {
        _userFormData.Id = context.Activity.From.Id;
        _userFormData.ConversationId = context.Activity.Conversation.Id;
        context.Call(new NameDialog(), NameReceivedAsync);
    }

    private async Task NameReceivedAsync(IDialogContext context, IAwaitable<string> result)
    {
        _userFormData.Name = await result;
        // Ask for email next
        await context.PostAsync($"Great, {_userFormData.Name}! Could you share your email address?");
        context.Call(new EmailDialog(), EmailReceivedAsync);
    }

    private async Task EmailReceivedAsync(IDialogContext context, IAwaitable<string> result)
    {
        _userFormData.Email = await result;
        // Ask for message
        await context.PostAsync("Perfect! Now feel free to leave your message.");
        context.Call(new MessageDialog(), MessageReceivedAsync);
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<string> result)
    {
        _userFormData.Message = await result;
        // Save the full form data to Cosmos DB
        await _documentClient.UpsertDocumentAsync(
            UriFactory.CreateDocumentCollectionUri(_databaseId, _userDataCollectionId),
            _userFormData);

        await context.PostAsync("Thanks for your submission! We'll get back to you soon.");
        context.Done(_userFormData);
    }
}

Key Notes

  • Conversation History: The IActivityLogger will automatically log every message (user input and Bot replies) to your Cosmos DB collection, so you have a full record of the interaction.
  • Form Data: By saving the structured data in dialog callbacks, you get clean, queryable records of each user's submission—perfect for follow-up with leads.
  • Cosmos DB Setup: The logger and dialog code will create databases/collections automatically if they don't exist, so you don't have to set them up manually in the Azure portal.

That should cover everything you need to save both conversation history and user form data. Let me know if you hit any snags with the code!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:45:28