惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tenable Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cisco Blogs
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
H
Heimdal Security Blog
Simon Willison's Weblog
Simon Willison's Weblog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cyberwarzone
Cyberwarzone
V2EX - 技术
V2EX - 技术
W
WeLiveSecurity
Help Net Security
Help Net Security
S
Secure Thoughts
P
Privacy & Cybersecurity Law Blog
S
Securelist
SecWiki News
SecWiki News
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes
Know Your Adversary
Know Your Adversary
The Last Watchdog
The Last Watchdog
N
News | PayPal Newsroom
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
H
Hacker News: Front Page
NISL@THU
NISL@THU
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
Attack and Defense Labs
Attack and Defense Labs
Security Archives - TechRepublic
Security Archives - TechRepublic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Google Online Security Blog
Google Online Security Blog
The Hacker News
The Hacker News
Cloudbric
Cloudbric
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
A
Arctic Wolf
Latest news
Latest news
S
Schneier on Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
Project Zero
Project Zero
P
Privacy International News Feed
B
Blog
云风的 BLOG
云风的 BLOG

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
【Agent Harness】Gliding Horse 记忆系统深度剖析:像 CPU 一样思考的 AI 记忆架构
doiito · 2026-06-25 · via 博客园_首页

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 预算内,通过语义感知的淘汰策略保留最重要的信息,同时确保关键补充输入不被误删。