读懂OpenViking:天下没有稀奇事

0 阅读7分钟

同学们,最近看到"Open***"开头的技术,都得吓的要死,openclaw要取代白领打工人啦,OpenMAIC要取代老师啦,opencode平替中级码农。等等等等...这都是很让人看到就头大的事情啦,与其说怕他不如弄懂他再说,以后被裁了(优化)就去卷别的行业啦~~ 这次是OepnViking,解决的又是谁呢,哦,看了readme,才知道不是取代人的,是用来当作llm的知识库的,这也是目前大模型的限制,小脑袋装不下多少记忆,写的写的忘记要干啥,像一个金鱼一样,这一直都是公认的问题,目前从模型难下手,框架有什么好思路呢。 好的先来一起看看怎么吹牛的:

智能体开发面临的挑战

在 AI 时代,数据丰富,但高质量的上下文却难以获得。在构建 AI 智能体时,开发者经常面临以下挑战:

  • 上下文碎片化:记忆存储在代码中,资源在向量数据库中,技能分散在各处,难以统一管理。
  • 上下文需求激增:智能体的长运行任务在每次执行时都会产生上下文。简单的截断或压缩会导致信息丢失。
  • 检索效果不佳:传统 RAG 使用扁平化存储,缺乏全局视图,难以理解信息的完整上下文。
  • 上下文不可观察:传统 RAG 的隐式检索链像黑盒,出错时难以调试。
  • 记忆迭代有限:当前记忆只是用户交互的记录,缺乏智能体相关的任务记忆。

上面这段是原文哦,我一直以来的思考也有点类似,去年最火的RAG+MCP搭建一个企业AI知识库平台,然后使用MCP暴露给大模型,作为大模型的Tool,想用的时候,想查什么知识的时候,去使用这个平台查一下,去年大家都是这种思路,但是一年下来呢,有什么落地的场景吗,哈哈哈还真不多 ~ 一般来说RAG都是什么处理,都是chunk->bm25+embeding并行去提取信息内容->落库。传统的RAG倾向于将文档切分成独立的块(Chunks),然后存储在向量数据库中。这种方式丢失了原文档的结构、上下文以及文档之间的关联,导致 AI 缺乏对知识的“全局视角”。查询的时候都是bm25关键词匹配+embeding的语义理解->RFF->Rerank,基本都这种方式。这样的存储很扁平化,我的理解就是这样的存储,每次查询的时候只是找到了对应的文章段落,如果切分的chunk很大,这一大串都会被返回回来,然后再把这一大串都丢给上下文。Agent还得自己去慢慢啃一遍你说的啥,需要自己去消化消化,啃生肉难免会拉稀,要不做成熟肉怎么样? 目前,很火的OpenClaw是怎么做的呢,记忆是:“文件即真相”设计哲学

  • 存储介质:纯 Markdown 文件(人类可读可编辑)

  • 索引机制:SQLite + 向量嵌入(机器可搜索)

  • 工作模式:文件优先,索引辅助

~/.openclaw/workspace/
├── MEMORY.md# 长期记忆(精选、持久化)
└── memory/
   └── YYYY-MM-DD.md   # 每日记忆日志(append-only)
  • MEMORY.md:长期记忆,存储决策、偏好、重要事实

  • memory/YYYY-MM-DD.md:短期记忆,存储日常笔记、临时上下文

记忆索引管理器 (MemoryIndexManager)

核心职责:

  • 管理 SQLite 索引数据库
  • 协调向量化搜索和全文搜索
  • 监视文件变化并自动同步
  • 处理 embedding 提供商

关键特性:

  • 单例模式:通过缓存避免重复实例
  • 混合搜索:向量 + BM25 关键词
  • 自动同步:文件监视 + 定时刷新
  • 会话集成:delta 更新机制

记忆搜索工具

  • memory_search - 语义搜索
    • 基于向量嵌入的语义检索
    • 返回相关片段(路径 + 行号 + 得分)
    • 支持 MMR 去重和时间衰减
  • memory_get - 定向读取
    • 按路径读取特定记忆文件
    • 支持行范围查询
    • 安全检查(防止路径穿越)

3.9.5 混合搜索机制

搜索流程

用户查询  ↓关键词提取 (FTS)向量嵌入  ↓并行搜索  ├─ 向量搜索(语义相似)  └─ BM25 搜索(关键词匹配)  ↓加权融合  ↓后处理  ├─ 时间衰减  └─ MMR 去重  ↓Top-K 结果

为什么需要混合搜索?

向量搜索优势:

  • 理解语义:"Mac Studio gateway host" ≈ "运行 gateway 的机器"
  • 容错性强:拼写错误、同义词

向量搜索劣势:

  • 对精确 token 弱:ID、代码符号、错误字符串

BM25 优势:

  • 精确匹配:a828e60、memorySearch.query.hybrid
  • 高信号 token

分数融合算法

finalScore = vectorWeight * vectorScore + textWeight * textScore

权重归一化为 1.0,默认 vectorWeight=0.7,textWeight=0.3

3.9.6 后处理机制

MMR(最大边际相关性)去重

目的:避免返回重复或高度相似的片段

算法:

score = λ × relevance - (1-λ) × max_similarity_to_selected

λ 参数:

  • 1.0:纯相关性(不去重)
  • 0.0:最大多样性(忽略相关性)
  • 默认 0.7:平衡
查询:"家庭网络设置"
无 MMR:1. memory/2026-02-10.md (0.92) ← 路由器 + VLAN2. memory/2026-02-08.md (0.89) ← 路由器 + VLAN(重复!)3. memory/network.md (0.85)
有 MMR (λ=0.7):1. memory/2026-02-10.md (0.92) ← 路由器 + VLAN2. memory/network.md (0.85) ← 参考文档(多样)3. memory/2026-02-05.md (0.78) ← AdGuard DNS(多样)

时间衰减

目的:让近期记忆排名更高

公式:

decayedScore = score × e^(-λ × ageInDays)

半衰期:默认 30 天

  • 今天:100%
  • 7 天:84%
  • 30 天:50%
  • 90 天:12.5%

常青文件(不衰减):

  • MEMORY.md 
  • 非日期命名的文件(如 memory/projects.md)

能看出来,对于Oepnclaw这种形式,就有点类似于前面说的RAG的过程,bm25+向量检索,耗费很多token还会带来一堆chunk进来,搞得上下文不干净还得llm自己重新看。 那OpenViking是怎么做的呢?

OpenViking 并不是在语义匹配上比 RAG 更强。它的核心卖点是:

技术架构

viking:// ├── resources/ # 资源:项目文档、代码库、网页等 ├── user/ # 用户:个人偏好、习惯等 └── agent/ # Agent:技能、指令、任务记忆等

分层存储结构:

  • L0 (Abstract):一句话摘要,快速检索识别
  • L1 (Overview):核心信息概览,规划阶段决策
  • L2 (Details):完整原始数据,深度阅读时加载

能看到目录,就是三级L0,L1,L2这三层的历史记忆,那就可以这样好理解了

┌─────────────────────────────────────────────────────────────┐ │ 文件系统存储 │ │ viking://resources/docs/auth/ │ │ ├── .abstract.md (L0: ~100 tokens 摘要) │ │ ├── .overview.md (L1: ~2k tokens 概览) │ │ ├── oauth.md (L2: 完整内容) │ │ └── jwt.md (L2: 完整内容) │ └─────────────────────────────────────────────────────────────┘ ↓ Embedding ↓ ┌─────────────────────────────────────────────────────────────┐ │ 内置向量数据库 VikingVectorIndex │ │ ┌───────────────────────────────────────────────────────┐ │ │ │ id │ uri │ level │ abstract │ vector │ │ │ │ xxx │ docs/auth │ 0 │ "API..." │ [0.1..] │ │ │ │ yyy │ docs/auth │ 1 │ "Guide.."│ [0.2..] │ │ │ │ zzz │ docs/auth/oauth │ 2 │ "OAuth.."│ [0.3..] │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ Dense Vector + Sparse Vector + C++引擎 + RocksDB持久化 │ └─────────────────────────────────────────────────────────────┘ 关键点:向量检索的是 L0/L1 的摘要,l0和l1变为词向量

从 collection_schemas.py 可以看到索引的字段:

 fields = [
      {"FieldName": "uri", "FieldType": "path"},
      {"FieldName": "level", "FieldType": "int64"},       # 0=L0, 1=L1, 2=L2
      {"FieldName": "abstract", "FieldType": "string"},   # 摘要文本
      {"FieldName": "vector", "FieldType": "vector"},      # Dense向量
      {"FieldName": "sparse_vector", "FieldType": "sparse_vector"},  # Sparse向量
  ]`

向量化的对象是 abstract 字段(L0/L1 层的摘要内容),不是 L2 的完整文件。

所以内部流转跑的就是!!递归查找!!是关键,先跑索引,再排序完后返回原上下文

这个思路不是很难去思考,但是为什么你实现不了呢?

这个向量检索是十分依赖与摘要的,摘要不准,向量检索也会不准 就类似于skill,如果你给大模型看的描述不准确,大模型也不会去展开你的这个skill去仔细看!

最后,他们实验接入到openclaw中的效果是:

image.png

大家也可以试试哦,这个外部记忆的思路不是很难,也可以开创属于自己的记忆系统!!! 冲吧大家,目前大模型上下文管理是一片迷雾状态,适合真正的勇士打开一条血路!!