




























摘要:本文深入剖析 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
每一层都有明确的职责边界:
此外,还有 一致性引擎 (MESI)、Hyperspace 向量引擎、预取引擎、记忆总线 和 调度器 等关键组件共同编织起整套记忆基础设施。
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>,
}
关键设计:
store_jsonld_node() 和 retrieve_jsonld_node() 提供了直接存取 JSON-LD 文档的能力,并自动合并具有相同 @id 的条目(标签取并集、重要性取平均、内容保留较长者)。tag:{tag})和命名图索引(graph:{name})让检索不再只是线性扫描,前缀范围扫描还能高效支持 IRI 前缀查询。L0 的定位是“永不丢失的全量记忆”,但并不会直接暴露给 LLM。上层通过 IRI 引用按需拉取细节,这正是 Token 控制的核心。
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 并非简单截断,而是执行一个两阶段淘汰策略:
relevance_score 低于阈值(默认 0.3)且已超过安全窗口(默认 5 分钟)的条目直接移除,但补充输入(is_supplement=true)受到保护。score = β * semantic_relevance + (1-β) * (recency * w_r + token_cost * w_c)
其中 semantic_relevance 是该条目与当前查询的向量余弦相似度,β 默认为 0.6。被淘汰的摘要并不会彻底消失,它的 l0_archive_iri 会被移至一个弱引用列表,Agent 随时可以通过此 IRI 从 L0 调取完整历史。这保证了即使上下文窗口被“清扫”,记忆本身依然是完整的。
L2 是整个记忆系统中最高频的读写区域。它是一个基于 Oxigraph 图数据库 的内存黑板,所有 Agent 实例都可以通过 SPARQL 直接查询和更新。
核心能力:
DashMap 内存缓存和 Oxigraph 存储,兼顾速度与持久性。L2 的定位是“当前任务的工作区”。当任务完成后,脏节点会被批量刷入 L0,形成持久记忆。
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 Engine 提供。它并非单一的外部向量数据库,而是一个融合了多种嵌入空间与索引策略的统一向量计算层。与黑箱式的外部服务不同,Hyperspace Engine 深度集成在流马内核中,与 JSON‑LD 图存储共享同一个 Arc<Store>,实现了零拷贝的语义增强。
其核心能力包括:
auth 标签、类型为 DesignDecision 且语义上与‘JWT 密钥长度’最相似的记忆”。基于意图驱动的预取机制能够主动预测 Agent 即将需要的数据。当 SA 检测到任务意图发生变化时,预取引擎会从当前涉及的实体出发,沿知识图谱做 BFS 扩展(默认 2 跳),根据衰减因子排序后选取 Top-K 个相关实体,异步加载到 L2 缓存。这种“想 Agent 之所想”的设计大幅减少了上下文切换时的等待延迟。
借鉴 CPU 的 MESI 缓存一致性协议,记忆系统在多 Agent 并发写入时保持数据一致。每个 L0/L2 的节点都携带有状态标记(Modified/Exclusive/Shared/Invalid)。当 L2 中的节点被修改,引擎会根据数据的关键程度自动选择 WriteThrough(关键标签如用户意图、确认事实)或 WriteBack(普通推理中间结果),并通过记忆总线广播失效事件,确保所有层最终一致。
MemoryBus 作为轻量级事件总线,负责传递缓存失效、预取请求等信号。MemoryScheduler 则统一调度上下文请求,根据 Agent 角色自动匹配对应的 L3 投影帧,并提供 L3→L2→L0 的三级回退读取路径。
Gliding Horse 的记忆系统不是简单的“对话历史存储”,而是一套借鉴计算机体系结构思想、面向 AI Agent 深度定制的认知基础设施。它将 JSON-LD 的语义能力、图数据库的查询能力、Hyperspace 引擎的双空间嵌入能力以及操作系统的缓存一致性思想融为一体,为构建可信赖、可演化的自主 Agent 提供了坚实的基座。
如果你正在探索如何让 AI Agent 拥有真正的长期记忆,欢迎来 GitHub 交流:https://github.com/doiito/gliding_horse。
下面通过一个完整的 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
);
}
}
运行结果分析:
relevance_score 低于 0.3 且超过 5 分钟,被直接移除。第 3 轮虽然是低相关度且超时,但因为 is_supplement=true 受到保护,得以保留。l0_archive_iri 从 L0 存储中完整恢复,实现了“上下文窗口有限,但记忆无限”的设计目标。这个示例直观展示了 L1 层如何在有限的 Token 预算内,通过语义感知的淘汰策略保留最重要的信息,同时确保关键补充输入不被误删。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。