v2-refactor-temp/docs/knowledge/knowledge-vector-migrator.md
这份文档用于说明 V2 知识库向量迁移器的职责边界和核心规则。
它关注的是:
embedjs 向量库的数据来源这份文档只描述当前已经落地的迁移器行为,不展开到未来在线向量数据重建或最终 retrieval API 设计。
对应实现:
src/main/data/migration/v2/migrators/KnowledgeVectorMigrator.tssrc/main/data/migration/v2/migrators/README-KnowledgeVectorMigrator.mdKnowledgeVectorMigrator 的职责不是迁移知识库业务主数据,而是:
embedjs 向量库vectorstores 布局knowledge_base / knowledge_item换句话说:
KnowledgeMigrator 负责业务主数据KnowledgeVectorMigrator 负责向量数据迁移两者共同完成知识库的完整迁移,但 source of truth 仍然是 V2 业务表,不是向量库。
迁移器依赖四类输入:
来源:
knowledge_base 表作用:
dimensions来源:
knowledge_item 表作用:
itemId来源:
knowledge.bases[].items[]作用:
uniqueId / uniqueIds[] 反查到已经迁移后的 knowledge_item.id来源:
${getDataPath()}/KnowledgeBase/<baseId>作用:
embedjs 的 vectors 表迁移目标不是继续保留旧 embedjs 格式,而是生成新的 vectorstores 兼容存储。
当前实现的目标结构是:
{knowledgeBaseDir}/{migratedBaseId}/.cherry/index.sqlite(不再沿用原 legacy DB 路径)schema.ts 的多表 index store 为准(meta/content/material/search_unit/search_text/embedding + 外部内容 FTS5 search_text_fts),由 openBetterSqlite3IndexDriver → createKnowledgeIndexSchema 建立。下方“1. 主表字段/2. 普通索引”是旧 embedjs/langchain 单表布局,已被上述多表 schema 取代。迁移器会为目标存储补齐必要 schema:
idexternal_idcollectiondocumentmetadataembeddingsexternal_idcollectionV1 的向量记录使用 uniqueLoaderId 关联 loader。
V2 迁移时,不保留这个旧字段作为最终业务标识,而是把它映射成新的 knowledge_item.id,并写入:
external_id映射规则:
uniqueIds[]uniqueIdknowledge_item 的 item 才能参与映射这一步的核心目标是:让新向量记录稳定关联回 V2 的业务 item,而不是继续依赖旧 loader identity。
这里有一个重要约束:
knowledge_item.id 的 legacy 向量记录,才属于有效可迁移数据knowledge_item.id 的 legacy 向量,即使仍存在于旧 embedjs DB 中,也视为无效残留数据目录(directory)的特殊处理:V1 把目录下每个文件都登记在该目录 item 的 loader id 上,没有 per-file item。迁移时不再把这些容器级向量直接丢弃,而是为每个嵌入文件合成一个 file 子项(一个 loader id 对应一个子项,见 KnowledgeMigrator.expandLegacyDirectoryItem),把目录向量重新归属(re-attribute)到这些子项上,目录因此保持可检索且无需重新 embedding。只有在 fallback 情况下——legacy 向量源不可读,或某个嵌入文件没有可迁移向量——才会跳过容器级向量,并把目录保留为 directory_not_migrated 失败墓碑。
旧向量记录中的内容字段会转换为:
pageContent -> documentknowledge_item.id -> metadata.itemId 和 external_idknowledge_item.type -> metadata.itemTypesource -> metadata.sourcemetadata.chunkIndexmetadata.tokenCount当前实现不会保留所有旧 metadata,只保留迁移和检索必需的最小信息。
迁移后的 metadata 必须满足 runtime KnowledgeChunkMetadataSchema:
itemId、itemType、source、chunkIndex、tokenCount 都是必填字段。
无法补出合法 source 的 legacy row 会被跳过,而不是写入不完整 metadata。
迁移器不会重新做 embedding。
它会直接复用 V1 已存在的向量:
vector 字段读取原始 little-endian float32 BLOB 字节number[]embeddings这意味着:
旧 chunk row 的 id 不会直接复用。
每一条迁移后的向量记录都会生成新的 UUID v4 id。
因此迁移的稳定关联语义不是依赖旧 chunk id,而是依赖:
baseIdexternal_id = knowledge_item.id当前迁移器采用“临时文件重建 + 目标路径原子替换 + v1 源原地不动”的策略。
规则如下:
{targetDbPath}.vectorstore.tmpembedjs DB({knowledgeBaseDir}/{legacyBaseId})在整个迁移过程中不被移动也不被删除
{migratedBaseId}/.cherry/index.sqlite,与 legacy flat path 不同名、不冲突,源文件无需腾挪EBUSY 时会重试(recursive + maxRetries + retryDelay),以兼容 Windows 上的瞬时文件锁这意味着:
KnowledgeVectorSourceReader 重新读取原始 legacy DB以下行为是当前实现明确接受的限制,不应误读为“未来理想方案”:
execute() 会直接返回 success: falseskippedCount,也不应只记 warning 后继续成功当前实现会做至少以下校验:
external_idmetadata.itemId,并与 external_id 保持一致如果不满足这些条件,应视为当前 base 迁移失败。
以下情况会被跳过,而不是强行写入:
knowledge_base 中不存在对应 basevectors 表uniqueLoaderId 无法映射回已迁移的 knowledge_item.idvector 或 vector 为空这些跳过通常会记录 warning,而不是让整个迁移流程全部中断。
补充说明:
knowledge_item,因此不再被视为有效业务向量数据当前迁移器只负责“向量数据重建”,不负责:
因此它的定位应该是:
基于当前迁移器行为,后续 V2 运行时设计需要遵守以下前提:
knowledge_base / knowledge_itemexternal_id 稳定关联到 knowledge_item.idembedjs 的 uniqueLoaderIdknowledge-backend-decisions.md
KnowledgeRuntimeService、data services、queue 和 runtime/vector 边界knowledge-schema.md
三者的关系可以简化为:
当前 runtime 向量侧实现位于:
src/main/services/knowledge/runtime/KnowledgeRuntimeService.tssrc/main/services/knowledge/vectorstore/KnowledgeVectorStoreService.tssrc/main/features/knowledge/vectorstore/indexStore/BetterSqlite3VectorIndex.ts这意味着迁移后的向量数据并不是孤立的一次性产物,而是会被当前 runtime 直接按 knowledge base 打开和查询。
当前已确认的衔接点是:
KnowledgeVectorStoreService 按 base.id 获取 storeBetterSqlite3VectorIndex因此,迁移器与 runtime 的共同前提是:
knowledge_base / knowledge_itemknowledge_item.id 为稳定标识,而不是继续依赖 V1 loader identity