.NET+AI | MEAI| 线程记忆存储(3)

AgentThread 消息存储机制:理解 ChatMessageStore

一句话简介

AgentThread 通过 ChatMessageStore 抽象基类管理对话消息,默认使用 InMemoryChatMessageStore 实现内存存储,支持扩展为数据库、Redis 等持久化方案。


🎯 核心价值

  • 统一抽象ChatMessageStore 定义标准接口,支持多种存储实现
  • 开箱即用InMemoryChatMessageStore 提供高性能默认实现
  • 灵活扩展:可自定义存储方案(数据库/Redis/文件)

📝 核心概念

AgentThread 内部使用 ChatMessageStore 管理所有对话消息:

核心特性:

  • 🔧 抽象设计ChatMessageStore 定义核心接口
  • 💾 内存实现InMemoryChatMessageStore 使用 List<ChatMessage> 存储
  • 🔄 集合接口 :实现 IList<ChatMessage>,支持直接操作

💻 访问消息存储

获取 ChatMessageStore

通过 GetService<T>() 方法获取 Thread 内部的消息存储:

csharp 复制代码
// 创建 Thread
AgentThread thread = agent.GetNewThread();

// 方式1: 通过抽象接口访问
ChatMessageStore? messageStore = thread.GetService<ChatMessageStore>();

// 方式2: 通过集合接口访问(直接操作)
IList<ChatMessage>? messageList = thread.GetService<IList<ChatMessage>>();

查看存储的消息

csharp 复制代码
// 获取所有历史消息
var messages = await messageStore.GetMessagesAsync();
var messageList = messages.ToList();

Console.WriteLine($"📊 消息统计:");
new 
{
    总消息数 = messageList.Count,
    User消息 = messageList.Count(m => m.Role == ChatRole.User),
    Assistant消息 = messageList.Count(m => m.Role == ChatRole.Assistant)
}.Display();

// 遍历消息
foreach (var message in messageList)
{
    Console.WriteLine($"[{message.Role}]: {message.Text}");
}

🏗️ ChatMessageStore 抽象基类

ChatMessageStore 定义了消息存储的标准接口:

核心方法

方法 功能 返回类型
GetMessagesAsync() 获取所有历史消息 Task<IEnumerable<ChatMessage>>
AddMessagesAsync(messages) 添加新消息 Task
Serialize(options) 序列化存储状态(持久化) JsonElement

扩展实现示例

csharp 复制代码
// 自定义数据库存储
public class SqlChatMessageStore : ChatMessageStore
{
    private readonly DbContext _dbContext;
    
    public override async Task<IEnumerable<ChatMessage>> GetMessagesAsync(...)
    {
        return await _dbContext.Messages
            .Where(m => m.ThreadId == threadId)
            .ToListAsync();
    }
    
    public override async Task AddMessagesAsync(IEnumerable<ChatMessage> messages, ...)
    {
        await _dbContext.Messages.AddRangeAsync(messages);
        await _dbContext.SaveChangesAsync();
    }
}

支持的实现方式:


💾 InMemoryChatMessageStore 默认实现

MAF 提供的默认内存存储实现:

核心特性

csharp 复制代码
public sealed class InMemoryChatMessageStore : ChatMessageStore, IList<ChatMessage>
{
    private List<ChatMessage> _messages;  // 内部使用 List 存储
}

关键特点:

  • 🧠 使用 List<ChatMessage> 内存存储
  • ⚡ 读写性能极高(无 I/O 开销)
  • 🔄 实现 IList<ChatMessage> 接口
  • 🎯 支持 ChatReducer 消息裁剪

构造函数选项

构造函数 使用场景
InMemoryChatMessageStore() 默认空存储(最常用)
InMemoryChatMessageStore(chatReducer) 配置消息裁剪策略
InMemoryChatMessageStore(serializedState) 从序列化状态恢复

⚠️ 内存存储的特性对比

优势与局限

特性 内存存储 数据库存储
性能 ⚡ 极高(无 I/O) ⚠️ 一般(有 I/O)
持久化 ❌ 程序重启丢失 ✅ 永久保存
扩展性 ❌ 单机限制 ✅ 多服务器共享
内存占用 ⚠️ 长对话占用大 ✅ 无限制
复杂度 ✅ 简单即用 ⚠️ 需配置服务

适用场景

✅ 适合内存存储:

  • 短期对话(几轮到几十轮)
  • 开发测试环境
  • 原型验证和 Demo
  • 单用户桌面应用

❌ 需要持久化存储:

  • 长期对话(需历史记录)
  • 生产环境(需可靠性)
  • 多服务器部署
  • 大规模用户场景

🏢 企业级最佳实践

1. 根据场景选择存储方式

csharp 复制代码
// 开发环境:使用内存存储
var devThread = agent.GetNewThread();

// 生产环境:使用自定义持久化存储
var prodMessageStore = new SqlChatMessageStore(dbContext);
var prodThread = new AgentThread(prodMessageStore);

2. 监控消息存储状态

csharp 复制代码
var messages = await messageStore.GetMessagesAsync();
var messageCount = messages.Count();

if (messageCount > 100)
{
    Console.WriteLine("⚠️ 消息数量过多,建议启用 ChatReducer");
}

3. 实现持久化方案

csharp 复制代码
// 序列化当前状态
var serializedState = messageStore.Serialize();

// 保存到数据库或文件
await SaveToDatabase(serializedState);

// 恢复对话
var restoredStore = new InMemoryChatMessageStore(serializedState);

🎯 总结

  • 抽象设计ChatMessageStore 提供统一接口,支持多种存储实现
  • 默认实现InMemoryChatMessageStore 提供高性能内存存储
  • 灵活扩展 :可通过继承 ChatMessageStore 实现自定义存储(数据库/Redis/文件)
  • 两种访问GetService<ChatMessageStore>()GetService<IList<ChatMessage>>()
  • ⚠️ 注意场景:内存存储适合开发测试,生产环境建议使用持久化方案

下一步: 学习如何使用手动保存并恢复会话。