0%

OpenViking - 基于文件的分层记忆系统

1. 系统概览

OpenViking 是一个开源的、专为 AI Agent 设计的上下文数据库。
OpenViking 通过文件系统范式统一管理 Agent 所需要的上下文(记忆、资源和技能),
并实现上下文的分层供给与自我迭代,最终目标是降低 Agent 开发门槛,
让开发者更专注于业务创新而非底层上下文管理。
与其它记忆不同的是,他既有最开始的原文,也有高层级的语义理解。
数据召回的时候,数量以及准确性,都会有较大的效果和性能提升。

说明:

  • 本地安装的时候,全部可以用类似 Sqlite 的方案,依赖三方包即可;
  • 数据写入后,L1、L0 阶段需要 LLM 参与生成摘要、做语义理解;
  • L2 如果原文超长,可以做逻辑语义拆分,符号提取 / 虚拟子目录 / 偏移分页;

1.1 系统架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+-------------------------------------------------------------------------+
| AI Agent Runtime |
| (Find / LS / Read / Tree / Memory Extraction / Tool Invocation) |
+------------------------------------+------------------------------------+
|
+------------------------------------v------------------------------------+
| OpenViking Core Engine |
| |
| +---------------------------+ +-------------------------------+ |
| | Virtual File System | | Hierarchical Context Store | |
| | viking:// schema router | <---> | - L0: Abstract Sidecar | |
| | (Resources/Memory/Skills)| | - L1: Overview Sidecar | |
| +---------------------------+ | - L2: Raw Payload | |
| | +-------------------------------+ |
| v | |
| +---------------------------+ v |
| | Recursive Retrieval Router| +-------------------------------+ |
| | (Intent Analysis -> | <---> | Vector Engine & Reranker | |
| | Global Path Locate -> | | (Embedding / Dense Vector / | |
| | Subtree Recursive Zoom) | | Lexical BM25 / Cross-Encoder)| |
| +---------------------------+ +-------------------------------+ |
+------------------------------------+------------------------------------+
|
+------------------------------------v------------------------------------+
| Persistence / Storage Layer |
| (Local Disk / Object Store / Key-Value / Vector Index) |
+-------------------------------------------------------------------------+

1.2 文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
viking://
├── resources/ # 资源:项目文档、代码库、网页等
│ └── my_project/
├── user/
│ └── {user_id}/ # 当前用户的私有上下文
│ ├── memories/ # 用户记忆:用户个性化、实体状态、交互历史(动态演进)。
│ ├── resources/ # 用户私有资源:文档、代码库、规约(静态长期)。
│ ├── skills/ # 用户私有技能(默认)
│ ├── peers/
│ │ └── {peer_id}/
│ │ ├── memories/
│ │ └── resources/
│ └── sessions/
└── agent/
└── skills/ # account 全局共享技能(可选)

1.3 上下文分层:

  • L0(Abstract,~256 字符): 作为全局向量检索与剪枝路由的轻量索引。
  • L1(Overview,~4000 字符): 提供该目录/模块的结构骨架与语义摘要,专用于Rerank 与上下文粗排。
  • L2(Detail,无上限): 原始 Payload(代码段、文档正文、执行轨迹)。L0/L1 命中后按需加载。

2. 数据入库与检索

保留了向量检索、BM25检索等,和常规的检索方案很类似。
相比于单纯的 embedding,这种综合性的方式相对来说比较准确,能够互补。
虽然多路召回也能做到类似的效果,但是多路+重排序都需要应用侧开发,openviking 不用。

2.1 数据写入过程

同步的过程只有写 meta + L2 原文。
涉及到数据加工需要异步处理。
在异步处理完成之前,涉及到语义、向量等操作是不可用的,只能直接查 L2 的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
+-------------------------------------------------------------------------------------------------------------+
| 1. 写入接入层 (API / SDK / MCP) |
| Client 发起 POST /write (路径 + 正文) |
+------------------------------------------------------+------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------------------------------------+
| 2. 同步快速通道 (Sync Fast Path) |
| +-------------------------------------+ +------------------------------------+ +--------------------+ |
| | Metadata Store | | Object Storage | | Event Queue | |
| | - 鉴权 & 创建 VFS Node (viking://) | | - 写入 L2 Raw Payload 原文 (S3) | | - 投递异步解析任务 | |
| | - 状态置为 Processing, Dirty: True | | (代码/Markdown/PDF/Session) | | (Task Payload) | |
| +-------------------------------------+ +------------------------------------+ +---------+----------+ |
+-----------------------------------------------------------------------------------------------|-------------+
| (返回 200 OK 给 Client) |
v (后台异步处理) v
+-------------------------------------------------------------------------------------------------------------+
| 3. 异步语法与结构解析 (AST / Parsing) |
| +-----------------------------------------------------+ +-----------------------------------------------+ |
| | 代码类 (Tree-sitter AST) | | 文档类 (Markdown DOM / Layout) | |
| | - 提取符号表 (Function/Struct/ErrorCode/Interface) | | - 提取 H1~H6 标题层级树与面包屑路径 | |
| | - 按语义边界切分 AST 块 (非机械字数暴力截断) | | - 剔除页眉页脚,保留完整自然段落与表格结构 | |
| +-----------------------------------------------------+ +-----------------------------------------------+ |
+------------------------------------------------------+------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------------------------------------+
| 4. 自底向上分层 Sidecar 生成 (LLM Synthesis) |
| +-------------------------------------------------------------------------------------------------------+ |
| | 目录级 L1 概览 (.overview.md, ~4000 字符) | |
| | 聚合目录下所有文件的 AST 骨架 + 核心接口 + 模块职责 (用于 Cross-Encoder Rerank 精排) | |
| +---------------------------------------------------+---------------------------------------------------+ |
| |
| v
| +-------------------------------------------------------------------------------------------------------+ |
| | 目录级 L0 摘要 (.abstract.md, ~256 字符) | |
| | 基于 L1 提炼高密度领域关键词与路由锚点 (用于全局向量检索快速初筛) | |
| +-------------------------------------------------------------------------------------------------------+ |
+------------------------------------------------------+------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------------------------------------+
| 5. 双轨索引并行构建 (Dual-Track Indexing) | |
| +---------------------------------------------------+ +------------------------------------------------+ |
| | 稠密向量通道 (Dense Vector) | | 稀疏倒排通道 (Sparse BM25) | |
| | - 对 L0 摘要 / L1 概览调用 Embedding 计算向量 | | - 对 AST 提取的精确符号 (函数名/错误码) 分词 | |
| | - 写入向量索引引擎 (HNSW / Milvus / Qdrant) | | - 写入轻量倒排引擎 (Tantivy / SQLite FTS) | |
| | - 绑定 VFS 节点路径 (用于全局与子树语义路由) | | - 建立精准倒排链 (用于无视层级的直达穿透召回) | |
| +---------------------------------------------------+ +------------------------------------------------+ |
+------------------------------------------------------+------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------------------------------------+
| 6. 原子提交与级联更新 (Atomic Commit) |
| +-------------------------------------------------------------------------------------------------------+ |
| | 1. Metadata Store 更新状态: Processing -> Ready | |
| | 2. 沿目录树向上级联检查父节点: 若需要则增量微调父目录 L0,并清除 Dirty 标记 | |
| | 3. 全链路就绪: 对 Agent 的 find / ls / read / recursive search 完全可见 | |
| +-------------------------------------------------------------------------------------------------------+ |
+-------------------------------------------------------------------------------------------------------------+

2.2 数据加工过程

从 L2 原始数据开始,逐步往上层处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[ L2: 原始叶子文件 (Code / Markdown / PDF) ]

├── 1. 基于 AST / DOM 的语义结构化解析与清洗
│ (保留函数签名、Markdown 标题树、调用关系,剔除样板代码)


[ L1 Sidecar: 目录级概览 (.overview.md, ~4000 chars) ]

├── 2. 局部上下文提炼:综合该目录下所有叶子文件的导出符号、核心职责生成


[ L0 Sidecar: 目录级极简索引 (.abstract.md, ~256 chars) ]

└── 3. 全局路由锚点:提取高密度的领域概念、核心关键字与路由元数据

2.3 检索过程

OpenViking 内置了多路召回(Hybrid Retrieval)与重排(Rerank)机制,但其设计并非传统搜索系统中对单一切片(Chunk)的扁平堆叠,而是与它的 VFS 虚拟文件树拓扑以及 L0/L1/L2 分层结构紧密绑定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[ User Query / Intent ]


┌─────────────────────────────────────────────────────────-─┐
│ 1. 意图分解与查询改写 (Intent Decomposition) │
│ - 提取语义 Query (Dense) │
│ - 提取结构化/关键词 Query (Sparse / Lexical / Path Filter)│
└──────────────────────────┬───────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│ 2. 多路召回阶段 (Multi-Channel Retrieval) │
│ ├── 路径 A: 稠密向量检索 (Dense Embedding on L0/L1) │
│ ├── 路径 B: 词法/关键词检索 (BM25 / Lexical on Sidecars) │
│ └── 路径 C: 确定性路径前缀过滤 (VFS Path Scoping) │
└──────────────────────────┬───────────────────────────────┘
│ 融合生成候选集 (Candidate Sets)

┌──────────────────────────────────────────────────────────┐
│ 3. 混合融合与粗排 (Fusion: RRF / Linear Combination) │
│ - 使用倒数排名融合 (RRF) 归一化多路得分 │
│ - 输出初步 Top-N 候选目录/节点 │
└──────────────────────────┬───────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│ 4. 交叉编码精排阶段 (Cross-Encoder Reranker on L1) │
│ - 将 Query 与 L1 (.overview.md,~4000字符) 拼接打分 │
│ - 评估完整语境相关度,裁决是否深入递归 │
└──────────────────────────┬───────────────────────────────┘
│ 命中高分目录 / 节点

┌──────────────────────────────────────────────────────────┐
│ 5. 递归下探与 L2 按需加载 (Recursive Tree-Search & Payload) │
│ - 若为目录:递归进入子树重复 2~4 步 │
│ - 若为叶子:读取 L2 正文并附带 Trace Path 返回给 Agent │
└──────────────────────────────────────────────────────────┘

3. 存储架构

OpenViking 属于存算分离的架构

上层是属于无状态的计算节点,底层可以接入各类云存储(L2)、向量数据库(L0/L1);

Metadata Store 主要承载

  • VFS 目录树拓扑与路径映射(Virtual File System Topology)
  • 多租户沙箱与 ACL 权限元数据(Auth & Access Control)
  • 存储寻址指针(Payload & Vector Address Index)
  • 写入流水线状态机(Ingestion State Machine)
  • 记忆与会话元数据(Memory & Session Metadata)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
+-----------------------------------------------------------------------------------+
| API Gateway / Ingress Router |
| (鉴权、租户路由、Rate Limiting、Session Affinity) |
+-----------------------------------------+-----------------------------------------+
|
+---------------------------------+---------------------------------+
| |
+-------v-------------------------------+ +-------------------------------v-------+
| OpenViking Stateless Server Node 1 | | OpenViking Stateless Server Node 2 |
| - VFS URI 解析与鉴权沙箱 | | - VFS URI 解析与鉴权沙箱 |
| - 递归检索状态机 (Recursive Search) | | - 递归检索状态机 (Recursive Search) |
| - 运行时 Context Assembly | | - 运行时 Context Assembly |
+-------+-------------------------------+ +-------------------------------+-------+
| |
+---------------------------------+---------------------------------+
|
+-----------------------------------------v-----------------------------------------+
| Asynchronous Ingestion & Memory Worker Cluster |
| (分布式消息队列/任务调度:AST 解析、L0/L1 生成、Session 异步抽取) |
+-----------------------------------------+-----------------------------------------+
|
+-----------------------------------------v-----------------------------------------+
| Enterprise Distributed Storage Layer |
| |
| +---------------------------+ +---------------------------+ +---------------+ |
| | Object Storage | | Distributed Vector DB | | Metadata Store| |
| | (S3 / MinIO / TOS / OSS) | | (Milvus / VikingDB / Qdrant) | | (PostgreSQL / |
| | -> 承载海量 L2 原始 Payload| | -> 承载分布式 L0/L1 向量索引 | | Distributed | |
| | (代码/PDF/历史 Trajectory| | 与 ANN 稠密检索 | | KV / MySQL) | |
| +---------------------------+ +---------------------------+ +---------------+ |
+-----------------------------------------------------------------------------------+

4. 怎么使用?

4.1 直接调用 SDK

如果是在自建应用程序,可以在对应的 AOP / middleware / 回调函数里面进行数据写入。
或者通过 MQ 解耦,异步处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"context"
"github.com/volcengine/OpenViking/sdk/go"
)

func main() {
client, err := openviking.NewClient(openviking.Config{
BaseURL: "http://localhost:1933",
APIKey: "your-api-key",
})
if err != nil {
panic(err)
}
defer client.CloseIdleConnections()

// 1. 导入外部资源(文档 URL / Git Repo / 本地数据)
// 系统会自动解析并生成 L0/L1 摘要及向量索引
err = client.AddResource(context.Background(), &openviking.AddResourceRequest{
URI: "viking://resources/internal_wiki/",
SourceURL: "https://wiki.company.com/docs/api_v2.md",
})

// 2. 写入或同步特定用户的私有上下文
err = client.Write(context.Background(), &openviking.WriteRequest{
URI: "viking://user/10086/memories/preferences.md",
Content: []byte("用户更倾向于使用 Golang 进行后端开发,关注高并发架构设计。"),
})
}

4.2 通过 MCP

Agent 在运行过程中,根据 MCP 的描述,决定是否要触发相关记录。

4.2.1 记忆沉淀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"name": "openviking_record_memory",
"description": "用于向用户的长期记忆空间 (viking://user/{user_id}/memories/) 持久化沉淀高价值信息。仅在以下场景主动调用:1. 识别到用户的全局开发习惯、框架偏好或明确禁止的编码风格;2. 总结出经过验证的复杂 Bug 解决方案或架构避坑经验;3. 跨项目的通用工作流规则。禁止记录临时的对话闲聊、单次简单修改或未验证的推论。",
"parameters": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["preferences", "experiences", "cases", "entities"],
"description": "记忆的分类:preferences (用户长期偏好), experiences (通用排错/避坑经验), cases (经典可复用案例), entities (核心项目实体与术语)"
},
"title": {
"type": "string",
"description": "记忆的简短标题,如 'Go 1.22+ 迭代器规范' 或 'MySQL 唯一索引并发死锁规避'"
},
"content": {
"type": "string",
"description": "提取出的核心经验、规则或代码片段正文 (Markdown 格式)"
}
},
"required": ["category", "title", "content"]
}
}

4.2.2 资源沉淀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "openviking_write_resource",
"description": "用于在 OpenViking 虚拟文件系统 (viking://resources/) 写入或更新持久化知识库。适用于用户明确指示保存设计文档、接口定义、技术选型方案或团队通用规章等具有长期重用价值的工程资产。",
"parameters": {
"type": "object",
"properties": {
"uri": {
"type": "string",
"description": "目标 VFS URI 路径,格式如 'viking://resources/architecture/payment_flow.md'"
},
"content": {
"type": "string",
"description": "完整的内容 Payload"
}
},
"required": ["uri", "content"]
}
}

5. 记忆处理

5.1 冲突处理

对 Resource(知识库/代码): 保持客观中立,依赖确定性版本覆盖和 VFS 路径隔离,不做主观语义魔改。
对 Memory(记忆/偏好): 引入 Entity Matching -> 仲裁模型 (修正/合并/细化) -> 覆写与归档 的自动化流水线,
结合时序优先和人工可读可编辑,从根本上解决记忆互相打架的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[ 新会话产生的新事实 / 新偏好 ]


┌──────────────────────────────────────────────────────────┐
│ 1. 语义相似度与实体对齐 (Entity & Semantic Matching) │
│ - 检索 memories/ 中是否存在同类实体或相近的主题规则 │
└──────────────────────────┬───────────────────────────────┘
│ 命中旧记忆: "语言偏好: Python 3.10"

┌──────────────────────────────────────────────────────────┐
│ 2. 认知一致性仲裁 (Cognitive Arbitration) │
│ - 调用仲裁 Prompt 判定关系类型: │
│ ├── [完全冲突 / 修正] ──> 触发直接覆盖与归档 (Overwrite) │
│ ├── [条件互补 / 细化] ──> 触发合并增补 (Merge / Append) │
│ └── [无关 / 新增分支] ──> 触发新建条目 (Create New) │
└──────────────────────────┬───────────────────────────────┘
│ 判定为"偏好修正"

┌──────────────────────────────────────────────────────────┐
│ 3. 原子更新与历史追溯 (Atomic Update & Invalidation) │
│ - 覆写 viking://user/1001/memories/preferences.md │
│ - 重新计算该 Memory 节点的 L0/L1 摘要及向量索引 │
│ - 将旧记录移入历史追踪版本库 (可审计/可回滚) │
└──────────────────────────────────────────────────────────┘

5.2 遗忘机制

通过目录隔离,在会话结束之后,把 session 目录下的内容抽取沉淀至长期记忆

短期工作记忆(Working Memory):

  • 存储路径:viking://user/{id}/sessions/{session_id}/
  • 生命周期:仅在当前 Agent 任务或对话生命周期内活跃
  • 使用场景:记录单次任务的多步执行轨迹(Scratchpad)、临时变量、中间推理步骤

长期记忆(Long-term Memory & Knowledge):

  • 存储路径
    • resources/(知识)
    • user/{id}/memories/(偏好与经验)
  • 生命周期:跨会话永久持久化,供后续所有任务共享
  • 使用场景:为整个后续提供记忆材料

6. 深挖细节

  • 目录树并发写入与惊群效应(级联重算)
    • 问题:如果短时间内多次变更,会触发 L1/L0 重复处理
    • 解决:
      • 留下缓存,合并处理;
      • 变更文件时加上排他锁;
  • 递归检索的长尾等耗时
    • 问题:可能会遇到多级”打分 -> 分支判断 -> 下探子目录 -> 再次打分”
    • 解决:
      • 并行展开
      • 最大深度
      • 尽早剪枝
  • 多级数据独立存储一致性
    • 问题:由于系统解耦了 meta、向量、对象存储三层,跨系统下分布式事务没法实现
    • 解决:
      • 软删除(数据还在,但是标记为无效) + 异步垃圾回收
      • 一致性校验机制
  • prompt 注入与记忆毒化
    • 问题:session 场景可能有恶意 prompt 提权等;
    • 解决:只能限制在 session 沙箱,而且需要做对抗性检查,保证加工数据的安全;
  • 多租户场景下资源竞争
    • 问题:如果没有物理隔离,怎么实现资源平衡?
    • 解决:
      • 基于账户的优先级队列
      • 做读写分离,线上部分只做读取 + L2 数据写入,离线部分做 L0/L1 数据加工计算
  • 面对长上下文的 LLM,记忆还有优势吗?
    • 问题:现在很多模型上下文到了 1M
    • 解决:
      • 少量 token 直接全量读取,不用多级加载
      • 合理布局拼接结果,最大可能实现 prompt cache
      • 对数据做事实性提取,模型擅长检索,不擅长推理
      • 主动挖掘数据之间隐含的关联关系,为用户提供更个性化、超预期的服务