OpenViking 存储架构解析:VikingFS URI 抽象层、AGFS 内容存储与向量库索引的双层设计
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
本文基于 OpenViking 概念文档「存储架构」展开,完整解析其双层存储模型:VikingFS(URI 抽象层)如何屏蔽底层差异、AGFS 如何作为唯一内容数据源、向量库如何只存引用不存内容,以及 VikingFS 如何在删除/移动时自动同步向量索引。读完本文,你将理解 OpenViking 上下文数据的落盘结构、上下文集合的完整 Schema、向量库多后端接入机制,以及多写模式的配置要点,足以支撑你针对自己的部署场景选择合适的存储后端并保持索引与内容的一致性。
概览:双层存储架构
OpenViking 采用双层存储架构,分离内容存储和索引存储。整体结构如下:
┌─────────────────────────────────────────┐ │ VikingFS (URI 抽象层) │ │ URI 映射 · 层级访问 │ └────────────────┬────────────────────────┘ ┌────────┴────────┐ │ │ ┌───────▼────────┐ ┌─────▼───────────┐ │ 向量库索引 │ │ AGFS │ │ (语义搜索) │ │ (内容存储) │ └────────────────┘ └─────────────────┘上层是 VikingFS 提供的统一 URI 抽象层,负责 URI 映射与层级访问;下层则分为两个独立角色:
| 存储层 | 职责 | 存储内容 |
|---|---|---|
| AGFS | 内容存储 | L0/L1/L2 完整内容、多媒体文件 |
| 向量库 | 索引存储 | URI、向量、元数据(不存文件内容) |
设计优势
- 职责清晰:向量库只负责检索,AGFS 负责存储
- 内存优化:向量库不存储文件内容,节省内存
- 单一数据源:所有内容从 AGFS 读取,向量库只存引用
- 独立扩展:向量库和 AGFS 可分别扩展
注:AGFS 已经重写为 Rust 实现(RAGFS),对应仓库中的
crates/ragfscrate 及其 Python 绑定crates/ragfs-python。
这一「内容与索引分离」的取舍在源码中体现得非常直接:向量记录中的content字段只写入截断后的文本(受VIKINGDB_CONTENT_MAX_SIZE约束,见 collection_schemas.py 中TextEmbeddingHandler._materialize_content),用于 VikingDB 系后端的服务端全文检索,而文件的完整内容始终留在 AGFS 一侧,检索命中后通过 URI 回到 AGFS 读取原文。
VikingFS 虚拟文件系统
VikingFS 是统一的 URI 抽象层,屏蔽底层存储细节。它的实现位于 openviking/storage/viking_fs 目录,按职责拆分为多个模块:_base.py(基础操作)、_access.py(访问控制)、_vector.py(向量同步)、_semantic.py(语义检索)、_sync.py(目录同步原语)等,对上层 SDK、HTTP API 和 CLI 暴露一致的文件系统接口。
URI 映射
viking://resources/docs/auth → /local/{account_id}/resources/docs/auth viking://~/memories → /local/{account_id}/user/{user_id}/memories viking://~/skills → /local/{account_id}/user/{user_id}/skills可以看到 URI 映射隐含了租户隔离:resources挂在账号(account_id)维度,~/开头的个人空间则进一步按user_id隔离。完整的 URI 规范见 Viking URI。
核心 API
| 方法 | 说明 |
|---|---|
read(uri) | 读取文件内容 |
write(uri, data) | 写入文件 |
mkdir(uri) | 创建目录 |
rm(uri) | 删除文件/目录(同步删除向量) |
mv(old, new) | 移动/重命名(同步更新向量 URI) |
abstract(uri) | 读取 L0 摘要 |
overview(uri) | 读取 L1 概览 |
find(query, uri) | 语义搜索 |
其中abstract和overview对应上下文层级的 L0/L1 两层(详见 上下文层级),find则是走向量库的语义搜索入口。
AGFS 底层存储
AGFS 提供 POSIX 风格的文件操作,支持多种后端。Python 侧的客户端接口封装在 openviking/pyagfs,包含async_client.py、protocols.py等模块。
单后端与多写模式
默认情况下,AGFS 使用一个后端作为内容存储。配置storage.agfs.backups后,OpenViking 会启用多写模式:
- 顶层
storage.agfs.backend是 primary,作为权威写入目标。 storage.agfs.backups.items[]是 backup,用于副本、迁移或读加速。- Python SDK、HTTP API 和 CLI 的文件系统接口保持不变。
- 多写内部使用
.redirect.json和.sync_log.json维护 redirect 映射与同步进度,这些文件对用户不可见。
更多概念说明见 多写存储,配置示例见 多写存储指南。
后端类型
| 后端 | 说明 | 配置 |
|---|---|---|
localfs | 本地文件系统 | path |
s3fs | S3 兼容存储 | bucket,endpoint |
memory | 内存存储(测试用) | - |
目录结构
每个上下文目录遵循统一结构:
viking://resources/docs/auth/ ├── .abstract.md # L0 摘要 ├── .overview.md # L1 概览 └── *.md # L2 详细内容L0/L1/L2 的命名规则与向量集合中的level字段一一对应:level=0对应{目录}/.abstract.md,level=1对应{目录}/.overview.md,level=2(默认)对应实际文件路径。这一约定直接写在集合 Schema 的字段注释中(见 collection_schemas.py 的CollectionSchemas.context_collection)。
向量库索引
向量库存储语义索引,支持向量搜索和标量过滤。
Context 集合 Schema
概念文档给出的核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 主键 |
uri | string | 资源 URI |
parent_uri | string | 父目录 URI |
context_type | string | resource/memory/skill |
is_leaf | bool | 是否叶子节点 |
vector | vector | 密集向量 |
sparse_vector | sparse_vector | 稀疏向量 |
abstract | string | L0 摘要文本 |
name | string | 名称 |
description | string | 描述 |
created_at | string | 创建时间 |
active_count | int64 | 使用次数 |
对照当前仓库的实现,collection_schemas.py 中context_collection的 Schema 在上述基础上还有几处值得注意的细节:
- 字段类型:
uri的FieldType是path(而非普通 string),vector字段声明了Dim(由config.embedding.dimension决定); context_type的推导规则(源码注释):URI 位于 user skills 目录下 →"skill";URI 包含"memories"→"memory";其他情况 →"resource";- 额外的标量字段:
level(L0/L1/L2 层级)、tags、search_tags(list<string>)、content(text 类型)、account_id、owner_user_id,以及一组 ACL 相关字段(acl_mode及 grant 字段); - ScalarIndex:
uri、type、context_type、created_at、updated_at、active_count、level、name、tags、search_tags、account_id、owner_user_id及 ACL 字段均建有标量索引,保证「向量召回 + 标量过滤」的复合查询可以下推到向量库执行; - FullText:
content字段配置了全文索引(standard分词器,过滤 symbol 停用词)。collection_schemas.py 中init_context_collection还有一段兼容逻辑:若既有集合缺少content/FullText 配置,只会在grep engine=auto时回退到文件系统 grep,而不是阻止服务启动。
此外,集合创建时还会把当前嵌入模型的元数据(provider/model/dimension/model_identity)编码进集合描述中。后续启动时若发现元数据与当前配置不一致,会抛出EmbeddingRebuildRequiredError要求重建;若仅 provider/model 变化而维度不变,可通过embedding.allow_metadata_override=true显式保留既有向量。
索引策略
index_meta = { "IndexType": "flat_hybrid", # 混合索引 "Distance": "cosine", # 余弦距离 "Quant": "int8", # 量化方式 }从源码看,IndexType 的实际取值只有两种(见 validation.py):
flat:仅密集向量的 flat 索引;flat_hybrid:密集 + 稀疏混合索引。
在 vectordb_adapters/base.py 中可以看到默认策略:index_type = "flat_hybrid" if use_sparse else "flat",即是否启用混合索引取决于稀疏向量(sparse embedding)是否开启。
后端支持
| 后端 | 说明 |
|---|---|
local | 本地持久化 |
http | HTTP 远程服务 |
volcengine | 火山引擎 VikingDB |
当前仓库的适配层位于 openviking/storage/vectordb_adapters,factory.py 中的注册表实际包含五个内置后端:
_ADAPTER_REGISTRY: dict[str, type[CollectionAdapter]] = { "local": LocalCollectionAdapter, "cuvs": CuVSCollectionAdapter, "http": HttpCollectionAdapter, "volcengine": VolcengineCollectionAdapter, "vikingdb": VikingDBPrivateCollectionAdapter, }即除了文档列出的三个,还支持cuvs(本地 GPU 索引)与vikingdb(私有化部署)。create_collection_adapter(config)按config.backend路由;若 backend 值包含.,则按完整类路径动态加载第三方 Adapter,并可通过custom_params传递自定义参数。完整的接入步骤(新增 Adapter 类、实现from_config/_load_existing_collection_if_needed/_create_backend_collection、注册到 factory、补充配置)见 vectordb_adapters/README.md。
两点实现细节:
- 懒加载集合:各 Adapter 通过
_load_existing_collection_if_needed懒加载已存在的 collection handle,不存在时保持为空,后续按需创建; - content 字段策略:Adapter 基类的
USE_CONTENT_FIELD类属性默认为False,仅 VikingDB 系后端开启,写入时其他后端会自动跳过content字段,避免无谓的全文存储开销。
向量同步
VikingFS 自动维护向量库与 AGFS 的一致性,相关逻辑收敛在openviking/storage/viking_fs/_vector.py(向量读写同步)与_sync.py(目录级 diff 与 mv/rm 合并原语)中。
删除同步
viking_fs.rm("viking://resources/docs/auth", recursive=True) # 自动递归删除向量库中所有 uri 以此开头的记录即删除 AGFS 侧目录时,向量库中所有uri以该路径为前缀的记录会被一并清理,不会出现「幽灵向量」指向已删除内容的问题。
移动同步
viking_fs.mv( "viking://resources/docs/auth", "viking://resources/docs/authentication" ) # 自动更新向量库中的 uri 和 parent_uri 字段mv在重命名目录后会自动把受影响向量记录的uri与parent_uri字段更新为新路径,保证向量库中的引用链与 AGFS 目录树始终对应。从_sync.py的实现可以看到,目录级同步(sync_tree)采用「自上而下的递归 diff + 移动合并」策略:对可见子节点做增/删/改对比(.开头的隐藏文件被跳过,parser 副产物如.image_mappings.json由调用方显式携带),并通过SyncDiff结构返回目标侧的变更清单,供增量重建索引的管线消费。
总结与延伸阅读
回到双层架构的四个核心收益:向量库只管检索、内容统一从 AGFS 读取、引用与原文一一对应、两层可独立扩展。理解这套架构后,再结合以下几个文档可以补全全貌:
- 架构概述 - 系统整体架构
- 上下文层级 - L0/L1/L2 模型
- Viking URI - URI 规范
- 多写存储 - primary/backup、多写路由与一致性
- 检索机制 - 检索流程详解
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考