知识引擎/Hermes 知识引擎/上下文压缩与缓存

Hermes Agent 使用双重压缩系统和 Anthropic 提示词缓存,在长对话中高效管理上下文窗口的使用。 源文件:agent/context engine.py(ABC)、agent/context compressor.py(默认引擎)、agent/prompt caching.py、gateway/run

> 📖 本文档翻译自 Context Compression and Caching > 最后更新:2026-04-16

上下文压缩与缓存

Hermes Agent 使用双重压缩系统和 Anthropic 提示词缓存,在长对话中高效管理上下文窗口的使用。

源文件:agent/context_engine.py(ABC)、agent/context_compressor.py(默认引擎)、agent/prompt_caching.pygateway/run.py(会话卫生)、run_agent.py(搜索 _compress_context

可插拔上下文引擎

上下文管理基于 ContextEngine ABC(agent/context_engine.py)构建。内置的 ContextCompressor 是默认实现,但插件可以用替代引擎替换它(例如 Lossless Context Management(无损上下文管理))。

context:
  engine: "compressor"    # 默认 — 内置有损摘要
  engine: "lcm"           # 示例 — 提供无损上下文的插件

引擎负责:

  • 决定何时触发压缩(should_compress()
  • 执行压缩(compress()
  • 可选地暴露 Agent 可以调用的工具(例如 lcm_grep
  • 从 API 响应跟踪 Token 使用量

选择通过 config.yaml 中的 context.engine 进行配置驱动。解析顺序:

  1. 检查 plugins/context_engine/<name>/ 目录
  2. 检查通用插件系统(register_context_engine()
  3. 回退到内置的 ContextCompressor

插件引擎从不自动激活——用户必须显式将 context.engine 设置为插件的名称。默认的 "compressor" 始终使用内置实现。

通过 hermes plugins → Provider Plugins → Context Engine 配置,或直接编辑 config.yaml

关于构建上下文引擎插件,请参阅 Context Engine Plugins

双重压缩系统

Hermes 有两个独立运行的压缩层:

                     ┌──────────────────────────┐
  入站消息           │   Gateway 会话卫生         │  在上下文 85% 时触发
  ─────────────────► │   (Agent 前,粗略估算)   │  大型会话的安全网
                     └─────────────┬────────────┘
                                   │
                                   ▼
                     ┌──────────────────────────┐
                     │   Agent ContextCompressor │  在上下文 50% 时触发(默认)
                     │   (循环内,真实 Token)    │  正常的上下文管理
                     └──────────────────────────┘

1. Gateway 会话卫生(85% 阈值)

位于 gateway/run.py(搜索 Session hygiene: auto-compress)。这是一个安全网,在 Agent 处理消息之前运行。它防止会话在轮次之间增长过大(例如 Telegram/Discord 中的隔夜累积)时发生 API 失败。

  • 阈值:固定在模型上下文长度的 85%
  • Token 来源:优先使用上一轮 API 报告的实际 Token;回退到基于字符的粗略估算(estimate_messages_tokens_rough
  • 触发条件:仅在 len(history) >= 4 且压缩启用时
  • 目的:捕获逃脱 Agent 自身压缩器的会话

Gateway 卫生阈值有意高于 Agent 的压缩器。将其设为 50%(与 Agent 相同)会导致在长 Gateway 会话中每轮都过早压缩。

2. Agent ContextCompressor(50% 阈值,可配置)

位于 agent/context_compressor.py。这是主要压缩系统,在 Agent 的工具循环内运行,可以访问准确的、API 报告的 Token 计数。

配置

所有压缩设置从 config.yamlcompression 键读取:

compression:
  enabled: true              # 启用/禁用压缩(默认:true)
  threshold: 0.50            # 上下文窗口的比例(默认:0.50 = 50%)
  target_ratio: 0.20         # 阈值中保留多少作为尾部(默认:0.20)
  protect_last_n: 20         # 最少保护的尾部消息数(默认:20)

# 摘要模型/Provider 在 auxiliary 下配置:
auxiliary:
  compression:
    model: null              # 覆盖摘要模型(默认:自动检测)
    provider: auto           # Provider:"auto"、"openrouter"、"nous"、"main" 等
    base_url: null           # 自定义 OpenAI 兼容端点

参数详情

参数默认值范围说明
threshold0.500.0-1.0当提示词 Token ≥ threshold × context_length 时触发压缩
target_ratio0.200.10-0.80控制尾部保护 Token 预算:threshold_tokens × target_ratio
protect_last_n20≥1始终保留的最近消息最小数量
protect_first_n3(硬编码)系统提示词 + 首次交换始终保留

计算值(以 200K 上下文模型在默认配置下为例)

context_length       = 200,000
threshold_tokens     = 200,000 × 0.50 = 100,000
tail_token_budget    = 100,000 × 0.20 = 20,000
max_summary_tokens   = min(200,000 × 0.05, 12,000) = 10,000

压缩算法

ContextCompressor.compress() 方法遵循 4 阶段算法:

阶段 1:清理旧工具结果(低成本,无需 LLM 调用)

受保护尾部之外的旧工具结果(>200 字符)被替换为:

[Old tool output cleared to save context space]

这是一个廉价的预处理步骤,可以从冗长的工具输出(文件内容、终端输出、搜索结果)中节省大量 Token。

阶段 2:确定边界

┌─────────────────────────────────────────────────────────────┐
│  消息列表                                                    │
│                                                             │
│  [0..2]  ← protect_first_n(系统 + 首次交换)                 │
│  [3..N]  ← 中间轮次 → 被摘要                                 │
│  [N..end] ← 尾部(按 Token 预算或 protect_last_n)            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

尾部保护是基于 Token 预算的:从末尾向后遍历,累积 Token 直到预算耗尽。如果预算保护的消息少于 protect_last_n 固定数量,则回退到该固定数量。

边界对齐以避免拆分 tool_call/tool_result 组。_align_boundary_backward() 方法跳过连续的工具结果以找到父 assistant 消息,保持组完整。

阶段 3:生成结构化摘要

:::warning 摘要模型上下文长度 摘要模型的上下文窗口必须至少与主 Agent 模型的一样大。整个中间部分在单次 call_llm(task="compression") 调用中发送给摘要模型。如果摘要模型的上下文较小,API 会返回上下文长度错误——_generate_summary() 会捕获它,记录警告并返回 None。然后压缩器没有摘要地丢弃中间轮次,默默地丢失对话上下文。这是压缩质量下降的最常见原因。 :::

中间轮次使用辅助 LLM 通过结构化模板进行摘要:

## Goal
[用户正在尝试完成什么]

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

## Progress
### Done
[已完成的工作 — 具体文件路径、运行的命令、结果]
### In Progress
[正在进行的工作]
### Blocked
[遇到的任何阻碍或问题]

## Key Decisions
[重要的技术决策及其原因]

## Relevant Files
[读取、修改或创建的文件 — 每个附简要说明]

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

## Critical Context
[特定值、错误消息、配置详情]

摘要预算随被压缩内容量缩放:

  • 公式:content_tokens × 0.20_SUMMARY_RATIO 常量)
  • 最小值:2,000 Token
  • 最大值:min(context_length × 0.05, 12,000) Token

阶段 4:组装压缩后的消息

压缩后的消息列表为:

  1. 头部消息(首次压缩时在系统提示词中追加备注)
  2. 摘要消息(角色选择以避免连续相同角色违规)
  3. 尾部消息(未修改)

孤立的 tool_call/tool_result 对由 _sanitize_tool_pairs() 清理:

  • 引用已删除调用的工具结果 → 被移除
  • 结果被移除的工具调用 → 注入存根结果

迭代重新压缩

在后续压缩中,之前的摘要会传递给 LLM,指示它更新而非从头摘要。这跨多次压缩保留信息——项目从"In Progress"移到"Done",添加新进展,移除过时信息。

压缩器实例上的 _previous_summary 字段为此目的存储上次摘要文本。

压缩前/后示例

压缩前(45 条消息,约 95K Token)

[0] system:    "You are a helpful assistant..." (系统提示词)
[1] user:      "Help me set up a FastAPI project"
[2] assistant:  terminal: mkdir project
[3] tool:      "directory created"
[4] assistant:  write_file: main.py
[5] tool:      "file written (2.3KB)"
    ... 30 more turns of file editing, testing, debugging ...
[38] assistant:  terminal: pytest
[39] tool:      "8 passed, 2 failed\n..."  (5KB output)
[40] user:      "Fix the failing tests"
[41] assistant:  read_file: tests/test_api.py
[42] tool:      "import pytest\n..."  (3KB)
[43] assistant: "I see the issue with the test fixtures..."
[44] user:      "Great, also add error handling"

压缩后(25 条消息,约 45K Token)

[0] system:    "You are a helpful assistant...
               [Note: Some earlier conversation turns have been compacted...]"
[1] user:      "Help me set up a FastAPI project"
[2] assistant: "[CONTEXT COMPACTION] Earlier turns were compacted...

               ## Goal
               Set up a FastAPI project with tests and error handling

               ## Progress
               ### Done
               - Created project structure: main.py, tests/, requirements.txt
               - Implemented 5 API endpoints in main.py
               - Wrote 10 test cases in tests/test_api.py
               - 8/10 tests passing

               ### In Progress
               - Fixing 2 failing tests (test_create_user, test_delete_user)

               ## Relevant Files
               - main.py — FastAPI app with 5 endpoints
               - tests/test_api.py — 10 test cases
               - requirements.txt — fastapi, pytest, httpx

               ## Next Steps
               - Fix failing test fixtures
               - Add error handling"
[3] user:      "Fix the failing tests"
[4] assistant:  read_file: tests/test_api.py
[5] tool:      "import pytest\n..."
[6] assistant: "I see the issue with the test fixtures..."
[7] user:      "Great, also add error handling"

提示词缓存(Anthropic)

来源:agent/prompt_caching.py

通过缓存对话前缀,在多轮对话中减少约 75% 的输入 Token 成本。使用 Anthropic 的 cache_control 断点。

策略:system_and_3

Anthropic 允许每个请求最多 4 个 cache_control 断点。Hermes 使用"system_and_3"策略:

断点 1: 系统提示词           (所有轮次中稳定)
断点 2: 倒数第 3 条非系统消息  ─┐
断点 3: 倒数第 2 条非系统消息   ├─ 滑动窗口
断点 4: 最后一条非系统消息      ─┘

工作原理

apply_anthropic_cache_control() 深拷贝消息并注入 cache_control 标记:

# 缓存标记格式
marker = {"type": "ephemeral"}
# 或者 1 小时 TTL:
marker = {"type": "ephemeral", "ttl": "1h"}

标记根据内容类型以不同方式应用:

内容类型标记放置位置
字符串内容转换为 [{"type": "text", "text": ..., "cache_control": ...}]
列表内容添加到最后一个元素的字典中
None/空值作为 msg["cache_control"] 添加
工具消息作为 msg["cache_control"] 添加(仅原生 Anthropic)

缓存感知设计模式

  1. 稳定的系统提示词:系统提示词是断点 1,在所有轮次中缓存。避免在对话中途修改它(压缩仅在首次压缩时追加备注)。

  2. 消息排序很重要:缓存命中需要前缀匹配。在中间添加或删除消息会使之后所有内容的缓存失效。

  3. 压缩缓存交互:压缩后,压缩区域的缓存失效但系统提示词缓存存活。滚动的 3 消息窗口在 1-2 轮内重新建立缓存。

  4. TTL 选择:默认为 5m(5 分钟)。对于用户在轮次之间休息的长运行会话使用 1h

启用提示词缓存

提示词缓存在以下条件自动启用:

  • 模型是 Anthropic Claude 模型(通过模型名称检测)
  • Provider 支持 cache_control(原生 Anthropic API 或 OpenRouter)
# config.yaml — TTL 可配置
model:
  cache_ttl: "5m"   # "5m" 或 "1h"

CLI 在启动时显示缓存状态:

💾 Prompt caching: ENABLED (Claude via OpenRouter, 5m TTL)

上下文压力警告

Agent 在压缩阈值的 85% 时发出上下文压力警告(不是上下文的 85%——而是阈值本身 50% 上下文的 85%):

⚠️  Context is 85% to compaction threshold (42,500/50,000 tokens)

压缩后,如果使用量降到阈值 85% 以下,警告状态被清除。如果压缩未能将使用量降到警告水平以下(对话太密集),警告持续存在但压缩不会重新触发,直到再次超过阈值。

Continue Exploring

继续探索

这不是课程式的上一篇下一篇,而是从当前节点向外继续漫游。

开发者指南

构建上下文引擎插件(Context Engine Plugin)

原文链接:Context Engine Plugins sidebar position: 9 title: "上下文引擎插件" description: "如何构建替换内置 ContextCompressor 的上下文引擎插件" 上下文引擎插件用替代策略替换内置的 ContextCompressor 来管理对话上下文

开发者指南

架构

本页面是 Hermes Agent 内部结构的顶层地图. 使用它来了解代码库的整体结构,然后深入子系统文档获取实现细节。 如果你是首次接触此代码库: 1. 本页面 — 了解整体结构 2. Agent 循环内部机制 — AIAgent 如何工作 3. 提示词组装 — 系统提示词构建

开发者指南

贡献指南

感谢你对 Hermes Agent 的贡献!本指南涵盖开发环境设置、理解代码库以及如何让你的 PR 被合并。 我们按以下顺序重视贡献: 1. Bug 修复 — 崩溃、不正确行为、数据丢失 2. 跨平台兼容性 — macOS、不同 Linux 发行版、WSL2 3. 安全加固 — Shell 注入、Prompt 注入、路

开发者指南

Agent 循环内部机制

sidebar position: 3 title: "Agent 循环内部机制" description: "AIAgent 执行、API 模式、工具、回调和回退行为的详细解析" 核心编排引擎是 run agent.py 中的 AIAgent 类——大约 10,700 行代码,负责从 Prompt(提示词)组装到工具

开发者指南

提示词组装

sidebar position: 5 title: "提示词组装" description: "Hermes 如何构建系统提示词、保持缓存稳定性和注入临时层" Hermes 刻意将以下两者分离: - 缓存的系统提示词状态 - 临时的 API 调用时添加内容 这是项目中最关键的设计决策之一,因为它影响:

开发者指南

网关内部机制

sidebar position: 7 title: "网关内部机制" description: "消息网关如何启动、授权用户、路由会话和投递消息" 消息网关是一个长运行进程,通过统一架构将 Hermes 连接到 14+ 个外部消息平台。 当消息从任何平台到达时: 1. 平台适配器 接收原始事件,将其规范化为 Mess

Developer Guide

开发者指南

面向二次开发者,解释架构、运行时、上下文引擎、插件、工具与扩展机制。

20 篇文档20 个节点

当前节点

上下文压缩与缓存

同主题继续探索

架构

本页面是 Hermes Agent 内部结构的顶层地图. 使用它来了解代码库的整体结构,然后深入子系统文档获取实现细节。 如果你是首次接触此代码库: 1. 本页面 — 了解整体结构 2. Agent 循环内部机制 — AIAgent 如何工作 3. 提示词组装 — 系统提示词构建

贡献指南

感谢你对 Hermes Agent 的贡献!本指南涵盖开发环境设置、理解代码库以及如何让你的 PR 被合并。 我们按以下顺序重视贡献: 1. Bug 修复 — 崩溃、不正确行为、数据丢失 2. 跨平台兼容性 — macOS、不同 Linux 发行版、WSL2 3. 安全加固 — Shell 注入、Prompt 注入、路

Agent 循环内部机制

sidebar position: 3 title: "Agent 循环内部机制" description: "AIAgent 执行、API 模式、工具、回调和回退行为的详细解析" 核心编排引擎是 run agent.py 中的 AIAgent 类——大约 10,700 行代码,负责从 Prompt(提示词)组装到工具

提示词组装

sidebar position: 5 title: "提示词组装" description: "Hermes 如何构建系统提示词、保持缓存稳定性和注入临时层" Hermes 刻意将以下两者分离: - 缓存的系统提示词状态 - 临时的 API 调用时添加内容 这是项目中最关键的设计决策之一,因为它影响:

网关内部机制

sidebar position: 7 title: "网关内部机制" description: "消息网关如何启动、授权用户、路由会话和投递消息" 消息网关是一个长运行进程,通过统一架构将 Hermes 连接到 14+ 个外部消息平台。 当消息从任何平台到达时: 1. 平台适配器 接收原始事件,将其规范化为 Mess

会话存储

Hermes Agent 使用 SQLite 数据库( /.hermes/state.db)来持久化会话元数据、完整消息历史和模型配置,覆盖 CLI 和网关会话。这取代了早期基于每个会话 JSONL 文件的方式。 源文件:hermes state.py 关键设计决策: - WAL 模式 用于并发读取 + 单个写入器(网

相关节点

构建上下文引擎插件(Context Engine Plugin)

原文链接:Context Engine Plugins sidebar position: 9 title: "上下文引擎插件" description: "如何构建替换内置 ContextCompressor 的上下文引擎插件" 上下文引擎插件用替代策略替换内置的 ContextCompressor 来管理对话上下文

架构

本页面是 Hermes Agent 内部结构的顶层地图. 使用它来了解代码库的整体结构,然后深入子系统文档获取实现细节。 如果你是首次接触此代码库: 1. 本页面 — 了解整体结构 2. Agent 循环内部机制 — AIAgent 如何工作 3. 提示词组装 — 系统提示词构建

贡献指南

感谢你对 Hermes Agent 的贡献!本指南涵盖开发环境设置、理解代码库以及如何让你的 PR 被合并。 我们按以下顺序重视贡献: 1. Bug 修复 — 崩溃、不正确行为、数据丢失 2. 跨平台兼容性 — macOS、不同 Linux 发行版、WSL2 3. 安全加固 — Shell 注入、Prompt 注入、路

Agent 循环内部机制

sidebar position: 3 title: "Agent 循环内部机制" description: "AIAgent 执行、API 模式、工具、回调和回退行为的详细解析" 核心编排引擎是 run agent.py 中的 AIAgent 类——大约 10,700 行代码,负责从 Prompt(提示词)组装到工具

提示词组装

sidebar position: 5 title: "提示词组装" description: "Hermes 如何构建系统提示词、保持缓存稳定性和注入临时层" Hermes 刻意将以下两者分离: - 缓存的系统提示词状态 - 临时的 API 调用时添加内容 这是项目中最关键的设计决策之一,因为它影响:

网关内部机制

sidebar position: 7 title: "网关内部机制" description: "消息网关如何启动、授权用户、路由会话和投递消息" 消息网关是一个长运行进程,通过统一架构将 Hermes 连接到 14+ 个外部消息平台。 当消息从任何平台到达时: 1. 平台适配器 接收原始事件,将其规范化为 Mess