跳到主要内容
长记忆大模型系统

第2章:多类型数据的访问规律建模

把四类长记忆数据(KV / 向量 / 多模态 / 推理状态)的访问模式做成可量化的指纹:六维通用框架 + 每类数据的具体模型 + 全栈 profile 工具链建议

访问规律 工作集 时序局部性 IO 放大 工作负载建模 profile

第 1 章把四类长记忆数据的”性格”讲清了——但对工程师来说,定性描述不够;放置算法、迁移策略、成本模型都需要可量化的访问规律输入。本章把”性格”翻译成”指纹”:一套适用于任何长记忆数据的六维通用建模框架,然后逐类给出 KV Cache、向量索引、多模态对象、中间推理状态各自的具体模型与典型测量值。最后给一份全栈 profile 工具链建议——告诉你拿到一个生产负载,从哪些层面采数据、怎么把数据汇成本模块后续章节能直接用的输入。本章是后续 Ch8 统一表示、Ch9 分层放置、Ch11 成本建模的”原料供应商”——没有它,所有调度算法都是无源之水。

📑 目录


1. 为什么访问规律是放置算法的前提

1.1 调度算法的”输入-决策-输出”

┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│  访问规律 (输入)  │ →  │  放置/迁移决策   │ →  │  存储介质 (输出) │
│  本章建模的对象   │    │  Ch9 / Ch10      │    │  HBM / DRAM / SSD │
└──────────────────┘    └──────────────────┘    └──────────────────┘

🌟 关键事实:调度器的所有聪明都是”对工作负载分布的拟合”。给它错的输入,再聪明的算法也是白搭。LRU、LFU、ARC、各种 RL 调度器之所以效果天差地别,核心差异不在算法,在它对工作负载分布的假设是否吻合实际。

1.2 长记忆系统比传统系统难在哪

传统系统(数据库 buffer pool、文件系统 cache、CDN)的访问规律建模已经有几十年研究——为什么不能直接套?

传统系统假设长记忆系统现实
单一数据类型(数据页 / 文件块)四类数据混存,各自分布完全不同
访问模式相对稳定(典型 Zipf 分布)强时变、跨用户跨任务差异大
失效成本对称(都是从磁盘重读)失效代价非对称——KV 失效要重 prefill,代价远超磁盘 IO
数据语义对系统透明数据语义可被利用——LLM 对小扰动鲁棒,可做有损压缩

结论:必须重新建模,不能套传统经验。这正是本章要做的事。


2. 六维通用建模框架

把任何一类长记忆数据的访问规律,都用同一套六维度量描述——这样四类数据之间可比、可统一处理。

2.1 六个维度

维度含义用什么测
D1 频率分布一段时间窗口内,各对象被访问次数的分布按对象 ID 计数 + 拟合分布(Zipf / 长尾)
D2 时序局部性最近被访问的对象,下一刻再被访问的概率inter-access time 分布、stack distance
D3 工作集大小给定时间窗口内被访问到的不同对象总量working-set 曲线
D4 访问粒度每次访问的数据块大小每个 IO 的字节数分布
D5 IO 放大系数一次”逻辑访问”触发的”物理 IO”次数应用层访问 / 存储层 IO 比值
D6 失效代价这次访问 miss 一级缓存,要付出多大代价miss 时延 + 重建延迟(如 KV 重 prefill)

2.2 为什么是六个不是更多

调度器的决策本质是回答四个问题:

  1. “这个对象多重要?” ← D1 频率 + D2 时序
  2. “我能装下多少?” ← D3 工作集
  3. “挪它要花多少钱?” ← D4 粒度 + D5 放大
  4. “挪错了赔多少?” ← D6 失效代价

🍎 直觉:六个维度像是给数据画的”身份证 + 信用评分”——任何放置策略最终都是在这六维空间里挑落点。

2.3 工程化:把六维做成”指纹向量”

# 概念示意,不是某个具体框架的 API
@dataclass
class AccessProfile:
    # D1
    frequency_dist: Distribution     # 频率分布
    # D2
    locality_curve: Callable         # P(access at t+τ | access at t)
    working_set_curve: Callable      # working set 大小随窗口
    # D3
    typical_working_set: int
    # D4
    io_size_dist: Distribution
    # D5
    amplification_factor: float      # 1 次逻辑访问 = ? 次物理 IO
    # D6
    miss_penalty: PenaltyModel       # miss 时延 + 重建代价

目标:每类数据(乃至每个 session / 每个 model)都有自己的 AccessProfile,调度器对它们一视同仁地处理


3. KV Cache 的访问指纹

3.1 指纹概览

维度KV Cache 的特征
D1 频率极度强偏 active session;同一 session 内 token-level Zipf-like(系统 prompt + 早期 token 高频)
D2 时序强短期局部性(active session 每 step 都用),长期局部性弱(session 结束后几乎不复用)
D3 工作集active sessions 数 × 平均 KV 大小;典型几 GB - 几百 GB
D4 粒度layer-wise 块(每 layer 几 MB);整 KV 几 GB
D5 放大每次 token decode = N_layer × 整 sequence 的 KV 扫描——放大系数等于 layer 数
D6 失效代价极高 —— miss 意味着重做整段 prefill,可能数百毫秒到数秒

3.2 KV 的两种使用阶段:画像不同

Prefill 阶段:                       Decode 阶段:
─────────────                       ─────────────
KV 大批量写入                         KV 小步逐次写
访问模式 = 顺序写主导                 访问模式 = 每步 attention 全扫
工作集 = 单 prompt 的全部 KV          工作集 = 历史所有 token 的 KV
持续时间 = 一次性                     持续时间 = 整轮 decode(几十秒到分钟)

🌟 关键洞察:同一份 KV 数据在 prefill 和 decode 两个阶段呈现不同的访问指纹——这给了”分阶段差异化放置”的空间(对应模块四的 PD 解耦思想)。

3.3 跨实例 / 跨用户的 KV 复用机会

复用类型复用率(典型)价值
系统 prompt(各租户共享)极高(几 KB - 几 MB,被所有请求复用)
RAG 文档前缀中-高高(文档级共享,跨用户)
多轮对话历史中(单用户,多 session)中(取决于会话生命周期)
单次工具调用结果低(几乎不复用)

📍 设计含义:LMCache、Mooncake 这类系统正是在挖这部分的”复用率红利”——系统 prompt 这一片放 HBM 永驻是高 ROI 决策,因为它几乎对所有请求都有效。

3.4 KV 工作集的”长尾观察”

不止单峰分布,长记忆系统下 KV 工作集是双峰:

访问频率

   │  ┌──────┐                          长尾(一次性 / 历史会话)
   │  │ HOT  │                              ▼
   │  │      │       ┌─────┐ ┌─────┐ ┌────────────────────────┐
   │  │      │       │     │ │     │ │.........................│
   │  └──────┘       └─────┘ └─────┘ └─────────────────────────┘
   ───────────────────────────────────────────────────────► 对象
      (active)    (recent)         (cold)

决策启发:HOT 部分应永驻 HBM,长尾部分应该激进卸载到 SSD/远端,中间灰区是放置算法的真正战场


4. 向量索引的图遍历轨迹

4.1 指纹概览

维度向量索引的特征
D1 频率非常强的长尾——少数热点向量(高频内容、热点用户)被反复访问
D2 时序弱(查询彼此独立),但簇局部性强(类似 query 检索同一簇)
D3 工作集千万到百亿向量,典型 GB-TB 级
D4 粒度单向量 KB 级、邻居表 KB 级、簇几 MB
D5 放大极高 —— 一次 ANN 查询触发几十-几百次邻居展开,每次都是一次随机 IO
D6 失效代价中(SSD 读延迟,但是可批量并发)

4.2 ANN 主流算法的访问模式

图索引(HNSW / Vamana / NSG)

   入口点 → (展开邻居 → 计算距离 → 选择继续展开的) × 几十轮 → 返回 top-k

每一步都要读一个邻居表(几 KB)和一组向量(几十 KB)——对存储是高随机 IO + 中等粒度

聚类索引(IVF / SPANN)

   query → (查找 top-N 簇,每簇是几千-几万向量) → 簇内逐一比较 → 返回 top-k

每次访问几个完整簇——对存储是中随机 IO + 大粒度顺序读

🧠 关键洞察:图索引比聚类索引对SSD 随机 IO 更敏感;聚类索引比图索引对带宽更敏感。在长记忆系统的多类型混存场景下,图 vs 聚类的选择影响存储介质的最佳选型

4.3 IO 放大的具体测量

应用层视角:    "查询用户 X 的相似记忆,top-10"

存储层视角:    HNSW 走 50 跳 → 50 次邻居表读 + 200 次向量读
              ≈ 250 次 SSD random IO
              ≈ 几 ms - 几十 ms(取决于盘并发)

📍 设计含义:单条向量查询的 IO 放大系数往往是 100-1000 倍——这是为什么传统纯 SSD 方案需要配合 PQ-compressed 的 RAM 副本(参考 DiskANN 的设计)。

4.4 长记忆向量库的特殊性

与”通用向量库”的差异影响
写入流式持续(Agent 不断追加新记忆)索引必须支持增量更新(FreshDiskANN 思路)
查询模式与 LLM 推理耦合(attention 触发)召回延迟必须卡在毫秒级(参考 AlayaDB)
数据有强 metadata(用户 / 时间窗 / 任务类型)过滤式 ANN 是必备(Filtered DiskANN 思路)
多模态混合(文本 + 图像 embedding 共存)不同模态的距离度量与索引可能要分开

5. 多模态对象的 burst-read 模式

5.1 指纹概览

维度多模态原始对象的特征
D1 频率极度长尾,90% 对象一辈子可能就被访问一两次
D2 时序弱时序,但有事件驱动 burst(一个 Agent 任务突然要调一批图片)
D3 工作集TB 级原始数据 + GB-TB 级 embedding
D4 粒度embedding KB 级 / 原始对象 MB-GB 级
D5 放大低(对象级直读)
D6 失效代价中-高(顺序大块读时延 + 网络成本)

5.2 双流共生:embedding 与 blob

   embedding(KB)              blob(MB)
      │                          │
      │ "这是一张包含猫的图"       │
      │                          │
      ▼                          ▼
   高频检索路径                  低频原始访问
   "找最像的 100 张"             "把这一张拿来给模型看"

🧠 关键洞察:embedding 和 blob 应该分开放但要 metadata 一致——embedding 进 DRAM 加速检索,blob 留 SSD 等具体被需要时再读。强行把它们放同一级是浪费

5.3 burst 模式建模

事件驱动的 burst 比平稳访问更难处理:

访问频率

   │  ┌──┐                          ┌──┐
   │  │  │                          │  │       ← 突发的 burst
   │ ─┘  └──────────────────────────┘  └──── ← 平稳低频
   ──────────────────────────────────────────► 时间
        ▲ Agent 启动一个新任务,一次性拉一批历史多模态记忆

📍 设计含义:burst 是 prefetch 的最佳应用场景——根据 Agent 当前任务上下文,提前把可能用到的多模态对象从 SSD 拉到 DRAM。这正是 Agent 长记忆 + 多模态系统的特色优化点。

5.4 顺序 IO 友好的存储格式

多模态对象最不适合零碎 random IO——存储时就应该:

  • 列式 + chunk 格式(Lance / Parquet)
  • 把 embedding 和 metadata 紧邻 blob 存放,避免分别 IO
  • 大对象优先走 GPUDirect Storage 直 DMA

6. 中间推理状态的写突发与回放概率

6.1 指纹概览(学术界几乎空白)

维度中间推理状态的特征
D1 频率写多读少 —— 99% 的 scratchpad 写完就丢
D2 时序强短期局部性(刚写完很可能很快读),长期局部性几乎为零
D3 工作集单任务 MB-GB,跨任务 TB 级(若全保留)
D4 粒度KB 级零碎写
D5 放大中(写入要 commit,读取偶发但可能是树状回放)
D6 失效代价低-中(失败的尝试本来就可丢)

6.2 三类典型形态

形态例子关键特征
Linear scratchpadCoT 推理顺序追加,偶尔回看一两步
Tree-shaped reasoningMCTS / ReAct tree分支多,大部分分支被剪枝
Tool call traceFunction call I/O强时序,经常出错重试,关键步骤要持久化

6.3 选择性持久化的语义

任务执行中:
  关键决策步骤 ── 记入持久化层(SSD 副本)
  探索性分支   ── 临时内存(失败就丢)
  Tool call I/O ── 全持久化(用于回放/调试)
  CoT thoughts  ── 短期保留 + 选择性归档

🌟 关键观察:这一类数据是”按语义分级,而不是按访问频率分级”——传统的 LRU 之类规则失效。需要应用层提示(Agent 框架告诉存储层”这个步骤重要”)。

6.4 为什么这一类是项目的”低垂果实”

学术界几乎没有专门的工作研究中间推理状态的存储——但生产 Agent 系统已经撞墙(Letta、Voyager、AutoGPT 等都各自工程地造轮子)。这意味着:

  • ⭐ 学术增量大(几乎从零开始)
  • ⭐ 工业需求强(已有痛点)
  • ⭐ 与第二/第三模块结合度高(分离式池化 + 容错都对它特别重要)

📍 建议:把这一类作为本项目第一模块的特色亮点——KV / 向量 / 多模态都已是热门战场,中间推理状态是无人区。


7. 四类访问模式的横向对比

把六维度量画成一张大表:

维度KV Cache向量索引多模态对象推理状态
频率分布强偏 active长尾 + 簇热点极长尾写多读少
时序局部性强短期事件 burst强短期
工作集GB - 数百 GBGB - TBTB+MB - GB
访问粒度layer 块(MB)向量(KB)/簇(MB)对象(MB-GB)KB 零碎
IO 放大layer 数(几十 ×)100-1000 ×1 ×~1 ×
失效代价极高(重 prefill)中-高低-中
可有损压缩中-高
生命周期秒-分钟月-年月-年分钟-小时
首选介质HBM(active)+ DRAM(spill)DRAM(导航)+ SSD(主体)DRAM(embedding)+ SSD(blob)DRAM(active)+ SSD(归档)

7.1 决策启发

🌟 观察一:没有任何两类数据的”首选介质”完全相同——任何”统一缓存”策略都是错的,必须按类分管 + 在统一抽象下汇总

🌟 观察二:IO 放大系数差三个数量级(KV 几十、向量 1000、多模态 1)——意味着相同的物理带宽下,不同数据类型能支持的”应用层 QPS”差异巨大。这是容量规划的关键约束

🌟 观察三:失效代价不对称(KV 极高,scratchpad 低)——意味着调度器在资源紧张时该先牺牲谁有清晰答案。这给了我们写”代价感知的 eviction policy” 的理论基础。


8. 全栈 profile 工具链建议

要把上面的模型在生产负载上具体测出来,需要分层 profile。

8.1 工具链分层

┌──────────────────────────────────────────────────┐
│ 应用层 (Agent / 推理 framework)                   │
│  - LangGraph / Letta / vLLM / SGLang 内置事件     │
│  - OpenTelemetry trace + 自定义 attribute         │
└──────────────────────────────────────────────────┘
                       ↓ 关联
┌──────────────────────────────────────────────────┐
│ 框架层 (PyTorch / NCCL / KV manager)              │
│  - PyTorch Profiler + Kineto                     │
│  - vLLM / Mooncake 自带的 KV trace               │
└──────────────────────────────────────────────────┘
                       ↓ 关联
┌──────────────────────────────────────────────────┐
│ 系统层 (kernel / IO)                              │
│  - eBPF (bpftrace / bcc):IO 路径全捕                │
│  - blktrace + iostat:块层 IO 模式                  │
│  - DCGM:GPU 内存带宽 / HBM 占用                   │
│  - Nsight Systems:跨 CPU/GPU 时间线               │
└──────────────────────────────────────────────────┘

8.2 关键采集点(对应六维度)

维度采集来源
D1 频率应用层日志(对象 ID + 访问计数)
D2 时序OpenTelemetry trace 时间戳 + inter-access time 直方图
D3 工作集滑窗 unique-key 计数
D4 粒度eBPF 抓 read/write syscall + 块层 IO size 分布
D5 放大应用层 / 系统层 IO 计数比值(应用一个 query 对应了多少次 syscall)
D6 失效代价应用层 miss 事件 + 重建延迟分布

8.3 推荐起步组合(轻量上手)

如果一上来就上 eBPF + Nsight 全套有点重,推荐:

  1. 应用层:在 vLLM / Mooncake / 你的 Agent 框架里加一组 hook,记录”对象 ID + 时间戳 + 操作类型”,落到 Parquet
  2. 系统层:iostat -x 1 持续记录 + nvidia-smi dmon 持续记录
  3. 离线分析:用 Pandas / DuckDB 跑 D1-D6 的统计

跑一周生产负载,就能拿到一份完整的 AccessProfile

📍 关键忠告:先别上 RL 调度器——把六维 profile 跑出来后,你会发现 90% 的优化机会用简单启发式(LRU + 类型感知)就能拿下,RL 是最后一公里的事。


9. 把 profile 数据喂给后续章节

9.1 数据流

                      ┌──────────────────────────┐
                      │ 生产负载                  │
                      └────────────┬─────────────┘
                                   │ profile

                ┌──────────────────────────────┐
                │ AccessProfile (六维指纹)     │ ← 本章产出
                └────────────────┬─────────────┘

       ┌───────────────┬─────────┼─────────┬──────────────┐
       ▼               ▼         ▼         ▼              ▼
   Ch3 物理上限     Ch8 统一抽象 Ch9 放置  Ch10 迁移   Ch11 成本建模
   (匹配介质)      (设计接口)  (LP/RL)   (触发策略)  (token 单位成本)

🌟 本章对后续章节的承诺:给定一份 AccessProfile,后面任何一章的算法都能直接用,不需要重新定义”什么是工作负载”

9.2 与生产对接

如果你正在做项目示范系统,推荐的工作流:

  1. 第 2 章(本章):跑 profile 出 AccessProfile
  2. 第 4-7 章:看看 KV / 向量 / 多模态 / 推理状态各自现成的最优实践
  3. 第 8 章:把 AccessProfile 接进统一抽象层
  4. 第 9-10 章:跑放置 / 迁移决策
  5. 第 11 章:用 token 单位成本做最终评估

关键准则:每一轮系统调整后重跑 profile——access pattern 会随着用户行为、模型升级、任务变化而漂移。长记忆系统的调度永远是动态目标,profile 必须是常态化的


✅ 自我检验清单

  • 六维度量:能默写 D1 频率 / D2 时序 / D3 工作集 / D4 粒度 / D5 放大 / D6 失效代价
  • 传统经验为何不能直接套:能列举至少 3 条传统系统假设与长记忆系统的差异
  • KV 双阶段:能区分 prefill 和 decode 的访问模式差异,以及对放置策略的不同含义
  • KV 复用红利:能讲清”系统 prompt 永驻 HBM”的高 ROI 论证
  • 向量索引 IO 放大:能粗略估算一次 HNSW 查询的 SSD random IO 次数
  • 多模态双流:能解释 embedding 和 blob “metadata 一致但分开放”的设计动机
  • 推理状态的语义分级:能解释为什么传统 LRU 在这一类上失效
  • 横向对比表:能默写四类数据在六维度量上的核心差异
  • profile 工具栈:能列出至少 3 个推荐工具及其对应采集的维度
  • 本章承诺:能讲清 AccessProfile 是后续章节算法的统一输入

📚 参考资料

长记忆系统访问规律相关

  • Memory in the Age of AI Agents(arXiv 2512.13564, 2025) —— 长记忆访问模式综述
  • Towards Efficient Generative LLM Serving: A Survey(CMU, 2023) —— LLM 推理负载特征
  • A Survey on Vector Database Management Systems(2024) —— 向量库访问模式

KV Cache 访问规律精读

向量索引访问规律

  • DiskANN(NeurIPS’19) —— 图索引 SSD 访问模式经典
  • SPANN(NeurIPS’21, Microsoft) —— 聚类索引访问模式
  • AlayaDB(HKUST, 2025) —— LLM-aware 向量库访问

推理状态(空白领域,可参考)

  • Voyager(2023) —— Minecraft Agent 长时任务
  • Generative Agents(Park et al., 2023):arXiv 2304.03442 —— 模拟社会 Agent 长记忆
  • A-MEM(NeurIPS’25):arXiv 2502.12110 —— Zettelkasten 记忆访问

Profile 工具

调研笔记腹地