Claw Code 源码解析(四):Agent 如何管理上下文与压缩历史
- Published on
- • 10 mins read
这是这组 Claw Code 源码解析的最后一篇主文。
整套源码里,如果只让我挑一个最值得单独写成文章的主题,我会选上下文管理。
原因很简单:很多人一提到 agent 的 memory,就默认想到向量数据库、长期记忆、embedding 检索;但 Claw Code 当前主线解决的问题并不是这个。
它更关心的是:在一个持续执行的 agent runtime 里,什么信息该进 prompt,什么时候会超预算,超预算后怎么保持任务连续性。
这套策略可以压缩成一条很清楚的主线:
Session transcriptProjectContextPrompt budgetPromptCacheSession compaction
这套系统的上下文管理分 5 层
我会把它拆成下面五层:
SessionSystemPromptBuilderProjectContextPromptCachecompact_session()
这个拆分说明一个很重要的事实:这里的 context management 不是“把所有资料都塞进去”,而是一个明确的预算和压缩系统。
Session:上下文的底座不是纯文本,而是结构化 transcript
这一步很关键。
在这个系统里,会话不是一串单纯文本,而是结构化消息序列。消息块里不仅有:
- user text
- assistant text
还有:
- tool use
- tool result
- compaction 元数据
- fork 信息
这会直接改变后面的上下文管理方式。因为一旦 transcript 本身是结构化的,系统就能知道:
- 之前执行过哪些工具
- 工具返回了什么
- 哪些消息已经被压缩过
换句话说,后续 compaction 做的不是“随便把聊天记录缩短”,而是基于真实执行历史做摘要。
System prompt 不是静态模板,而是动态组装
第二层是 SystemPromptBuilder。
这里最值得注意的地方是:system prompt 并不是一段固定文案,而是由静态部分加动态部分组成。动态部分会注入:
- 当前工作目录
- 当前日期
- git 状态和 diff
- 发现到的指令文件
- 部分配置和环境上下文
这意味着系统不是只靠“历史消息”理解任务环境,环境本身也会进入 prompt。
这很像一个真正的 coding agent,而不是普通聊天机器人。
ProjectContext:它注入的是环境快照,而不是仓库全文
这里还有一个很值得注意的点:ProjectContext 做的是 环境信息发现与筛选,不是把工程所有内容一股脑塞进去。
典型注入内容包括:
- cwd
- today
- git status
- git diff
- instruction files
这说明它的设计目标非常克制:
- 给模型足够的任务环境
- 但不试图把整个仓库全文变成 prompt
这种做法很现实,因为真正限制 agent 的不是“有没有更多信息”,而是上下文预算是否可控。
指令文件也有预算,不会无限塞
这套实现里一个很成熟的信号是:连 instruction files 都有字符预算。
这件事看起来不起眼,但其实说明整个系统的思路很一致:
- prompt 是预算受限资源
- 即使是高优先级上下文,也要做上限控制
- 不允许某一类上下文无限膨胀
如果你做过真实 agent 系统,就会知道这是非常重要的工程判断。因为很多项目不是模型能力不够,而是 prompt 管理失控。
PromptCache:从 provider 视角管理上下文复用
PromptCache 解决的是另一个常被忽略的问题:provider 视角下,哪些 prompt 前缀是稳定的,哪些会导致缓存失效。
这和“系统内部怎么记上下文”不是一回事。
它更像是在问:
- 这轮请求和上一轮相比,前缀有没有大幅变化
- 哪些动态注入让缓存命中率下降
- 长会话里如何减少重复成本
所以 PromptCache 其实处在“上下文管理”和“成本优化”的交叉地带。
这类设计非常像成熟 runtime,而不是单次调用脚本。
什么时候触发 compaction
真正让这套系统有意思的,是 compaction。
核心逻辑并不复杂:当累计输入接近预算阈值时,系统会触发压缩,把旧消息摘要化,保留最近消息原文继续运行。
这比“直接截断历史”成熟很多,因为它承认了两个事实:
- 旧消息并非完全没用
- 但也不值得全部原文保留
于是系统采用了一个折中方案:
- 旧历史压成 summary
- 最近消息保留原文 tail
- 然后继续当前任务
这和很多聊天产品简单丢前文,完全不是一回事。
token 估算并不花哨,但足够实用
这里还有一个我很喜欢的工程取舍:token 估算并没有追求极致精确,而是采用相对朴素的近似估算。
这类策略的价值在于:
- 实现简单
- 运行时成本低
- 足以支持压缩阈值判断
这其实是很典型的 runtime 工程判断。很多时候,系统不需要“理论最优”,而需要“足够稳定并且可解释”。
compaction 不是裁掉旧消息,而是“摘要旧消息 + 保留最近 tail”
这是整套实现里最值得记住的一点。
如果只是把旧消息硬裁掉,系统会立刻丢失:
- 之前做过的决策
- 调用过的工具
- 遇到过的错误
- 用户明确给过的约束
而摘要旧消息再保留最近 tail,意味着系统仍然保住了两层东西:
- 长期任务记忆的摘要版
- 当前局部执行上下文的原文版
这个策略对 coding agent 特别重要,因为任务连续性往往比对话自然度更重要。
自动压缩和手动压缩是两条路径
这一点也很成熟。
如果一个系统只有自动 compaction,用户很难在关键时刻主动整理上下文;如果只有手动 compaction,长任务又容易自己失控。
所以更合理的方式就是两条路径同时存在:
- 自动压缩:当输入规模超过阈值时由 runtime 触发
- 手动压缩:给用户显式入口,在需要时主动收束上下文
这种设计说明它既考虑了系统自治,也保留了人类干预能力。
它解决的不是“长期记忆”,而是“长任务连续性”
这是我对这套设计最重要的判断。
Claw Code 当前主线的 context management,并不是在做一个通用 AI memory system。它真正解决的是:
- 当前任务上下文怎么进入 prompt
- 长会话怎么控制预算
- 超长之后怎么不丢失执行连续性
所以它的核心不是向量检索,而是:
- transcript
- budget
- cache
- compaction
这是一条非常工程化,也非常务实的路线。
小结
- 上下文管理可以拆成
Session、SystemPromptBuilder、ProjectContext、PromptCache、compaction五层 - 这里的主线不是向量记忆,而是长任务 prompt 管理
Session是结构化 transcript,不只是聊天记录- compaction 的核心策略是“摘要旧消息 + 保留最近 tail”
- 自动压缩和手动压缩并存,说明系统同时考虑了自治和人工控制
如果你在做 coding agent 或长任务 agent,这一套思路非常值得借鉴。
如果你想补前面的仓库定位、扩展系统和验证体系,建议回到第一篇开始: