LLM 也需要虚拟内存:Demand Paging for Context Window
最近看到一篇非常有意思的论文:
《The Missing Memory Hierarchy: Demand Paging for LLM Context Windows》
论文地址:
- The Missing Memory Hierarchy: Demand Paging for LLM Context Windows
- 原文:https://arxiv.org/abs/2603.09023
这篇论文的核心观点非常简单但极具启发性:
LLM 的上下文窗口,本质上是一个没有内存管理系统的“裸内存”。
如果把 LLM Agent 看成一个程序,那么当前的设计就像:
- 所有历史数据都一直留在内存里
- 每次执行都重新扫描整个内存
- 没有分页
- 没有缓存
- 没有工作集
- 没有淘汰策略
这显然是非常原始的。
论文作者做了一件很“操作系统味”的事情:
给 LLM 上下文加一个虚拟内存系统。
一、问题:LLM 上下文在大量浪费 token
作者首先做了一件很重要的事情:
分析真实生产日志。
数据来源:
- 857 个 Claude Code 会话
- 54,170 次 API 调用
- 总输入 token:4.45B
作者把每条消息拆成几类:
- 用户输入
- 模型回复
- 工具输出(read file / bash / etc)
然后统计:
- token 占比
- 使用频率
- 重复读取情况
结果非常惊人。
1 结构性浪费:21.8%
约 21.8% 的 token 是结构性浪费:
主要来自:
1️⃣ 工具定义
很多工具定义几千 token 但会话里从未调用。
却在 每一轮请求里重复发送。
2️⃣ 旧工具输出
比如:
Read file: file.py (10KB)
后续 80 轮对话里:
- 每一轮 API 调用
- 都重新发送这 10KB
- 都参与 attention
但实际 再也没被引用过。
3️⃣ 重复配置
例如:
- skill 列表
- agent 说明
- prompt 模板
经常被复制多份。
二、关键观察:工具输出会被“无限放大”
论文提出一个概念:
Amplification Factor(放大因子)
例如:
- 第 5 轮读取一个文件
- 文件大小 10KB
- 会话共 85 轮
那么这个文件会被:
重复发送 ≈ 80 次
放大因子:
80×
而每一次都要参与 attention 计算。
三、解决方案:给 LLM 上下文做“虚拟内存”
论文的核心系统叫:
Pichay
它的实现非常巧妙。
不是改模型。
而是:
在客户端和 LLM API 之间插一个代理。
架构如下:
IDE / Agent
↓
Pichay Proxy
↓
LLM API
代理负责:
- 统计 token
- 修改上下文
- 删除旧内容
- 必要时再恢复
模型和客户端完全无感。
四、核心思想:Demand Paging
作者把 LLM 上下文类比为操作系统内存。
| OS | LLM |
|---|---|
| RAM | Context Window |
| Page | 工具输出 |
| Page fault | 再次读取文件 |
| Page eviction | 删除旧工具输出 |
1 淘汰策略(Eviction)
非常简单:
当一个工具输出满足:
大小 > 500B
且
超过 4 个用户回合未使用
就淘汰。
2 被淘汰的内容如何表示?
替换成一个 占位符:
[Paged out: Read file.py (8192 bytes). Re-read if needed]
模型如果需要:
就会再次调用工具。
3 Page Fault(缺页)
如果模型再次调用:
Read file.py
代理检测到:
刚刚淘汰过这个内容
于是:
- 重新读取文件
- 注入上下文
这就是:
LLM 的 page fault。
4 Fault-driven Pinning
如果某个页面:
被淘汰 → 发生缺页
说明淘汰太早。
系统就会:
pin 这个页面
以后不再淘汰。
除非:
文件内容发生变化
五、离线实验:删错概率极低
作者用 29 个完整会话做离线重放实验。
模拟:
如果当时删掉这些内容
后面会不会再用?
结果:
- 模拟删除:1,393,000 次
- 缺页:354 次
缺页率:
0.0254%
也就是说:
99.97% 的删除都是安全的。
六、在线实验:token 下降 37%
作者对比三种策略:
| 模式 | 策略 |
|---|---|
| Baseline | 不做任何修改 |
| Trimmed | 裁剪工具定义 |
| Compact+Trim | 加分页 |
结果:
Trimmed
↓
token -22.6%
Compact + Trim
↓
token -37.1%
而任务:
- 全部完成
- 没有质量下降
甚至有些情况下:
回答质量更好。
原因很简单:
噪音减少,注意力更集中。
七、真实生产案例
作者把 Pichay 用在自己的开发环境。
Case A:正常开发
原始上下文:
剩余空间:7%
启用分页:
剩余空间:43%
删除:
15 个数据块
缺页:
1 次
几乎完美。
Case B:极端长会话
681 轮。
系统出现:
Thrashing(抖动)
删除 680 次
缺页 659 次
原因:
工作集太大。
不断:
删 → 读 → 删 → 读
类似操作系统的:
swap storm。
八、最重要的理论结论
传统操作系统:
缺页很贵
所以策略是:
尽量减少缺页
但 LLM 完全相反。
LLM 的成本模型
如果一个内容一直留在上下文:
每生成一个 token 都要重新计算 attention。
复杂度:
O(n²)
因此:
保留 token 的累计成本非常高。
而重新读取文件:
成本是:
O(n)
所以结论是:
在 LLM 中,宁可多一点 page fault,也不要保留太多 token。
九、这件事为什么很重要
这篇论文其实在说一件很大的事情:
未来的 LLM 系统必须有内存层级。
可以类比成:
| 层级 | 含义 |
|---|---|
| L1 | 当前上下文 |
| L2 | Working set |
| L3 | 会话总结 |
| L4 | 长期记忆 |
当前 LLM 系统:
只有 L1。
十、未来方向
论文提出了很多值得研究的方向:
1 成本驱动的 eviction
不再看:
回合数
而是计算:
未来 attention 成本
2 phase-aware memory
识别:
planning phase
execution phase
不同阶段使用不同策略。
3 pin decay
当前:
缺页一次 → 永久 pin
未来:
pin 逐渐衰减
4 object-level memory
不再按:
文件
消息
分页。
而是:
决策
任务
debug 会话
十一、我的一些思考
这篇论文的最大价值是:
把 LLM memory 问题彻底系统化。
它告诉我们:
未来 AI Agent 的架构可能会像这样:
LLM
↓
Memory Manager
↓
Context Cache
↓
Retrieval System
↓
Persistent Memory
而不是:
LLM
↓
巨大 prompt
换句话说:
Prompt Engineering → Context Engineering
十二、总结
这篇论文最核心的发现:
1️⃣ 21.8% token 是结构性浪费
2️⃣ 简单分页策略缺页率只有 0.025%
3️⃣ token 使用减少 37%
4️⃣ 回答质量没有下降
5️⃣ LLM 的成本模型与传统虚拟内存完全相反
如果未来 LLM agent 真的要:
- 长时间工作
- 多工具协作
- 持续项目开发
那么:
Memory hierarchy 可能会成为 AI 系统的核心基础设施。
而这篇论文很可能会成为这个领域的 早期经典工作之一。