我是如何理解 OpenViking 的

我是如何理解 OpenViking 的

这不是一篇 OpenViking 使用手册,而是我这次学习过程的记录。重点不是罗列它有哪些 API,而是保留我是从什么问题出发、形成过哪些判断、又为什么修正这些判断的。以后重新接触 OpenViking 时,我希望先恢复这套认知,而不是重新啃一遍官方文档。

一开始:它不就是一个资料库吗

我最初看到的 OpenViking 很像一个资料库:可以向里面添加各种类型的资源,然后列出、读取或搜索这些资源。如果只看这些能力,它和文件系统似乎没有本质区别,无非是换了一组 API。

这个判断不算错,但只看到了 L2,也就是具体内容。OpenViking 在普通文件之上增加了几件事:

  • 为内容生成可供快速判断相关性的摘要和概览;
  • 把内容、目录和语义索引组织到统一的 URI 空间;
  • 用 embedding 和向量检索,从“我知道文件在哪里才能读取”变成“我描述需求,它替我找相关内容”;
  • 把资源、长期记忆和 Skill 放进同一套可检索、可逐层展开的上下文系统;
  • 记录 session,并从对话中提取可以跨 session 使用的长期记忆。

所以我后来更愿意把它理解成:

OpenViking 是建立在类文件系统之上的 Agent 上下文与长期记忆管理层。文件系统负责“保存”,它还负责“理解、索引、召回和逐步展开”。

这也解释了为什么它并不只适合 AI Agent。资源管理、语义搜索和分层浏览本身可以被普通应用使用;只是记忆提取、自动召回和 Skill 检索明显是围绕 Agent 工作流设计的。

我曾把 L0、L1、L2 理解成三层缓存

看到 .abstract.md.overview.md 和具体文档后,我自然形成了一个映射:

.abstract.md  = L0
.overview.md  = L1
具体文档      = L2

这个映射对 Resource 目录基本成立,但“三层缓存”这个说法不准确。它们不是同一份内容在不同速度介质中的缓存副本,而是不同信息密度的表达:

  • L0 回答“这是什么,是否值得继续看”;
  • L1 回答“这里大致有什么,下一步该进入哪里”;
  • L2 才是实际内容。

我的下一个疑问是:是不是每个文件夹都有 .abstract.md.overview.md?答案也不是绝对的。受 OpenViking 语义处理和目录管理的资源目录通常会有这些派生文件,但不能把它当成任意文件夹都必须满足的物理文件系统规则。某些目录是预置的,某些内容按需生成,普通目录或尚未完成语义处理的目录未必具备完整的 L0/L1。

接着我追问 Memory 的目录是不是也完全复制这套结构。这里又需要修正:Memory 也会参与摘要、概览和向量化,但它首先由记忆 schema 决定目录和文件。例如 event 按日期组织,preference 按用户和主题组织,profile 是固定文件。不能先假定“所有 Memory 都是一棵标准 Resource 三层树”,再去套它。

Resource、Memory、Skill 是三类上下文,不是三种文件格式

我逐渐确认 OpenViking 面向使用者的核心上下文可以分成三类:

  • resource:外部资料、项目文件、网页、代码、图片等;
  • memory:从交互中形成的长期信息,包括画像、偏好、实体、事件和经验;
  • skill:告诉 Agent 如何完成某类任务的可调用能力说明。

它们底层都能以 URI 和文件形式出现,也都可能被搜索,但语义和生命周期不同。Resource 通常由人或程序导入;Memory 可以由 session commit 后自动提取;Skill 更像操作规程和能力入口。把它们理解成“同一个资料库里的三个文件夹”会漏掉这些行为差异。

接入 Agent 的本质:既要给能力,也要给使用时机

我随后把问题转向了 Agent 集成。我形成的第一个结论是:只要把 OpenViking 是什么、里面有什么、接口怎么调用告诉 Agent,Agent 就可以通过 API 或 MCP 获取 Resource、Memory 和 Skill。

这个结论基本正确,但后来我把接入拆成了三层:

Skill / 系统提示词:告诉模型 OpenViking 是什么、什么时候应该使用
MCP / API:给模型真正执行 search、read、recall、remember 的工具
Hook / Middleware:不依赖模型主动判断,在生命周期节点自动召回和捕获

纯 MCP 是最小接入:模型知道工具存在,需要时主动调用。自动召回则不是 MCP 自己完成的,而是 Agent 平台提供的 Hook 或中间件在每次 prompt 前调用 OpenViking,再把结果附加到上下文。

这也让我理解了官方给 Codex 的安装脚本。它并不是只“注册一个 MCP”:它安装了 Codex 生命周期 Hook、stdio 到 HTTP 的 MCP 代理,以及告诉 Agent 如何使用经验记忆的 Skill。它把主动工具和被动生命周期集成组合在了一起。

因此,OpenViking 的记忆并不只有“被动注入”一种使用方式:

  • Hook 可以在每轮输入前自动召回;
  • Agent 也可以主动调用 searchrecallread
  • 对话结束或 compact 前,Hook 可以捕获并提交 session;
  • Agent 还可以主动调用 rememberforget

向量数据库只是索引,不是内容本体

我后来问:OpenViking 是否内置了向量数据库?答案是有向量检索层,但不是把所有内容只存进向量数据库。

我现在使用下面这个模型理解它:

实际文件、摘要、概览、关系
        ↓ 保存在 AGFS/RAGFS

URI、embedding、元数据、摘要字段
        ↓ 保存在 VectorDB

查询 → 生成查询向量 → VectorDB 找 URI → 回到文件系统读取内容

embedding 模型负责“把内容变成向量”,VectorDB 负责“保存并搜索向量”,两者不是一个组件。默认可以使用本地向量后端,并不意味着必须额外购买或部署某个固定的云向量数据库。

自动召回让我开始怀疑上下文成本

确认 Codex 插件会在每次用户输入前搜索并注入记忆后,我马上想到一个问题:如果在同一个 session 中反复问相同问题,同一份记忆是不是会被一遍遍放进上下文?如果是这样,上下文会更快撑满,更频繁 compact;而 compact 时,大量无用记忆又可能挤掉真正重要的信息。

这个风险是真实的。OpenViking 只能缓解,不能让它消失。当前实现会使用相关性阈值、分类配额、Token 预算、摘要压缩和默认 5 轮的 URI 冷却去重。刚注入过的 URI 通常不会下一轮立刻再次注入。

但这些机制仍有边界:

  • 去重按 URI,而不是严格按语义;同一事实存在多个文件时仍可能重复;
  • 冷却期以后可以再次召回;
  • 排除最相关结果后,可能补进相关性更差的结果;
  • 即使每轮都不重复,长期 session 仍会积累许多不同的召回内容;
  • compact 是 Agent 侧行为,OpenViking 无法保证压缩摘要保留哪些细节。

这让我形成了一个更保守的集成偏好:

自动召回适合少量、高置信度、经常需要的用户事实;历史事件、项目资料和 Skill 更适合让 Agent 按需主动检索。

如果上下文纯净度比“不遗忘”更重要,可以关闭自动召回,只保留 MCP 工具。自动与主动不是二选一,也可以采用小预算自动召回加主动下钻的混合方式。

记忆不是简单追加,而是按类型决定新增或更新

我用“今天下雨,第二天今天晴天”来测试记忆更新逻辑。最终理解是:OpenViking 没有一条适用于所有记忆的覆盖规则,行为由记忆类型的 schema 决定。

events 是按日期存储的 add_only 类型。“今天”会结合消息时间转换成具体日期,所以两天的天气是两条历史事件,不应互相覆盖。当然,这种信息也可能因为价值太低而根本不被提取。

而 profile、preferences、entities 等是可更新类型:

  • “我从北京搬到上海”适合更新 profile 中的当前居住地;
  • “我以前喜欢咖啡,现在不喝了”适合修改同一主题的 preference;
  • 对同一个人或项目的新事实适合 patch 到已有 entity。

自动提取不是纯代码规则。它大致经历:

代码整理会话和时间信息
  → 向量搜索候选旧记忆
  → LLM 判断什么值得记、属于哪一类、是否冲突
  → LLM 输出新增、patch 或 delete 操作
  → 代码校验并执行操作、更新文件和向量索引

因此记忆维护具有概率性。LLM 可能没有找到旧记忆、主题命名不一致,或者没有正确识别冲突。OpenViking 能保证操作格式和存储过程相对可控,但不能把语义判断变成强一致事务。

在淘汰方面,我没有发现长期记忆默认按天数或容量自动删除的策略。旧记忆通常持续保留,除非提取器在冲突合并时删除、Agent 调用 forget,或者用户直接删除。热度可以影响排序,但排序降权不等于物理淘汰。

跨 Session 之后,身份模型成了新的难点

长期记忆写入 user 或 peer 的记忆目录,所以它天然可以跨 session。只有尚未 commit、尚未完成提取的消息仍局限在当前 session。

当我想到多个职责不同的 Agent 时,accountuserpeer_idactor_peer_idagent_id 一度混在了一起。现在我用下面的方式区分:

Agent         外部运行的 Codex 或我自己开发的程序,不是当前主要存储身份
account       哪个团队或租户
user          数据归谁,是记忆、会话、技能的主要所有者
session       哪一次具体对话
peer_id       一条 session 消息关联哪个稳定交互对象/子空间
actor_peer_id 当前请求允许查看和操作哪个 peer 子空间
agent_id      旧兼容字段,现在映射到 actor_peer_id,不应再作为新设计使用

典型结构是:

account: acme
└── user: alice
    ├── memories/                         # Alice 的公共用户记忆
    ├── sessions/
    └── peers/
        ├── coding-agent/memories/        # 编程上下文
        └── research-agent/memories/      # 研究上下文

peer_idactor_peer_id 常使用相同的值,但职责不同:前者影响写到哪里,后者影响这次请求能看到哪里。

如果多个 Agent 服务同一个人,希望共享用户画像而隔离职责上下文,可以使用同一 user、不同 peer。不过 actor peer 的隔离仍会保留 user 根记忆,所以这不是完全隔离。

如果任何长期记忆都不能串用,更可靠的方式是每个 Agent 使用不同的 user key。同一 account 下仍然可以共享 viking://resources/。如果连公共资源和权限域也必须隔离,则使用不同 account。

纯 MCP 和纯 Skill 的身份边界

最后一个容易误解的地方是:Skill 并不承载身份。Skill 只是告诉模型何时以及如何使用 OpenViking。真正的身份来自它最终调用的 MCP、CLI 或 SDK。

纯 MCP 连接中:

  • user 通常由 Authorization: Bearer <user-key> 决定;
  • actor_peer_idX-OpenViking-Actor-Peer 请求头决定;
  • trusted 模式才会显式发送 account/user 请求头;
  • 模型不能靠普通工具参数任意切换 user。

这里还有一个当前实现上的重要限制:MCP remember 的消息只有 rolecontent,没有 peer_id。所以纯 MCP 可以用 actor peer 限制读取视图,但 remember 默认仍把提取结果写到 user 公共记忆,而不会自动写入对应 peer。

如果一定要产生 peer 级记忆,需要插件 Hook,或者通过 REST/SDK 向 session 添加带 peer_id 的消息后再 commit。正因为这个限制,如果只打算使用纯 MCP,又要求多个 Agent 的写入严格隔离,“一个 Agent 一个 user key”比 peer 方案可靠。

我现在对 OpenViking 的整体认识

经过这一轮追问,我不再把 OpenViking 简单看作“带向量搜索的文件系统”,也不把它想象成会自动替 Agent 管好一切的黑盒记忆系统。

我目前的理解是:

OpenViking 提供了一套统一的上下文存储、分层表达、语义索引、session 归档和 LLM 记忆提取基础设施。它能让 Agent 获得跨 session 的外部长期记忆,但何时召回、召回多少、如何隔离身份,以及是否允许模型主动管理记忆,仍然是 Agent 集成层必须做出的设计选择。

对我而言,评估一个实际接入方案时,应该先回答这些问题:

  1. 我需要的是资料搜索、长期记忆,还是两者都有?
  2. 哪些内容可以自动注入,哪些应该由 Agent 主动搜索?
  3. 每轮召回的预算和去重窗口应该多大?
  4. 多个 Agent 是共享同一个人的公共记忆,还是必须完全隔离?
  5. 纯 MCP 是否足够,还是需要 Hook 捕获和 session commit?
  6. 我是否接受 LLM 提取和冲突判断的概率性?
  7. 是否需要自己的审计、纠错、过期或淘汰策略?

这些问题比“OpenViking 有哪些命令”更接近我真正需要做的架构决策。

仍值得继续验证的问题

这次学习形成了总体模型,但还有一些适合通过实际运行验证的问题:

  • 自动提取对我真实对话的准确率如何,哪些内容最容易误记?
  • 在长 session 中,自动召回实际增加了多少 Token,并使 compact 提前了多少?
  • 关闭自动召回、完全依赖 MCP 主动搜索时,Agent 是否经常忘记查记忆?
  • 同一 user 下使用 peer 隔离时,哪些记忆仍会落入 user 公共目录?
  • 不同职责 Agent 应该共享哪些 Resource、Skill 和用户事实?
  • 是否需要在 OpenViking 之上增加人工确认、记忆 TTL 或定期整理流程?

这些问题无法只靠阅读文档回答,需要用我自己的 Agent、对话和数据做实验。也正因为如此,这篇记录保留的是我的问题链路,而不是试图替代官方手册。