OpenViking OVPack:.ovpack 数据包的导入导出与备份恢复完整实践
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
OVPack 是 OpenViking 提供的数据迁移与备份机制,用于将 Viking URI 下的资源树打包为.ovpack文件并在目标环境完整恢复。本文基于 OVPack API 文档 与仓库源码,讲清四个核心接口(export / import / backup / restore)的参数、冲突与向量策略,并深入.ovpack文件的内部格式、完整性校验与源码调用链,读完后可独立完成 OpenViking 数据的迁移、备份与恢复。
一、OVPack API 总览
OVPack API 挂载在/api/v1/pack路由下(见 路由定义),共提供四个接口,覆盖"导出—导入—备份—恢复"完整链路:
| 接口 | 路径 | 用途 | 权限要求 |
|---|---|---|---|
| export_ovpack | POST /api/v1/pack/export | 将指定 URI 下资源导出为.ovpack文件流 | ROOT / ADMIN / USER |
| import_ovpack | POST /api/v1/pack/import | 将.ovpack文件导入到指定父级 URI | ROOT / ADMIN / USER |
| backup_ovpack | POST /api/v1/pack/backup | 将全部公开 scope 备份为 restore-only 包 | 仅 ROOT / ADMIN |
| restore_ovpack | POST /api/v1/pack/restore | 恢复 backup 生成的备份包到原始 scope root | 仅 ROOT / ADMIN |
从源码结构看,权限控制在路由层通过装饰器实现:export 和 import 使用@require_auth_role(Role.ROOT, Role.ADMIN, Role.USER),而 backup 与 restore 使用更严格的@require_auth_root_or_admin(见 pack.py 路由 与 backup 路由)。服务层PackService在 backup/restore 时还会通过_account_maintenance_ctx将 ADMIN 上下文的角色提升为 ROOT 执行账号级维护,但保持 URI 归属不变(见 PackService)。
二、.ovpack 文件格式解剖
.ovpack本质是一个 ZIP 压缩包,导出时用户内容原样放在<root>/files/下,内部元数据放在<root>/_ovpack/下。这些目录名在 format.py 中定义为常量:OVPACK_INTERNAL_DIR = "_ovpack"、OVPACK_FILES_DIR = "files",包的类型标识为kind: "openviking.ovpack"。
包内关键文件:
| 文件 | 作用 |
|---|---|
<root>/_ovpack/manifest.json | 包清单,含format_version、kind、root、entries、index等字段 |
<root>/_ovpack/index_records.jsonl | 可迁移的索引标量记录(每行一个 JSON 对象) |
<root>/_ovpack/dense.f32 | 仅当include_vectors=true时存在;纯 dense float32 向量快照,little-endian |
<root>/files/... | 用户内容文件,路径相对于导出 root |
manifest 的核心语义(来自 manifest.py 与 format.py):
entries[].path是相对导出 root 的路径,空字符串""表示 root 目录本身;每个条目kind必须是directory或file。- 文件条目包含
size和sha256;整体content_sha256是对按路径排序后的文件列表(path、size、sha256)做规范化 JSON 序列化后的 SHA-256,实现见 manifest_content_sha256。 id、uri、account_id、created_at、updated_at、active_count等运行态字段会在目标环境重新生成,不从包内恢复——这是迁移不产生 ID 冲突的关键设计。- 当前支持的
format_version为3(OVPACK_FORMAT_VERSION = 3,见 format.py)。导入时版本不匹配会被直接拒绝,错误信息会同时给出包内版本与当前支持版本(见 read_manifest)。 - OVPack 不额外设置包大小、文件数量或目录深度上限;实际可处理规模由 ZIP、存储后端和运行环境决定。
安全方面,导入时对 ZIP 成员路径做了严格校验:拒绝反斜杠、绝对路径、Windows 盘符、..逃逸段,并要求所有条目位于<base_name>/之下(见 validate_ovpack_member_path),防止恶意构造的包污染存储目录。
三、export_ovpack:导出资源树
处理流程:验证用户权限 → 遍历指定 URI 下的资源 → 写入内容文件和 manifest → 打包成 zip(.ovpack)→ 以文件流形式返回。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| uri | string | 是 | - | 要导出的 Viking URI |
| include_vectors | boolean | 否 | false | 导出纯 dense 向量快照;底层 index type 为 hybrid 时会拒绝 |
权限要求:ROOT、ADMIN 或 USER,且仍受常规 URI 访问控制约束。
使用示例
HTTP API:
curl -X POST http://localhost:1933/api/v1/pack/export \ -H "Content-Type: application/json" \ -H "X-API-Key: your-admin-key" \ -d '{ "uri": "viking://resources/my-project/", "include_vectors": false }' \ --output my-project.ovpack此接口直接返回文件流(Content-Type: application/zip),不返回 JSON 包装体。从源码看,路由层先把包写入临时文件export_<随机hex>.ovpack,再经FileResponse流式返回,文件名取 URI 末段并补.ovpack后缀,响应完成后通过BackgroundTask清理临时文件(见 export_ovpack 路由)。
CLI:
# 导出资源 ov export viking://resources/my-project/ ./exports/my-project.ovpack # 导出 dense 向量快照 ov export viking://resources/my-project/ ./exports/my-project.ovpack --include-vectorsTypeScript SDK:
const outputPath = await client.exportOVPack( "viking://resources/docs/", "./exports/docs.ovpack", true, ); console.log(outputPath);Go SDK:
outPath, err := client.ExportOVPack( ctx, "viking://resources/my-project/", "./exports/my-project.ovpack", &openviking.PackOptions{IncludeVectors: false}, ) if err != nil { return err } fmt.Println(outPath)Python SDK(HTTP SDK 会自动处理下载;导出功能也主要通过 CLI 使用):
import openviking as ov client = ov.SyncHTTPClient(url="http://localhost:1933", api_key="your-admin-key") client.initialize()CLI 端ov export命令由 handle_export 接收入口,统一转发到 HTTP 客户端的 pack 命令模块。
四、import_ovpack:导入与迁移
处理流程:验证用户权限 → 解析上传的.ovpack文件 → 校验 manifest 元数据、路径、文件和目录集合、文件大小和 checksum → 应用on_conflict→ 导入资源到目标位置并重建向量。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| temp_file_id | string | 是 | - | 临时上传文件 ID(通过 temp_upload 获取) |
| parent | string | 是 | - | 目标父级 URI(导入到此处) |
| on_conflict | string | 否 | fail | 冲突策略:fail、overwrite或skip |
| vector_mode | string | 否 | auto | 向量处理方式:auto、recompute或require |
权限要求:ROOT、ADMIN 或 USER。请求模型对额外字段采用extra="forbid"策略,API 已不再接受旧的vectorize或force参数(见 ImportRequest)。
向量处理策略 vector_mode
auto:存在兼容 dense 快照时直接恢复,否则重新向量化;recompute:总是忽略包内向量,重新计算;require:要求必须存在兼容 dense 快照,否则导入失败。
dense 快照的兼容性会比较 embedding provider、model、input、query/document 参数和维度。从源码看,这些元数据在导出时由 embedding_snapshot_metadata 从当前 embedding 配置中采集(provider、model、input、query_param、document_param、dimensions),导入时逐一比对,任何一项不一致都视为不兼容。
完整性与冲突行为
导入校验相当严格,以下情况都会被拒绝:
- 没有 manifest 的包(无法提供内容完整性校验);
- 带 manifest entries 的包缺少内容文件或目录、混入额外文件或目录、文件大小不同、单文件
sha256不同,或整体content_sha256缺失/不匹配; - manifest
format_version不是当前支持版本(3)的包; viking://resources/这类顶级 scope 包必须导入到viking://。
冲突策略(root 级):
on_conflict=fail(默认):目标 root 已存在时返回结构化的409 CONFLICT;on_conflict=overwrite:替换已有目标 root;on_conflict=skip:保留已有目标 root,直接返回该路径,不写入包内容。注意skip是 root 级跳过,不是文件级补齐。
其他行为细节:
- Session 文件属于 user 命名空间(
viking://user/{user_id}/sessions/...),恢复后不触发向量化; .abstract.md和.overview.md作为语义侧边文件一并恢复;.relations.json和 OVPack 内部文件(_ovpack/)会被排除;- manifest index 标量中的
context_type如果存在,必须和最终导入路径语义一致; - OVPack 不额外设置导入包大小、文件数量或目录深度上限。
使用示例
HTTP API(两步:先 temp_upload 再 import):
# 第一步:上传 .ovpack 文件 TEMP_FILE_ID=$( curl -s -X POST http://localhost:1933/api/v1/resources/temp_upload \ -H "X-API-Key: your-admin-key" \ -F "file=@./exports/my-project.ovpack" \ | jq -r '.result.temp_file_id' ) # 第二步:导入 curl -X POST http://localhost:1933/api/v1/pack/import \ -H "Content-Type: application/json" \ -H "X-API-Key: your-admin-key" \ -d "{ \"temp_file_id\": \"$TEMP_FILE_ID\", \"parent\": \"viking://resources/imported/\", \"on_conflict\": \"overwrite\", \"vector_mode\": \"auto\" }"CLI:
# 导入 .ovpack 文件 ov import ./exports/my-project.ovpack viking://resources/imported/ # 显式冲突策略 ov import ./exports/my-project.ovpack viking://resources/imported/ --on-conflict overwrite # 要求恢复兼容 dense 向量快照 ov import ./exports/my-project.ovpack viking://resources/imported/ --vector-mode requireTypeScript SDK:
const uri = await client.importOVPack( "./exports/docs.ovpack", "viking://resources/", { onConflict: "overwrite", vectorMode: "auto", }, ); console.log(uri);Go SDK:
uri, err := client.ImportOVPack( ctx, "./exports/my-project.ovpack", "viking://resources/imported/", &openviking.ImportPackOptions{ OnConflict: "overwrite", VectorMode: "auto", }, ) if err != nil { return err } fmt.Println(uri)响应示例:
{ "status": "ok", "result": { "uri": "viking://resources/imported/my-project/" }, "telemetry": { "operation_id": "550e8400-e29b-41d4-a716-446655440000" } }冲突错误示例:
{ "status": "error", "error": { "code": "CONFLICT", "message": "Resource already exists at viking://resources/imported/my-project. Use on_conflict='overwrite' to replace it.", "details": { "resource": "viking://resources/imported/my-project" } } }从源码看,import 路由先用TempUploadStore.resolve_for_consume把temp_file_id解析为本地文件并校验归属,导入完成后在finally中清理临时文件(见 import_ovpack 路由),临时上传本身有独立的 temp_upload 接口 可查。
五、backup_ovpack:账号级全量备份
backup_ovpack将公开 scope root 备份为只能通过 restore 恢复的.ovpack文件。备份包含:
resources全部公开资源;- 当前账号下所有
user/{user_id}内容;session 通过 user 命名空间下的user/{user_id}/sessions一起包含。
不包含temp、queue等内部运行态数据,也不包含用户账号或 API Key。该接口仅允许 ROOT 或 ADMIN 调用。
设置include_vectors=true时额外导出兼容的纯 dense 向量快照;底层 index type 为 hybrid 时会拒绝导出向量快照。从源码看,这个限制由 ensure_dense_snapshot_supported 实现:它读取向量索引元数据中的VectorIndex.IndexType,一旦包含hybrid即抛出 "ovpack vector snapshots only support pure dense vector indexes" 错误。
重要限制:备份是在线逐文件读取,不保证同一时刻的原子快照。需要严格一致性时,调用方应在备份窗口暂停写入。
使用示例
HTTP API:
curl -X POST http://localhost:1933/api/v1/pack/backup \ -H "Content-Type: application/json" \ -H "X-API-Key: your-admin-key" \ -d '{"include_vectors":false}' \ --output openviking-backup.ovpackGo SDK:
outPath, err := client.BackupOVPack( ctx, "./backups/openviking.ovpack", &openviking.PackOptions{IncludeVectors: true}, ) if err != nil { return err } fmt.Println(outPath)CLI:
ov backup ./backups/openviking.ovpack ov backup ./backups/openviking.ovpack --include-vectors响应:HTTP 成功时返回application/zip字节流,不使用标准 JSON 响应包:
HTTP/1.1 200 OK Content-Type: application/zip Content-Disposition: attachment; filename="openviking-backup.ovpack" <ovpack binary body>Go SDK 和 CLI 将字节流写入指定路径,并返回或输出该本地路径。
六、restore_ovpack:备份恢复
restore_ovpack恢复backup_ovpack生成的备份包到原始公开 scope root;普通 import 不接受备份包。该接口仅允许 ROOT 或 ADMIN 调用,并恢复当前账号下包内所有用户路径。向量处理遵循vector_mode;user 命名空间下的 session 文件只恢复文件状态,不触发向量化。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| temp_file_id | string | 是 | - | 临时上传文件 ID |
| on_conflict | string | 否 | fail | 冲突策略:fail、overwrite或skip |
| vector_mode | string | 否 | auto | 向量处理方式:auto、recompute或require |
合并覆盖语义
on_conflict=overwrite使用合并覆盖:包内缺失于目标的路径会创建,同路径会覆盖,目标独有路径会保留;不会删除整个viking://resources或viking://user。向量只更新包内新增或覆盖的内容。skip仍是 scope root 级跳过,但返回前也会完整校验 manifest、内容 checksum 和向量元数据——损坏的备份不会因为skip而返回成功。
账号前提
OVPack 不创建用户账号或恢复 API Key。新环境恢复后,需要使用包内相同的user_id创建用户,并使用目标环境新生成的 API Key。
使用示例
HTTP API:
TEMP_FILE_ID=$( curl -s -X POST http://localhost:1933/api/v1/resources/temp_upload \ -H "X-API-Key: your-admin-key" \ -F "file=@./backups/openviking.ovpack" \ | jq -r '.result.temp_file_id' ) curl -X POST http://localhost:1933/api/v1/pack/restore \ -H "Content-Type: application/json" \ -H "X-API-Key: your-admin-key" \ -d "{\"temp_file_id\":\"$TEMP_FILE_ID\",\"on_conflict\":\"overwrite\",\"vector_mode\":\"auto\"}"Go SDK:
uri, err := client.RestoreOVPack( ctx, "./backups/openviking.ovpack", &openviking.ImportPackOptions{ OnConflict: "overwrite", VectorMode: "require", }, ) if err != nil { return err } fmt.Println(uri)CLI:
ov restore ./backups/openviking.ovpack --on-conflict overwrite ov restore ./backups/openviking.ovpack --on-conflict overwrite --vector-mode require响应:
{ "status": "ok", "result": { "uri": "viking://" } }uri是备份恢复到的公开 scope root。
七、源码调用链速览
从源码结构看,OVPack 的完整调用链为三层:
HTTP 路由(openviking/server/routers/pack.py) └─ PackService(openviking/service/pack_service.py) └─ openviking/storage/ovpack/operations.py ├─ format.py # ZIP 路径、checksum、路径安全校验 ├─ manifest.py # manifest 解析与结构校验 ├─ index.py # 索引标量记录 ├─ policy.py # root/scope 策略 ├─ validation.py # 内容完整性校验 └─ vectors.py # dense 快照与兼容性 CLI:crates/ov_cli/src/handlers.rs(handle_export / handle_import / handle_backup / handle_restore)关键实现细节对应关系:
- 导出/备份流式返回:路由层生成随机临时文件 →
FileResponse以application/zip流式下发 →BackgroundTask清理(见 pack.py); - 备份/恢复提权:
_account_maintenance_ctx仅允许 ROOT/ADMIN,并将执行上下文角色提升为 ROOT 以覆盖账号下全部 user 命名空间(见 pack_service.py); - 格式版本硬约束:
format_version != 3的包直接拒绝,避免跨版本数据损坏(见 manifest.py); - 混合索引保护:hybrid 索引下禁用向量快照导出,防止快照语义失真(见 vectors.py)。
八、相关文档
- OVPack 指南 - 格式、迁移和操作流程
- 快照 - 工作区版本管理
- 临时上传 - 上传待导入的包
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考