首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >你的文件上传后,到底经历了什么?—— OpenViking 向量化全链路硬核拆解

你的文件上传后,到底经历了什么?—— OpenViking 向量化全链路硬核拆解

作者头像
HELLO程序员
发布2026-06-26 21:22:23
发布2026-06-26 21:22:23
1040
举报

导读:你以为 client.add_resource() 只是"存了个文件"?天真了。这行代码背后,是一场涉及 Parser、DAG、LLM、双队列、向量数据库的"大型流水线工程"。今天,我们把 OpenViking 的向量化链路扒个底朝天。


一、先抛结论:文件确实入了向量,而且比你想象的复杂

很多开发者第一次用 OpenViking 时,心里都会嘀咕:

"我传了个文件,它到底进没进向量库?"

答案是:进了,而且走了整整两套异步队列。

OpenViking 没有采用传统 RAG "解析→切片→Embedding→入库" 的扁平套路,而是设计了一套文件系统范式 + 三级向量索引 + 层级递归检索的架构。目录有向量,文件有向量,摘要还有向量。检索时不是简单搜一遍,而是像剥洋葱一样层层递归。

这套设计的硬核程度,足以让传统 RAG 框架沉默。

二、五行代码背后的"暗战"

快速开始里的代码看起来人畜无害:

代码语言:javascript
复制
client = ov.OpenViking(path="./data")
client.initialize()
client.add_resource("https://github.com/volcengine/OpenViking")
resources = client.ls("viking://resources/")
results = client.find("context database")

但这五行代码,每一行都在后台掀起波澜。

第 1-2 行:初始化 = 启动一个微服务集群

client.initialize() 不是简单读个配置。它在后台:

  • 启动 RAGFS(Rust 实现的高性能文件系统,支持 Local/S3/Memory 三后端)
  • 启动 QueueManager,拉起 SemanticQueue 和 EmbeddingQueue 的 Worker 线程
  • 初始化 VikingDB 向量数据库连接
  • 加载 Embedder(Dense + Sparse 双模态)

换句话说,你调的不是一个客户端,是在本地起了一个完整的上下文服务集群。

第 3 行:add_resource = 一场六幕大戏

你以为 add_resource() 就是"复制文件到某个目录"?太年轻了。

代码语言:javascript
复制
add_resource(path, build_index=True)
  └→ ResourceService.add_resource()
       └→ ResourceProcessor.process_resource()  ← 六幕大戏从这里开始
            ├→ 第一幕:MediaProcessor.process() —— Parser 解析,写入 viking://temp/
            ├→ 第二幕:TreeBuilder.finalize_from_temp() —— temp → AGFS 最终目录
            ├→ 第三幕:生命周期锁 + mv 落盘
            └→ 第四幕:Summarizer.summarize() —— 入队 SemanticQueue
                 └→ SemanticQueue.enqueue(SemanticMsg)
                      └→ SemanticProcessor.on_dequeue()
                           ├→ SemanticDagExecutor.run() —— DAG 驱动,自底向上遍历
                           │    ├→ 叶子文件:LLM/VLM 生成摘要
                           │    └→ 目录节点:生成 .abstract.md + .overview.md
                           │
                           ├→ _vectorize_directory() —— 目录 L0+L1 入 EmbeddingQueue
                           └→ _vectorize_single_file() —— 文件内容入 EmbeddingQueue
                                └→ EmbeddingQueue.enqueue()
                                     └→ TextEmbeddingHandler.on_dequeue()
                                          ├→ Embedder 生成 Dense + Sparse 向量
                                          └→ 写入 VectorDB

六幕分解:

操作

耗时

是否阻塞用户

第一幕

Parser 解析文件结构

秒级

同步,阻塞

第二幕

TreeBuilder 计算目标路径

毫秒

同步,阻塞

第三幕

mv 落盘 + 生命周期锁

毫秒

同步,阻塞

第四幕

Summarizer 构造消息入队

毫秒

同步,阻塞

第五幕

SemanticQueue → L0/L1 生成

秒~分钟

异步,不阻塞

第六幕

EmbeddingQueue → 向量入库

秒~分钟

异步,不阻塞

关键设计:前四幕同步完成,保证用户立即看到文件已上传;后两幕异步执行,LLM 摘要和 Embedding 在后台慢慢消化。用户体验和系统吞吐量兼得。

第 4 行:ls = 直接读文件系统

这行最简单,就是 VikingFS.ls() → AGFS 列目录。没有向量参与,纯文件系统操作。

第 5 行:find = 一场向量检索的"深度搜索"

这行代码背后,OpenViking 执行了一套层级递归检索

代码语言:javascript
复制
用户查询: "context database"
   │
   ▼
Step 1: 查询文本 → Embedder → 查询向量 (Dense + Sparse)
   │
   ▼
Step 2: 全局向量搜索 —— 在整棵目录树的 L0/L1/L2 中找 top-K 候选
   │
   ▼
Step 3: Rerank 精排 —— 用 thinking 模式对候选摘要做二次排序
   │
   ▼
Step 4: 递归目录搜索 —— 像 DFS 一样逐层深入,分数从父目录向子节点传播
   │
   ▼
Step 5: 收敛检测 —— 连续 3 轮 top-K 不变就自动停止,不浪费算力
   │
   ▼
Step 6: 热度分融合 + 返回

传统 RAG 是"搜一遍完事",OpenViking 是"搜到收敛为止"。

三、L0/L1/L2:OpenViking 的"三级火箭"

OpenViking 最核心的创新之一,是目录也入向量

传统 RAG 只给文档切片建索引,OpenViking 给每个目录和文件都建了三级索引:

级别

文件

内容规模

作用

L0

.abstract.md

~100 tokens

一句话摘要,用于快速筛选

L1

.overview.md

~2,000 tokens

详细概览,用于精排和展示

L2

原始文件

全文

最终返回的完整内容

一个目录会产生 2 条向量记录(L0 + L1),一个文件产生 1 条。这意味着检索时,系统可以先在 L0 层面快速排除不相关目录,再深入 L1 层面做精排,最后才读取 L2 的完整内容。

这就像搜索引擎的"标题 → 摘要 → 全文"三级过滤,但发生在向量空间。

四、双队列架构:SemanticQueue + EmbeddingQueue

OpenViking 没有让 LLM 摘要和 Embedding 生成串行执行,而是拆成了两个独立队列:

代码语言:javascript
复制
┌─────────────────┐      ┌──────────────────┐      ┌─────────────────┐
│   Summarizer    │ ──→  │  SemanticQueue   │ ──→  │ SemanticProcessor│
│  (构造消息入队)  │      │  (语义处理队列)   │      │ (LLM 生成 L0/L1) │
└─────────────────┘      └──────────────────┘      └────────┬────────┘
                                                            │
                                                            ▼
                                                   ┌─────────────────┐
                                                   │  EmbeddingQueue  │
                                                   │  (向量化队列)     │
                                                   └────────┬────────┘
                                                            │
                                                            ▼
                                                   ┌─────────────────┐
                                                   │ TextEmbeddingHandler
                                                   │ (Embedder → VectorDB)│
                                                   └─────────────────┘

为什么拆两个队列?

  1. 解耦:LLM 摘要慢(秒~分钟级),Embedding 生成快(毫秒~秒级),分开后互不拖累
  2. 可扩展:可以独立扩容 SemanticProcessor 或 EmbeddingHandler 的 Worker 数量
  3. 容错:SemanticQueue 支持 45 秒窗口去重,同目录的 memory 消息自动合并;EmbeddingQueue 有熔断器保护,API 故障时自动重新入队

五、记忆系统:你的对话,最终也走向了量

OpenViking 不只是一个"文件搜索引擎",它还有完整的记忆系统

当你这样使用时:

代码语言:javascript
复制
session = client.create_session()
client.add_message(session["session_id"], "user", "我喜欢用 Python,偏好简洁风格")
client.commit_session(session["session_id"])

后台发生了一场"记忆提取 + 向量化"的完整闭环:

代码语言:javascript
复制
commit_session()
  ├→ Phase 1: 归档消息 → 写入 history/archive_xxx/ → 生成 L0/L1 → 向量化
  └→ Phase 2: 记忆提取
       → SessionCompressorV2 (V2 版本)
           → ExtractLoop (ReAct 编排器,最多 3 轮 LLM+Tool Use)
               → 输出 MemoryOperations
           → MemoryUpdater (字段级合并策略: patch/sum/immutable)
               → 写入记忆文件 (10 种类型: profile/preferences/entities/...)
           → _flush_semantic_operations()
               → SemanticMsg(context_type="memory")
                   → SemanticQueue → L0/L1 生成 → EmbeddingQueue → VectorDB

10 种记忆类型,从用户侧的 profile、preferences、entities,到 Agent 侧的 cases、patterns、skills、soul、identity,全部走向量化。

下次你搜索时,这些记忆会和资源同时被检索,但通过 owner_space 字段自动隔离——你的记忆只有你能看到。

六、与传统 RAG 的"降维打击"

维度

传统 RAG

OpenViking

向量化对象

仅文档切片

目录 L0+L1 + 文件内容,三级索引

索引结构

扁平列表

目录层级树 + 向量索引

检索方式

单次向量搜索

全局搜索 + 递归目录搜索 + Rerank

分数计算

纯向量相似度

向量分 × Rerank × 分数传播 × 热度分

结果粒度

固定大小切片

L0 摘要 / L1 概览 / L2 全文,按需加载

租户隔离

通常无

owner_space 字段自动隔离

一致性

更新可能残留旧向量

rm/mv 操作同步更新向量索引

收敛控制

连续 3 轮 top-K 不变自动停止

传统 RAG 把知识库当成一个"大文档堆",OpenViking 把它当成一个"有结构的文件系统"。这不仅仅是工程实现的不同,是范式级别的差异

七、写在最后

回到开头的问题:你的文件上传后,到底经历了什么?

答案是:它经历了一场从文件系统到向量空间的完整蜕变。

Parser 把它拆解成目录树,LLM 给它生成三级摘要,Embedder 把它变成 Dense + Sparse 双模态向量,HierarchicalRetriever 在检索时像剥洋葱一样层层深入。而你的每一次对话,又通过记忆系统回流到这个向量空间中,形成自迭代的闭环。

OpenViking 的设计哲学很清晰:上下文不是数据,是结构化的、可检索的、可迭代的记忆。

这五行代码的背后,是一套完整的上下文操作系统。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HELLO程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、先抛结论:文件确实入了向量,而且比你想象的复杂
  • 二、五行代码背后的"暗战"
    • 第 1-2 行:初始化 = 启动一个微服务集群
    • 第 3 行:add_resource = 一场六幕大戏
    • 第 4 行:ls = 直接读文件系统
    • 第 5 行:find = 一场向量检索的"深度搜索"
  • 三、L0/L1/L2:OpenViking 的"三级火箭"
  • 四、双队列架构:SemanticQueue + EmbeddingQueue
  • 五、记忆系统:你的对话,最终也走向了量
  • 六、与传统 RAG 的"降维打击"
  • 七、写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档