存储
MerryBot 有两套独立的存储体系,都基于 LiteDB(本地 NoSQL,无需外部服务):
| 存储 | 文件 | 用途 | 数据目录 |
|---|---|---|---|
| 插件存储 | plugin_data.db | 核心配置、插件配置、插件数据 | <data>/ |
| 历史记录 | group_history.db + storage/ | 群消息、图片/文件、事件、AI 消息审计 | <data>/(botClient.PathPrefix) |
两库分工明确:插件存储面向配置与键值数据(小而频繁),历史记录面向消息流水(大而顺序)。
插件存储(DataProvider 项目)
PluginStorageDatabase
PluginStorageDatabase(LiteDB.Async 封装)管理 plugin_data.db,含两个物理集合:
| 集合 | 键格式 | 内容 |
|---|---|---|
Plugin_Data_Table | {prefix}/{pluginId} | 插件对象数据(StorePluginData / GetPluginData) |
Plugin_Config_Table | {prefix}/{pluginId} | 插件配置(SetPluginConfig / GetPluginConfig) |
关键设计:
- 前缀命名空间:
prefix参数区分归属,默认"plugin"保持插件数据兼容;核心使用prefix: "core"(如核心配置键为core/config,时钟存储键为core/clock) - 旧键兼容:带前缀键不存在时回退读取无前缀旧键,下次保存时迁移到规范键,平滑升级存量数据
- 原始 BSON 读取:
GetRawDataEntriesAsync/GetRawConfigEntriesAsync以BsonDocument返回全部条目,避免已删除插件类型缺失导致强类型反序列化抛LiteException;供 WebUI「高级配置」面板排查残留数据 - schema 迁移:
PluginStorageDatabase.Migrations.cs提供幂等迁移(MigrateAsync),启动时执行,当前版本 1
PluginDatabaseScope
CreateScope(pluginId, prefix = "plugin") 返回按插件隔离的集合视图(PluginDatabaseScope):只允许访问该插件名下的集合,防止插件越界读写其他插件的数据。
核心用法示例:PluginStorageDatabase.CreateScope("clock", prefix: "core")(时钟存储)、CreateScope("agent")(Agent 上下文快照、记忆)。agent-service 有意复用 agent scope,避免管理服务与 Agent 看到不同的数据。
历史记录(DataService 项目)
HistoryRecorder / AiMessageStore
HistoryRecorder 管理 group_history.db(数据库文件、对象存储、schema 迁移、生命周期的唯一属主),集合按用途划分。AI 消息相关读写拆分在 AiMessageStore:由 HistoryRecorder 构造组合(HistoryRecorder.AiMessages),共享同一数据库连接,迁移仍统一走 HistoryRecorder.MigrateAsync:
| 集合 | 内容 | 归属 |
|---|---|---|
messages | 群消息(GroupId/SenderId/MessageId/Time 索引) | HistoryRecorder |
images / files | 图片床 / 文件床(Hash 唯一索引,幂等去重) | HistoryRecorder |
events | 群事件(进群/退群/禁言等) | HistoryRecorder |
forward_messages | 合并转发消息 | HistoryRecorder |
group_names | 群名历史 | HistoryRecorder |
ai_messages | AI 消息审计(按 SessionKey 索引,Agent 对话留痕;assistant 行带 token 用量:InputTokens/OutputTokens/CachedTokens,供 WebUI Token 用量页聚合) | AiMessageStore |
resource_references | merrybot:// 资源引用(消息与对象的映射) | HistoryRecorder |
设计要点:
- 对象存储分离:图片/文件等二进制对象存入
storage/目录(FileSystemObjectStorage,bucket 为images/files),LiteDB 只存元数据与哈希引用,避免数据库膨胀 - 雪花 ID:
IdGen.IdGenerator(机器码MachineCode0–31,由核心配置生成并落库),保证跨进程时间有序 - 索引容错:索引创建失败只记日志不抛异常(历史数据问题不导致启动失败);Hash 唯一索引已有重复数据时创建失败,由写入侧的幂等兜底
- 消息入库通过
MessageService完成(同时负责merrybot://资源引用与 AI 消息审计)
存储与 Agent 的关系
- Agent 插件与宿主共享
agentscope(ContextSnapshotService等),有明确注释说明为何共用 - Agent 对话通过
ai_messages集合做消息审计(MessageService),用户/助手/工具消息各留一条记录 - LLM Provider / Key 的存储与加密由
LlmProviderPlugin管理(见 WebUI 子系统)