长运行 Agent
Session 不等于 Context Window
两个最容易混淆的概念:Claude 当前能看到的,和所有发生过的事。它们必须分离。
两个容易混淆的概念
临时 / 有限
Context Window
Claude 当前能看到的 Token。就像人的工作记忆:容量有限,过了就忘。每次对话开始时构建,用完就丢。
当前推理所需的精选内容
永久 / 可回溯
Session
所有发生过的事件的持久日志。就像完整的录像:从头到尾什么都记着,永远可以倒回去看。Append-only,只增不减。
所有原始事件的完整记录
为什么必须分离
上下文管理的不可逆性
- 1 Compaction(压缩)和 Trimming(裁剪)都是不可逆操作,一旦压缩,原始细节就丢了
- 2 压缩时很难知道未来哪些 Token 重要,今天看似无关的信息,可能是明天关键决策的依据
- 3 如果压缩后原始信息丢了,就永远回不来了,这是信息论的基本约束
所以正确的做法是:原始事件永久保存在 Session 里,Context Window 只是从 Session 中临时取景的一个视角。丢了 Context 没关系,因为 Session 还在,随时可以重建。
Session 作为持久上下文对象
getEvents() 接口
Session 提供一个类似数据库的查询接口:Brain 可以按需读取任意范围的事件,摆脱固定上下文窗口的限制。
// Brain 可以灵活查询 Session
const recentEvents = session.getEvents({
from: position - 100, // 从某个位置开始读
to: position // 读到当前位置
});
// 可以倒回到某个时间点
const beforeDecision = session.getEvents({
from: decisionPoint - 20,
to: decisionPoint + 5
});
// 可以过滤特定类型的事件
const toolCalls = session.getEvents({
filter: "tool_use"
});
这就像 REPL 中的对象一样,LLM 可以写代码来查询和过滤 Session 中的事件。Brain 从任意位置开始读、倒回到某个时间点、重读某个决策前后的上下文。
Harness 的灵活性
从 Session 取出的事件可以做任意变换
- 优化 Prompt Cache 命中率:保持前缀稳定,减少重复计算的 Token 开销
- 做上下文工程:根据当前任务类型,选择性地放入最相关的历史事件
- Harness 是可换的:不同模型可能需要不同的上下文策略,换 Harness 不影响 Session
核心对比
| 维度 | Context Window | Session |
|---|---|---|
| 持久性 | 临时,用完即丢 | 永久,持久化存储 |
| 内容 | 精选后的 Token | 所有原始事件 |
| 操作 | 只读(对 Claude 来说) | 可追加(append-only) |
| 大小 | 有限(模型上限) | 无限制 |
| 用途 | 当前推理 | 历史回溯、状态恢复 |
硬件类比
内存 (RAM)
Context Window
速度快、容量小、断电即失。CPU 正在用的数据必须在内存里,但内存不是用来长期存储的。
硬盘 (Disk)
Session
速度慢、容量大、断电不失。所有数据最终都存在硬盘上,需要时再加载到内存。
你不会把所有文件同时加载到内存里,那样内存会爆。同样的道理,你不应该把所有历史事件都塞进 Context Window,那样 Token 会爆。正确的做法是按需加载:Session 存全量,Context 取子集。
Session 是 Agent 的硬盘,Context Window 是内存,不要把内存当硬盘用。把所有原始事件存进 Session,让 Harness 按需组装 Context。这样即使上下文压缩了、模型换了,历史永远不会丢。