OpenViking — Agent 上下文数据库:架构与原理
30 秒导读: OpenViking 是给 AI Agent 用的上下文数据 库。它不把资料切成一堆零散向量丢进 RAG,而是把 Agent 需要的三样东西——记忆、资源、技能——统一成一棵可以
ls/find/grep的虚拟文件系统树(根是viking://)。每个节点在写入时就被自动切成三层摘要(L0 一句话 / L1 概览 / L2 全文),检索时先锁定高分目录、再逐层往里细化,只把真正相关的那几片全文喂给模型。会话结束还能自动抽取记忆回写,让 Agent 越用越聪明。
1. 这是什么(零基础也能懂)
一句话定义: OpenViking 把"Agent 的大脑"做成一个能像文件系统一样操作的数据库——记忆、资源、技能都是这棵树上的"文件/目录",每个都有唯一地址(URI)。
它想解决的痛点。 传统 Agent 的上下文有几个老毛病,OpenViking 一条条对着治:
| 老毛病 | 传统做法 | OpenViking 的解法 |
|---|---|---|
| 上下文散落各处 | 记忆写死在代码、资源塞进向量库、技能到处放 | 统一成一棵 viking:// 虚拟文件系统树 |
| 上下文越滚越大 | 简单截断/压缩,信息丢失 | L0/L1/L2 三层,按需加载 |
| 检索效果差 | 扁平存储、只做一次向量召回 | 目录递归检索,兼顾语义与全局结构 |
| 检索过程是黑盒 | 隐式召回链,出错难查 | 目录浏览轨迹全程留痕、可视化 |
| 记忆只会记用户 | 缺 Agent 自身的任务经验 | 会话结束抽取用户记忆 + Agent 经验 |
给谁用。 写 AI Agent 的开发者。把 OpenViking 当成一个独立 HTTP 服务跑起来,Agent 通过 CLI / SDK 往里存资料、取上下文,不用再自己管一堆向量库和记忆逻辑。
用起来什么样。 一段最小的命令行交互,直观感受"上下文即文件":
ov add-resource https://github.com/volcengine/OpenViking # 把一个仓库塞进上下文库
ov ls viking://resources/ # 像列目录一样看有什么
ov tree viking://resources/volcengine -L 2 # 看目录树
ov find "what is openviking" # 语义检索,按需取相关片段
ov grep "openviking" --uri viking://resources/volcengine/OpenViking/docs/zh
一句话直觉。 把它想成 Agent 的一块"带语义索引的磁盘":目录结构给你全局视野和精确寻址(像真文件系统),向量索引给你模糊语义召回(像 RAG),两者长在同一棵树上。
2. 顶层全景(它大概怎么转)
先看两条主线:资料怎么进来(写)、上下文怎么出去(读)。中间夹着一个"每个节点切三层"的关键动作。
怎么读这张图: 上半是写入路径(左→右),下半是检索路径(右→左往回取),中间的 VikingFS 是所有操作的统一门面,底下是两套存储引擎。
┌───────────────────────────────────────────────┐
写入 (add-resource) │ ① 解析 parse/ ② 建树 core/building_tree │
原始文件/网页/仓库 ───▶ │ 抽文本·识别类型 ───▶ 一个节点一个 Context │
│ 每个节点切 L0/L1/L2 │
└───────────────────────┬───────────────────────┘
│ 写 .abstract.md/.overview.md
│ + 三层各自向量化
▼
┌────────────────────────── VikingFS(统一门面 / viking:// 寻址) ──────────────────────────┐
│ ls · tree · read · write · find · grep · 权限 · 加密 · 命名空间路由 │
└───────────────┬───────────────────────────────────────────────┬──────────────────────────┘
│ │
文件内容/目录树 向量与标量索引
▼ ▼
┌────────────────────────┐ ┌────────────────────────────┐
│ RAGFS(Rust 聚合文件系统)│ │ 向量库 vectordb(C++ 引擎) │
│ 真实落盘 / 加密 / 多后端 │ │ ANN 召回 + 稀疏检索 + KV 存储 │
└────────────────────────┘ └──────────────── ────────────┘
▲ ▲
检索 (find/search) │ ④ 目录递归:先锁高分目录 → 逐层 search_children → 分数传播 │
查询 ────────────▶│ ③ 意图分析 intent_analyzer → 生成多路检索条件 │
└────────────── retrieve/hierarchical_retriever ─────────────┘
会话结束 (commit) ──▶ ⑤ session 抽取:压缩对话 → 抽用户记忆 + Agent 经验 → 回写 viking://user/...
部件一句话职责:
| 部件 | 干什么 | 关键位置(符号) |
|---|---|---|
| VikingFS | 所有上下文操作的统一门面,负责 viking:// 寻址、权限、加密、命名空间路由 | openviking/storage/viking_fs.py:253(VikingFS) |
| 解析层 parse | 把文件/网页/仓库抽成文本、识别资源类型 | openviking/parse/parser_router.py、understanding_api.py:35(UnderstandingAPI) |
| 建树 building_tree | 把解析结果组织成一棵 Context 节点树 | openviking/core/building_tree.py:11(BuildingTree) |
| 分层写入 | 每个节点生成 L0/L1/L2 并各自向量化 | openviking/storage/content_write.py:47(ContentWriteCoordinator) |
| 意图分析 | 把用户查询拆成多路检索条件 | openviking/retrieve/intent_analyzer.py:38(IntentAnalyzer) |
| 目录递归检索 | 先锁高分目录、逐层细化的核心算法 | openviking/retrieve/hierarchical_retriever.py:350(_recursive_search) |
| RAGFS | Rust 写的聚合文件系统,真实存文件、管加密与多后端 | crates/ragfs/src/core/filesystem.rs:97(FileSystem trait) |
| 向量库引擎 | C++ 写的 ANN 索引 + 稀疏检索 + KV 存储 | src/index/index_engine.h:14(IndexEngine)、src/store/kv_store.h:10(KVStore) |
| 会话记忆 | 会话结束抽取用户记忆与 Agent 经验并回写 | openviking/session/memory/extract_loop.py:82(ExtractLoop) |
主线走一遍(高层,不进代码):
- 写:
add-resource→ 解析层抽文本、识别类型 →BuildingTree建成节点树 → 每个节点由ContentWriteCoordinator生成.abstract.md(L0)/.overview.md(L1),三层分别向量化 → 文件落进 RAGFS、向量落进 C++ 向量库。 - 读:
find "…"→IntentAnalyzer把查询拆成多路条件 → 向量召回先定位到高分目录 →_recursive_search进入该目录search_children、把子节点分数与父目录分数传播融合,若还有子目录就递归下钻 → 汇总最相关的若干片段返回。 - 自迭代: 会话
commit→ 压缩对话与工具调用 → 抽取"用户偏好"和"Agent 经验/轨迹" → 回写到viking://user/{id}/memories等目录,下次直接可检索。
三条主线的细节各有一章,见下方阅读地图。
3. 阅读地图(建议按顺序读)
六章由浅入深,前三章讲"范式与两条主线",后三章往存储与底层引擎钻:
- 文件系统范式:viking:// URI 与三类上下文 —— 为什么用"文件系统"而不是扁平向量;
viking://地址怎么分resources/user/skills/peers;三类上下文(SKILL / MEMORY / RESOURCE)如何统一寻址。 - 写入路径:解析 → 建树 → L0/L1/L2 分层 —— 一份资料从原始文件到"三层摘要节点树"的完整流水:解析、识别类型、建
Context树、生成 L0/L1、三层各自向量化落库。 - 目录递归检索:先锁高分目录再逐层细化 —— 核心算法:意图分析拆多路条件 → 目录优先队列 →
search_children逐层下钻 → 父子分数传播融合 → 收敛与聚合;以及检索轨迹为何可观测。 - 存储层:VikingFS 门面、RAGFS 虚拟文件系统与向量库 ——
VikingFS如何做统一门面(寻址/权限/加密/命名空间);Rust 的 RAGFS 怎么真实落盘、支持加密与多后端;向量库如何按目录组织索引。 - 会话记忆自迭代:v3 抽取、用户记忆与 Agent 经验 —— 会话压缩、
commit触发抽取、ExtractLoop如何产出"用户记忆"与"Agent 经验/轨迹",以及回写与合并策略。 - 底层引擎:C++ ANN 索引 + KV 存储(及 Rust 绑定) —— 最底层:C++ 的
IndexEngine(向量召回 + 稀疏检索 + 标量过滤)与KVStore,通过 abi3 PyCapsule 暴露给 Python;RAGFS 的 Rust→Python 绑定。
4. 巧妙之处(读者要带走的精华)
① 用"文件系统"当上下文的统一抽象,一举解决三个问题。 把记忆/资源/技能都映射成 viking:// 下的目录节点,于是 Agent 可以用 ls/find/grep 这类确定性命令精确寻址,而不只是"模糊语义匹配"。同一套抽象顺带解决了"碎片化"和"黑盒不可观测"——因为每一步都是可见的文件操作。三类上下文用同一个枚举收口(openviking/core/context.py:26,ContextType = SKILL/MEMORY/RESOURCE)。
② 分层在"写入时"就做好,把省 token 前置。 传统 RAG 是查询时才压缩;OpenViking 在写入时就把每个节点切成 L0/L1/L2(openviking/core/context.py:34,ContextLevel ABSTRACT=0 / OVERVIEW=1 / DETAIL=2),派生出 .abstract.md、.overview.md 两个小文件(openviking/storage/content_write.py:41,_DERIVED_FILENAMES)。检索时先用 L0/L1 判断相关性,只有真需要才加载 L2 全文——渐进式披露落到了存储结构上。
③ 目录递归 + 分数传播,让召回带上"全局上下文"。 不是一次性向量召回 top-k,而是把命中先归到目录,进入目录再对子节点二次检索,并把父目录分数按 final = α·子分数 + (1-α)·父分数 融合传播(openviking/retrieve/hierarchical_retriever.py:350,_recursive_search)。好处:既找到语义最像的片段,又保留了"它在整棵树哪个位置"的全局信息;用优先队列 + 并发子目录搜索控制成本。
④ Python 编排、Rust 存文件、C++ 管索引——各用所长。 上层业务用 Python 写(灵活、好接模型);真实文件系统用 Rust 的 RAGFS(内存安全、并发、可插拔多后端与加密,crates/ragfs/src/core/filesystem.rs:97);向量/标量索引这种性能热点用 C++ 引擎,通过 abi3 稳定 ABI 的 PyCapsule 暴露给 Python(src/abi3_engine_backend.cpp:24,kIndexCapsuleName / kStoreCapsuleName)。三层语言边界清晰。
⑤ 记忆会"自迭代",还分用户与 Agent 两类。 会话 commit 时不仅抽"用户偏好",还抽"Agent 的操作经验/轨迹"(openviking/session/memory/agent_experience_context_provider.py、agent_trajectory_context_provider.py),回写成可检索的记忆节点——这正是它宣称"越用越聪明"的机制,也补上了传统记忆"只记用户不记 Agent"的缺口。
5. 代码地图(导航索引)
按"想看哪块就跳哪"组织。行号 as-of sourceCommit;行号漂移时用符号名 grep 更稳。
| 主题 | 文件路径 | 关键符号 |
|---|---|---|
| viking:// 命名空间与路由 | openviking/core/namespace.py | classify_uri、canonical_user_root、_CONTENT_TYPES_BY_SCOPE(:13) |
| 三类上下文 / 三层枚举 | openviking/core/context.py | ContextType(:26)、ContextLevel(:34)、Context(:61) |
| 建树 | openviking/core/building_tree.py | BuildingTree(:11)、to_directory_structure(:77) |
| 解析层(建 Context 树) | openviking/parse/tree_builder.py | TreeBuilder(:39) |
| 解析层(理解 API / VLM) | openviking/parse/understanding_api.py | UnderstandingAPI(:35) |
| 分层写入 / 生成 L0·L1 | openviking/storage/content_write.py | ContentWriteCoordinator(:47)、_DERIVED_FILENAMES(:41) |
| 统一门面 VikingFS | openviking/storage/viking_fs.py | VikingFS(:253)、init_viking_fs(:166) |
| 意图分析 | openviking/retrieve/intent_analyzer.py | IntentAnalyzer(:38) |
| 目录递归检索 | openviking/retrieve/hierarchical_retriever.py | HierarchicalRetriever(:45)、_recursive_search(:350)、search_children(:413) |
| 向量后端(Python 侧) | openviking/storage/viking_vector_index_backend.py | VikingVectorIndexBackend(:644)、_SingleAccountBackend(:93) |
| C++ 引擎的 Python 封装 | openviking/storage/vectordb/engine/_python_api.py | build_abi3_exports(:414)、IndexEngine(:420) |
| 会话与压缩 | openviking/session/session.py、compressor_v3.py | SessionCompression(:214) |
| 会话服务(commit 触发抽取) | openviking/service/session_service.py | SessionService(:33)、commit(:249)、commit_async(:272) |
| 记忆抽取主循环 | openviking/session/memory/extract_loop.py | ExtractLoop(:82) |
| Agent 经验 / 轨迹记忆 | openviking/session/memory/agent_experience_context_provider.py | AgentExperienceContextProvider(:42) |
| C++ 向量索引引擎 | src/index/index_engine.h | IndexEngine(:14)、add_data(:21)、search(:25) |
| C++ KV 存储 | src/store/kv_store.h | KVStore(:10)、exec_op(:14)、seek_range(:26) |
| C++↔Python abi3 桥 | src/abi3_engine_backend.cpp | kIndexCapsuleName(:24)、kStoreCapsuleName(:25) |
| RAGFS 文件系统抽象(Rust) | crates/ragfs/src/core/filesystem.rs | FileSystem trait(:97) |
| RAGFS 装配(Rust) | crates/ragfs/src/core/builder.rs | RagfsStack(:41)、RagfsConfig(:27) |
| RAGFS 的 Python 绑定 | crates/ragfs-python/src/lib.rs | CacheProviderFactory、缓存/后端配置 |
说明:
repo取自克隆内README.md声明的volcengine/OpenViking;其余结论均以sourceCommit处真实源码为准。上游若未改动本页所引文件,结论仍成立,仅需重盖sourceCommit。