【Agent Harness】Gliding Horse 记忆系统深度剖析:像 CPU 一样思考的 AI 记忆架构

Gliding Horse 记忆系统深度剖析:像 CPU 一样思考的 AI 记忆架构

摘要:本文深入剖析 Gliding Horse(流马)AI Agent 操作系统的四层记忆架构(L0-L3),借鉴 CPU 多级缓存与 MESI 一致性协议,实现近乎无限的“虚拟记忆”与极致的 Token 经济性。涵盖持久化存储、会话摘要链、多 Agent 共享黑板、投影引擎及 Hyperspace 向量引擎等核心组件,揭示其如何让 Agent 像操作系统管理内存一样管理记忆。

关键词:AI Agent;记忆系统;Gliding Horse;流马;多级缓存;MESI 协议;向量引擎;图数据库;Token 优化;语义搜索

在构建 AI Agent 操作系统的过程中,记忆管理是最核心的挑战之一。传统的 Agent 框架往往将对话历史简单堆砌在上下文窗口中,一旦超出 Token 限制就直接截断,导致关键信息丢失。而 Gliding Horse(流马)则借鉴了 CPU 多级缓存架构,设计了一套 L0-L3 四层记忆体系,并引入了 MESI 缓存一致性协议意图驱动的预取引擎 以及 语义增强的淘汰策略,让 Agent 像操作系统管理内存一样管理自己的记忆。

本文将深入拆解这套记忆系统的架构设计、核心模块及运作机制,展示它如何为 AI Agent 提供近乎无限的“虚拟记忆”,同时将上下文 Token 消耗降至最低。


一、四层记忆架构总览

Gliding Horse 的记忆系统模仿了 CPU 的缓存层次:越靠近 LLM 的层级容量越小、速度越快,但保存的是高度压缩的摘要;越远离 LLM 的层级容量越大、速度越慢,但保存的是完整的原始数据。

graph TB subgraph L3["L3 投影引擎 (ProjectionEngine)"] direction LR Cache["物化视图缓存 + 反向索引"] Frames["9个预定义投影帧"] end subgraph L2["L2 共享黑板 (Blackboard)"] direction LR Nodes["DashMap 缓存 + Oxigraph SPARQL"] TaskTree["任务树 DAG"] Agents["Agent 态势感知"] Locks["资源锁 + 权限矩阵"] end subgraph L1["L1 会话摘要链 (L1Session)"] direction LR Summary["摘要链 + Token 预算"] Eviction["两阶段语义淘汰"] end subgraph L0["L0 持久化存储 (L0Store)"] direction LR Sled["sled KV 存储"] Index["标签 + 命名图索引"] JSONLD["JSON-LD 节点合并"] end L3 <-->|投影/失效| L2 L2 <-->|归档/加载| L1 L1 <-->|归档/检索| L0

每一层都有明确的职责边界:

  • L0:基于 sled 的持久化存储,保存所有原始数据,全量可靠。
  • L1:每个 Agent 会话内的高压缩摘要链,严格控制 Token 预算。
  • L2:多 Agent 共享的内存黑板,基于 Oxigraph 图数据库,提供实时协作与态势感知。
  • L3:按需从 L0/L2 提取子图并物化为视图,起到“图虚拟内存”的作用。

此外,还有 一致性引擎 (MESI)Hyperspace 向量引擎预取引擎记忆总线调度器 等关键组件共同编织起整套记忆基础设施。


二、L0 持久化存储:永不丢失的“磁盘”

L0 是整个记忆体系的基石。它使用高性能嵌入式数据库 sled 作为存储后端,每个记忆条目都包含完整的 JSON-LD 内容以及丰富的元数据。

pub struct L0Entry {
    pub iri: String,
    pub content: String,          // 完整 JSON-LD
    pub importance: f32,
    pub access_count: u32,
    pub tags: Vec<String>,
    pub mesi_state: MesiState,
    pub content_hash: String,
    pub named_graph: Option<String>,
    pub qdrant_point_id: Option<String>,
    pub jsonld_context: Option<String>,
    pub jsonld_types: Vec<String>,
}

关键设计

  • JSON-LD 节点存储store_jsonld_node()retrieve_jsonld_node() 提供了直接存取 JSON-LD 文档的能力,并自动合并具有相同 @id 的条目(标签取并集、重要性取平均、内容保留较长者)。
  • 多级索引:标签二级索引(tag:{tag})和命名图索引(graph:{name})让检索不再只是线性扫描,前缀范围扫描还能高效支持 IRI 前缀查询。
  • 与 Hyperspace 引擎联动:写入 L0 时可以同步将嵌入向量存入 Hyperspace 引擎,并在条目中记录向量索引信息,打通语义搜索通道。

L0 的定位是“永不丢失的全量记忆”,但并不会直接暴露给 LLM。上层通过 IRI 引用按需拉取细节,这正是 Token 控制的核心。


三、L1 会话摘要链:像 CPU L1 缓存一样精简

L1 直接服务于 LLM 的上下文窗口,是记忆系统中对 Token 最敏感的一层。它并不保存完整的对话内容,而是将每一轮 LLM 的 summary 输出串联成一条摘要链。

pub struct L1Session {
    turns: Vec<L1Turn>,
    token_budget: usize,
}

pub struct L1Turn {
    pub role: String,
    pub summary: String,
    pub l0_archive_iri: Option<String>,  // 完整内容在 L0 的地址
    pub embedding: Option<Vec<f32>>,
    pub relevance_score: f32,           // 当前任务关联度
    pub is_supplement: bool,            // 是否为补充输入
}

两阶段语义淘汰

当摘要链的 Token 总量超出预算时,L1 并非简单截断,而是执行一个两阶段淘汰策略:

  1. 硬阈值淘汰:对 relevance_score 低于阈值(默认 0.3)且已超过安全窗口(默认 5 分钟)的条目直接移除,但补充输入(is_supplement=true)受到保护。
  2. 评分淘汰:对剩余条目计算得分,综合语义相关度最近访问时间Token 成本三个因子,得分最低的条目被淘汰。具体公式为:
    score = β * semantic_relevance + (1-β) * (recency * w_r + token_cost * w_c)
    
    其中 semantic_relevance 是该条目与当前查询的向量余弦相似度,β 默认为 0.6。

被淘汰的摘要并不会彻底消失,它的 l0_archive_iri 会被移至一个弱引用列表,Agent 随时可以通过此 IRI 从 L0 调取完整历史。这保证了即使上下文窗口被“清扫”,记忆本身依然是完整的。


四、L2 共享黑板:多 Agent 协作的“工作内存”

L2 是整个记忆系统中最高频的读写区域。它是一个基于 Oxigraph 图数据库 的内存黑板,所有 Agent 实例都可以通过 SPARQL 直接查询和更新。

核心能力

  • 节点缓存与持久化双写:每个写入操作同时更新 DashMap 内存缓存和 Oxigraph 存储,兼顾速度与持久性。
  • 任务树 DAG:复杂任务可以被拆解为子任务,并在 L2 中维护一棵有向无环图。拓扑排序、依赖检查、子树释放等功能让并行 Agent 协作变得井然有序。
  • Agent 态势感知:每个 Agent 在 L2 中注册自己的心跳和状态(Idle/Working/Waiting/Completed/Error)。超时未心跳的 Agent 会被自动检测,防止“僵尸”进程占用资源。
  • 资源锁与权限矩阵:对关键资源支持 Read/Write/Exclusive 三级锁,并提供基于角色的图级权限控制(CRUD),保障并发安全。
  • 协调消息:Agent 之间可以通过黑板发布/订阅协调消息,实现松耦合通信。

L2 的定位是“当前任务的工作区”。当任务完成后,脏节点会被批量刷入 L0,形成持久记忆。


五、L3 投影引擎:按需裁剪的“图虚拟内存”

L3 是记忆系统的“内存管理单元(MMU)”。它不存储数据本身,而是根据 Agent 的当前需求,从 L0/L2 中实时裁剪出相关子图,物化为 JSON-LD 视图。

9 个预定义投影帧覆盖了常见的 Agent 上下文场景:

帧名 用途 目标角色
summary_only 仅摘要 通用
pa_init PA 初始化上下文 计划 Agent
da_input DA 输入上下文 执行 Agent
ca_review CA 审查上下文 检查 Agent
aa_decision AA 决策上下文 决策 Agent
health_check 健康检查 运维
error_analysis 错误分析 调试
reference_only 仅引用数据 预取
5w2h_summary 5W2H 摘要 通用

每个投影帧本质上是一个预编译的 SPARQL CONSTRUCT 查询,它决定了该角色“能看到什么、看到多少”。例如 pa_init 帧会提取当前任务相关的技能、历史决策和领域实体,而不会包含庞大的代码详情。

物化视图缓存 + 反向索引 O(1) 失效:一旦从 L0/L2 提取出子图,L3 会将其缓存。当某个节点的数据发生变更时,反向索引可以在 O(1) 时间内定位到所有包含该节点的物化视图,并将其标记为失效。这种机制既保证了视图的实时性,又避免了每次请求都重新执行昂贵的图查询。

Token 预算控制project_with_budget() 允许指定最大 Token 数,如果投影结果过大,会自动截断或降级为更轻量的引用模式,确保上下文窗口不会超限。


六、周边关键组件

Hyperspace 向量引擎

记忆的语义检索能力由 Hyperspace Engine 提供。它并非单一的外部向量数据库,而是一个融合了多种嵌入空间与索引策略的统一向量计算层。与黑箱式的外部服务不同,Hyperspace Engine 深度集成在流马内核中,与 JSON‑LD 图存储共享同一个 Arc<Store>,实现了零拷贝的语义增强。

其核心能力包括:

  1. 双空间嵌入:每个实体都可以获得两种嵌入表示——文本嵌入(用于常规语义相似度)和结构嵌入(基于 Poincaré 双曲空间,专门捕捉类层次结构)。Poincaré 嵌入使得“检查 Agent”和“执行 Agent”这种在文本上差异巨大的概念,在结构空间中却非常接近。
  2. 混合搜索:支持向量相似度与结构化过滤(标签、类型、命名图、重要性范围)的联动查询。例如,可以高效检索“带有 auth 标签、类型为 DesignDecision 且语义上与‘JWT 密钥长度’最相似的记忆”。
  3. 自动降级与多后端:引擎可以根据配置自动切换后端——生产环境可对接 OpenAI 兼容 API 或本地 ONNX 模型,测试环境则使用轻量级的哈希降级方案,始终保证可用性。
  4. 与 L0/L3 无缝集成:写入 L0 的节点可自动生成嵌入并存入 Hyperspace Engine;L3 投影引擎在做语义投影时,会并行发起 SPARQL 结构查询和 Hyperspace 语义搜索,最终按可配置的权重混合排序,兼顾精确与模糊。

预取引擎

基于意图驱动的预取机制能够主动预测 Agent 即将需要的数据。当 SA 检测到任务意图发生变化时,预取引擎会从当前涉及的实体出发,沿知识图谱做 BFS 扩展(默认 2 跳),根据衰减因子排序后选取 Top-K 个相关实体,异步加载到 L2 缓存。这种“想 Agent 之所想”的设计大幅减少了上下文切换时的等待延迟。

一致性引擎 (MESI)

借鉴 CPU 的 MESI 缓存一致性协议,记忆系统在多 Agent 并发写入时保持数据一致。每个 L0/L2 的节点都携带有状态标记(Modified/Exclusive/Shared/Invalid)。当 L2 中的节点被修改,引擎会根据数据的关键程度自动选择 WriteThrough(关键标签如用户意图、确认事实)或 WriteBack(普通推理中间结果),并通过记忆总线广播失效事件,确保所有层最终一致。

记忆总线与调度器

MemoryBus 作为轻量级事件总线,负责传递缓存失效、预取请求等信号。MemoryScheduler 则统一调度上下文请求,根据 Agent 角色自动匹配对应的 L3 投影帧,并提供 L3→L2→L0 的三级回退读取路径。


七、给平台带来的核心优势

  1. Token 经济性:通过“摘要 + IRI”取代“全文”,将上下文 Token 消耗从 O(n) 降为 O(1),长对话场景下 Token 节省超过 40%。
  2. 长程记忆能力:Agent 可以随时通过 IRI 回溯任意历史轮次的完整细节,真正摆脱了“失忆”困境。
  3. 多 Agent 协作安全:MESI 一致性、资源锁和权限矩阵让多个 Agent 可以像多核 CPU 一样安全地共享和修改记忆。
  4. 可追溯与可审计:所有记忆都以 JSON-LD 节点形式存储,拥有全球唯一 IRI,任何决策都能被精确溯源。
  5. 自适应淘汰:基于语义相关度的淘汰策略让记忆系统在有限的 Token 预算内始终保留最重要的信息。
  6. 主动性:预取引擎和 L3 投影让系统能提前准备好 Agent 所需的知识,而非被动等待请求。
  7. 层次化语义搜索:Hyperspace 引擎的双空间嵌入让系统既能理解“文本相似”,也能理解“结构相似”,在模式识别与知识关联上远超普通向量检索。

八、结语

Gliding Horse 的记忆系统不是简单的“对话历史存储”,而是一套借鉴计算机体系结构思想、面向 AI Agent 深度定制的认知基础设施。它将 JSON-LD 的语义能力、图数据库的查询能力、Hyperspace 引擎的双空间嵌入能力以及操作系统的缓存一致性思想融为一体,为构建可信赖、可演化的自主 Agent 提供了坚实的基座。

如果你正在探索如何让 AI Agent 拥有真正的长期记忆,欢迎来 GitHub 交流:https://github.com/doiito/gliding_horse


九、实战示例:L1Session 两阶段淘汰模拟

下面通过一个完整的 Rust 代码示例,展示如何初始化 L1Session 并模拟其两阶段淘汰策略。该示例定义了 L1Turn 结构、计算淘汰分数以及触发淘汰的完整流程。

use std::time::{Duration, Instant};

/// 单轮对话摘要
#[derive(Clone, Debug)]
pub struct L1Turn {
    pub role: String,
    pub summary: String,
    pub l0_archive_iri: Option<String>,
    pub relevance_score: f32,
    pub is_supplement: bool,
    pub created_at: Instant,
    pub token_cost: usize,
}

/// L1 会话摘要链
pub struct L1Session {
    pub turns: Vec<L1Turn>,
    pub token_budget: usize,
}

impl L1Session {
    pub fn new(token_budget: usize) -> Self {
        Self {
            turns: Vec::new(),
            token_budget,
        }
    }

    /// 添加一轮摘要
    pub fn add_turn(&mut self, turn: L1Turn) {
        self.turns.push(turn);
    }

    /// 计算当前总 Token 数
    pub fn total_tokens(&self) -> usize {
        self.turns.iter().map(|t| t.token_cost).sum()
    }

    /// 两阶段淘汰
    pub fn evict(&mut self, query_embedding: &[f32]) {
        let now = Instant::now();
        let safe_window = Duration::from_secs(300); // 5 分钟

        // 阶段一:硬阈值淘汰
        self.turns.retain(|turn| {
            if turn.is_supplement {
                return true; // 补充输入受保护
            }
            let age = now.duration_since(turn.created_at);
            !(turn.relevance_score < 0.3 && age > safe_window)
        });

        // 阶段二:评分淘汰(直到总 Token 低于预算)
        while self.total_tokens() > self.token_budget && !self.turns.is_empty() {
            let beta = 0.6;
            let w_r = 0.5;
            let w_c = 0.5;

            // 找到得分最低的条目
            let idx = self
                .turns
                .iter()
                .enumerate()
                .map(|(i, turn)| {
                    let semantic_relevance = cosine_similarity(
                        query_embedding,
                        &turn.relevance_score,
                    );
                    let age = now.duration_since(turn.created_at);
                    let recency = 1.0 / (1.0 + age.as_secs_f32());
                    let token_cost = 1.0 / (1.0 + turn.token_cost as f32);
                    let score = beta * semantic_relevance
                        + (1.0 - beta) * (recency * w_r + token_cost * w_c);
                    (i, score)
                })
                .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
                .map(|(i, _)| i)
                .unwrap();

            // 移除得分最低的条目(保留 l0_archive_iri 到弱引用列表)
            let removed = self.turns.remove(idx);
            println!(
                "淘汰条目: role={}, summary={:.20}..., score={:.4}",
                removed.role,
                removed.summary,
                removed.relevance_score
            );
        }
    }
}

/// 简化的余弦相似度(实际应使用 Hyperspace 引擎的向量)
fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
    let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
    let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
    let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
    if norm_a == 0.0 || norm_b == 0.0 {
        0.0
    } else {
        dot / (norm_a * norm_b)
    }
}

fn main() {
    let mut session = L1Session::new(200); // Token 预算 200

    // 模拟添加 5 轮对话摘要
    let turns = vec![
        L1Turn {
            role: "user".into(),
            summary: "用户询问如何配置 JWT 认证".into(),
            l0_archive_iri: Some("mem://jwt-config".into()),
            relevance_score: 0.9,
            is_supplement: false,
            created_at: Instant::now(),
            token_cost: 60,
        },
        L1Turn {
            role: "assistant".into(),
            summary: "提供了 JWT 密钥生成和验证的代码".into(),
            l0_archive_iri: Some("mem://jwt-code".into()),
            relevance_score: 0.85,
            is_supplement: false,
            created_at: Instant::now(),
            token_cost: 80,
        },
        L1Turn {
            role: "user".into(),
            summary: "补充说明:需要支持 RS256 算法".into(),
            l0_archive_iri: Some("mem://rs256-supplement".into()),
            relevance_score: 0.2, // 低相关度
            is_supplement: true,  // 补充输入受保护
            created_at: Instant::now() - Duration::from_secs(400), // 超过 5 分钟
            token_cost: 40,
        },
        L1Turn {
            role: "user".into(),
            summary: "用户切换到讨论数据库连接池".into(),
            l0_archive_iri: Some("mem://db-pool".into()),
            relevance_score: 0.1, // 低相关度
            is_supplement: false,
            created_at: Instant::now() - Duration::from_secs(600), // 超过 5 分钟
            token_cost: 50,
        },
        L1Turn {
            role: "assistant".into(),
            summary: "提供了 HikariCP 配置示例".into(),
            l0_archive_iri: Some("mem://hikari-example".into()),
            relevance_score: 0.15, // 低相关度
            is_supplement: false,
            created_at: Instant::now() - Duration::from_secs(600), // 超过 5 分钟
            token_cost: 70,
        },
    ];

    for turn in turns {
        session.add_turn(turn);
    }

    println!("淘汰前总 Token: {}", session.total_tokens());
    println!("淘汰前条目数: {}", session.turns.len());

    // 模拟查询向量(实际由 Hyperspace 引擎生成)
    let query_embedding = vec![0.5, 0.3, 0.8, 0.1];
    session.evict(&query_embedding);

    println!("淘汰后总 Token: {}", session.total_tokens());
    println!("淘汰后条目数: {}", session.turns.len());
    for turn in &session.turns {
        println!(
            "  保留: role={}, summary={:.30}..., relevance={:.2}",
            turn.role, turn.summary, turn.relevance_score
        );
    }
}

运行结果分析

  1. 硬阈值淘汰:第 4、5 轮(数据库连接池相关)的 relevance_score 低于 0.3 且超过 5 分钟,被直接移除。第 3 轮虽然是低相关度且超时,但因为 is_supplement=true 受到保护,得以保留。
  2. 评分淘汰:剩余条目总 Token 为 180(60+80+40),未超过预算 200,因此无需进入评分淘汰阶段。如果预算设为 150,则系统会计算各条目的综合得分,淘汰得分最低的条目。
  3. 记忆完整性:被淘汰的条目仍可通过 l0_archive_iri 从 L0 存储中完整恢复,实现了“上下文窗口有限,但记忆无限”的设计目标。

这个示例直观展示了 L1 层如何在有限的 Token 预算内,通过语义感知的淘汰策略保留最重要的信息,同时确保关键补充输入不被误删。

posted @ 2026-06-25 07:36  doiito  阅读(49)  评论(0)    收藏  举报