一、为什么要单独看 OpenViking
OpenViking 值得分析,不是因为它最近热,而是因为它踩中了 Agent 系统里最难啃的一块:上下文管理。
模型这两年进步很快,但一个真正能连续工作的 Agent,问题常常不出在模型本身,而出在下面这些地方:
- 用户偏好放哪儿
- 历史任务怎么留
- 项目文档和代码怎么组织
- 当前会话里的临时信息怎么接住
- 做完任务以后,哪些东西该沉淀成长期记忆
传统 RAG 更像一个"文本碎片检索器":把文档切块、向量化,再从一堆 chunk 里挑若干片段塞进 prompt。这个办法做 FAQ 或单轮问答没什么问题,但场景换成长任务、多步骤执行,问题就会变得很具体:
- 目录结构和上下文边界被切碎了。
- 检索拿到的内容局部相关,全局却可能跑偏。
- 为了保险,系统容易把太多文本塞进模型,token 成本一路涨。
- 出了问题不好查,因为很难解释检索链路到底是怎么走偏的。
OpenViking 想处理的就是这一类问题。它没有沿着"做一个更强的向量库"这条路继续卷,而是换了个抽象:把 Agent 的上下文当成文件系统来组织。
官方对它的定义是 open-source context database for AI Agents。这个定义里最值得看的是后半句——它并不把上下文理解成一堆孤立文本,而是理解成一个有目录、有层次、能递归导航的空间。
先看一张总览图:
我更愿意把 OpenViking 看成一层上下文操作层。它上面接 Agent 或应用,下面接 embedding、模型服务和本地存储,中间负责三件事:
- 统一命名空间
- 分层加工上下文
- 按路径把正确内容送到模型面前
二、它的核心思路:别再把上下文只当 chunk 池
OpenViking 的关键变化,不是检索接口换了个名字,而是上下文抽象变了。
在传统 RAG 里,一份知识通常会经历这样的路径:
- 文档
- chunk 切分
- embedding
- 全局相似度检索
- TopK 拼 prompt
在 OpenViking 里,更接近下面这个过程:
- 资源进入
viking://命名空间 - 资源被组织成目录和文件
- 每一层目录都生成摘要和概览
- 查询先定位目录,再逐层下钻
- 最后只读取真正要看的细节
这两个思路的区别,看起来只是"多了一层目录",其实差别很大。
很多 Agent 任务,本来就不是"找到最像的三段文本",而是:
- 先找到正确的项目空间
- 再找到正确的模块或资料目录
- 再决定读哪几个文件
- 最后把少量细节交给模型
人类处理复杂项目资料时,基本也是这么干的。先找位置,再看内容。OpenViking 只是把这个过程做成了系统能力。
三、架构设计:OpenViking 到底由哪些部分组成
从官网描述、README 和仓库结构看,OpenViking 的架构可以拆成四层。
3.1 命名空间层:viking://
第一层是统一寻址。官方示例中,所有上下文都放在 viking:// 协议下,大致长这样:
viking://
├── resources/
│ └── my_project/
├── user/
│ └── memories/
└── agent/
├── skills/
├── memories/
└── instructions/
这层设计看起来简单,但非常关键。
它至少解决了三个工程问题:
- 资源、记忆、技能不再散落在不同后端,地址统一了。
- 目录关系能保留下来,不会一上来就被切成无父无子的碎片。
- 后面的递归检索终于有了真正可以走的路径。
3.2 表示层:L0 / L1 / L2
这是 OpenViking 公开资料里出现最频繁的一组概念:
- L0:一句摘要,官方给出的量级大约是 100 tokens。
- L1:结构化概览,量级大约是 2k tokens。
- L2:完整内容,只有真的需要时才加载。
这不是普通的"摘要 + 原文"两层结构,而更像一个分辨率切换机制。
- 系统刚开始判断相关性时,看 L0 就够了。
- 进入规划或决策阶段时,再看 L1。
- 需要实现细节、代码或完整上下文时,才读 L2。
真正有意思的是,README 里给出的例子不只给文件生成 .abstract 和 .overview,目录本身也会有自己的摘要和概览。这样一来,目录不是静态容器,而成了可检索节点。
3.3 检索层:递归式检索引擎
OpenViking 最像它自己的地方,就在这儿。
它不是一上来就在全局语料里捞 TopK,而是先找高分目录,再进目录里继续找。如果还有更细的子目录,就继续往下走。
这条链路可以概括成下面几步:
- 分析查询意图,扩展检索条件。
- 在 L0 / L1 层定位高相关目录。
- 进入目录后继续检索子目录和文件。
- 在更小的候选集合里继续筛选。
- 只在最后阶段读取 L2 详情。
对应的思路,可以用一段很短的伪代码说明:
def recursive_retrieve(query, node):
intents = expand_query_intent(query)
candidates = semantic_search(node.children_summaries(), intents)
hits = []
for child in top_k(candidates):
if child.is_directory:
hits.extend(recursive_retrieve(query, child))
else:
hits.append(child)
return rerank_and_load_details_on_demand(hits)
这不是源码,只是把 README 里的公开思路压成了一个更好懂的形状。重点不在代码,而在背后的策略:先收缩搜索空间,再读细节。
3.4 会话与记忆层
很多检索系统擅长"查",但不太擅长"用完以后怎么办"。Agent 系统不一样,它必须处理回写问题:
- 用户偏好要不要沉淀成长期记忆
- 新生成的文档要不要进入项目资源
- 这次任务学到的执行经验要不要被 Agent 复用
OpenViking 的公开说明里提到自动会话管理、记忆压缩和长期记忆抽取。换句话说,它不只是 retrieval store,还试图负责记忆的回流。
3.5 工程形态:不是单语言项目
从仓库结构能看到,OpenViking 并不是把所有东西都塞在一个 Python 包里:
openviking:核心 Python 包crates/ov_cli、openviking_cli:Rust CLI- AGFS:README 提到需要 Go 1.22+ 构建
src和 native extension:承接性能敏感部分bot:面向 Agent / Bot 的接入层
这套拆法挺务实。Python 负责生态集成和开发效率,Rust 适合做 CLI,Go 负责部分系统能力,底层扩展则用来处理性能热点。
四、工作流程:一次查询在 OpenViking 里会怎么走
下面这张图可以先把检索路径看清楚:
如果按一次真实查询来拆,流程大致是这样的。
4.1 资源接入
第一步通常是把外部资料收进 viking://resources/。官方 CLI 示例里,直接把一个 GitHub 仓库加进来就行:
openviking-server
ov add-resource https://github.com/volcengine/OpenViking --wait
ov tree viking://resources/volcengine -L 2
这一步背后通常会发生几件事:
- 拉取或解析外部资源
- 把内容映射为目录和文件
- 为目录和文件生成 L0 / L1 表示
- 为后续检索建立向量表示
4.2 查询进入系统
当 Agent 抛出一个问题,比如"这个项目的上下文加载机制怎么做",OpenViking 不会立刻把一大段正文推给模型。它先会在摘要层做粗定位,先判断:
- 相关信息更可能在
resources/、user/还是agent/ - 哪个目录最值得先进去看
- 哪些目录可以直接排除
4.3 目录内继续细化
一旦命中了某个目录,检索空间就从全局语料缩到局部子树。接下来的步骤不是"重复全局搜索",而是在目录内部继续看:
- 子目录摘要
- 文件摘要
- 局部候选的相关性
如果信息还不够,就继续往下钻。
4.4 按需读取 L2
只有当系统判断"这份文件真的值得读"时,才会把完整内容拿出来。这个延迟加载很重要,因为它直接决定了 token 成本。
传统 RAG 的典型问题是过早读太多内容。OpenViking 的做法刚好相反:能不碰 L2 就先别碰,直到最后一刻再说。
4.5 任务结果回写
任务结束后,链路还没完。系统还得决定哪些东西应该留下:
- 稳定的用户偏好写入
user/memories - Agent 的经验写入
agent/memories - 新产出的文档、代码或说明写回
resources/
这也是 OpenViking 和很多"只负责查资料"的系统不太一样的地方。它更像一个持续演化的工作空间。
五、技术实现细节:它为什么能省 token,还能把完成率拉上去
5.1 省 token,不只是因为有摘要
OpenViking 的公开结果里,最容易被注意到的是成本下降。但如果把原因简单归结成"它有摘要,所以更省",其实说浅了。
更准确一点,它是在四个地方一起省:
1)目录摘要先挡掉无关区域
不是每次都要在全局语料里做一轮大规模召回。很多无关内容在目录层就被刷掉了,根本轮不到 L2 出场。
2)L0 / L1 让平均读取粒度变小
很多问题到 L1 就已经足够判断下一步,不需要完整原文。系统平均读取的文本量自然会降下来。
3)递归下钻会一路缩小候选集合
每深入一层,候选空间都更小。后面的 rerank 更容易做准,误召回也更少。
4)记忆回写减少重复检索
一些任务结果一旦沉淀成用户记忆或 Agent 记忆,后面就不用每次都从原始资源里重新翻一遍。
5.2 公开 benchmark 说明了什么
README 里给出了一组基于 LoCoMo10 和 OpenClaw Context Plugin 的公开结果。先看图:
对应数据如下:
| 实验组 | 任务完成率 | 输入 Token 总成本 |
|---|---|---|
| OpenClaw(memory-core) | 35.65% | 24,611,530 |
| OpenClaw + LanceDB(关闭原生 memory) | 44.55% | 51,574,530 |
| OpenClaw + OpenViking Plugin(关闭原生 memory) | 52.08% | 4,264,396 |
| OpenClaw + OpenViking Plugin(启用原生 memory) | 51.23% | 2,099,622 |
如果只看结论:
- 相比原始 OpenClaw,任务完成率最高提升 43%。
- 输入 token 成本最高下降 91%。
- 相比接了 LanceDB 的对照组,完成率更高,成本却低得多。
这组数字背后的意思很明确:OpenViking 不是靠"多喂一点上下文"把效果硬堆上去,反而是在更少输入的情况下,把任务链路做得更稳。
5.3 为什么完成率会更高
我觉得最关键的原因,不是它比别家"更会做向量检索",而是它更擅长处理结构。
传统向量检索拿到的是"语义接近的片段"。这当然有用,但在长链路 Agent 任务里,真正关键的往往是:先找到对的位置。
位置一旦错了,后面再精细的 rerank 都是在错误范围内优化。OpenViking 的目录递归检索,本质上是在先解决"搜索空间对不对",再解决"片段像不像"。
5.4 可观测性不只是调试功能
公开资料里还提到可视化检索轨迹。这个点我挺认同,因为它不是锦上添花,而是工程上必须有的一层。
复杂 Agent 系统最难调的,经常不是模型输出,而是中间过程:
- 是目录设计有问题
- 还是摘要写得太空
- 是递归深度不够
- 还是某个 provider 的 embedding 本身就不适合当前数据
没有轨迹时,优化基本靠猜。有了轨迹,你至少能知道链路是在哪一步开始偏的。
六、如果放到生产环境,哪些地方最值得调
OpenViking 的设计思路很清楚,但落地效果仍然高度依赖你怎么组织内容。我更建议先调这几件事。
6.1 先调目录,不要上来就卷模型
因为它的检索链路本来就是"先目录、后细节",所以目录树本身就是性能的一部分。
比较稳妥的做法是:
resources/按项目、域、模块分层- 避免一个目录塞太多异构资料
- 目录命名尽量直接反映语义
- 会一起被使用的内容尽量放在同一子树
目录结构乱,再好的 embedding 也只能补一部分。
6.2 摘要质量会直接影响上层命中率
L0 / L1 本质上是路标。路标写得空,系统前两步就容易跑偏。
我会更看重三点:
- 有没有区分度
- 有没有任务导向
- 有没有清楚边界
换句话说,摘要不是写给人"欣赏"的,而是写给后续检索流程用的。
6.3 长期记忆和项目资源最好分开治理
user/、agent/、resources/ 这三个空间,最好别混着用。
原因很简单:
- 检索时边界清楚
- 治理时责任清楚
- 更新频率可以不同
长期偏好、策略说明、项目事实,本来就不该在同一个桶里维护。
6.4 递归深度要按任务类型定
递归不是越深越好。下钻太深,同样会引入额外延迟和无效探索。
一般来说:
- 简单问答用浅递归就够了
- 文档库、代码库分析可以适当加深
- 长任务场景更适合动态决定要不要继续下钻
README 里也提到了 embedding / VLM 的并发配置。真到高吞吐场景,除了递归深度,还要一起看:
- embedding 批量化
- provider 选择
- 并发上限
- 缓存命中
6.5 把它放在正确的位置上
OpenViking 很适合作为 Agent 系统里的上下文层,但它不等于完整的 Agent Runtime。
比较合理的分工通常是:
- 上层 Agent 框架负责任务规划和工具调用
- OpenViking 负责上下文组织、检索和记忆沉淀
- 模型服务负责生成
- 外部工具负责真正执行动作
把它当 Context Layer 来用,通常会比试图让它包办一切更顺。
七、基于 OpenViking 搭一套完整的多模态 RAG 方案
前面六节都是在讲 OpenViking 本身。但如果你真想把它用到生产环境里做知识库问答,光有上下文数据库是不够的——上游还差一段:多模态文件预处理。
现实中的知识库,很少只有干净的 Markdown 或纯文本。更常见的情况是一堆 PDF(扫描件、电子版混着来)、Word/PPT、截图、手写白板照片,甚至还有音视频会议录音。这些东西如果不先做结构化处理,直接喂给 OpenViking,目录树和 L0/L1 摘要的质量都没法保证。
下面这张图是我整理的一条完整链路,从文件进来到回答出去,四个阶段:
7.1 Stage 1:多模态文件预处理——把乱七八糟的文件变成干净的 Markdown
这一步的目标很明确:不管进来的是什么格式,出去的都是结构化的 Markdown + 元数据 JSON。
不同文件类型要走不同的处理路径
| 文件类型 | 处理策略 | 推荐工具 |
|---|---|---|
| 电子版 PDF(原生文本) | 直接提取文本和表格,保留标题层级 | PyMuPDF、pdfplumber |
| 扫描版 PDF / 影印件 | 先 OCR 再做版面分析 | MinerU、olmOCR |
| 图片(PNG/JPG/TIFF) | OCR + 图像描述(VLM) | MinerU、olmOCR、Qwen-VL |
| Word / PPT / Excel | 解析富文本,提取表格和嵌入图片 | python-docx、python-pptx、openpyxl |
| 代码仓库 | 按目录结构保留,可附加 AST 摘要 | tree-sitter、OpenViking 原生支持 |
| 音视频 | ASR 转文字后按时间戳分段 | Whisper、FunASR |
OCR 不只是"把图变成字"
很多人一提 OCR 就想到 Tesseract,但现在做 RAG 预处理,OCR 只是第一步,后面至少还有三件事要做:
- 版面分析(Layout Analysis):识别页面中的标题、正文、表格、图注、页眉页脚,不然 OCR 出来的文本是一团浆糊。
- 表格重建(Table Reconstruction):把 OCR 识别到的单元格重新拼成结构化表格,输出 HTML 或 Markdown table。
- 图像描述(Image Captioning):对文档中嵌入的图表、流程图、截图,用 VLM(如 Qwen-VL、InternVL)生成文字描述,让后续检索也能覆盖到视觉信息。
如果你的 PDF 里有跨页表格,推荐试一下 MinerU,它在复杂版面下的表现比较稳。
一段典型的预处理伪代码
from pathlib import Path
from preprocessor import detect_file_type, extract_pdf_native, ocr_and_layout, parse_office, describe_images
def preprocess(input_dir: Path, output_dir: Path):
for file in input_dir.rglob("*"):
ftype = detect_file_type(file)
if ftype == "pdf_native":
md, meta = extract_pdf_native(file)
elif ftype in ("pdf_scanned", "image"):
md, meta = ocr_and_layout(file) # OCR + 版面分析 + 表格重建
elif ftype in ("docx", "pptx", "xlsx"):
md, meta = parse_office(file)
else:
md, meta = file.read_text(), {}
# 对文档中的图片单独跑一遍 VLM 描述
md = describe_images(md, meta.get("images", []))
# 输出标准化 Markdown,保留原始目录层级
out_path = output_dir / file.relative_to(input_dir).with_suffix(".md")
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(md)
这段代码的重点不是具体实现,而是思路:按文件类型分流 → OCR + 版面分析 → 图像描述 → 统一输出 Markdown,同时保留目录层级。 保留目录层级这一点非常重要,因为后面 OpenViking 要靠它来建 viking:// 命名空间。
7.2 Stage 2:入库——让 OpenViking 把 Markdown 变成可检索的上下文
预处理完成后,就可以把输出目录整个推给 OpenViking:
# 启动 OpenViking 服务
openviking-server
# 把预处理后的 Markdown 目录加进来
ov add-resource ./preprocessed_docs --wait
# 检查目录树是否符合预期
ov tree viking://resources/preprocessed_docs -L 3
这一步 OpenViking 会自动做几件事:
- 把 Markdown 文件映射到
viking://resources/下的目录树。 - 为每个目录和文件生成 L0(~100 tokens 摘要)和 L1(~2k tokens 概览)。
- 对 L0/L1/L2 分别做 embedding,建立多粒度向量索引。
这里有个容易踩的坑:如果预处理阶段输出的目录结构是扁平的(所有文件丢在同一个文件夹),OpenViking 的目录递归检索就废了一半。 所以上一步保留原始目录层级不是可选项,而是必须的。
7.3 Stage 3:递归检索——先找对地方,再读对内容
这一层就是前面第四节讲的 OpenViking 的核心能力。在完整 RAG 方案里,它的角色是:
- 接收用户 query
- 在 L0 层快速排除无关子树
- 在 L1 层逐层下钻找到目标目录和文件
- 只在最后一步加载 L2 全文
相比传统 RAG 方案里"全局 embedding → TopK → 拼 prompt"的路径,这条路的好处前面已经说过了:搜索空间收缩更快,误召回更少,token 成本更低。
但在多模态场景下有一个额外的收益:因为预处理阶段已经把图像描述也写进了 Markdown,所以图表、流程图里的信息也能被检索到,不再是黑洞。
7.4 Stage 4:生成与记忆回写——闭环才是关键
检索结果组装成 prompt 交给 LLM 生成回答,这一步和大多数 RAG 系统没什么区别。真正值得注意的是回写:
- 如果用户在问答中表达了偏好(“我只关心 Python 实现”),这个信息可以写入
viking://user/memories/。 - 如果 Agent 在执行过程中总结出了某个模块的经验(“这个项目的配置文件在 config/ 下”),可以写入
viking://agent/memories/。 - 如果问答过程中产生了新的文档或代码,可以写回
viking://resources/。
这个回写机制让整套 RAG 方案不再是一次性的"查了就完",而是每次交互都在让知识库变得更好。
7.5 落地时的几个实操建议
1)预处理管线要做好兜底
OCR 不是万能的。扫描质量差、手写体、水印遮挡都可能导致识别率下降。实际部署时建议:
- 对 OCR 置信度低于阈值的页面标记为"待人工复核"
- 对同一文件跑多个 OCR 引擎,取结果交集或做投票
- 图像描述走 VLM 时,控制 prompt 长度,避免描述过于冗长
2)目录设计比选模型更重要
这一点怎么强调都不过分。不管你的 embedding 模型多好,如果预处理输出的目录结构不合理,OpenViking 的目录递归检索就会跑偏。
几个原则:
- 同一主题的文档放在同一目录下
- 目录深度控制在 3-5 层
- 目录名用人类能理解的语义,不要用纯 ID
- 大文件拆成多个子文件,每个子文件一个清晰主题
3)预处理和入库最好做成 pipeline
手动跑一次可以,但生产环境需要自动化。一个实用的做法是:
# 监听文件变更 → 预处理 → 入库
watch_dir ./raw_docs | preprocess --output ./preprocessed | ov add-resource --incremental
增量更新是关键。每次有新文件进来,不需要全量重建,只处理变化的部分。
4)评估不能只看回答质量
完整的评估应该覆盖四个维度:
| 维度 | 指标 | 说明 |
|---|---|---|
| 预处理质量 | OCR 准确率、表格还原率 | 垃圾进垃圾出,前面不行后面全白搭 |
| 检索质量 | 召回率、目录命中率 | OpenViking 的目录定位是否准确 |
| 生成质量 | 答案准确率、引用命中率 | LLM 输出是否可靠 |
| 成本效率 | 平均 Token 消耗、端到端延迟 | 能不能用得起 |
八、它的边界也要看清楚
OpenViking 不是所有场景都合适。
8.1 对扁平问答场景,优势没那么明显
如果你的数据本来就没有清晰层级,任务也只是小规模 FAQ 或单轮问答,那它的目录抽象未必能带来明显收益,反而可能增加建模成本。
8.2 它很依赖内容组织质量
OpenViking 的优势建立在"结构存在且结构靠谱"这个前提上。所以:
- 目录设计差,效果会掉
- 摘要质量差,效果会掉
- 命名混乱,效果也会掉
这更像一套上下文工程方法,而不是装上就自动最优的黑盒。
8.3 公开 benchmark 仍然要自己复验
README 里的结果很有参考价值,但毕竟是在特定数据集和插件场景下得到的。真正上线前,还是要拿自己的 workload 去看:
- 语料规模和层级深度
- 模型和 embedding 组合
- 是问答任务,还是规划和执行任务
- 多租户与并发压力下的表现
九、总结
如果把 OpenViking 只看成"另一个向量数据库",其实会看偏。
它更像是在做一件朴素但很重要的事:把 Agent 的上下文重新组织起来,让系统别再一上来就面对一大池碎片,而是先找到地方,再读内容,再把结果写回去。
它真正有价值的地方,我觉得有四个:
- 用
viking://把资源、记忆和技能放进统一命名空间 - 用 L0 / L1 / L2 控制上下文粒度
- 用递归检索缩小搜索空间
- 用记忆回写把一次任务变成后续任务的上下文资产
而如果你想在此基础上搭一套真正能用的多模态 RAG 方案,前面还要加一段预处理管线:用 OCR、版面分析、表格重建和图像描述把异构文件变成干净的 Markdown,再保留目录层级喂给 OpenViking。整条链路走通以后,你得到的不是一个一次性的问答系统,而是一个会随着使用不断变好的知识工作空间。
这条路线能不能成为 Agent 基础设施的主流,现在还不好说。但至少在今天,它提供了一个很值得认真验证的方向:上下文系统不一定非得从 chunk 开始,也可以从结构开始。
参考资料
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付