10. 上下文压缩¶
心智模型:对话是有寿命的¶
graph LR
subgraph "一次长对话的生命周期"
T1[5k tokens<br/>舒适] --> T2[50k<br/>还行]
T2 --> T3[100k<br/>该压缩了]
T3 --> T4[180k<br/>紧迫]
T4 --> T5[200k<br/>💥 OOM]
end
T3 -.压缩时机.-> C[/compress<br/>自动压缩]
T4 -.已经晚了.-> R[/new + memory 转移]
style T3 fill:#FFD700,color:#000
style T4 fill:#FFA07A,color:#000
style T5 fill:#FF6B6B,color:#fff
三个关键认知:
- 上下文不是免费的 —— 每一轮 LLM 调用都要把整个对话历史发一次。100k tokens 的对话 × 10 轮 = 100 万 tokens 账单。
- 模型有窗口上限 —— 超了直接失败(Claude 200k、Gemini 1M、多数模型 64-128k)
- prompt caching 是长对话的救星 —— 只要历史不变,后续轮次只 pay cache 折扣价
压缩的本质:把对话中段用总结替换,保留头 + 尾 + 总结中段,让有效信息浓缩到更小空间。
Hermes 的压缩策略¶
graph TB
subgraph "原始对话"
H[头部<br/>system + 最初几轮]
M1[中段消息 1]
M2[中段消息 2]
M3[中段消息 ...]
M4[中段消息 N]
T[尾部<br/>最近几轮]
end
subgraph "压缩后"
H2[头部<br/>保留]
S[一段压缩摘要<br/>结构化]
T2[尾部<br/>保留]
end
H --> H2
M1 & M2 & M3 & M4 -.auxiliary LLM 总结.-> S
T --> T2
style H2 fill:#E8F5E9,color:#000
style S fill:#FFF3E0,color:#000
style T2 fill:#E8F5E9,color:#000
设计要点:
- 头保留 —— 系统提示 + 初始指令不能丢,它们锚定整个对话的目标
- 尾保留 —— 最近几轮是当前进行中的工作,不能被总结掉
- 中段总结 —— 用 auxiliary model(便宜快的,如 Gemini Flash)
- token-budget tail —— 尾部不是固定"保留 N 条",而是"保留最近总计 M tokens",更弹性
- 预先修剪工具结果 —— 大段工具输出(比如
ls -la /几千行)先截短再送去总结
压缩后的摘要长什么样¶
Hermes 压缩产出的摘要是结构化模板:
[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted
into the summary below. This is a handoff from a previous context
window — treat it as background reference, NOT as active instructions.
Do not respond to any questions in the summary directly.
## Task Context
用户正在给 Hermes Agent 写中文指南。已完成第一部(入门),正在写第二部(日常)。
本指南使用 MkDocs Material,部署到 GitHub Pages。
## Key Decisions Resolved
- 仓库位置:/Users/katya/Files/hermes-agent-guide/
- 章节结构:6 章(模型策略、工具、记忆、技能、会话搜索、压缩、个性)
- 每章三段式:心智模型 + 最小实践 + 坑点
## Active Files
- docs/part-2-daily/05-model-strategy.md
- docs/part-2-daily/06-tools-overview.md
- ...
## Pending Questions
- 是否需要新增「配合 watch_patterns 做后台任务」的章节?(暂搁置)
## Remaining Work
- 完成第 11 章(个性)
- 更新 part-2-daily/index.md 的卡片
- 构建验证 + commit + push
几个细节: - 开头警告 "REFERENCE ONLY" —— 防止模型把摘要里的问题当成用户当前问题去回答(v0.8 修了这个坑) - Resolved / Pending 分开 —— 已定的和未定的不混 - Remaining Work 替代 "Next Steps" —— 后者有命令口吻,前者是状态
最小实践:三种触发方式¶
方式 1 · 自动压缩¶
不用你管。当 context 接近上限时,Hermes 自动触发压缩,你会看到:
⚠ Context pressure: 89% of budget.
⟳ Compacting middle context (using auxiliary model)...
✓ Reduced from 180k → 65k tokens.
对话无缝继续。
方式 2 · 手动 /compress¶
你看 /usage 觉得该压了,直接:
agent 立刻压缩,告诉你压缩前后的 token 数。
方式 3 · 带 focus 的 /compress <focus>(v0.8+)¶
这是最强的用法。告诉压缩器保留哪部分最重要:
压缩器在总结时,会优先保真你指定的主题,其他内容更激进地合并。
典型场景:
- 你做了 2 小时调试,早期试了 5 个方案都不对,现在锁定第 6 个
→ /compress 保留第 6 个方案的细节,前面 5 个方案只要失败原因一句话
- 长对话里涉及多个文件,你现在只关心其中一个
→ /compress focus: 重点保留 src/api/handlers.ts 相关讨论
什么时候压缩 vs 什么时候开新会话¶
两个选项,怎么选?
graph TB
Q[对话太长了,怎么办?]
Q --> A{当前话题还在继续吗?}
A -->|还在| B[/compress<br/>保留当前状态继续]
A -->|告一段落| C{需要记住什么跨会话?}
C -->|是| D[让 agent 总结进 MEMORY<br/>然后 /new]
C -->|否| E[直接 /new]
style B fill:#FFD700,color:#000
style D fill:#98FB98,color:#000
style E fill:#98FB98,color:#000
决策规则:
| 情况 | 推荐 |
|---|---|
| 任务进行中,压力来自历史冗余 | /compress |
| 任务进行中,你知道其中一部分最重要 | /compress <focus> |
| 当前任务结束,但学到了东西(约定、坑) | 总结进 MEMORY → /new |
| 完全换话题,之前内容用不上 | /new |
| 再等等 —— 触发自动压缩更省心 | 什么都不做 |
压缩的代价¶
压缩本身是要花钱的,不是免费的。
一次压缩 = 用 auxiliary model 跑一次大总结: - 输入:被压缩的中段消息(比如 50k tokens) - 输出:摘要(几千 tokens)
用 Gemini Flash 这种便宜 auxiliary 的话,单次压缩大约 $0.01-0.05。 用主模型压缩(如果你把 auxiliary 设成了主模型),单次可能 $0.50-2。
所以别把 auxiliary model 设得太贵
hermes model → "Configure auxiliary models" → 确保压缩用的是便宜快的。默认一般是 Gemini Flash,可以放心。
多次压缩的累积: - 长对话可能触发 3-5 次自动压缩 - 每次压缩之间有增量内容,压缩也会更新之前的摘要 - 所以摘要迭代式积累信息,不会每次都重新总结全部 —— 这是 Hermes 的优化
Prompt Cache 的边界 · 超级重要¶
这是 Hermes 写代码的头号规则。你日常用 Hermes 也值得知道:
这些操作会破坏 prompt cache
- 修改系统提示(personality 切换、memory 重新加载)
- 切换 toolsets(启用/禁用工具)
- 在中段插入消息(不是追加尾部)
破坏缓存后,后续每一轮都要按全价重新发送历史 —— 成本飙升 5-10 倍。
压缩为什么可以破坏缓存? 因为它替换了历史内容,本来就必须重建缓存。但这是唯一应该破坏缓存的场景,而且之后能带着新摘要享受很多轮的缓存命中 —— 净收益为正。
坑点¶
坑 1 · 压缩把重要东西压没了¶
现象:压缩后你继续问,agent 对某个关键细节一脸懵。
原因:默认压缩算法不知道你最看重哪一段。
对策:
- 压缩前就用 /compress <focus> —— 提前告诉保留什么
- 重要结论提前写进 MEMORY —— 压缩不影响 MEMORY
- 压缩后如果发现细节丢了,把它再说一遍 —— 当前尾部是保留的,再说一次会永远留存
坑 2 · 压缩后 agent 对已经定了的事情再提问¶
现象:压缩后 agent 说「请问您的目标是什么?」—— 这在对话早期已经说过了。
原因:老版本 bug(v0.8 之前压缩摘要会被当成用户的当前问题)。
对策:升级到 v0.8+,压缩摘要有明确的 "REFERENCE ONLY" 警告,不会被误当成问题。
坑 3 · 连续多次压缩效果差¶
现象:压了 3 次后,摘要丢失大量细节。
原因:每次压缩都是对前一版摘要 + 新内容再总结,信息损失是累积的。
对策:
- 长对话超过 3 次压缩后,考虑/new 开新会话
- 把当前状态总结进 MEMORY 或一个 markdown 文件,新会话引用它
坑 4 · 压缩器本身报错¶
现象:
原因: - Auxiliary model 当前不可用 - 网络问题 - Auxiliary model 的 API key 失效
对策:
- hermes doctor 看 auxiliary model 状态
- 临时措施:手动 /new 开新会话,用 memory 转移重要事实
坑 5 · 「我不想压缩」¶
现象:你正在做敏感任务,不想让 auxiliary model 看到对话内容。
原因:压缩默认发给 auxiliary model,可能跟主模型不同厂商。
对策:
- 把 auxiliary 设置成跟主模型同家:hermes model → "Configure auxiliary models" → 改成同一 provider
- 或者 设成 "auto"(v0.10 默认)—— auxiliary 沿用主模型,一个信任域
组合拳:压缩 + memory + session_search 三位一体¶
真实长期使用场景:
# 早上开始一个长项目
> 帮我设计一个新功能 X
# 工作中段
> /compress 保留 X 的设计决策
# 中午吃饭前
> 这个 X 的设计已经定了,帮我写成 memory,我下次从 /new 开始实现
> /new
# 下午实现
> [memory 自动注入,agent 记得早上的设计]
> 开始实现 X
# 一周后回来
> 我们之前设计过一个 X,帮我回忆一下细节
> [agent 调 session_search,找到早上那次设计会话,总结细节]
→ 压缩让单个长会话不爆,memory 让重要决策跨会话,session_search 让遥远的历史可追。
进阶¶
- 第 26 章(第四部)—— Prompt Cache 的边界(给开发者的详细规则)
- 第 28 章(第四部)—— Context Compression 算法详解 + 源码走读
- 第 20b 章(第三部)—— 自定义 Context Engine 实现完全自定义压缩逻辑
下一章:11. 个性与提示工程 →