技术方案:OpenClaw + OpenViking 高可用上下文引擎技术方案

技术方案:OpenClaw + OpenViking 高可用上下文引擎技术方案

OpenClaw + OpenViking 高可用上下文引擎技术方案

版本: v1.0 | 日期: 2026-03-10 | 作者: wairesearch
状态: 提案 (Proposal)


目录

  1. 价值和意义 — 为什么 AI Agent 需要高可用上下文引擎
  2. 方案总览
  3. OpenClaw Context Engine 接口详解
  4. OpenViking 架构深度分析
  5. 集成方案设计
  6. 高可用架构设计
  7. 风险评估与缓解
  8. 附录

1. 价值和意义 — 为什么 AI Agent 需要高可用上下文引擎

1.1 从”健忘助手”到”可靠伙伴”:AI Agent 的记忆困局

今天的 AI Agent 正在从”单次问答工具”演进为”长期协作伙伴”。用户期望 Agent 能记住三个月前的架构决策、理解项目的演化脉络、在多 Agent 团队中共享工作记忆。然而现实却远远落后于期望——大多数 Agent 仍然是”健忘”的

每一次新会话开启,Agent 都像失忆患者一样从零开始。用户被迫反复解释相同的上下文,Agent 反复犯相同的错误,团队中的多个 Agent 彼此割裂、无法共享认知。这不是 AI 能力的问题,而是基础设施的缺失

AI Agent 要真正成为”可靠伙伴”,需要的不仅仅是更大的上下文窗口或更好的 RAG 检索——它需要一个工业级的、高可用的上下文引擎

1.2 当前 Agent 记忆系统的四大痛点

痛点 现状 后果
碎片化 记忆散落在会话日志、Markdown 文件、向量数据库等多个系统中,缺乏统一的组织范式 Agent 无法形成完整认知,同一信息被重复存储却无法有效检索
不可靠 大部分记忆方案基于单机文件系统,没有冗余、没有备份、没有故障转移 一次服务器崩溃就可能丢失数月积累的 Agent 记忆,项目上下文灰飞烟灭
Token 浪费 传统 RAG 采用扁平化存储,检索时返回大量低相关度内容塞入上下文窗口 每次推理调用消耗大量无效 Token,成本飙升而效果不佳
孤岛效应 多 Agent 系统中,每个 Agent 各自为政,无法读取同伴的工作记忆 协调者需要在 Agent 之间手动传递信息,协作效率低下

这四个痛点叠加在一起,导致了一个根本矛盾:Agent 的记忆能力远远跟不上其推理能力的进化速度

1.3 本方案解决的根本问题

本方案通过将 OpenClaw 的 pluggable Context EngineOpenViking 上下文数据库深度集成,从根本上解决上述四大痛点:

  • 统一范式,消除碎片化 — OpenViking 的文件系统范式(viking://)将 Memory、Resource、Skill 统一组织在层级化的 URI 命名空间下,Agent 的所有认知形成一棵结构清晰的知识树,而非散落的碎片。

  • 三级加载,终结 Token 浪费 — L0/L1/L2 三级按需加载机制彻底改变了”全量灌入上下文窗口”的粗暴模式。先用 ~100 Token 的摘要做向量匹配,再用 ~2K Token 的概览做语义重排,只在真正需要时才加载完整内容。Token 成本直降 91%

  • 共享存储 + 多实例部署,实现 99.9% 可用性 — 通过 S3/NFS 后端 + 主备实例 + 自动故障转移,将 Agent 记忆从脆弱的单机文件提升为工业级高可用服务。故障切换时间 < 15 秒,可用性达 99.9%

  • 多 Agent 共享命名空间,打破孤岛 — 5 个 Agent(main/waicode/waidesign/wairesearch/waiqa)共享同一上下文数据库,通过 URI 命名空间实现隔离与共享的平衡。waicode 发现的 bug 模式,wairesearch 可以立即检索到。

1.4 量化价值

基于 OpenViking 官方 LoCoMo10 评测(1,540 个案例)的实测数据:

指标 基线 (memory-core) 集成 OpenViking 后 提升幅度
任务完成率 35.65% 51.23% - 52.08% +43% ~ +46%
输入 Token 消耗 24,611,530 2,099,622 - 4,264,396 -83% ~ -91%
系统可用性 单机无冗余 主备 + 自动故障转移 99.9%
故障恢复时间 手动恢复(小时级) 自动切换 < 15s 秒级

这不只是技术指标的提升。Token 成本降低 91% 意味着同样的预算可以支撑 11 倍的 Agent 交互量;任务完成率提升 43% 意味着Agent 从”勉强可用”跨越到”真正可靠”;99.9% 可用性意味着Agent 记忆成为可以信赖的基础设施,而不是随时可能消失的脆弱状态。

1.5 对 AI Agent 生态的意义

这个方案的意义超越了 OpenClaw 自身:

  • 为 Agent 记忆树立工业级标准 — 三级加载 + HA 部署 + 共享命名空间,定义了 Agent 上下文管理应有的工程水准
  • 让多 Agent 协作成为可能 — 共享记忆层是多 Agent 团队从”松散协调”走向”深度协作”的基础设施
  • 降低 Agent 部署的运维风险 — 自动故障转移和增量备份让生产环境部署 Agent 不再是冒险行为
  • 推动 Agent 从工具走向伙伴 — 当 Agent 拥有了可靠的、高效的、可共享的长期记忆,它才真正具备成为”伙伴”的基础能力

2. 方案总览

核心价值回扣:本节呈现方案的整体架构视图——一个统一的、高可用的、多 Agent 共享的上下文引擎,正是解决上述四大痛点的技术载体。

本方案通过 OpenClaw 的 pluggable Context Engine 机制与 OpenViking 上下文数据库深度集成,实现:

  • 统一上下文管理:Memory/Resource/Skill 通过文件系统范式统一组织
  • 三级按需加载:L0/L1/L2 层级控制 token 消耗,降低 83-96% 输入 token 成本
  • 高可用存储:通过 S3/NFS 后端 + 多实例部署,消除单点故障
  • 多 Agent 共享:5 个 Agent(main/waicode/waidesign/wairesearch/waiqa)共享同一上下文数据库
  • 自动故障转移:主从复制 + 健康检查 + 自动切换

目标架构总览

┌──────────────────────────────────────────────────────────────────┐
                         OpenClaw Gateway                          
                                                                    
  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ 
    main      waicode   waidesign  wairesearch  waiqa   
    Agent      Agent      Agent      Agent     Agent   
  └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ 
                                                               
  ┌────▼─────────────▼────────────▼─────────────▼────────────▼──┐  
            OpenViking Context Engine Plugin                      
    (implements ContextEngine interface: ingest/assemble/compact)  
  └──────────────────────┬──────────────────────────────────────┘  
                                                                   
  ┌──────────────────────▼──────────────────────────────────────┐  
            OpenViking Memory Slot Plugin                         
    (memory_recall / memory_store / memory_forget tools)          
  └──────────────────────┬──────────────────────────────────────┘  
└──────────────────────────┼─────────────────────────────────────────┘
                            HTTP API (:1933)
              ┌────────────▼────────────┐
                  Load Balancer / VIP   
              └─────┬──────────┬────────┘
                              
          ┌─────────▼──┐  ┌───▼──────────┐
           OpenViking     OpenViking   
            Primary        Standby     
            Instance       Instance    
          └─────┬──────┘  └──────┬────────┘
                                
          ┌─────▼────────────────▼────────┐
               Shared Storage Layer       
            (S3 / NFS / Distributed FS)   
            ┌─────────┐  ┌────────────┐  
              AGFS     Vector Index  
             Content      (Dense +    
             Storage      Sparse)     
            └─────────┘  └────────────┘  
          └────────────────────────────────┘

3. OpenClaw Context Engine 接口详解

核心价值回扣:Context Engine 是 OpenClaw 实现”统一上下文管理”和”三级按需加载”的接口契约。正是这个 pluggable 架构,使得我们可以将 OpenViking 的高效记忆检索无缝接入 Agent 运行时,从根本上解决 Token 浪费和记忆碎片化问题。

3.1 接口概述

OpenClaw 通过 plugins.slots.contextEngine 配置项选择活跃的 Context Engine。默认使用内置的 legacy 引擎。自定义引擎通过插件注册:

api.registerContextEngine("openviking", () => new OpenVikingContextEngine(config));

3.2 ContextEngine 接口定义

源码位置:/usr/lib/node_modules/openclaw/dist/plugin-sdk/context-engine/types.d.ts

interface ContextEngine {
  /** 引擎元信息 */
  readonly info: ContextEngineInfo;

  /** 初始化引擎状态,可导入历史上下文 */
  bootstrap?(params: {
    sessionId: string;
    sessionFile: string;
  }): Promise<BootstrapResult>;

  /** 写入单条消息到引擎存储 */
  ingest(params: {
    sessionId: string;
    message: AgentMessage;
    isHeartbeat?: boolean;
  }): Promise<IngestResult>;

  /** 批量写入一个完整 turn */
  ingestBatch?(params: {
    sessionId: string;
    messages: AgentMessage[];
    isHeartbeat?: boolean;
  }): Promise<IngestBatchResult>;

  /** Turn 完成后的生命周期处理(持久化、后台压缩等) */
  afterTurn?(params: {
    sessionId: string;
    sessionFile: string;
    messages: AgentMessage[];
    prePromptMessageCount: number;
    autoCompactionSummary?: string;
    isHeartbeat?: boolean;
    tokenBudget?: number;
    runtimeContext?: ContextEngineRuntimeContext;
  }): Promise<void>;

  /** 在 token 预算内组装模型上下文 */
  assemble(params: {
    sessionId: string;
    messages: AgentMessage[];
    tokenBudget?: number;
  }): Promise<AssembleResult>;

  /** 压缩上下文以减少 token 使用 */
  compact(params: {
    sessionId: string;
    sessionFile: string;
    tokenBudget?: number;
    force?: boolean;
    currentTokenCount?: number;
    compactionTarget?: "budget" | "threshold";
    customInstructions?: string;
    runtimeContext?: ContextEngineRuntimeContext;
  }): Promise<CompactResult>;

  /** 为子代理准备上下文状态 */
  prepareSubagentSpawn?(params: {
    parentSessionKey: string;
    childSessionKey: string;
    ttlMs?: number;
  }): Promise<SubagentSpawnPreparation | undefined>;

  /** 子代理生命周期结束通知 */
  onSubagentEnded?(params: {
    childSessionKey: string;
    reason: SubagentEndReason;
  }): Promise<void>;

  /** 释放资源 */
  dispose?(): Promise<void>;
}

3.3 核心方法详解

ingest() — 消息写入

参数 类型 说明
sessionId string 会话 ID
message AgentMessage 单条消息(user/assistant/tool)
isHeartbeat boolean? 是否心跳消息

返回 IngestResult { ingested: boolean } — 是否成功写入(去重时可返回 false)。

设计要点

  • Legacy 引擎的 ingest 是 no-op(SessionManager 自行处理持久化)
  • 自定义引擎应将消息转发到 OpenViking 的 Session API

assemble() — 上下文组装

参数 类型 说明
sessionId string 会话 ID
messages AgentMessage[] 当前所有消息
tokenBudget number? Token 预算上限

返回:

type AssembleResult = {
  messages: AgentMessage[];        // 排序后的模型上下文消息
  estimatedTokens: number;         // 估算总 token 数
  systemPromptAddition?: string;   // 注入到系统提示的额外内容
};

设计要点

  • 这是集成 L0/L1/L2 三级加载的关键入口
  • 可通过 systemPromptAddition 注入 OpenViking 检索到的记忆

compact() — 上下文压缩

参数 类型 说明
sessionId string 会话 ID
sessionFile string 会话 JSONL 文件路径
tokenBudget number? 目标 token 预算
force boolean? 是否强制压缩
currentTokenCount number? 当前 token 数
compactionTarget string? 压缩目标模式

返回:

type CompactResult = {
  ok: boolean;
  compacted: boolean;
  reason?: string;
  result?: {
    summary?: string;
    firstKeptEntryId?: string;
    tokensBefore: number;
    tokensAfter?: number;
    details?: unknown;
  };
};

设计要点

  • info.ownsCompaction = true 时,引擎完全接管压缩生命周期
  • OpenViking 的 Session Compressor 天然适配此接口

3.4 注册机制

// registry.d.ts
type ContextEngineFactory = () => ContextEngine | Promise<ContextEngine>;

function registerContextEngine(id: string, factory: ContextEngineFactory): void;
function resolveContextEngine(config?: OpenClawConfig): Promise<ContextEngine>;

配置激活:

{
  "plugins": {
    "slots": {
      "contextEngine": "openviking-context"
    }
  }
}

3.5 插件清单 (openclaw.plugin.json)

{
  "id": "openviking-context",
  "kind": "context-engine",
  "name": "OpenViking Context Engine",
  "description": "Three-tier context assembly with OpenViking backend",
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "mode": {
        "type": "string",
        "enum": ["local", "remote"],
        "default": "remote"
      },
      "baseUrl": {
        "type": "string",
        "default": "http://localhost:1933"
      },
      "apiKey": {
        "type": "string"
      },
      "l1TokenBudget": {
        "type": "number",
        "default": 2000
      },
      "autoCompact": {
        "type": "boolean",
        "default": true
      }
    }
  }
}

4. OpenViking 架构深度分析

核心价值回扣:OpenViking 是实现”Token 成本 -91%”的核心引擎。它的 L0/L1/L2 三级加载机制和文件系统范式,正是将”粗暴的全量检索”转变为”精准的按需加载”的技术基础,同时也是多 Agent 共享记忆的统一数据层。

4.1 系统架构

┌────────────────────────────────────────────────────────┐
                   OpenViking Server (:1933)              
                                                          
  ┌────────────┐  ┌──────────────┐  ┌────────────────┐  
    Retrieve        Session           Parse        
    Module          Module            Module       
                                                  
    Intent       Add msg        Doc parsing    
     Analysis     Commit         L0/L1/L2       
    Hierarch.    Extract        Tree build     
     Retrieval     memories                       
    Rerank       Compress                       
  └─────┬──────┘  └──────┬───────┘  └───────┬────────┘  
                                                       
  ┌─────▼─────────────────▼───────────────────▼────────┐  
                    Storage Layer                        
    ┌──────────────────┐  ┌─────────────────────────┐   
          AGFS               Vector Index            
      (File Content)      (URI + Vector + Meta)      
                                                      
     Backends:           Backends:                    
       localfs            local                     
       s3fs               http                      
       memory             volcengine VikingDB       
    └──────────────────┘  └─────────────────────────┘   
  └─────────────────────────────────────────────────────┘  
└────────────────────────────────────────────────────────────┘

4.2 L0/L1/L2 三级上下文加载机制

这是 OpenViking 的核心创新,也是实现 Token 消耗降低 91% 的关键技术。对比传统 RAG 的扁平存储,三级加载实现了”用 1% 的 Token 完成 100% 的检索精度”:

层级 名称 文件 Token 限制 用途
L0 Abstract .abstract.md ~100 tokens 向量检索 + 快速过滤
L1 Overview .overview.md ~2k tokens 重排序 + 内容导航
L2 Detail 原始文件 无限制 完整内容,按需加载

加载策略

Query  L0 向量匹配  L1 语义重排  L2 按需读取
                                            
             ~100 tok       ~2k tok      完整内容

目录结构示例

viking://user/memories/
├── .abstract.md          # L0: "用户偏好:中文沟通,TypeScript 开发"
├── .overview.md          # L1: 详细偏好列表 + 子目录导航
├── preferences/
   ├── .abstract.md
   ├── .overview.md
   └── coding-style.md   # L2: 完整编码风格文档
└── decisions/
    ├── .abstract.md
    ├── .overview.md
    └── arch-2026-01.md    # L2: 2026年1月架构决策

这种层级结构让 Agent 像人类浏览目录一样逐层深入,而不是把整个图书馆搬进对话窗口。

4.3 文件系统范式

OpenViking 用 viking:// URI 统一管理三种上下文类型,消除记忆碎片化的核心就在于这个统一的寻址方案:

URI Scheme 上下文类型 说明
viking://user/ 用户记忆 偏好、历史、决策
viking://agent/ Agent 记忆 技能、工作区、指令
viking://resources/ 资源 文档、代码、知识库

4.4 HTTP API 接口 (端口 1933)

核心 API 端点

类别 Method Path 说明
系统 GET /health 健康检查(无需认证)
系统 GET /api/v1/system/status 系统状态
文件系统 GET /api/v1/fs/ls 目录列表
文件系统 GET /api/v1/fs/tree 目录树
文件系统 DELETE /api/v1/fs 删除资源
内容 GET /api/v1/content/read 读取完整内容 (L2)
内容 GET /api/v1/content/abstract 读取摘要 (L0)
内容 GET /api/v1/content/overview 读取概览 (L1)
检索 POST /api/v1/search/find 语义搜索
会话 POST /api/v1/sessions 创建会话
会话 POST /api/v1/sessions/:id/messages 添加消息
会话 POST /api/v1/sessions/:id/extract 提取记忆
资源 POST /api/v1/resources 导入资源
导出 POST /api/v1/pack/export 导出 .ovpack
导入 POST /api/v1/pack/import 导入 .ovpack

认证

  • Header: X-API-Key: your-keyAuthorization: Bearer your-key
  • /health 端点不需要认证

响应格式

{
  "status": "ok",
  "result": { ... },
  "time": 0.123
}

4.5 现有 Memory 插件分析

现有 memory-openviking 插件实现了 Memory Slotkind: "memory"),注册了三个工具:

工具 功能
memory_recall 语义搜索记忆,同时检索 user/agent 两个 scope
memory_store 创建临时 session → 添加消息 → 提取记忆 → 清理
memory_forget 按 URI 或搜索匹配删除记忆

关键特性

  • 支持 localremote 两种模式
  • Local 模式自动启动 OpenViking Python 进程
  • Auto-recall:在 before_prompt_build hook 中自动检索相关记忆
  • Auto-capture:在 after_compaction hook 中自动提取新记忆

5. 集成方案设计

核心价值回扣:本节是”怎么做”的核心——通过 Context Engine + Memory Slot 双层集成,将 OpenViking 的能力无缝融入 Agent 运行时。这一集成使得每次 Agent 调用都自动享受三级加载的 Token 优化和跨 Agent 记忆共享,无需 Agent 自身做任何适配

5.1 双层集成架构

我们采用 Context Engine + Memory Slot 双层集成:

┌─────────────────────────────────────────────────┐
                OpenClaw Agent Runtime              
                                                    
  每次 Agent Run:                                   
  1. assemble()  Context Engine 组装上下文         
     ├─ 注入 L1 概览作为 systemPromptAddition       
     ├─ 基于 token budget 智能裁剪历史消息          
     └─ 附加相关记忆摘要                            
                                                    
  2. ingest()  每条消息写入 OpenViking Session     
                                                    
  3. afterTurn()  Turn 完成后触发                   
     ├─ 持久化 canonical context                    
     └─ 触发后台压缩决策                            
                                                    
  4. compact()  Token 超预算或 /compact 触发       
     ├─ 委托 OpenViking Session Compressor          
     ├─ 压缩历史  生成 L0/L1 归档                 
     └─ 提取长期记忆写入 memory store               
                                                    
  独立工具调用:                                      
   memory_recall  显式语义搜索                     
   memory_store  显式存储记忆                      
   memory_forget  删除记忆                         
└─────────────────────────────────────────────────────┘

5.2 Context Engine 插件实现

// openviking-context-engine/index.ts
import type {
  OpenClawPluginApi,
  OpenClawPluginDefinition,
} from "openclaw/plugin-sdk";
import type {
  ContextEngine,
  ContextEngineInfo,
  AssembleResult,
  CompactResult,
  IngestResult,
  IngestBatchResult,
  BootstrapResult,
} from "openclaw/plugin-sdk/context-engine";
import type { AgentMessage } from "@mariozechner/pi-agent-core";

// ─── Configuration ─────────────────────────────────────
interface OpenVikingContextConfig {
  mode: "local" | "remote";
  baseUrl: string;
  apiKey?: string;
  agentId?: string;
  /** L1 token budget for assemble() systemPromptAddition */
  l1TokenBudget: number;
  /** Whether to auto-compact via OpenViking */
  autoCompact: boolean;
  /** Threshold ratio to trigger compaction (e.g., 0.85 = 85% of budget) */
  compactThreshold: number;
  /** Max memories to inject per assemble */
  maxMemoryInjection: number;
}

// ─── HTTP Client (simplified) ─────────────────────────
class OVClient {
  constructor(
    private baseUrl: string,
    private apiKey?: string,
    private agentId?: string,
  ) {}

  private async request<T>(path: string, init?: RequestInit): Promise<T> {
    const headers = new Headers(init?.headers ?? {});
    if (this.apiKey) headers.set("X-API-Key", this.apiKey);
    if (this.agentId) headers.set("X-OpenViking-Agent", this.agentId);
    if (init?.body) headers.set("Content-Type", "application/json");

    const res = await fetch(`${this.baseUrl}${path}`, { ...init, headers });
    const payload = await res.json() as { status?: string; result?: T };
    if (!res.ok || payload.status === "error") {
      throw new Error(`OpenViking error: HTTP ${res.status}`);
    }
    return (payload.result ?? payload) as T;
  }

  async health(): Promise<void> {
    await this.request("/health");
  }

  async createSession(): Promise<string> {
    const r = await this.request<{ session_id: string }>("/api/v1/sessions", {
      method: "POST", body: JSON.stringify({}),
    });
    return r.session_id;
  }

  async addMessage(sessionId: string, role: string, content: string): Promise<void> {
    await this.request(`/api/v1/sessions/${sessionId}/messages`, {
      method: "POST", body: JSON.stringify({ role, content }),
    });
  }

  async extractMemories(sessionId: string): Promise<unknown[]> {
    return this.request(`/api/v1/sessions/${sessionId}/extract`, {
      method: "POST", body: JSON.stringify({}),
    });
  }

  async find(query: string, targetUri: string, limit: number): Promise<{
    memories?: Array<{ uri: string; abstract?: string; score?: number; level?: number }>;
  }> {
    return this.request("/api/v1/search/find", {
      method: "POST",
      body: JSON.stringify({ query, target_uri: targetUri, limit }),
    });
  }

  async overview(uri: string): Promise<string> {
    return this.request(`/api/v1/content/overview?uri=${encodeURIComponent(uri)}`);
  }

  async abstract(uri: string): Promise<string> {
    return this.request(`/api/v1/content/abstract?uri=${encodeURIComponent(uri)}`);
  }

  async deleteSession(sessionId: string): Promise<void> {
    await this.request(`/api/v1/sessions/${sessionId}`, { method: "DELETE" });
  }
}

// ─── Context Engine Implementation ────────────────────
class OpenVikingContextEngine implements ContextEngine {
  readonly info: ContextEngineInfo = {
    id: "openviking-context",
    name: "OpenViking Context Engine",
    version: "1.0.0",
    ownsCompaction: true, // 引擎接管压缩生命周期
  };

  private client: OVClient;
  private sessionMap = new Map<string, string>(); // openclawSessionId → ovSessionId
  private cfg: OpenVikingContextConfig;

  constructor(config: OpenVikingContextConfig) {
    this.cfg = config;
    this.client = new OVClient(config.baseUrl, config.apiKey, config.agentId);
  }

  // ── bootstrap: 初始化 OpenViking session ──
  async bootstrap(params: {
    sessionId: string;
    sessionFile: string;
  }): Promise<BootstrapResult> {
    try {
      const ovSessionId = await this.client.createSession();
      this.sessionMap.set(params.sessionId, ovSessionId);
      return { bootstrapped: true, importedMessages: 0 };
    } catch (err) {
      return { bootstrapped: false, reason: String(err) };
    }
  }

  // ── ingest: 写入消息到 OpenViking session ──
  async ingest(params: {
    sessionId: string;
    message: AgentMessage;
    isHeartbeat?: boolean;
  }): Promise<IngestResult> {
    if (params.isHeartbeat) return { ingested: false };

    const ovSessionId = await this.ensureOVSession(params.sessionId);
    const role = this.mapRole(params.message);
    const content = this.extractContent(params.message);

    if (!content) return { ingested: false };

    await this.client.addMessage(ovSessionId, role, content);
    return { ingested: true };
  }

  // ── ingestBatch: 批量写入 ──
  async ingestBatch(params: {
    sessionId: string;
    messages: AgentMessage[];
    isHeartbeat?: boolean;
  }): Promise<IngestBatchResult> {
    if (params.isHeartbeat) return { ingestedCount: 0 };

    let count = 0;
    for (const msg of params.messages) {
      const result = await this.ingest({
        sessionId: params.sessionId,
        message: msg,
        isHeartbeat: false,
      });
      if (result.ingested) count++;
    }
    return { ingestedCount: count };
  }

  // ── assemble: 核心 — 三级加载组装上下文 ──
  async assemble(params: {
    sessionId: string;
    messages: AgentMessage[];
    tokenBudget?: number;
  }): Promise<AssembleResult> {
    const budget = params.tokenBudget ?? 128000;
    const reservedForMemory = Math.min(this.cfg.l1TokenBudget, budget * 0.15);

    // 1. 提取最新用户消息作为检索 query
    const query = this.extractLatestUserQuery(params.messages);
    let memoryAddition = "";

    if (query) {
      // 2. L0 级检索:快速语义匹配
      const results = await this.client.find(
        query,
        "viking://user/memories",
        this.cfg.maxMemoryInjection * 3,
      );

      if (results.memories && results.memories.length > 0) {
        // 3. 取 top-N,用 L1 概览丰富上下文
        const topMemories = results.memories
          .filter(m => m.level === 2)
          .slice(0, this.cfg.maxMemoryInjection);

        const memoryTexts: string[] = [];
        let tokenUsed = 0;

        for (const mem of topMemories) {
          const text = mem.abstract ?? "";
          const estimatedTokens = text.length / 4;
          if (tokenUsed + estimatedTokens > reservedForMemory) break;
          memoryTexts.push(`- ${text}`);
          tokenUsed += estimatedTokens;
        }

        if (memoryTexts.length > 0) {
          memoryAddition = [
            "\n## Recalled Memories (OpenViking)",
            ...memoryTexts,
          ].join("\n");
        }
      }
    }

    // 4. 裁剪消息列表到 token budget
    const messagesForContext = this.trimMessages(
      params.messages,
      budget - reservedForMemory,
    );

    return {
      messages: messagesForContext,
      estimatedTokens: this.estimateTokens(messagesForContext) +
        (memoryAddition.length / 4),
      systemPromptAddition: memoryAddition || undefined,
    };
  }

  // ── afterTurn: Turn 完成后的后台处理 ──
  async afterTurn(params: {
    sessionId: string;
    sessionFile: string;
    messages: AgentMessage[];
    prePromptMessageCount: number;
    tokenBudget?: number;
  }): Promise<void> {
    // 评估是否需要后台压缩
    if (!this.cfg.autoCompact) return;

    const budget = params.tokenBudget ?? 128000;
    const currentTokens = this.estimateTokens(params.messages);
    const threshold = budget * this.cfg.compactThreshold;

    if (currentTokens > threshold) {
      // 异步触发压缩,不阻塞当前 turn
      this.compact({
        sessionId: params.sessionId,
        sessionFile: params.sessionFile,
        tokenBudget: budget,
      }).catch(() => { /* logged internally */ });
    }
  }

  // ── compact: 委托 OpenViking 压缩 + 提取记忆 ──
  async compact(params: {
    sessionId: string;
    sessionFile: string;
    tokenBudget?: number;
    force?: boolean;
  }): Promise<CompactResult> {
    const ovSessionId = this.sessionMap.get(params.sessionId);
    if (!ovSessionId) {
      return { ok: false, compacted: false, reason: "No OpenViking session" };
    }

    try {
      // 1. 触发记忆提取
      const extracted = await this.client.extractMemories(ovSessionId);

      // 2. 创建新 session(压缩后的干净 session)
      const newOvSession = await this.client.createSession();

      // 3. 迁移 session 映射
      const oldSession = ovSessionId;
      this.sessionMap.set(params.sessionId, newOvSession);

      // 4. 清理旧 session
      await this.client.deleteSession(oldSession).catch(() => {});

      return {
        ok: true,
        compacted: true,
        result: {
          summary: `Extracted ${extracted.length} memories, session rotated`,
          tokensBefore: params.tokenBudget ?? 0,
          tokensAfter: 0,
          details: { extractedCount: extracted.length },
        },
      };
    } catch (err) {
      return {
        ok: false,
        compacted: false,
        reason: String(err),
      };
    }
  }

  // ── prepareSubagentSpawn: 子代理上下文准备 ──
  async prepareSubagentSpawn(params: {
    parentSessionKey: string;
    childSessionKey: string;
    ttlMs?: number;
  }) {
    // 为子代理创建独立 OpenViking session
    // 但共享同一个 user/agent memory space
    const childOvSession = await this.client.createSession();
    this.sessionMap.set(params.childSessionKey, childOvSession);

    return {
      rollback: async () => {
        this.sessionMap.delete(params.childSessionKey);
        await this.client.deleteSession(childOvSession).catch(() => {});
      },
    };
  }

  // ── onSubagentEnded: 子代理结束 ──
  async onSubagentEnded(params: { childSessionKey: string }) {
    const ovSession = this.sessionMap.get(params.childSessionKey);
    if (ovSession) {
      // 提取子代理产生的记忆到共享空间
      await this.client.extractMemories(ovSession).catch(() => {});
      await this.client.deleteSession(ovSession).catch(() => {});
      this.sessionMap.delete(params.childSessionKey);
    }
  }

  // ── dispose ──
  async dispose(): Promise<void> {
    for (const [key, ovSession] of this.sessionMap) {
      await this.client.deleteSession(ovSession).catch(() => {});
    }
    this.sessionMap.clear();
  }

  // ── Private helpers ──

  private async ensureOVSession(sessionId: string): Promise<string> {
    let ovSession = this.sessionMap.get(sessionId);
    if (!ovSession) {
      ovSession = await this.client.createSession();
      this.sessionMap.set(sessionId, ovSession);
    }
    return ovSession;
  }

  private mapRole(msg: AgentMessage): string {
    const role = (msg as { role?: string }).role;
    if (role === "assistant") return "assistant";
    if (role === "tool") return "tool";
    return "user";
  }

  private extractContent(msg: AgentMessage): string {
    const content = (msg as { content?: unknown }).content;
    if (typeof content === "string") return content;
    if (Array.isArray(content)) {
      return content
        .map((c: { text?: string; type?: string }) =>
          c.type === "text" ? c.text ?? "" : "")
        .join("\n");
    }
    return "";
  }

  private extractLatestUserQuery(messages: AgentMessage[]): string | null {
    for (let i = messages.length - 1; i >= 0; i--) {
      const msg = messages[i] as { role?: string };
      if (msg.role === "user") {
        return this.extractContent(messages[i] as AgentMessage);
      }
    }
    return null;
  }

  private trimMessages(
    messages: AgentMessage[],
    tokenBudget: number,
  ): AgentMessage[] {
    // 简单策略:保留最新消息直到 budget 用完
    const result: AgentMessage[] = [];
    let tokens = 0;

    for (let i = messages.length - 1; i >= 0; i--) {
      const content = this.extractContent(messages[i] as AgentMessage);
      const msgTokens = content.length / 4;
      if (tokens + msgTokens > tokenBudget && result.length > 0) break;
      result.unshift(messages[i]!);
      tokens += msgTokens;
    }
    return result;
  }

  private estimateTokens(messages: AgentMessage[]): number {
    return messages.reduce((sum, msg) => {
      return sum + this.extractContent(msg).length / 4;
    }, 0);
  }
}

// ─── Plugin Definition ─────────────────────────────────
const plugin: OpenClawPluginDefinition = {
  id: "openviking-context",
  name: "OpenViking Context Engine",
  description: "Three-tier context assembly powered by OpenViking",
  kind: "context-engine",

  register(api: OpenClawPluginApi) {
    const config = api.pluginConfig as OpenVikingContextConfig;

    const defaults: OpenVikingContextConfig = {
      mode: "remote",
      baseUrl: "http://localhost:1933",
      l1TokenBudget: 2000,
      autoCompact: true,
      compactThreshold: 0.85,
      maxMemoryInjection: 10,
      ...config,
    };

    api.registerContextEngine(
      "openviking-context",
      () => new OpenVikingContextEngine(defaults),
    );

    api.logger.info(
      `openviking-context: registered (mode=${defaults.mode}, baseUrl=${defaults.baseUrl})`,
    );
  },
};

export default plugin;

5.3 多 Agent 共享上下文方案

多 Agent 共享记忆是打破”孤岛效应”的关键。通过统一的 URI 命名空间,5 个 Agent 既能共享用户级记忆,又能保持各自工作记忆的独立性:

┌──────────────────────────────────────────────────────────────────┐
                     OpenViking URI Namespace                       
                                                                    
  viking://user/{userId}/                                          │
  ├── memories/            所有 Agent 共享的用户记忆              
     ├── preferences/     用户偏好                               
     ├── decisions/       架构/产品决策                           
     └── context/         项目上下文                              
                                                                   
  viking://agent/{agentHash}/                                      │
  ├── memories/            Agent 专属记忆                          
     ├── main/            Coordinator 的任务协调记忆              
     ├── waicode/         编码经验bug 模式                     
     ├── waidesign/       设计规范UI 模式                      
     ├── wairesearch/     研究结论技术选型                     
     └── waiqa/           测试策略质量标准                     
  ├── skills/              Agent 技能定义                          
  ├── instructions/        Agent 工作指令                          
  └── workspaces/          工作区上下文                            
                                                                    
  viking://resources/                                              │
  ├── codebase/            代码库知识                              
  ├── docs/                技术文档                                
  └── designs/             设计文档                                
└──────────────────────────────────────────────────────────────────┘

Agent ID 隔离方案

每个 Agent 连接 OpenViking 时使用不同的 X-OpenViking-Agent header:

// OpenClaw 配置 - 每个 agent 独立的 pluginConfig 覆盖
// ~/.openclaw/openclaw.json
{
  "agents": {
    "main": {
      "plugins": {
        "entries": {
          "openviking-context": {
            "config": { "agentId": "main" }
          }
        }
      }
    },
    "waicode": {
      "plugins": {
        "entries": {
          "openviking-context": {
            "config": { "agentId": "waicode" }
          }
        }
      }
    }
    // ... 其他 agent 类似
  }
}

跨 Agent 记忆共享机制

Agent 写入流程:
  waicode ingest("found a bug pattern in auth module")
     OpenViking Session  Extract  viking://agent/{hash}/memories/waicode/

Agent 读取流程 (assemble):
  main assemble() 
     find("auth module") 
     搜索 viking://user/memories + viking://agent/memories
     返回所有 Agent 的相关记忆包括 waicode 发现的 bug 模式

5.4 配置示例

完整 OpenClaw 配置

// ~/.openclaw/openclaw.json
{
  "plugins": {
    "enabled": true,
    "slots": {
      "memory": "memory-openviking",        // Memory 工具层
      "contextEngine": "openviking-context"  // Context Engine 层
    },
    "entries": {
      // Layer 1: Memory Plugin (工具 + auto-recall/capture)
      "memory-openviking": {
        "config": {
          "mode": "remote",
          "baseUrl": "http://openviking-lb:1933",
          "apiKey": "${OPENVIKING_API_KEY}",
          "autoRecall": true,
          "autoCapture": true,
          "recallLimit": 15,
          "recallScoreThreshold": 0.3,
          "targetUri": "viking://user/memories"
        }
      },
      // Layer 2: Context Engine (assemble/compact 生命周期)
      "openviking-context": {
        "config": {
          "mode": "remote",
          "baseUrl": "http://openviking-lb:1933",
          "apiKey": "${OPENVIKING_API_KEY}",
          "l1TokenBudget": 2000,
          "autoCompact": true,
          "compactThreshold": 0.85,
          "maxMemoryInjection": 10
        }
      }
    }
  }
}

OpenViking Server 配置(HA 模式)

// ~/.openviking/ov.conf
{
  "storage": {
    // 使用 S3 后端实现共享存储
    "workspace": "s3://openviking-prod/workspace",
    "backend": "s3fs",
    "s3": {
      "bucket": "openviking-prod",
      "endpoint": "https://tos-s-sgp.volces.com",
      "access_key": "${S3_ACCESS_KEY}",
      "secret_key": "${S3_SECRET_KEY}",
      "region": "ap-southeast-1"
    }
  },
  "log": {
    "level": "INFO",
    "output": "file"
  },
  "embedding": {
    "dense": {
      "api_base": "https://ark.cn-beijing.volces.com/api/v3",
      "api_key": "${VLM_API_KEY}",
      "provider": "volcengine",
      "dimension": 1024,
      "model": "doubao-embedding-vision-250615"
    },
    "max_concurrent": 10
  },
  "vlm": {
    "api_base": "https://ark.cn-beijing.volces.com/api/v3",
    "api_key": "${VLM_API_KEY}",
    "provider": "volcengine",
    "model": "doubao-seed-2-0-pro-260215",
    "max_concurrent": 100
  },
  // 向量索引使用托管服务
  "vector": {
    "backend": "volcengine",
    "config": {
      "endpoint": "https://vikingdb.volces.com",
      "api_key": "${VIKINGDB_API_KEY}"
    }
  },
  // 认证
  "auth": {
    "api_key": "${OPENVIKING_API_KEY}"
  },
  // 服务器配置
  "server": {
    "host": "0.0.0.0",
    "port": 1933,
    "workers": 4
  }
}

HAProxy 负载均衡配置

# /etc/haproxy/haproxy.cfg
global
    log stdout format raw local0
    maxconn 4096

defaults
    log     global
    mode    http
    option  httplog
    timeout connect 5s
    timeout client  120s
    timeout server  120s

frontend openviking_front
    bind *:1933
    default_backend openviking_back

backend openviking_back
    option httpchk GET /health
    http-check expect status 200

    # 主实例(优先路由)
    server ov-primary 10.0.1.10:1933 check inter 5s fall 3 rise 2
    # 备用实例
    server ov-standby 10.0.1.11:1933 check inter 5s fall 3 rise 2 backup

    # 可选:多活模式(需要共享存储 + 写锁)
    # server ov-node1 10.0.1.10:1933 check inter 5s
    # server ov-node2 10.0.1.11:1933 check inter 5s

6. 高可用架构设计

核心价值回扣:高可用不是锦上添花,而是 Agent 记忆从”玩具”到”基础设施”的分水岭。本节设计的 HA 架构,正是实现 99.9% 可用性秒级故障恢复的技术保障——让用户可以信任 Agent 的记忆永远在那里。

6.1 HA 架构总览

                    ┌─────────────────────┐
                       OpenClaw Gateway   
                     (Context Engine +    
                      Memory Plugin)      
                    └──────────┬───────────┘
                               
                    ┌──────────▼───────────┐
                      HAProxy / Nginx LB   
                      (Health Check :1933)  
                      ┌────────────────┐   
                       Active Probing    
                       GET /health       
                       interval: 5s      
                      └────────────────┘   
                    └──────┬──────────┬─────┘
                                     
              ┌────────────▼──┐  ┌────▼───────────┐
                OpenViking        OpenViking    
                Instance A        Instance B    
                (Primary)         (Standby)     
                :1933             :1933         
              └───────┬───────┘  └────────┬────────┘
                                         
              ┌───────▼───────────────────▼────────┐
                       Shared Storage Layer         
                                                    
                Option A: S3-Compatible Storage     
                ┌─────────┐    ┌────────────────┐  
                  MinIO        AWS S3 / R2 /    
                 (on-prem)     Volcengine TOS   
                └─────────┘    └────────────────┘  
                                                    
                Option B: NFS Shared Mount          
                ┌─────────────────────────────┐    
                  NFS Server (RAID + Backup)      
                └─────────────────────────────┘    
                                                    
                Vector Index: Volcengine VikingDB   
                (Managed service, built-in HA)      
              └──────────────────────────────────────┘

6.2 存储层 HA 方案

方案 A:S3 后端(推荐生产环境)

OpenViking 的 AGFS 原生支持 s3fs 后端:

{
  "storage": {
    "workspace": "s3://openviking-context/workspace",
    "backend": "s3fs",
    "s3": {
      "bucket": "openviking-context",
      "endpoint": "https://tos-s-sgp.volces.com",
      "access_key": "...",
      "secret_key": "...",
      "region": "ap-southeast-1"
    }
  }
}

优势

  • 99.999999999% (11个9) 持久性
  • 多实例可同时读写(通过对象锁定保证一致性)
  • 无需自建存储基础设施
  • 跨区域复制能力

注意事项

  • S3 延迟高于本地文件系统(~50-200ms vs ~1ms)
  • 需要考虑 list 操作的最终一致性
  • 建议搭配本地缓存层

方案 B:NFS 共享挂载

{
  "storage": {
    "workspace": "/mnt/nfs/openviking_workspace",
    "backend": "localfs"
  }
}

优势

  • 延迟低(~2-5ms)
  • POSIX 兼容,与 localfs 后端无缝对接
  • 运维熟悉度高

注意事项

  • NFS 本身是单点(需要额外 HA 方案如 DRBD + Pacemaker)
  • 文件锁性能问题
  • 大规模并发写入需要调优

方案 C:混合方案(推荐)

AGFS Content  S3 (持久化跨实例共享)
Vector Index  Volcengine VikingDB (托管服务内建 HA)
Hot Cache     Local SSD (减少 S3 延迟)

6.3 多实例部署方案

┌─────────────────────────────────────────────────────┐
                 Deployment Topology                   
                                                       
  ┌──────────────────┐    ┌──────────────────┐        
     Node 1 (SGP)          Node 2 (SGP)           
                                                    
    OpenClaw GW            OpenViking               
    OpenViking (A)         Instance (B)             
    [Primary]              [Standby]                
                                                    
    Local Cache:           Local Cache:             
    /tmp/ov-cache          /tmp/ov-cache            
  └────────┬──────────┘    └────────┬──────────┘        
                                                       
           └────────┬───────────────┘                    
                                                        
           ┌────────▼────────┐                           
              S3 Storage                               
            (VolcEngine TOS)                           
           └─────────────────┘                           
└─────────────────────────────────────────────────────────┘

6.4 故障转移策略

健康检查

# HAProxy 配置示例
backend openviking_backend
    option httpchk GET /health
    http-check expect status 200

    server ov-primary 10.0.1.10:1933 check inter 5s fall 3 rise 2
    server ov-standby 10.0.1.11:1933 check inter 5s fall 3 rise 2 backup

故障转移流程

                    Primary 健康检查失败
                           
                    ┌──────▼──────┐
                     连续 3 次失败  
                     (15 秒窗口)   
                    └──────┬──────┘
                           
                    ┌──────▼──────┐
                     LB 自动切换   
                      Standby   
                    └──────┬──────┘
                           
              ┌────────────▼────────────┐
               Standby 变为 Active      
                连接共享存储 (已就绪)   
                加载向量索引 (已同步)   
                服务恢复时间 < 5s       
              └──────────────────────────┘

6.5 增量备份与全量重建

增量备份策略

#!/bin/bash
# cron: 每小时执行增量备份
# S3 后端自带版本控制,额外备份到冷存储

BACKUP_BUCKET="s3://openviking-backup"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# 1. 导出 ovpack(OpenViking 原生导出格式)
curl -X POST http://localhost:1933/api/v1/pack/export \
  -H "X-API-Key: ${OV_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"scope": "all"}' \
  -o "/tmp/backup_${TIMESTAMP}.ovpack"

# 2. 上传到备份存储
aws s3 cp "/tmp/backup_${TIMESTAMP}.ovpack" \
  "${BACKUP_BUCKET}/incremental/${TIMESTAMP}.ovpack"

# 3. 保留最近 72 小时的增量备份
aws s3 ls "${BACKUP_BUCKET}/incremental/" | \
  awk '{print $4}' | head -n -72 | \
  xargs -I {} aws s3 rm "${BACKUP_BUCKET}/incremental/{}"

全量重建流程

#!/bin/bash
# 从备份恢复 OpenViking 实例

# 1. 启动空 OpenViking 实例
openviking-server &
sleep 5

# 2. 导入最新备份
LATEST_BACKUP=$(aws s3 ls s3://openviking-backup/incremental/ | tail -1 | awk '{print $4}')
aws s3 cp "s3://openviking-backup/incremental/${LATEST_BACKUP}" /tmp/restore.ovpack

curl -X POST http://localhost:1933/api/v1/pack/import \
  -H "X-API-Key: ${OV_API_KEY}" \
  -F "file=@/tmp/restore.ovpack"

# 3. 等待语义处理完成
curl -X POST http://localhost:1933/api/v1/system/wait \
  -H "X-API-Key: ${OV_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"timeout": 300}'

echo "Restore complete"

7. 风险评估与缓解

风险 概率 影响 缓解措施
OpenViking API 延迟过高影响 assemble 本地缓存 L0/L1 + 5s 超时 + 降级到 legacy
Context Engine 与 Memory Plugin 冲突 明确分工 + 集成测试覆盖
S3 存储最终一致性导致读取脏数据 强一致性读取策略 + 缓存验证
OpenViking 版本升级 breaking changes 锁定版本 + 升级测试环境
VLM/Embedding API 限流 令牌桶限流 + 队列化
多 Agent 并发写入冲突 写入是 append-only + 不同 scope 隔离
灾难恢复时 ovpack 恢复耗时过长 定期演练 + 增量备份缩小恢复时间窗口

降级策略

// Context Engine 内建降级逻辑
class OpenVikingContextEngine implements ContextEngine {
  private fallbackToLegacy = false;

  async assemble(params: AssembleParams): Promise<AssembleResult> {
    if (this.fallbackToLegacy) {
      // 降级:直接返回消息,不做 OpenViking 检索
      return {
        messages: params.messages,
        estimatedTokens: this.estimateTokens(params.messages),
      };
    }

    try {
      return await this.assembleWithOpenViking(params);
    } catch (err) {
      this.logger.warn(`OpenViking assemble failed, falling back: ${err}`);
      this.fallbackToLegacy = true;

      // 60 秒后尝试恢复
      setTimeout(() => {
        this.fallbackToLegacy = false;
      }, 60_000);

      return {
        messages: params.messages,
        estimatedTokens: this.estimateTokens(params.messages),
      };
    }
  }
}

8. 附录

A. OpenViking HTTP API 快速参考

# 健康检查
curl http://localhost:1933/health

# 系统状态
curl http://localhost:1933/api/v1/system/status -H "X-API-Key: xxx"

# 目录列表
curl "http://localhost:1933/api/v1/fs/ls?uri=viking://user/memories/"

# 语义搜索
curl -X POST http://localhost:1933/api/v1/search/find \
  -H "Content-Type: application/json" \
  -d '{"query": "TypeScript best practices", "target_uri": "viking://user/memories", "limit": 10}'

# 创建会话
curl -X POST http://localhost:1933/api/v1/sessions \
  -H "Content-Type: application/json" -d '{}'

# 添加消息
curl -X POST http://localhost:1933/api/v1/sessions/{id}/messages \
  -H "Content-Type: application/json" \
  -d '{"role": "user", "content": "Hello world"}'

# 提取记忆
curl -X POST http://localhost:1933/api/v1/sessions/{id}/extract \
  -H "Content-Type: application/json" -d '{}'

# 导出备份
curl -X POST http://localhost:1933/api/v1/pack/export \
  -H "Content-Type: application/json" -d '{"scope": "all"}' -o backup.ovpack

# 导入恢复
curl -X POST http://localhost:1933/api/v1/pack/import -F "file=@backup.ovpack"

B. OpenClaw Plugin SDK 关键类型引用

// Context Engine 注册
api.registerContextEngine(id: string, factory: ContextEngineFactory): void;

// 可用 Plugin Hooks
type PluginHookName =
  | "before_model_resolve"
  | "before_prompt_build"
  | "before_agent_start"
  | "llm_input" | "llm_output"
  | "agent_end"
  | "before_compaction" | "after_compaction"
  | "before_reset"
  | "message_received" | "message_sending" | "message_sent"
  | "before_tool_call" | "after_tool_call"
  | "session_start" | "session_end"
  | "subagent_spawning" | "subagent_spawned" | "subagent_ended"
  | "gateway_start" | "gateway_stop";

// Plugin Slots
"plugins.slots.memory"        // Memory 插件选择
"plugins.slots.contextEngine" // Context Engine 选择 (default: "legacy")

C. 性能基准参考

基于 OpenViking 官方 LoCoMo10 评测(1,540 个案例):

方案 任务完成率 输入 Token
OpenClaw (memory-core) 35.65% 24,611,530
OpenClaw + LanceDB 44.55% 51,574,530
OpenClaw + OpenViking (-memory-core) 52.08% 4,264,396
OpenClaw + OpenViking (+memory-core) 51.23% 2,099,622

结论:OpenViking 集成后,任务完成率提升 43-49%,Token 消耗降低 83-96%。


文档结束。如有疑问请联系 wairesearch。

📖 相似文章推荐

上一篇
研究报告:OpenClaw Gateway 架构深度研究报告
下一篇
研究报告:OpenClaw 多Agent记忆系统与上下文共享研究报告