ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Operit 记忆空间资料文档:user.md 存储布局与 +4/+5 数据迁移方案解析

Operit 记忆空间资料文档:user.md 存储布局与 +4/+5 数据迁移方案解析 AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载导读本文基于 1_StorageAndMigration.md 展开系统讲解 OperitAndroid AI Agent如何把原本全局唯一的user.md用户资料文档改造为「每个记忆空间memory space各自拥有一份user.md」的存储模型以及从已发布版本 4、5 数据格式无损迁移到新模型的完整方案。读完本文你将掌握memory-space-profiles/id/user.md的目录布局与 12,000 字符原子写入约束、独立 schema marker 的版本触发机制、4 结构化 Profile 直迁与 5 归档文件匹配回迁两条迁移路径、遗留分类锁到整文档锁的策略映射以及update_user_profile/update_user_preferences两个工具在迁移后的运行时行为。一、背景为什么全局 user.md 必须拆分为按记忆空间隔离的资料文档在 Release 1.12.05 之前Operit 只在应用私有目录下保存一份全局的user.md文档。但记忆空间memory space本身已经通过稳定的标识符相互隔离每个空间对应独立的 ObjectBox 记忆数据库却并不拥有属于自己的资料文档——用户资料与记忆数据库的归属关系是脱节的。Release 4 的旧实现则把多个结构化用户 Profile以 JSON 形式存入 DataStore每个 Profile 携带与记忆数据库相同的标识符。新模型的意图见 index.md是每个记忆空间拥有一份自己的user.md当前激活的空间负责向 Prompt 提供资料上下文并接收自动 Profile 更新用户对资料的管理入口独立为 Settings 中的User Preferences 专用设置页不再混入记忆库memory library操作已发布的全局资料文档不再被读取、注入、复制或展示。这套改动的核心难点在于迁移用户可能从 4结构化多 Profile 数据或 5全局单文档 归档文件升级到新版本两种已发布格式必须各自得到无损、可逆的迁移路径且不能破坏记忆数据库与角色卡character card对空间标识符的既有绑定。二、存储布局memory-space-profiles/id/user.md与原子写入新存储模型在应用私有文件目录filesDir下建立memory-space-profiles/目录每个记忆空间一个子目录资料文档固定命名为user.mdfilesDir/ ├── memory-space-profiles/ │ ├── memory-space-id/user.md │ ├── memory-space-id-2/user.md │ └── ... └── (已发布旧格式仅迁移时读取) ├── user.md # 5 的根资料文档 └── legacy-user-profiles.md # 5 的归档文件独立 schema marker这一布局在源码中由 MemorySpaceProfileDocumentRepository.kt 实现STORAGE_DIRECTORY memory-space-profilesL23、USER_FILE_NAME user.mdL21profileDirectory(id) File(profileRoot, memorySpaceId)、documentFile(id) File(profileDirectory(id), USER_FILE_NAME)L223-L230并以require(memorySpaceId.isNotBlank())拒绝空白 ID文档内容限定12,000 字符MAX_CONTENT_CHARS 12_000L20save()在写入前require(markdown.length MAX_CONTENT_CHARS)L85-L87超限直接抛出异常不会产生半写文件写入是原子的writeAtomically()使用 Android 的AtomicFile——先startWrite()写临时文件成功后finishWrite()提交失败时failWrite()回滚L299-L311。因此任何时刻磁盘上要么是旧内容、要么是完整的新内容不存在损坏的中间态。读写通过load(memorySpaceId)/save(memorySpaceId, markdown)暴露二者共用一把writeMutex保证并发安全reset()将文档清空为空字符串delete()删除文档与整个空间目录L102-L112。三、Schema 版本标记与已发布全局文档 marker 分离迁移触发依赖一个独立的 schema marker它必须与已发布的全局文档 marker 分离避免新版本误读旧格式、或旧逻辑误触发新迁移。仓库中的实现使用独立 SharedPreferences 文件 版本号MIGRATION_PREFERENCES memory_space_profile_documents、SCHEMA_VERSION_KEY schema_version、CURRENT_SCHEMA_VERSION 2MemorySpaceProfileDocumentRepository.ktinitialize()在首次进入时检查schemaVersion CURRENT_SCHEMA_VERSION若低于目标版本则执行migrateReleasedData()成功后才以commit()持久化新版本号L54-L70只有文件写入与 DataStore 写入都成功后迁移才被标记为完成——这继承了 user_md_profile/1_StorageAndMigration.md 中「Mark the migration complete only after file and DataStore writes succeed」的约束。migrateReleasedData()的分类逻辑L114-L129是整个迁移的分流入口if (manager.hasLegacyUserProfileMetadata()) → 4 结构化 Profile 直迁 if (manager.hasMemorySpaceMetadata()) → 5 全局文档回迁 else → 4 结构化 Profile 直迁兜底注意分类顺序的细节只要存在profile_list就优先按 4 处理即使磁盘上同时出现 memory-space 元数据。这是因为 4 的原始快照恢复raw snapshot restore可能让旧profile_list与新默认记忆空间记录共存仅凭 memory-space 元数据存在与否无法可靠识别 5详见 6_RawSnapshotRestoreCompatibility.md 与 UserPreferencesManager.kt 的注释。四、5 安装的迁移归档匹配与根文档归属判定4.1 迁移目标5 安装升级后磁盘上存有一个无归属标识的根文档filesDir/user.md和一份归档文件filesDir/legacy-user-profiles.md。迁移必须回答一个关键问题根 user.md 到底属于哪个记忆空间4.2 判定算法唯一缺失空间5 在发布时把当时的激活 Profile 写成了根user.md把其余 Profile 按发布时的顺序写进了归档文件。归档中保留了 4 的 profile-list 顺序。迁移算法migratePublishedGlobalDocumentsL148-L195读取memory_space_list得到全部记忆空间 ID并逐一读取其名称解析归档文件按##二级标题切分出各归档 Profile 文档的名称与内容parsePublishedArchiveL238-L254排除Basic information、Personality、Preferred assistant style三个内容分区标题枚举每个可能的「根文档归属空间」检查剔除该空间后剩余空间的名称序列是否为归档名称的子序列isSubsequenceOfL197-L205——因为 5 写入归档时保持了发布顺序这一匹配是确定性的不会因重名而随机唯一满足条件的空间即根文档的所有者若候选多于一个无法唯一判定则保留源文件不动并记录警告L172-L179其余归档文档按名称依次匹配剩余空间写入根文档写入唯一缺失的空间。val rootDocumentCandidates spaces.filterIndexed { rootIndex, _ - archiveNames.isSubsequenceOf( spaces.filterIndexed { index, _ - index ! rootIndex }.map { it.name } ) }这一设计有意不依赖迁移发生时刻的active_memory_space_id因为用户可能在 5 迁移之后又切换过激活空间激活值已不能代表根文档写入时的归属。文档明确强调「The current active space is not used because it could have changed after 5 wrote the root document」即根文档归属完全由归档序列与空间列表的顺序关系推导。4.3 写入保护归档与根文档通过writeIfDocumentEmpty()L216-L221写入只有当目标文档不存在或内容为空时才落盘避免覆盖用户在迁移前已产生的任何内容。整个迁移为一次性操作完成后的根user.md与legacy-user-profiles.md保留在磁盘上但不再被读取它们是「只读的迁移源」永不修改、永不注入 Prompt。五、4 安装的迁移结构化 Profile 直迁5.1 读取旧记录4 安装在 DataStore 中保存了profile_list、active_profile_id和逐条的profile_id记录见 UserPreferencesManager.kt 的键定义PROFILE_LIST、ACTIVE_PROFILE_ID、以及动态拼接的memory_space_id/profile_id键。hasLegacyUserProfileMetadata()以profile_list键是否存在作为权威判据。5.2 直迁流程migrateLegacyStructuredProfilesL131-L140通过readLegacyUserProfiles()读取完整快照含激活 Profile ID、Profile 列表、遗留分类锁标志为每一个Profile 调用profile.toUserMarkdown()生成 Markdown 并原子写入memory-space-profiles/id/user.md调用migrateLegacyProfilesToMemorySpaces(snapshot)UserPreferencesManager.kt把 DataStore 中的旧 Profile 元数据改写为 memory-space 元数据。5.3 标识符保留重复名称也不会串档直迁的关键保证是以 identifier 为准、而非显示名称。migrateLegacyProfilesToMemorySpaces中val ids profiles.map { it.id }.distinct().toMutableList()且默认空间DEFAULT_PROFILE_ID始终排在列表首位随后写入memory_space_list、active_memory_space_id并逐条把profile_id改写为memory_space_id后删除旧键。由于目标空间 ID 完全沿用原 Profile ID即使两个 Profile 的显示名称相同也绝不会发生「按名字选中/覆盖彼此」的串档问题。更重要的是该 ID 同时是ObjectBox 记忆数据库名见 MemorySpace.kt 的注释identifier 在用户 Profile 迁移中刻意保持稳定因为它也是 ObjectBox 数据库名且可能被角色卡引用因此迁移后记忆数据与角色卡绑定全部原样保留。5.4 Markdown 格式与发布版转换器对齐直迁生成的 Markdown 使用与已发布 5 转换器相同的分区结构toUserMarkdown/appendProfileSectionsL256-L297# About me ## Basic information - Gender: xxx - Birth date: yyyy-MM-dd - Identity: xxx - Occupation: xxx ## Personality personality 自由文本 ## Preferred assistant style aiStyle 自由文本字段映射自 LegacyUserProfile.ktgender、birthDateUnix 毫秒格式化为yyyy-MM-dd、identity、occupation、personality、aiStyle。若 Profile 没有任何结构化内容hasStructuredUserContent()为假即生日、性别、性格、身份、职业、AI 风格全部为空则生成空文档避免产生无意义占位文件。六、已发布格式审计4 与 5 的磁盘/DataStore 形态对照原文档「Released-format audit」一节对两种已发布格式做了精确盘点整理如下维度Release 4Release 5数据存放DataStoreprofile_id存PreferenceProfileJSONDataStorememory_space_list、active_memory_space_id、memory_space_id持久化字段id、name、birthDate、gender、personality、identity、occupation、aiStyle、isInitializedMemorySpaceid、name、profileAutoUpdateEnabled默认 true、profileAutoUpdateLocked默认 false激活资料DataStore 内多 Profile 并存active_profile_id指向激活项根文档filesDir/user.md其余资料同 DataStore归档文件filesDir/legacy-user-profiles.md独立user_profile_documentschema marker迁移方式Profile ID 直迁到对应空间文档归档名称序列匹配 唯一缺失空间判定迁移专用模型LegacyUserProfile的序列化形态与已发布PreferenceProfile完全一致因此可以直接读取旧 JSONLegacyUserProfile.kt 注释明确说明它是「Released structured profile payload, retained only so later migrations can preserve data」。七、遗留分类锁与整文档锁pending migration state 策略4 时代支持按字段分类锁定birth_date / gender / personality / identity / occupation / ai_style 各自的*_LOCKED开关。新模型改为整文档锁whole-document lock两种策略无法 1:1 映射——分类锁只锁住单个字段而整文档锁会锁住整个user.md的自动更新。迁移处理见 UserPreferencesManager.kt若快照中任一分类锁为 true则新空间的profileAutoUpdateLocked true。其意图是既然无法在文档粒度精确复现用户对单字段的保护就直接锁定整个文档的自动重写避免 AI 自动更新把用户曾保护过的字段改掉具体采用何种新策略解锁、整锁、关闭自动更新由用户在迁移后的新 UI 中主动选择。因此在用户做出选择之前自动 Profile 重写保持禁用pending migration state绝不擅自代用户决策。运行时对锁的检查位于 MemoryLibrary.ktval profileUpdateEnabled memorySpace.profileAutoUpdateEnabled !memorySpace.profileAutoUpdateLocked val profileDocument if (profileUpdateEnabled) profileDocumentRepository.load(profileId) else 锁关闭时资料文档既不注入 Prompt也不参与自动更新实现上属于「仓库边界repository boundary检查」——即saveAutomatic()L94-L100在真正写入前再次校验profileAutoUpdateEnabled与profileAutoUpdateLocked两个开关双保险。八、运行时注入与工具兼容8.1 Prompt 注入激活记忆空间的user.md通过 FunctionalPrompts.kt 注入当profileUpdateEnabled为真时文档被包裹在user_profile_document.../user_profile_document标记中提供给模型并要求模型在确认稳定偏好、约束、身份事实或交流方式后以profile_markdown参数返回完整替换文档无充分依据时返回 JSON null且明确禁止删除已有有效内容、记录临时请求或写入常识。8.2 工具双轨update_user_profile面向 Prompt 可见的文档工具参数为markdown调用MemorySpaceProfileDocumentRepository.save(激活空间ID, markdown)整篇覆盖当前激活空间的user.mdMemoryQueryToolExecutor.ktupdate_user_preferences已发布的旧工具包与持久化工具调用仍可能发出旧字段参数birth_date、gender、personality、identity、occupation、ai_style它作为隐藏兼容适配器保留把旧字段以## Imported profile update分区追加写入激活空间的user.md信息不丢失但不再恢复旧的结构化 Profile 运行时L750-L805。两个工具最终都解析到激活记忆空间的文档符合 2_RuntimeAndAutoUpdate.md 约定的「使用激活空间标识符加载唯一资料文档」原则。8.3 自动更新管线既有记忆自动保存候选管线本就按记忆空间运行per memory space。资料抽取阶段只写当前空间的文档并在仓库边界执行整文档自动更新锁检查因此自动更新与 Prompt 注入共享同一把锁语义不会出现「注入未锁、写入被锁」的不一致。九、用户界面承载与兼容性用户资料管理入口最终落在 Settings 的User Preferences页独立于记忆库相关实现见 UserPreferencesSettingsScreen.kt空间选择器 user.mdMarkdown 编辑器含语法高亮、空占位、字符计数、文档菜单自动更新开关与整文档锁放在独立的政策面板policy sheet中并即时持久化L624-L647。记忆库只保留浏览对应记忆数据库所需的激活空间选择器不再承担创建、重命名、删除、编辑用户配置的职责见 4_StandaloneUserConfigurationUi.md。由于配置继续沿用既有 memory-space IDmemory-space-profiles/id/user.md、ObjectBox 数据库与固定角色卡绑定对 4、5 及当前 worktree 的升级用户全部保持不变。十、原始快照恢复兼容4 快照不回退一个必须防御的边界4 创建的原始快照raw snapshot包含完整的profile_list负载。把它恢复到正在运行的新版进程时磁盘上可能同时残留旧profile_list键与不完整的 memory-space 列表——若此时把「存在 memory-space 列表」误判为 5就会跳过剩余的 4 记录导致数据丢失。对策详见 6_RawSnapshotRestoreCompatibility.md只要负载包含profile_list就优先按 4 分类再看 memory-space 元数据恢复目录时按快照的完整状态替换而不是与较新文件合并从而清除过期的迁移标记恢复采用原子文件替换成功后立即重启确保下一个进程是恢复后 DataStore 状态的第一个读取者避免已打开进程把陈旧内存偏好写回。预期结果被检视的 4 原始快照能恢复其三个结构化用户 Profile 及各自既有的 memory-space 标识符而不是只剩一个空默认空间。十一、验证要点与源码导航迁移触发MemorySpaceProfileDocumentRepository.ktinitialize→migrateReleasedData的分类与两种迁移实现DataStore 键与改写UserPreferencesManager.kthasLegacyUserProfileMetadata/hasMemorySpaceMetadata/migrateLegacyProfilesToMemorySpaces格式模型LegacyUserProfile.kt 与 MemorySpace.kt运行时注入与锁MemoryLibrary.kt、FunctionalPrompts.kt工具执行MemoryQueryToolExecutor.ktUI 与策略持久化UserPreferencesSettingsScreen.kt配套设计文档index.md、2_RuntimeAndAutoUpdate.md、4_StandaloneUserConfigurationUi.md、6_RawSnapshotRestoreCompatibility.md以及前身方案 user_md_profile/1_StorageAndMigration.md。验证建议围绕四条主线① 全新安装 → 默认空间创建空文档且 schema 版本置 2② 4 数据升级 → 每个 Profile 生成独立user.md、DataStore 旧键被清除、ObjectBox 与角色卡绑定不变③ 5 数据升级 → 根文档归属唯一空间判定正确且切换激活空间后再升级仍正确④ 4 原始快照恢复 → 三份结构化 Profile 与标识符完整恢复、进程立即重启。这四类场景共同构成了对「存储 迁移」契约的完整回归覆盖。赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐Operit 记忆空间 Profile 文档体系全解析从全局 user.md 到一空间一文档的存储、迁移、运行时注入与独立配置 UIOperit 记忆空间 Profile 文档体系全解析从全局 user.md 到一空间一文档的存储、迁移、运行时注入与独立配置 UI 导读本文以 memAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit 原始快照恢复兼容性内存空间档案在 4/5 数据迁移中的正确重建Operit 原始快照恢复兼容性内存空间档案在 4/5 数据迁移中的正确重建 本指南围绕 Operit 中「内存空间档案文档Memory Space PAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化XianyuAutoAgent深度拆解基于多专家协同的闲鱼智能客服实战手册XianyuAutoAgent深度拆解基于多专家协同的闲鱼智能客服实战手册 面对闲鱼平台7×24小时的海量咨询需求传统人工客服模式已难以满足现代电商运营的高人工智能大模型AI 应用AI Agent电商上一篇Vibe Kanban清理脚本配置指南如何高效管理AI编程代理资源下一篇打造逼真环境光照GettingStartedWithRTXRayTracing项目的Light Probe技术详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表