跳转至

06 - 数据存储

插件通过 SealDice 的 ext.storageGet / storageSet 以 JSON 字符串持久化数据。存储键(带不带 aiplugin4_ 前缀取决于 SealDice 扩展命名,代码内统一用 ext.storage*)清单:

存储键 内容 写入方
storage_version 存储结构版本号(当前 2) src/index.ts 启动时
agent_<name> 智能体配置(默认 agent_*、agent_compress_agent、agent_summarize_agent) Agent.save
session_<sid> 会话完整状态:context、memory、setting(overrides 为 .ai ctxset 的按会话窗口/截断覆盖)、imageManager、stream、bucket、tool、state 等(lastCtx 排除);上下文侧另含 archiveSeq / rawSeq / settledSeq(结算水位)与 lastArchivedAt 等持久化字段 Session.save / SessionService.save
session_<sid>:pending 挂起队列轻量分片(仅 content/userId/messageId/kind/systemReason/epId/isPrivate,不含运行时 ctx/msg 对象),重载 JS 后按 epId/isPrivate 重建上下文续跑;队列清空时写空串 Session.savePending / Session.clearPending
session_sub:<父会话ID>:<childId> 子代理 child 会话载荷(完整序列化;删除时写空串) 子代理存储 SubAgentStore.saveChildSession
subagent_index:<父会话ID> 子代理记录索引(ChildRecord 数组,保留上限 100,自动清理最旧已结束记录) 子代理存储 SubAgentStore
subagent_seq:<父会话ID> 子代理单调序号计数器(删除记录不回退,保证 childId 不复用) 子代理存储 SubAgentStore
subagent:roots 产生过子代理的父会话根索引(重载清扫/对账用) 子代理存储 SubAgentStore
group_<gid> 群档案:群名、角色、群主、管理员、成员、忽略名单 Group.save
user_<uid> 用户档案:用户名 User.save
cmdPriv 自定义命令权限 PrivilegeManager.saveCmdPriv
timerQueue 定时器队列 TimerManager.saveTimerQueue
mediaTaskQueue 媒体生成任务队列(MediaTask 数组:序号/会话/来源/状态/时间/参数摘要/结果句柄/错误/是否已呈现),每会话最多保留 50 条已结束任务;只存句柄与短摘要,不落 base64 MediaTaskManager.save
usageMap token 用量({models: 模型 → 日期 → 计数, purposes: 用途 → 日期 → 计数};旧格式为纯模型映射,读取时自动升级) UsageManager.saveUsageMap
session_<sid>:arc:<三位序号> 归档分片:移出上下文的群聊原文(条目含 messageId/userId/time/text),只写一次、永不重写;序号由 session.archiveSeq 记录;上限见「原文分片上限」(默认 200,超出时最旧的置空)、按「原文分片保留天数」(默认 30)惰性淘汰;由 read_raw kind=user id=arc:序号 回查 Context.writeArchiveShard / src/context/archive_store.ts
session_<sid>:raw:<序号> 窗口移出分片:上下文超过「窗口上限字数」时移出的原文(条目结构同归档分片),只写一次、永不重写;序号由 session.rawSeq 记录;上限与 TTL 同上;由 read_raw kind=user id=raw:序号 回查 Context.trimToWindow / src/context/archive_store.ts
memv2:<bankId>:meta 记忆 v2 元信息分片(meta) 记忆 v2 仓库 MemoryRepository
memv2:<bankId>:units 记忆 v2 单元分片(units) 记忆 v2 仓库 MemoryRepository
memv2:<bankId>:links 记忆 v2 关联分片(links) 记忆 v2 仓库 MemoryRepository
memv2:<bankId>:docs 记忆 v2 文档分片(documents + chunks) 记忆 v2 仓库 MemoryRepository
memv2:<bankId>:obs 记忆 v2 观察分片(observations + entities + mentalModels) 记忆 v2 仓库 MemoryRepository
pubSessions 公开会话目录(JSON:platform/botId/sid/title/addedBy 等条目数组,幂等 add) src/pub/registry.ts
modelPurposeOverrides 全局分用途模型覆盖(use → 模型标识),.ai model 写入,余额不足自动切模型也写 src/config/configs/model.ts
version 上次运行的插件版本(用于启动更新日志) checkUpdate

会话 ID 与档案 ID 格式

  • 私聊会话:QQ:<用户ID>,例如 QQ:1234567890。
  • 群聊会话:QQ-Group:<群号>,例如 QQ-Group:1234。
  • 群档案 ID 与群会话 ID 相同;用户档案 ID 与私聊会话 ID 相同。
  • 工具调用时常见 QQ: 前缀归一(ob11 接口传参前会 replace(/^.+:/, ''))。

序列化与反序列化(revive)

存储内容不是直接 JSON.parse 后使用,而是经 src/utils/utils.ts 的 revive() 恢复:

  • 每个可持久化类声明静态 validKeysMap,描述每个字段的类型(字符串/数字/布尔/数组/嵌套对象/嵌套类)。
  • revive 用 new constructor() 先建默认实例,再按 validKeysMap 逐个字段恢复,跳过缺失或类型不符的字段,嵌套类递归恢复。
  • 这保证存档缺字段/增字段时能平滑兼容;Context.reviveMessages()、MemoryService.reviveMemoryMap() 等再做消息与记忆条目的二次清洗。

版本迁移

  • STORAGE_VERSION = 2 写入 storage_version;代码里目前只是记录版本,存储迁移按存储键自动进行。
  • mediaTaskQueue(4.24.0 起)的启动恢复:读盘时把 running 任务标记为 failed('插件重载中断')(已发出的 HTTP 请求无法续跑),并对 done && !notified 且来源为工具的任务补一次通知;参考图 base64 只存内存、不落盘,重载后待执行任务会以「参考图已失效,请重新提交」失败。
  • 记忆 v2 旧格式单 key(memv2:<bankId>:bank)首次读取时一次性迁移为 5 个分片 key(meta/units/links/docs/obs),迁移完成后清空旧 key;旧会话挂起队列(session_<sid> 内嵌 pendingQueue)在 4.19.3 起不再读写,改由 session_<sid>:pending 轻量分片承载,读取不到分片时视为空队列。
  • 记忆旧格式迁移由 src/memory/v2/migrate.ts 的 migrateLegacyMemory 负责:把旧存档的 memoryMap(长期记忆)、summaries(旧短期/观察记忆)与 persona 分别迁移为新引擎的 MemoryUnit / Observation / 心智模型;旧 persona 另由 MemoryManager.migrateLegacyPersona 在首次构建长期记忆提示时懒迁移(幂等,已有该问题的心智模型时只清空 persona)。
  • 插件自身版本升级通过 version 存储键对比 VERSION,输出 src/changelog.ts 中对应版本的更新日志。