Skip to content

Hermes 的上下文压缩不是简单的截断,而是五阶段结构化算法:先裁剪旧工具输出(无需 LLM),再保护头尾消息,然后用 LLM 生成结构化摘要压缩中间段,多次压缩时迭代更新而非从头重写。关键设计是「摘要模板」——7 个固定章节确保每次压缩后 AI 都能找到继续工作所需的所有上下文。

Hermes Agent 上下文压缩机制深度解析:五阶段算法与迭代摘要策略

上下文管理是长对话 AI Agent 的核心工程问题。对话越长,需要的 token 越多,成本越高,而且超过模型的 context length 后对话就断了。

大多数工具的解法是:限制历史消息条数(简单但损失信息)或者直接截断(最简单但最粗暴)。Hermes 选择了更复杂的路径:结构化压缩 + 迭代摘要更新。

什么时候触发压缩

python
threshold_percent: float = 0.50  # 默认:上下文使用率达到 50% 触发

Hermes 在每次 API 调用后检查 token 使用量,达到阈值就触发压缩。50% 看起来很激进,但有两个原因:

  1. 压缩需要空间:压缩后的摘要本身要占一定 token,太晚触发会没有足够空间放摘要
  2. 提前压缩可以保留更多有效上下文:70~80% 时才压缩,能压缩的历史消息已经不多了

还有一个 pre-flight 检查(在 API 调用前的粗略估算),避免发出已经超限的请求:

python
def should_compress_preflight(self, messages):
    rough_estimate = estimate_messages_tokens_rough(messages)
    return rough_estimate >= self.threshold_tokens

五阶段压缩算法

阶段 1:工具输出裁剪(最廉价,无 LLM)

目标:清除旧的工具调用结果,用占位符替换。

python
_PRUNED_TOOL_PLACEHOLDER = "[Old tool output cleared to save context space]"

判断逻辑

  • 只裁剪 role == "tool" 的消息
  • 保护「尾部 token 预算」内的消息(最近的 ~20K token 不动)
  • 只裁剪内容超过 200 字符的工具输出(小输出留着,裁剪收益不大)

为什么先做这步?工具输出通常很大(整个文件内容、搜索结果的完整 JSON、命令输出),而且往往是一次性信息(AI 已经处理过了,不需要再看)。裁剪后上下文可能已经够用,就不用调 LLM 做摘要了。

阶段 2:保护「头部」消息

python
protect_first_n: int = 3  # 默认保护前 3 条消息

系统提示 + 第一轮对话(通常是任务描述)不参与压缩。原因:第一轮消息通常包含最关键的上下文信息(你要我做什么、用什么约束),丢失这些会让 AI 失去方向。

阶段 3:确定「尾部」保护范围(基于 token 预算)

python
protect_last_n: int = 20  # 默认保护最近 20 条消息
# 但同时有 token 预算限制:tail_token_budget

保护范围用 token 预算而不是固定消息数:从尾部往前数,累积 token 直到达到预算上限。这样对话里有大量长消息时,保护的消息数会少一些;短消息多时保护范围更大。比固定条数更合理。

阶段 4:生成结构化 LLM 摘要

中间的消息(头部之后、尾部之前)被送给一个「辅助 LLM」(通常是比主模型便宜的快速模型)生成摘要。

摘要模板(7 个固定章节)

markdown
## Goal
[用户在做什么]

## Constraints & Preferences
[用户偏好、约束、重要决策]

## Progress
### Done / In Progress / Blocked

## Key Decisions
[技术决策和原因]

## Relevant Files
[涉及的文件列表]

## Next Steps
[接下来需要做什么]

## Critical Context
[不保留会丢失的具体数据:错误信息、配置值、数值结果]

## Tools & Patterns
[工具使用方式和发现]

摘要 token 预算(和被压缩内容等比例):

python
_SUMMARY_RATIO = 0.20   # 摘要 ≈ 被压缩内容的 20%
_MIN_SUMMARY_TOKENS = 2000
_SUMMARY_TOKENS_CEILING = 12_000

被压缩 10,000 tokens 的对话 → 摘要约 2,000 tokens。比例缩放比固定上限更合理:对于大 context 窗口(200K+),固定 8K token 上限意味着摘要只有 4%,很容易丢失细节。

阶段 5:迭代更新(多次压缩时)

这是 Hermes 压缩机制里最精妙的设计:

第一次压缩:从头生成结构化摘要。

后续压缩self._previous_summary 已存在):不重新摘要,而是更新

python
if self._previous_summary:
    prompt = f"""Update the summary... 
    
    PREVIOUS SUMMARY:
    {self._previous_summary}
    
    NEW TURNS TO INCORPORATE:
    {content_to_summarize}
    
    PRESERVE all existing information that is still relevant.
    ADD new progress. Move items from "In Progress" to "Done".
    Remove information only if clearly obsolete."""

为什么迭代更新比重新摘要更好

对话经历多次压缩后,如果每次都从头重新摘要,早期的关键决策可能在多次压缩中逐渐丢失(每次压缩都有信息损失,多次叠加的损失是指数级的)。迭代更新策略确保一旦写入「Key Decisions」的内容,会在后续所有压缩中持续保留,除非明确过时了才删除。

压缩失败的降级策略

摘要 LLM 调用失败时(网络错误、API Key 无效等),Hermes 有失败降级策略:

python
_SUMMARY_FAILURE_COOLDOWN_SECONDS = 600  # 失败后 10 分钟内不重试

def _generate_summary(self, turns):
    if time.monotonic() < self._summary_failure_cooldown_until:
        return None  # 冷却期内直接跳过

    # ... 尝试生成
    except Exception:
        self._summary_failure_cooldown_until = time.monotonic() + _SUMMARY_FAILURE_COOLDOWN_SECONDS
        return None

返回 None 时,调用方直接丢掉中间消息(没有摘要占位),而不是插入一条「摘要失败」的无用消息。这样对话虽然有信息损失,但不会因为失败消息混入上下文而让 AI 困惑。

消息被注入时的前缀

压缩后的摘要以特定前缀注入对话:

python
SUMMARY_PREFIX = (
    "[CONTEXT COMPACTION] Earlier turns in this conversation were compacted "
    "to save context space. The summary below describes work that was "
    "already completed, and the current session state may still reflect "
    "that work (for example, files may already be changed). Use the summary "
    "and the current state to continue from where things left off, and "
    "avoid repeating work:"
)

这个前缀的作用:明确告诉后续对话中的 AI「这是压缩摘要,不是对话历史,继续工作不要重复已做的事」。没有这个说明,AI 可能把摘要当成用户发来的新消息处理。

和 Claude Code 的 /compact 对比

维度Hermes 自动压缩Claude Code /compact
触发时机自动(50% context 阈值)手动(用户运行 /compact
摘要模型辅助 LLM(便宜快速模型)Claude 自身
摘要结构7 章节固定模板自由格式
迭代更新是(多次压缩累积摘要)否(每次重新生成)
工具输出裁剪先做廉价裁剪,再做 LLM 摘要整体 LLM 处理

FAQ

Q: 辅助摘要 LLM 用哪个模型?

优先用 summary_model_overrideconfig.yaml 里可以配),没有配置时用和主会话相同的 Provider 的最便宜模型(如 Gemini Flash 或 GPT-4o-mini)。目标是摘要成本远低于被压缩对话的 token 成本。

Q: 压缩会导致 AI 忘记前面说过的事吗?

是的,有信息损失,这是不可避免的权衡。摘要结构的设计目标是最大限度保留「做了什么、为什么这样做、接下来要做什么」——这些对任务连续性最重要。单条对话的细节(比如某次搜索的完整结果)会丢失。

Q: 能关闭自动压缩吗?

可以,设置阈值为 1.0(永远不触发)或者直接关闭配置项。但超过 context length 后会直接 API 报错,那时只能手动清理。