【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 的层级容量越大、速度越慢,但保存的是完整的原始数据。
每一层都有明确的职责边界:
- 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 并非简单截断,而是执行一个两阶段淘汰策略:
- 硬阈值淘汰:对
relevance_score低于阈值(默认 0.3)且已超过安全窗口(默认 5 分钟)的条目直接移除,但补充输入(is_supplement=true)受到保护。 - 评分淘汰:对剩余条目计算得分,综合语义相关度、最近访问时间和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>,实现了零拷贝的语义增强。
其核心能力包括:
- 双空间嵌入:每个实体都可以获得两种嵌入表示——文本嵌入(用于常规语义相似度)和结构嵌入(基于 Poincaré 双曲空间,专门捕捉类层次结构)。Poincaré 嵌入使得“检查 Agent”和“执行 Agent”这种在文本上差异巨大的概念,在结构空间中却非常接近。
- 混合搜索:支持向量相似度与结构化过滤(标签、类型、命名图、重要性范围)的联动查询。例如,可以高效检索“带有
auth标签、类型为DesignDecision且语义上与‘JWT 密钥长度’最相似的记忆”。 - 自动降级与多后端:引擎可以根据配置自动切换后端——生产环境可对接 OpenAI 兼容 API 或本地 ONNX 模型,测试环境则使用轻量级的哈希降级方案,始终保证可用性。
- 与 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 的三级回退读取路径。
七、给平台带来的核心优势
- Token 经济性:通过“摘要 + IRI”取代“全文”,将上下文 Token 消耗从 O(n) 降为 O(1),长对话场景下 Token 节省超过 40%。
- 长程记忆能力:Agent 可以随时通过 IRI 回溯任意历史轮次的完整细节,真正摆脱了“失忆”困境。
- 多 Agent 协作安全:MESI 一致性、资源锁和权限矩阵让多个 Agent 可以像多核 CPU 一样安全地共享和修改记忆。
- 可追溯与可审计:所有记忆都以 JSON-LD 节点形式存储,拥有全球唯一 IRI,任何决策都能被精确溯源。
- 自适应淘汰:基于语义相关度的淘汰策略让记忆系统在有限的 Token 预算内始终保留最重要的信息。
- 主动性:预取引擎和 L3 投影让系统能提前准备好 Agent 所需的知识,而非被动等待请求。
- 层次化语义搜索: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
);
}
}
运行结果分析:
- 硬阈值淘汰:第 4、5 轮(数据库连接池相关)的
relevance_score低于 0.3 且超过 5 分钟,被直接移除。第 3 轮虽然是低相关度且超时,但因为is_supplement=true受到保护,得以保留。 - 评分淘汰:剩余条目总 Token 为 180(60+80+40),未超过预算 200,因此无需进入评分淘汰阶段。如果预算设为 150,则系统会计算各条目的综合得分,淘汰得分最低的条目。
- 记忆完整性:被淘汰的条目仍可通过
l0_archive_iri从 L0 存储中完整恢复,实现了“上下文窗口有限,但记忆无限”的设计目标。
这个示例直观展示了 L1 层如何在有限的 Token 预算内,通过语义感知的淘汰策略保留最重要的信息,同时确保关键补充输入不被误删。
浙公网安备 33010602011771号