TencentDB-Agent-Memory 数据迁移实战:v2 → v3 租户隔离表结构升级与 L2/L3 文件重定位 TencentDB-Agent-Memory 数据迁移实战v2 → v3 租户隔离表结构升级与 L2/L3 文件重定位【免费下载链接】TencentDB-Agent-MemoryTencentDB Agent Memory is a team-level memory hub for AI Agents — turning conversations, docs, and code into four reusable memory assets (Chat Memory, Skill, LLM-Wiki, Code-Graph) that are governed, shared, and equipped across agents and frameworks.项目地址: https://gitcode.com/GitHub_Trending/te/TencentDB-Agent-Memory本指南完整讲解 TencentDB-Agent-Memory 的 MemoryCore 数据面从 v1.x/v0.x 升级到 v2.0.0数据格式 v3时使用的官方迁移脚本v2-to-v3-migrate.py包括何时需要迁移、三条核心命令与全部参数、vectors.db六张表的 DDL 级变更细节、L2/L3 文件的 scoped 路径重定位规则以及脚本的备份、dry-run 与幂等机制。读完你将掌握如何安全地把存量记忆数据升级到支持租户隔离的 v3 格式并能在升级新版 Gateway 前独立完成迁移演练与故障恢复。迁移背景为什么 v3 数据格式需要一次显式升级MemoryCore 的本地数据默认写入~/.memory-tencentdb/memory-tdai对应环境变量TDAI_DATA_DIR见 MemoryCore/README_CN.md。当用户从旧版本v1.x 或 v0.x升级到 v2.0.0 时数据面的表结构和文件布局发生了破坏性变更因此必须先运行迁移脚本把存量数据升级到 v3 格式再启动新版 Gateway。全新安装的新版 Gateway 会自动创建 v3 格式数据不需要执行迁移。迁移脚本的源码位于 MemoryCore/scripts/migrate-v2-to-v3/v2-to-v3-migrate.py官方中文说明见 MemoryCore/scripts/migrate-v2-to-v3/README_CN.md。受影响的数据包括两类数据对象变更内容vectors.db新增team_id、task_id、user_id、agent_id、version等租户隔离字段并新增审计表与技能相关表scene_blocks/、persona.md、.metadata/L2/L3 文件迁移到profiles/子目录下的 scoped 路径⚠️ 迁移前请务必备份整个数据目录避免意外数据丢失脚本虽然默认会自动备份vectors.db但文件部分的迁移同样需要整体备份兜底。这一表结构变更是三维租户隔离three-dim tenancy isolation即 team / agent / user 维度落地到数据面存储的直接结果。在 SQLite 存储实现 中l1_records、l0_conversations的表定义注释明确标注了 user_id / agent_id added for three-dim tenancy isolation且 FTS 全文索引行中也镜像写入隔离字段保证召回recall后的过滤能跨租户正确隔离。前置条件与数据目录结构运行迁移脚本只需满足两点Python 3.8脚本仅使用argparse、os、shutil、sqlite3、sys、time等标准库无第三方依赖数据目录路径默认数据目录为~/.memory-tencentdb/memory-tdai/该目录下必须存在vectors.db脚本会先检查文件存在性不存在则报错退出。一个典型的数据目录包含vectors.dbSQLite 主数据库含 L0 原始对话、L1 结构化记忆的元数据表与向量表、scene_blocks/L2 场景块文件、persona.mdL3 画像文件、.metadata/元数据目录以及后续迁移生成/使用的profiles/v3 scoped 路径根目录。命令用法与参数说明迁移脚本提供三条核心用法官方文档推荐先 dry-run 检查、确认无误后再真正执行# 1. 先 dry-run 检查不实际修改数据 python v2-to-v3-migrate.py /path/to/memory-tdai --dry-run # 2. 确认无误后执行迁移 python v2-to-v3-migrate.py /path/to/memory-tdai # 3. 仅迁移数据库跳过 L2/L3 文件 python v2-to-v3-migrate.py /path/to/memory-tdai --db-only参数说明参数说明/path/to/memory-tdai数据目录路径必填目录下需包含vectors.db--dry-run仅检查不实际修改数据库以只读模式连接--db-only仅迁移vectors.db表结构跳过 L2/L3 文件--no-backup跳过自动备份默认会自动创建.bak.{timestamp}文件在仓库 README 中两种典型场景Hermes 与 OpenClaw 插件的完整示例为需在 MemoryCore 目录下执行# Hermes 场景 python scripts/migrate-v2-to-v3/v2-to-v3-migrate.py ~/.memory-tencentdb/memory-tdai --dry-run python scripts/migrate-v2-to-v3/v2-to-v3-migrate.py ~/.memory-tencentdb/memory-tdai # OpenClaw 场景 python scripts/migrate-v2-to-v3/v2-to-v3-migrate.py ~/.memory-tencentdb/memory-tdai --dry-run python scripts/migrate-v2-to-v3/v2-to-v3-migrate.py ~/.memory-tencentdb/memory-tdaidry-run 模式的行为可以从源码得到精确印证v2-to-v3-migrate.py#L397-L428脚本以sqlite3.connect(file:{db_path}?modero, uriTrue)的只读 URI 模式打开数据库遍历l1_records、l0_conversations、l1_fts、l0_fts、memory_audit、skills六张表打印各自的列数与行数及列名清单若未指定--db-only还会检查scene_blocks、.metadata、persona.md三个文件/目录的源与目标存在状态最后打印DRY-RUN 完成未做任何修改。因此 dry-run 是安全的预演手段可用于核对存量数据的规模与结构。迁移内容一vectors.db数据库表结构升级数据库迁移共覆盖六张表官方文档的变更总览如下表变更l1_records新增team_id、task_id、user_id、agent_id、version字段l0_conversations新增team_id、task_id、user_id、agent_id字段l1_fts/l0_fts重建 FTS5 索引增加租户隔离列memory_audit新增审计表skills新增技能表skill_fts新增技能全文索引表l1_records五个隔离字段 存量数据回填 复合索引迁移函数migrate_l1_recordsv2-to-v3-migrate.py#L180-L218先记录迁移前的记录数然后通过safe_alter对duplicate column name错误做幂等吞并见 L168-L177依次添加字段字段列定义存量数据回填默认值team_idTEXT DEFAULT defaulttask_idTEXT DEFAULT 空串user_idTEXT NOT NULL DEFAULT defaultdefaultagent_idTEXT NOT NULL DEFAULT defaultdefaultversionINTEGER NOT NULL DEFAULT 00回填使用UPDATE ... WHERE 列 OR 列 IS NULL的形式保证老数据全部落入默认租户域同时把空session_id统一置为default。随后创建 5 个面向隔离查询的复合索引idx_l1_task_updatedtask_id updated_time、idx_l1_team_agent_updatedteam_id agent_id updated_time、idx_l1_user_agent_session、idx_l1_user_updated、idx_l1_agent_updated。对照新版 Gateway 的建表语句sqlite.ts#L609-L630v3 目标结构中的列默认值与迁移脚本完全一致team_id TEXT DEFAULT default、user_id TEXT NOT NULL DEFAULT default、agent_id TEXT NOT NULL DEFAULT default、task_id TEXT DEFAULT 、version INTEGER NOT NULL DEFAULT 0。值得说明的是新版运行时同样内置了在线迁移逻辑sqlite.ts#L632-L643会对缺列的旧库执行幂等ALTER TABLE ADD COLUMN迁移脚本的意义在于在启动新版 Gateway 之前把数据库与文件布局一次性升级到位。l0_conversations四个隔离字段migrate_l0_conversationsv2-to-v3-migrate.py#L221-L256为原始对话表添加team_id、task_id、user_id、agent_id四列不含version同样执行默认值回填与 5 个复合索引创建idx_l0_task、idx_l0_team_agent、idx_l0_user_agent_session、idx_l0_user_recorded、idx_l0_agent_recorded。l1_fts / l0_ftsFTS5 不支持 ALTER必须 DROP 后重建FTS5 虚拟表不支持ALTER TABLE ADD COLUMN因此增加租户隔离列只能采用删除旧表 → 建新表 → 从数据表全量重建索引的路径。rebuild_fts函数v2-to-v3-migrate.py#L259-L293实现了这一逻辑通过sqlite_master检查旧 FTS 表是否存在存在时用PRAGMA table_info读取旧列名若新列集合已是旧列集合的子集则判定已包含所有新列跳过重建幂等关键否则DROP TABLE IF EXISTS后按新 DDL 建表再INSERT INTO ... SELECT ... FROM 数据表全量重建并打印重建行数。新版 L1 FTS 表结构v2-to-v3-migrate.py#L121-L141共 17 列其中正文列content参与分词索引其余content_original、record_id、type、priority、scene_name、session_key、session_id、team_id、task_id、user_id、agent_id、version、timestamp_str、timestamp_start、timestamp_end、metadata_json全部标记为UNINDEXED仅存储不索引用于召回后过滤。L0 FTSL146-L161则包含message_text、message_text_original、record_id、session_key、session_id及四个隔离列与role、recorded_at、timestamp。这一FTS5 无法 ALTER、只能 DROP 重建的技术约束在运行时实现中同样成立新版 Gateway 的 FTS 版本检查逻辑sqlite.ts#L3100-L3150按content_originalv2 标记、user_id/agent_idv3 标记、versionv4 标记、task_idv5 标记逐级判断 FTS 版本一旦发现落后就DROP TABLE IF EXISTS l1_fts/l0_fts并触发全量重建与迁移脚本的rebuild_fts思路一致。memory_audit全新审计表MEMORY_AUDIT_DDLv2-to-v3-migrate.py#L49-L63创建审计表关键设计点layer TEXT NOT NULL CHECK (layer IN (L1,L2,L3))—— 记录被审计的记忆层级action TEXT NOT NULL CHECK (action IN (update,delete))—— 仅审计更新与删除两类写操作携带完整的隔离维度team_id、agent_id、user_id、task_id与version、updated_at_ms、request_id配套 3 个索引idx_memory_audit_recordrecord_id updated_at_ms、idx_memory_audit_isolation四元组隔离查询、idx_memory_audit_time时间范围扫描。审计表的消费端同样存在于新版存储实现中——sqlite.ts#L3308 附近可见INSERT OR REPLACE INTO memory_audit的写入口及对应的审计查询语句迁移脚本负责把这张表凭空建立起来使旧库升级后立即具备审计能力。skills 与 skill_fts全新技能存储SKILLS_DDLv2-to-v3-migrate.py#L71-L93创建技能主表采用单表多行多版本模型每行是(skill_id, version)的一个不可变快照is_head标记当前生效版本manifest_json、storage_dir、status、metadata_json等列承载技能元数据与内容存储位置。配套 6 个索引中最关键的是部分唯一索引CREATE UNIQUE INDEX IF NOT EXISTS uniq_skills_team_agent_name_head ON skills(team_id, owner_agent_id, name) WHERE is_head1 AND statusactive;它保证同一团队、同一拥有者 Agent 下处于 active 状态的 head 版本技能名唯一。SKILL_FTS_DDLL104-L116创建技能全文索引name、description、content参与索引隔离维度列全部UNINDEXED分词器为unicode61 remove_diacritics 1。这两张表的 DDL 与新版运行时的 Skill 存储 DDL 常量SKILLS_DDL见 L21-L65SKILL_FTS_DDL见 L71-L83逐字对应迁移脚本相当于把运行时依赖的表结构提前预置进存量数据库。迁移内容二L2/L3 文件迁移到 scoped 路径L2场景/L3画像文件在 v3 中不再直接位于数据目录根部而是迁入带 URL 编码作用域标识的profiles/子目录源路径目标路径{data_dir}/scene_blocks/{data_dir}/profiles/team%3Adefault%7Cagent%3Adefault/scene_blocks/{data_dir}/persona.md{data_dir}/profiles/team%3Adefault%7Cagent%3Adefault/persona.md{data_dir}/.metadata/{data_dir}/profiles/team%3Adefault%7Cagent%3Adefault/.metadata/目标目录名team%3Adefault%7Cagent%3Adefault是team:default|agent:default的 URL 编码形式:→%3A|→%7C即默认团队 × 默认 Agent的 scoped 路径——这正是 v3 将 L2/L3 文件从全局单份改造为按租户作用域隔离存放的体现。这一设计在运行时得到了印证召回侧通过profiles/${encodeURIComponent(profileScope)}/构造作用域存储路径见 MemoryCore/src/core/hooks/auto-recall.ts#L173而persona.md、scene_blocks/的读取逻辑如 persona-trigger.ts依旧按dataDir下的相对路径工作二者通过 scoped 目录衔接。migrate_l2_l3_filesv2-to-v3-migrate.py#L316-L361的实现要点复制而非移动目录用shutil.copytree递归复制文件用shutil.copy2复制保留元数据源文件永远不会被删除这是迁移失败可回退的保证目标已存在则跳过目标目录/文件已存在时打印已存在跳过配合数据库侧的幂等判断保证脚本可重复执行若某源路径不存在如从未生成过persona.md打印不存在跳过不报错。脚本的工程化细节备份、WAL 与执行时序main函数v2-to-v3-migrate.py#L364-L495展示了完整的安全执行时序路径校验vectors.db不存在直接sys.exit(1)避免对错误目录操作dry-run 分支只读连接检查后直接返回绝不写库自动备份除非--no-backup以 UTC 时间戳命名vectors.db.bak.{YYYYMMDD_HHMMSS}shutil.copy2复制WAL checkpoint先执行PRAGMA wal_checkpoint(TRUNCATE);将 WAL 日志落盘并截断确保后续直接连接能看到全部已提交数据正式迁移PRAGMA journal_mode WAL;开启 WAL 后依次执行 L1 → L0 → FTS 重建 → 新建表最后commit()文件迁移未指定--db-only时执行 L2/L3 文件复制耗时统计打印迁移完成! 耗时: X.XXs。脚本头部注释还明确列出了刻意不处理的对象理解这些边界可避免迁移后产生困惑skill_vecvec0 虚拟表其维度依赖运行时 embedding dimensions 参数由 v3 服务启动时自动创建仅当dimensions 0时创建对应 skill-store-ddl.ts#L92-L97 中SKILL_VEC_DDL_TEMPLATE的__DIM__占位符机制metadata.db独立数据库由管控面控制面创建和维护l1_vec/l0_vec/embedding_meta表结构无变更无需处理。常见问题Q: 迁移失败了怎么办脚本默认在迁移前自动备份vectors.db生成vectors.db.bak.{timestamp}文件L2/L3 文件采用复制而非移动源文件不会被删除。如果迁移失败直接用备份文件替换vectors.db、删除或保留重复的profiles/目录即可恢复原状。更稳妥的做法是迁移前手动整体备份整个数据目录。Q: 可以重复执行吗可以。脚本是幂等的safe_alter对已存在列打印字段已存在跳过并吞掉duplicate column name错误rebuild_fts在旧 FTS 已包含全部新列时跳过重建migrate_l2_l3_files对已存在的目标文件/目录跳过复制。重复执行不会产生重复数据或报错。Q: 全新安装需要跑迁移吗不需要。迁移脚本仅用于从旧版v1.x升级到新版v2.0.0的存量用户。全新安装的新版 Gateway 会按 v3 格式自动创建表结构与profiles/目录布局包括运行时内置的 FTS 在线迁移逻辑sqlite.ts#L3162 附近的rebuildFtsIndex会在检测到数据时自动全量重建索引。迁移执行核对清单完成升级前建议按以下顺序操作备份整个数据目录含vectors.db、scene_blocks/、persona.md、.metadata/运行python v2-to-v3-migrate.py data_dir --dry-run核对六张表的列/行数与三个 L2/L3 文件的源/目标状态执行正式迁移python v2-to-v3-migrate.py data_dir确认输出包含备份WAL checkpoint各表迁移完成迁移完成! 耗时等关键日志抽查结果sqlite3 vectors.db PRAGMA table_info(l1_records)应包含五个新字段ls profiles/team%3Adefault%7Cagent%3Adefault/应看到scene_blocks/、persona.md、.metadata/再启动新版 Gateway随后即可正常读取 v3 格式的存量记忆数据。整个迁移链路以官方文档为主体、以 迁移脚本 的源码细节和 SQLite 存储实现 的运行时行为为佐证一次成功的 v2 → v3 迁移本质上就是把租户隔离能力下沉到每一行记录、每一个 FTS 索引项和每一份 L2/L3 文件路径中让升级后的 MemoryCore 在团队级多 Agent 共享场景下具备可隔离、可审计、可检索的完整数据底座。【免费下载链接】TencentDB-Agent-MemoryTencentDB Agent Memory is a team-level memory hub for AI Agents — turning conversations, docs, and code into four reusable memory assets (Chat Memory, Skill, LLM-Wiki, Code-Graph) that are governed, shared, and equipped across agents and frameworks.项目地址: https://gitcode.com/GitHub_Trending/te/TencentDB-Agent-Memory创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考