长程 Agent 的限制不只来自 context window 大小。窗口里装了什么、旧材料怎样退出、compact 后哪些内容恢复、外部记忆何时失效,同样决定任务能否连续推进。

操作系统用 paging 把有限内存接到更大的外部存储。Agent 的上下文管理也在形成类似结构:当前窗口是工作集,文件、索引和 memory 是外部状态,compact 负责压缩历史,检索和恢复机制负责按需换入。Prompt cache 则位于另一条轴上,影响相同前缀的成本和延迟,却不减少它占用的 context window。

Context Paging 管理的是工作集

长窗口解决“能否放入”,没有保证“能否稳定使用”。《Lost in the Middle》显示,模型利用长上下文中的信息时会受到位置影响。Coding 会话还会积累失败命令、过时文件内容、被否决方案和大段日志。即使它们仍在窗口内,也未必属于当前任务需要的工作集。

Context paging 的目标不是保存所有历史,而是在每次推理前组织一个可执行工作集:

  • 当前目标与完成条件;
  • 仍然有效的约束;
  • 关键证据及其来源;
  • 修改状态与未通过的验证;
  • 下一步动作和恢复入口。

已完成且可从外部复现的轨迹可以退出窗口。尚未完成、无法轻易重建或会影响安全边界的信息需要保留。

组件视角:换入换出的职责分工

Context paging 不是一个 compact() 函数,而是一组相互独立的组件。

flowchart LR
    I["Context Assembler<br/>system / tools / rules / skill listing"] --> W["Working Context"]
    H["Message Store<br/>user / assistant / tool history"] --> W
    S["External State<br/>repo / spec / logs / memory / index"] --> R["Selector / Retriever"]
    R --> W
    W --> C["Compactor"]
    C --> H
    C --> S
    S --> G["Rehydrator"]
    G --> W
    P["Prompt Cache"] -. "复用相同前缀,不缩小窗口" .-> W

Context Assembler 负责启动时的 system、工具目录、项目规则和 Skill 目录。Message Store 保存会话轨迹。External State 保存可跨轮次或跨会话访问的材料。Selector 筛选本轮换入项。Compactor 把历史变成更短的状态。Rehydrator 在 compact 或恢复会话后重新注入必要材料。Prompt Cache 优化重复前缀的处理成本。

把“compact 后又加载了规则”笼统归因于摘要器,会掩盖实际的体积来源;把“命中 cache”理解成“这些 token 不在窗口里”,则会误判剩余容量。

运行时视角:一次 page in / page out

flowchart TB
    A["外部世界:repo / docs / logs / memory"] --> B["选择 Select"]
    B --> C["工作集:当前上下文窗口"]
    C --> D["模型推理与工具调用"]
    D --> E["结果 / 观察 / 失败"]
    E --> F["写回 Write"]
    F --> A
    E --> G["压缩 Compress"]
    G --> A
    H["隔离 Isolate:子 Agent / 新会话"] --> C

Anthropic 在 context engineering 资料中使用 write、select、compress、isolate 四类动作。用 paging 术语翻译:

  • write:把状态 page out 到外部;
  • select:从外部确定候选工作集;
  • compress:把高体积轨迹转成低体积状态;
  • isolate:让高噪声工作在独立窗口中完成,只返回结果。

这套循环还需要索引。常驻上下文只保存小目录、路径和语义钩子,完整内容留在文件或知识节点中。目录过大同样会挤占窗口;目录过小则可能让检索入口无法触发。Skill listing、工具命名空间和 repo map 都属于这一层的不同实现。

人的 context switching 是另一种成本

人的任务切换和模型的 token refill 不是同一机制,但存在相似的工程后果。APA 对 multitasking 的综述把切换引起的额外时间称为 switching costs。工程任务切换后,需要重新建立目标、进度、约束和下一步动作;交接材料质量会直接影响恢复时间。

因此,Agent 的 handoff artifact 同时服务两类消费者:下一次模型调用和下一位工程参与者。只保留聊天摘要不够,状态还应落到可验证的仓库事实、测试输出、issue、spec 或 commit 中。

Compact 会重写历史并保留工作状态

Compact 通常以摘要替换一段 message history,并保留或恢复由系统配置标记为必需的材料。它和 /clear 的语义不同:compact 继续当前任务,clear 清空 active context;旧 session 是否仍可恢复取决于产品的持久化机制。工作阶段与产品会话的边界不能只由这两个命令推断,详见《Agent 会话边界设计》

一个可用的 compact 输出至少应包含:

  • 未完成目标;
  • 已修改文件;
  • 关键约束和已否决路径;
  • 尚未通过的测试;
  • 下一步动作与验证命令;
  • 必须回到外部读取的证据位置。

Compact 后的百分比不是压缩质量的单一指标。状态栏可能显示已用比例,也可能显示剩余比例;不同版本和工具的口径不能混用。Claude Code 可以通过 /context 查看当前分类占用,比凭百分比猜测更可靠。

压缩后的占用可以拆成:

1
2
3
4
5
6
compact 后上下文
= 不属于 message history 的固定基线
+ 压缩摘要
+ 自动重新注入的项目材料
+ 按触发条件重新加载的规则与文件
+ 自定义恢复逻辑注入的内容

未完成任务越复杂,摘要下限越高。项目规则、Skill 和工具目录越大,固定或恢复基线越高。追求一个固定的“compact 后应降到 0%”没有通用意义。

Claude Code 当前公开的恢复契约

以下内容来自 2026 年 7 月 17 日访问的 Claude Code 公开文档。它描述的是当时的产品合同,不代表更早版本,也不应外推到其他 coding agent。

机制 Compact 后行为
System prompt 与 output style 不属于 message history,保持不变
项目根 CLAUDE.md 与 unscoped rules 从磁盘重新注入
Auto memory 从磁盘重新注入
paths: frontmatter 的规则 暂时丢失,直到再次读取匹配文件
子目录中的嵌套 CLAUDE.md 暂时丢失,直到再次读取该目录下文件
已调用的 Skill body 重新注入;每个 Skill 最多保留前 5,000 token
所有恢复的 Skill body 合计预算 25,000 token;优先最近调用,较旧 Skill 可整体丢弃
Hooks 以代码形式在生命周期事件运行,本身不是常驻上下文

5,000 / 25,000 是当前 Claude Code 公布的具体预算。公开 changelog 没有给出这份合同首次出现的明确版本,因此只能写“当前公开行为”,不能把它标成某个未经证实的历史版本新增能力。

文档还说明,Skill 截断保留文件开头。高优先级指令应位于 SKILL.md 前部。多个大 Skill 在同一会话中使用后,较旧 Skill 可能在 compact 后完全不再附着;需要完整正文时应重新调用。

其他工具的恢复预算:公开信息不足就标 unknown

各类 coding agent 普遍具备 session persistence、规则文件、摘要或 memory,但公开文档的粒度差异很大。以下项目若没有官方合同,就不应根据界面现象或类比补齐:

问题 Claude Code 当前公开状态 其他产品的默认结论
单个 Skill compact 后恢复上限 5,000 token unknown
所有 Skill 合计恢复上限 25,000 token unknown
Skill 淘汰顺序 最近调用优先,较旧先丢弃 unknown
项目规则是否从磁盘自动重注入 根级与 unscoped rules 会 逐产品查证
path-scoped 规则何时重新加载 再次读取匹配文件时 逐产品查证
hooks 是否直接构成常驻上下文 否;hooks 是代码 逐产品查证

“unknown”不是“没有能力”。它只表示缺少可引用的公开合同或本地 trace。要做横向比较,需要固定版本并采集 compact 前后的实际 context 分类、请求结构和恢复日志。

Prompt Cache 与窗口占用是两条轴

无状态 API 或 agent harness 通常会在新请求中携带有效上下文。逻辑上重复携带,不等于每次都按普通输入价格结算。

以 Anthropic prompt caching 为例,相同前缀在 TTL 和断点条件满足时可以进入 cache read;新增或变化部分进入 cache write 或未缓存输入。当前公开乘数是 5 分钟 cache write 为基础输入价的 1.25 倍、1 小时 cache write 为 2 倍、cache read 为 0.1 倍。具体账单应以响应 usage 为准。

缓存命中只改变处理成本与延迟,不改变以下事实:

  • 旧内容仍占 context window;
  • 模型仍能访问该内容;
  • 噪声、过期假设和位置效应仍存在;
  • compact、prune 或外化仍可能有必要。

因此,缓存优化和 context paging 不能互相替代。前者关心相同前缀怎样复用,后者关心哪些信息应该存在于工作集。

自定义恢复包可能让 Compact 后仍很满

compact 后的上下文由摘要、不可压缩基线和回灌层共同决定

公开 hooks 合同只说明事件和输入输出边界,不会自动产生“hot paths 恢复包”。PreCompactPostCompact 可以被插件或自定义 harness 用来记录状态、更新外部文件或注入额外上下文;注入什么、注入多少属于具体实现。

如果某套自定义恢复逻辑在 compact 后重新读取状态文件、plan、长日志和多个 Skill,窗口可能迅速回升。此时不能把问题归因于 Claude Code 默认 compact。应把日志拆成几类:

1
2
3
4
5
6
7
compact 后上下文
= 固定基线
+ 任务摘要
+ 自定义 PreCompact 保留包
+ 自定义 PostCompact 回灌包
+ 自定义 hot paths / plan / 文件重读
+ 产品公开合同规定的 Skill 与项目材料恢复

治理重点在恢复层:

  • 保留指针和摘要,不自动注入大段原文;
  • 对重复回灌做幂等和去重;
  • 为自动恢复设置 token 预算;
  • 对状态文件和日志设置热度衰减或排除规则;
  • 避免在恢复阶段读取含密钥的环境文件;
  • 任务完成后切换新会话,不用连续 compact 维持旧任务。

图中的“回灌型 compact”是自定义 harness 场景,不是 Claude Code 默认行为的产品承诺。

Externalize 之后还需要 Memory 生命周期

把内容写到文件、向量库或数据库,只完成了 page out。可用的 memory 还需要完整生命周期:

stateDiagram-v2
    [*] --> Capture: 生成候选记忆
    Capture --> Index: 绑定来源、时间、作用域与检索键
    Index --> Select: 按当前任务选择
    Select --> PageIn: 注入工作集
    PageIn --> Validate: 与 repo、测试或外部事实核验
    Validate --> Refresh: 仍有效
    Validate --> Demote: 过期、冲突或低价值
    Refresh --> Index
    Demote --> Archive
    Archive --> [*]

每个阶段都解决不同风险:

  • Capture 防止关键决策只留在易失会话中;
  • Index 让后续任务能够找到它;
  • Select 控制本轮 token 预算;
  • Page In 保留来源和版本信息;
  • Validate 防止旧记忆覆盖当前事实;
  • Refresh 更新仍有效的内容;
  • Demote / Archive 避免错误和过期结论长期常驻。

Memory 不是事实豁免区。源码、测试结果和生产状态发生变化后,旧 memory 必须降级为线索,不能继续作为当前结论。

四层记忆与能力演变

按信息来源,可以把 Agent 记忆分为训练数据、会话内数据、跨会话数据和外部世界数据。按系统成熟度,这些机制对应一条能力演变路径:

1
2
3
4
5
6
无状态调用
→ 会话历史持久化
→ 项目规则与索引注入
→ Compact + 会话恢复
→ Skill / Tool 的渐进式加载
→ 有来源、有验证、有淘汰的 Memory 生命周期

前半段解决“能否接着运行”,后半段解决“恢复的内容是否仍正确、是否值得占用窗口”。能力越靠后,越依赖 Context Assembler、Selector、Compactor、Rehydrator 和验证机制之间的明确接口。

CoALA 将 language agent 描述为 LLM、memory、action space 和 decision-making procedure 的组合。这个视角能解释为什么单独增加 context window 不足以完成长程 scaling:窗口只是工作内存,选择、写回和验证策略决定了工作集质量。

实践规则

长任务可以按以下顺序治理:

  1. 限制单次工具结果,避免一个日志占满窗口。
  2. 把 spec、测试报告和阶段状态写到外部事实源。
  3. 在常驻上下文中只保留小目录、路径和关键约束。
  4. Compact 时明确未完成目标、修改文件、失败测试和验证命令。
  5. Compact 后检查实际 context 分类,不根据百分比猜测。
  6. 对自动恢复包设置预算、去重和过期策略。
  7. 检索到的 memory 先核验,再提升为当前约束。
  8. 新任务与旧任务无关时清空会话,从外部状态重新 page in。

下一阶段的 scaling 不只发生在模型参数和窗口长度上,也发生在工作集的选择、压缩、恢复和验证上。有限窗口能够连接更长的任务边界,前提是外部状态可检索、恢复合同可观测,并且 memory 具备时效与淘汰规则。

相关文章

参考资料