LLM386:借用 1990 年代的想法来管理 LLM 上下文
Source: Dev.to
(请提供您希望翻译的正文内容,我将把它翻译成简体中文并保留原始的格式、Markdown 语法以及技术术语。)
Introduction
在 1989 年,DOS 的常规内存上限为 640 KB。EMM386 利用 80386 CPU 的地址转换硬件,通过位于这 640 KB 内的一个小固定窗口,对更大内存空间的块进行分页。礼貌请求的程序通过这个小孔获得了实际上无限的内存,只分页当前操作相关的部分。
LLM 也面临同样的问题。
上下文窗口是有限的——32 K、128 K、1 M 令牌。你的数据(对话历史、检索到的文档、工具结果、持久化事实)将超出你愿意付费的任何窗口。每一次调用都必须决定哪些内容能够通过。
常见的做法是临时拼凑:将消息保存在列表中,检索“最近的 N 条加上向量匹配”,然后拼接发送。当提示词增长到你无法追踪已包含内容时,这种方法就会失效。模型给出答案,却没人能解释原因;两轮对话会因未记录的原因产生不同的响应。
LLM386 就是运行时的 EMM386,只不过它应用于 LLM 的上下文窗口。
论点
f(context) → output
模型是一个纯函数:没有记忆、没有持久化、没有跨调用状态。所有连续性必须在每次调用时重新构建。
- 持久状态存放在运行时拥有的存储中。模型是一个无状态的消费者。
- 每次调用的 prompt 都会从该存储重新计算,模型的输入预算作为约束。
运行时包含内容
- Persistent block store – LMDB,content‑addressed,deduped on hash。
- Pager – 通过并行运行配置的检索器(recency、BM25、embedding ANN、custom),对分数进行归一化,按 max‑per‑block 合并,并在 canonical sections 之间分配:System、Task、State、Plan、Retrieved、Tools、Recent、Background,以选择符合模型输入预算的块。
- Packer – 将选取的内容渲染为 deterministic prompt string 或 role‑tagged chat‑message list。
- Tracer – 记录模型看到的内容及原因,使用 byte‑level prompt hashes 以便 replay。
- Reducer – 通过 parsed events 将模型输出转回 committed state。
- Typed‑edge graph – 将 dependent blocks 关联起来,使 pager 能保持 tool results 与调用它们的 assistant message 配对。
- Diff layer – turn‑over‑turn 比较两个 trace records。
Implemented as a Rust library, Python SDK (PyO3 native extension), and CLI. Licensed Apache‑2.0. Current version: 1.0.0‑alpha.
有意不包含的内容
- 没有聊天机器人 UI。
- 提示中没有隐藏状态。
- 不把模型输出当作真理。
- 初始版本中没有分布式存储。
- 热路径中没有学习组件——每个检索器、打包器和归约器都是确定性的,这使得追踪可重放。学习型重排序器或训练过的嵌入微调器会破坏该属性,因此被刻意省略。
尝试一下
git clone https://github.com/fitzee/llm386
cd llm386
export ANTHROPIC_API_KEY=sk-ant-...
docker compose -f examples/langgraph-agent/docker-compose.yml run --rm agent
从克隆到聊天只需五分钟。一个带有两个存根工具(计算器和一个虚假的用户资料查询)的简易聊天机器人使用 LLM386 作为记忆层。对话在容器重启后仍然保持,因为存储是 Docker 卷。模型能够回忆起之前回合的内容;这种回忆完全由运行时提供,因为 LangGraph 在回合之间不保留状态。
是否应该使用它?
- 是 – 如果你有一个在开发中可用的代理,但提示词混乱,且你无法推断模型看到的内容。
- 可能不 – 如果你只需要一个快速的聊天机器人演示;使用最简单可运行的方案。
- 是 – 如果你想在不重写提示组装的情况下切换模型。
ModelProfile抽象封装了上下文窗口、分词器和能力标志;分页器和打包器会遵守该契约,无论你换入哪个模型。
随着代理变得更加复杂,“当前提示到底包含了什么?” 成为许多技术栈的难题。运行时的设计旨在保持这一过程低成本且透明。
EMM386 能够成功是因为对更大内存的有界窗口是结构受限系统的正确抽象。三十年后,同样的抽象同样适用于 LLM 的上下文窗口。
GitHub: https://github.com/fitzee/llm386