2026年深入解析OpenClaw上下文窗口压缩方案 :一切都是为了效果与省钱

深入解析OpenClaw上下文窗口压缩方案 :一切都是为了效果与省钱关注腾讯云开发者 一手技术干货提前解锁 最近很火的 OpenClaw 的出镜率是越来越高了 内外网的技术文章 新产品的问世 Mac Mini 的涨价 自媒体的宣传层出不穷 作者是国外一个叫 Peter Steinberger 现已经被奥特曼高薪挖到 OpenAI 说是花了一个周末就烹饪完成的 小龙虾 随着司内 OpenClaw 也支持了 我也在企微中增加了对应的机器人

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



关注腾讯云开发者,一手技术干货提前解锁👇

最近很火的 OpenClaw 的出镜率是越来越高了,内外网的技术文章,新产品的问世,Mac Mini 的涨价,自媒体的宣传层出不穷。作者是国外一个叫 Peter Steinberger(现已经被奥特曼高薪挖到 OpenAI ),说是花了一个周末就烹饪完成的“小龙虾”。 随着司内 OpenClaw 也支持了,我也在企微中增加了对应的机器人,给它取名「靓靓蒸虾🦞」,协助处理一些需求管理上的事情。我一直也在做上下文相关的事情,现在我们就拨开虾外壳,看看它内部详细是如何调味的(进行上下文窗口管理的)。

仓库: github.com/openclaw/openclaw 本文基于该仓库源码进行分析。

在 AI Agent 的长会话场景中,上下文窗口溢出是一个绕不开的问题。当对话越来越长、工具调用结果越来越多,LLM 的上下文窗口终将被填满。OpenClaw 为此设计了一套多层防御系统,从"尽量不溢出"到"溢出了也能恢复",覆盖了整个上下文生命周期。本文将从架构到实现细节,完整剖析这套方案。

整体架构

OpenClaw 的上下文管理分为三个阶段,形成递进的防御纵深:

一个关键的设计原则是渐进式降级:先做轻量级裁剪(只丢弃冗余数据),再尝试 LLM 摘要(有损但保留语义),最后才是暴力截断(只保留头部内容)。每一层只在前一层不够用时才会介入。

第一层:预防性裁剪(发送 LLM 之前)

这一层的目标是:在消息发送给 LLM 之前,尽可能裁剪掉不再需要的冗余内容,避免触发上下文溢出。

2.1 会话历史轮次限制(History Turn Limit)

文件:src/agents/pi-embedded-runner/history.ts

这是最简单、最粗粒度的保护——直接限制保留的用户对话轮次数。

工作原理

limitHistoryTurns() 函数从消息列表的末尾向前遍历,计数 的消息。当计数超过 limit 时,丢弃更早的所有消息:

注意这里的截断边界是 lastUserIndex——即被计数的最后一个 user 消息的位置。这意味着截断点始终在一个完整的 user 轮次边界上,不会把一个 user-assistant-toolResult 的三元组截断成碎片。

配置解析

限制数值通过 getHistoryLimitFromSessionKey() 从配置中解析,支持多级覆盖:

这个分层设计使得运营者可以为特定的高频用户设置更严格的限制,同时保持其他用户的默认行为。session key 的 kind 字段(dm/direct/channel/group)决定走哪条解析路径。

2.2 Context Pruning 扩展(渐进式裁剪旧 Tool Results)

文件:src/agents/pi-extensions/context-pruning/

如果说 History Turn Limit 是"剪头",Context Pruning 就是"瘦身"。它是一个运行时扩展(extension),注册在 context 事件上,在每次构造 LLM 请求时拦截消息列表。

触发机制

扩展采用 cache-ttl 模式运行,默认 TTL 为 5 分钟。这意味着:

两级裁剪策略

核心逻辑在 pruneContextMessages() 中,使用字符占比作为触发条件:

其中 charWindow = contextWindowTokens × 4(使用 1 token ≈ 4 字符的粗略估算)。

Soft Trim 的截断实现很精巧——它不是简单地 slice,而是在文本块(text content block)的层面操作,分别从头部和尾部取字符,保持换行符的完整性:

裁剪范围与保护规则

并非所有消息都会被裁剪,有几条硬性保护规则:

Hard Clear 还有一个额外的安全阈值:只有当可裁剪的工具结果总量超过 minPrunableToolChars(默认 50,000 字符)时才会执行,避免对少量工具结果做无意义的清理。

默认参数一览

2.3 单条 Tool Result 截断

文件:src/agents/pi-embedded-runner/tool-result-truncation.ts

Context Pruning 处理的是"很多旧的 tool result 累加起来太大"的情况,而单条截断处理的是另一种场景——单条工具返回了巨量内容(比如读取了一个大文件或执行了一个产生大量输出的命令)。

大小限制

最终限制取两者中的较小值:

对于一个 200K token 上下文窗口的模型,单条 tool result 最大约为 200K × 0.3 × 4 = 240K 字符。对于 2M 上下文的模型,理论值 2.4M 会被 HARD_MAX_TOOL_RESULT_CHARS 限制到 400K。

截断策略

截断时保留内容的头部(开头通常包含最重要的信息),并尽量在换行符处断开以避免切断行中间:

截断后追加一段提示信息,引导模型通过 offset/limit 等参数获取更多内容:

多文本块的比例分配

一个 tool result 可能包含多个 text content block。此时截断预算会按各 block 的原始长度比例分配:

两种截断模式

持久级截断的实现很有意思——它通过 Session Manager 的分支机制(branching)来修改历史:

这种方式避免了直接修改已有的 entry,保持了 session 文件的 append-only 语义。

第二层:Compaction(基于 LLM 的主动压缩)

这是 OpenClaw 最核心的上下文压缩机制——用另一次 LLM 调用来生成对话历史的摘要,用摘要替代原始消息。

核心文件:

3.1 触发时机

Compaction 在两种场景下触发:

3.2 Compaction Safeguard 协调流程

compaction-safeguard 扩展监听 session_before_compact 事件,协调整个 compaction 流程。它不只是简单地做摘要,而是一个完整的信息保留+压缩 pipeline:

摘要失败保护

整个流程用 try-catch 包裹,任何异常都会导致 { cancel: true }——取消 compaction,保留原始历史。这是一个重要的设计决策:宁可让上下文溢出(进入溢出恢复流程),也不要因为摘要失败而丢失历史。

3.3 摘要生成算法详解

分段摘要(summarizeInStages)

当消息量较大时,不能一次性把所有消息送给 LLM 做摘要(摘要请求自身也有上下文窗口限制)。summarizeInStages 采用分而治之的策略:

关键参数:

splitMessagesByTokenShare 的分割算法按 token 总量均分,确保每个 chunk 的 token 数接近 totalTokens / parts。分割点在消息边界上,不会切断单条消息。

自适应 chunk 大小

如果消息平均体积很大(比如用户频繁读取大文件),固定的 chunk 比例可能仍然导致单个 chunk 溢出摘要模型。computeAdaptiveChunkRatio 会动态缩小 chunk 比例:

举例:如果平均消息占上下文窗口的 20%(avgRatio = 0.2),那么 reduction = 0.4,chunk ratio 被缩小到 MIN_CHUNK_RATIO = 0.15。每个 chunk 只会放 15% 上下文窗口大小的内容。

超大消息的三级降级(summarizeWithFallback)

对于包含极大单条消息的情况,摘要可能直接失败。summarizeWithFallback 实现了三级降级:

每一级都能产出一个结果,不会让 compaction 因为单条超大消息而完全中断。

摘要调用的容错

每个 chunk 的摘要调用通过 retryAsync 封装,具有内建重试机制:

最多重试 3 次,退避延迟从 500ms 到 5000ms,附加 20% 的抖动避免雷同重试。只有 AbortError(用户主动取消)不重试。

3.4 历史裁剪预处理(pruneHistoryForContextShare)

在开始摘要之前,如果待摘要的消息总量太大,需要先做一轮预裁剪。这发生在新内容(摘要后需要保留的部分)已经占用了超过 maxHistoryShare(默认 50%)的上下文窗口时。

裁剪算法:

这里的 repairToolUseResultPairing 至关重要——丢弃消息后,可能出现 tool_result 的对应 tool_use(在 assistant 消息中)已被丢弃的情况。Anthropic 的 API 会严格检查配对关系,孤立的 tool_result 会导致 "unexpected tool_use_id" 错误。修复函数会:

3.5 Compaction Summary 的结构化输出

最终生成的 summary 不仅仅是对话摘要。OpenClaw 在摘要文本后附加了结构化信息,确保 compaction 后 AI 仍然知道"自己做过什么":

这些附加信息的意义:

3.6 安全保护机制

Compaction 涉及将对话历史送入 LLM 处理,有专门的安全考虑:

第三层:溢出后恢复

文件:src/agents/pi-embedded-runner/run.ts

即使有了预防性裁剪和主动压缩,仍然可能出现上下文溢出——比如模型的实际 token 计数与估算的 chars/4 启发式有较大偏差,或者 SDK 自动 compaction 后上下文仍然超限。

溢出检测

通过 isLikelyContextOverflowError() 检测 LLM 返回的错误是否为上下文溢出。检测逻辑覆盖两个来源:

恢复决策树

恢复约束

Token 估算策略

OpenClaw 使用 chars / 4 的启发式方法估算 token 数量(即 1 token ≈ 4 字符),这是一个有意为之的简化:

在 chunkMessagesByMaxTokens 中:

在 compaction-safeguard 中,计算历史裁剪阈值时也会应用安全系数:

此外,stripToolResultDetails() 在估算前移除 toolResult.details,避免不可信的大体积附加数据干扰估算和摘要。

配置项汇总

全景流程图

核心设计思路

附录:上下文管理对 Provider KV Cache 的影响分析

上下文管理方案不可避免地会改变发送给 LLM 的消息序列。而主流 LLM Provider(Anthropic、OpenAI、Google 等)都提供了 Prompt Caching 机制——如果新请求的 prompt 前缀与前一次请求相同,Provider 可以复用已有的 KV Cache,大幅降低延迟和计费。

以 Anthropic 为例:cache read 价格仅为普通 input 的 10%,cache write 则为 125%。一次 cache miss 可能导致成本翻倍。

9.1 OpenClaw 对 Provider Cache 的感知

OpenClaw 明确知晓并利用了 Provider 的 Prompt Caching 能力:

9.2 各层操作对 KV Cache 的影响

1. History Turn Limit — 对 cache 无直接影响

这个操作只会在首次构建消息列表时截断最老的轮次。由于每次 LLM 调用的消息列表都是从 session 文件重建的,截断行为在每次请求间是一致的。只要 limit 不变,每次调用的 prompt 前缀是稳定的,不会导致 cache miss。

但如果 limit 触发了截断(消息数超过限制),被截断的那一次请求的 prompt 前缀会与前一次完全不同——这一次一定是 cache miss。不过这通常只发生在长时间运行的会话中。

2. Context Pruning — 会导致 cache 失效,但有刻意的缓解设计

这是 cache 影响最大的操作。Soft Trim 和 Hard Clear 会修改旧 tool result 的内容,改变 prompt 中间的文本。由于 KV Cache 是严格前缀匹配的,一旦修改了 prompt 中间的某条消息内容,从修改点到末尾的所有 token 都会 cache miss。

OpenClaw 的缓解设计——Cache TTL 对齐:

Context Pruning 的 5 分钟 TTL 不是随意选择的。它与 Anthropic 的 "short" cache retention(也是 5 分钟)精确对齐:

设计意图是:

此外,isCacheTtlEligibleProvider() 确保只有支持 cache 的 Provider 才启用这个基于 TTL 的 pruning 模式。不支持 cache 的 Provider(如 OpenAI、DeepSeek)不会使用 cache-ttl 模式,因此不受 TTL 约束。

Cache 失效时的成本影响:

当 pruning 确实执行时:

但由于 pruning 只在 cache 已过期时执行,这次 miss 的额外成本仅是"cache write"(比普通 input 贵 25%),而不是"本可以 cache read 却 miss 了"(cache read 便宜 90%)。

3. 单条 Tool Result 截断 — 内存级无影响,持久级会 cache miss

4. Compaction — 完全重建 prompt,cache 完全失效

Compaction 是最极端的操作——用一段摘要替代了大量历史消息。compaction 后的 prompt 与之前完全不同,KV Cache 必然 100% miss。

这是一个有意接受的 trade-off:

9.3 成本影响量化估算

以 Anthropic Claude 的定价为例(claude-opus-4-6):

假设一次 100K token 的请求:

关键观察:Compaction 虽然导致 cache 完全失效,但由于 prompt 大幅缩短,即使全量 cache write 的成本也远低于溢出前每次请求的成本。

9.4 总结

OpenClaw 的设计在 cache 效率上下文管理 之间取得了合理的平衡。最关键的缓解机制是 Context Pruning 的 TTL 与 Provider Cache 周期对齐——这确保了最频繁的上下文修改操作不会浪费有效的 cache。而 Compaction 虽然代价最大,但它本身就是一个"救命"操作,执行后 prompt 缩短带来的长期成本节约远超一次 cache miss 的损失。

猜你所想彩蛋时刻

-End-

原创作者|杨柏

感谢你读到这里,不如关注一下?👇

你对本文内容有哪些看法?同意、反对、困惑的地方是?欢迎留言,我们将邀请作者针对性回复你的评论,欢迎评论留言补充。我们将选取1则优质的评论,送出腾讯云定制文件袋套装1个(见下图)。3月11日中午12点开奖。

扫码领取腾讯云开发者专属服务器代金券!

小讯
上一篇 2026-03-11 08:57
下一篇 2026-03-11 08:59

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/212479.html