首页
/ Headroom CCR(Compress-Cache-Retrieve)架构解析:让 LLM 上下文压缩可逆的四阶段机制

Headroom CCR(Compress-Cache-Retrieve)架构解析:让 LLM 上下文压缩可逆的四阶段机制

2026-09-06 10:25:26作者:钟日瑜

Headroom 的 CCR(Compress-Cache-Retrieve)架构把"压缩"从一次性的有损操作变成可逆流程:内容被压缩时原文入缓存,LLM 需要时可用 headroom_retrieve 工具即时取回。本文基于仓库中的 wiki/ccr.md 展开,结合 headroom/ccr/ 模块的源码实现,完整讲清 CCR 的四阶段工作原理、各阶段的源码级细节(标记格式、哈希校验、防循环配置等)、CLI 配置项,以及如何用 examples/test_ccr.py 验证压缩质量。读完后你将能够理解 Headroom 代理在"省 token"与"不丢数据"之间做平衡的全部机制,并知道如何开关、调优这套行为。

传统有损压缩的两难

传统上下文压缩是有损的——如果你对"什么内容重要"判断错误,数据就永久丢失了。这构成了一个难以调和的取舍:

  • 激进压缩:有丢失 LLM 真正需要的数据的风险
  • 保守压缩:拿不到足够的 token 节省

CCR 的存在就是消除这个取舍:压缩后原文始终可取回,"省得激进"和"回答准确"可以兼得。

CCR 覆盖的压缩组件

CCR 并不是一个独立的压缩器,而是挂接在现有压缩组件上的"可逆层"。根据 wiki/ccr.md

组件 压缩对象 CCR 集成方式
SmartCrusher JSON 数组(工具输出) 存下原始数组,标记中携带哈希
ContentRouter 代码、日志、搜索结果、文本 按压缩策略存下原始内容

从源码结构看,CCR 层由 headroom/ccr/ 下的四个模块组成,与 模块入口 的文档注释一一对应:

  1. Tool Injectiontool_injection.py):代理在发生压缩时向请求注入 headroom_retrieve 工具
  2. Response Handlerresponse_handler.py):拦截响应,自动处理 CCR 工具调用
  3. Context Trackercontext_tracker.py):跨轮次追踪压缩内容,支持主动展开
  4. Batch Processingbatch_processor.pybatch_store.py):在 Batch API 结果中异步处理 CCR 工具调用,兼容 Anthropic、OpenAI、Google 三家提供商

此外 CCR 的检索工具有两条分发通道:代理注入(默认)和独立的 MCP Server(通过 MCP 协议暴露 headroom_retrieve)。配置了 MCP 时会跳过注入,避免同一工具重复出现。

四阶段工作原理

整体流程如下(原文档的示意图):

┌─────────────────────────────────────────────────────────────────┐
│  TOOL OUTPUT (1000 items)                                        │
│  └─ SmartCrusher compresses to 20 items                         │
│  └─ Original cached with hash=abc123                            │
│  └─ Retrieval tool injected into context                        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  LLM PROCESSING                                                  │
│  Option A: LLM solves task with 20 items → Done (90% savings)   │
│  Option B: LLM calls headroom_retrieve(hash=abc123)             │
│            → Response Handler executes retrieval automatically  │
│            → LLM receives full data, responds accurately        │
└─────────────────────────────────────────────────────────────────┘

Phase 1:压缩与存储

当 SmartCrusher 压缩工具输出时:

  1. 原始内容存入 LRU 缓存
  2. 生成一个用于检索的哈希键
  3. 在压缩输出中插入标记:[1000 items compressed to 20. Retrieve more: hash=abc123]

源码层面,标记格式远比文档示例丰富。tool_injection.pyCCRToolInjector 维护了一组标记识别模式,覆盖了不同压缩器的输出形态:

# - SmartCrusher: [100 items compressed to 10. Retrieve more: hash=abc123]
# - Kompress: [100 lines compressed to 10. Retrieve more: hash=abc123]
# - LogCompressor: [200 lines compressed to 20. Retrieve more: hash=abc123]
# - SearchCompressor: [50 matches compressed to 5. Retrieve more: hash=abc123]
  • 标准括号标记[N <类型> compressed to M. Retrieve more: hash=xxx],哈希为 24 位十六进制(SHA-256 截取前 24 位,约 96 bit,用于抗碰撞)
  • SmartCrusher 行剥离标记<<ccr:HASH N_rows_offloaded>> 与不透明块形式 <<ccr:HASH,KIND,SIZE>>,哈希为 12~24 位十六进制
  • read_lifecycle 陈旧标记[Read content stale/superseded: ... Retrieve original: hash=xxx]——这类标记不含 "compressed" 字样,早期版本会漏检导致"模型拿到一个无法兑换的标记"(静默数据丢失),因此源码单独增加了对 Retrieve original: hash= 短语的直接匹配

Phase 2:工具注入

代理会把 headroom_retrieve 工具注入 LLM 的可用工具列表。文档给出的简化工具定义:

{
  "name": "headroom_retrieve",
  "description": "Retrieve original uncompressed data from Headroom cache",
  "parameters": {
    "hash": "The hash key from the compression marker"
  }
}

实际实现见 create_ccr_tool_definition,它按提供商生成三种格式:

  • OpenAI{"type": "function", "function": {"name": "headroom_retrieve", "parameters": {...}}}
  • Anthropic:顶层 name + input_schema 结构
  • Google:顶层 name + parameters 结构

三者都声明 hash 为必填字符串参数,description 明确提示"哈希在形如 [N items compressed... hash=abc123] 的压缩标记中提供"。

注入时机有两层值得注意的机制:

  1. 哈希归属校验(verify_ownership:括号标记的"形状"并非 Headroom 独有——其他上下文工具也会产生视觉相同的标记。tool_injection.py 中的 verify_ownership() 会在扫描标记后、注入工具前,用压缩存储的 exists() 检查把"本代理从未存过的哈希"剔除,避免模型调用 headroom_retrieve 后必然落空、重做已有工作(issue #2836 修复)。
  2. 会话级粘性注入(sticky-on):一旦某会话发生过 CCR,headroom_retrieve 必须留在后续每次请求的 tools 数组中(inject_tool_definitionsession_has_done_ccr 参数)。否则工具列表的字节在会话中途时开时关,会打爆提示词缓存的前缀稳定性。

除工具注入外,inject_into_system_message 还会向 system 消息追加一段"Compressed Context Available"说明,列出当前可用的哈希(最多展示 5 个),告知模型如何调用检索工具;若请求中没有 system 消息则新建一条。

Phase 3:响应处理器

当 LLM 调用 headroom_retrieve 时:

  1. Response Handler 拦截该工具调用
  2. 从本地缓存取回数据(约 1ms)
  3. 把结果作为工具结果加入对话
  4. 自动继续发起下一轮 API 调用,直到模型给出不再含 CCR 工具调用的最终响应

客户端全程看不到 CCR 工具调用——它们被透明处理掉了。

源码中 ResponseHandlerConfig 定义了关键的防错与防循环参数,默认值如下:

参数 默认值 作用
enabled True 是否自动处理 CCR 工具调用
max_retrieval_rounds 3 最大 CCR 检索轮数,防止无限循环
strip_ccr_from_response True 最终响应中剥离 CCR 工具调用,客户端不可见
continuation_timeout_ms 120000 续接请求的超时(2 分钟)

此外,residual_ccr_status 定义了三种残余状态信号,让调用方能区分"处理完成"、"CCR 与客户方工具混合出现而刻意跳过"(此时必须原样透传而非失败关闭)和"真正的处理失败"(应失败关闭)。哈希解析侧还有严格校验:parse_tool_call 只接受 12 或 24 位十六进制哈希,并统一转小写——因为压缩存储的键始终是小写形式,模型若回显大写哈希,不规范化就会查询落空。

Phase 4:上下文追踪器(多轮主动展开)

跨多个轮次,Context Tracker 负责:

  1. 记住前几轮压缩过什么
  2. 分析新查询与压缩内容的相关性
  3. 在 LLM 主动要求之前,主动展开相关的压缩数据

示例:

Turn 1: User searches for files
        → Tool returns 500 files
        → SmartCrusher compresses to 15, caches original (hash=abc123)
        → LLM sees 15 files, answers question

Turn 5: User asks "What about the auth middleware?"
        → Context Tracker detects "auth" might be in abc123
        → Proactively expands compressed content
        → LLM sees full file list, finds auth_middleware.py

context_tracker.py 中的 ContextTrackerConfig 给出了这一机制的全部可调参数:

参数 默认值 含义
enabled True 是否启用追踪
max_tracked_contexts 100 最多追踪的压缩上下文数(LRU 淘汰)
relevance_threshold 0.3 推荐展开的相关性阈值(0-1)
max_context_age_seconds 300.0 上下文最大年龄(5 分钟),越老越不易展开
proactive_expansion True 是否基于查询分析主动展开
max_proactive_expansions 2 每轮最多主动展开的条目数

相关性评分(_calculate_relevance)使用简单但有效的启发式:新查询关键词与压缩内容样本的重叠度(权重 0.5,4 字符以上的完整子串命中额外加 0.2)、与压缩时原始查询上下文的重叠度(权重 0.3)、工具名相关性加分(find/glob/search/grep/ls 类工具 + 文件类查询词命中加 0.1),再乘以随时间衰减的年龄因子(1 - (age/300) * 0.5)。

另一个重要的安全设计:每条 CompressedContext 都强制携带 workspace_keycontext_tracker.py),把每个压缩事件绑定到单一项目/CWD 身份,analyze_query 会拒绝跨工作区匹配——从源码注释看,这是针对一次真实事故的修复(Python 项目的文件内容曾出现在另一个 Ruby 项目的会话中)。

CCR 压缩的是内容块,而非整条消息

这一点直接决定了 CCR 与提示词缓存的兼容性:Headroom 永远不会从对话历史中删除整条消息。CCR 只作用于内容块(content blocks)——即 live-zone 流水线压缩的最新工具输出、工具结果和用户内容。原始块存入缓存并可通过标记中的哈希按需取回:

┌─────────────────────────────────────────────────────────────────┐
│  LATEST TOOL RESULT (500 files, 12K tokens)                      │
│  └─ ContentRouter / SmartCrusher compresses the block           │
│  └─ Original cached with hash=def456                            │
│  └─ Marker inserted: "500 items compressed, retrieve: def456"   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  LLM PROCESSING                                                  │
│  Option A: LLM solves task with the compressed block → Done     │
│  Option B: LLM needs the full content                           │
│            → Calls headroom_retrieve(hash=def456)               │
│            → Full original block restored                        │
└─────────────────────────────────────────────────────────────────┘

较早的对话轮次、system prompt 和工具定义——也就是提供商缓存的热区(hot zone)——永远不会被改动,因此提示词缓存机制持续生效。压缩只发生在 live zone(最新的内容块),并且通过 CCR 完全可逆。

TOIN 集成:当用户取回过某些被压缩内容时,TOIN 会学习将这些模式标记为更高价值,从而改善面向所有用户的未来压缩决策(对应 headroom/ 下的 TOIN 反馈链路,参见 test_toin_feedback.py)。

核心特性总览

特性 说明
自动响应处理 LLM 调用 headroom_retrieve 时由代理自动完成,客户端无感知
多轮上下文追踪 跨轮次追踪压缩内容,相关性达标时主动展开
哈希键检索 headroom_retrieve(hash) 始终返回完整原文
反馈学习 从检索模式中学习,改进未来的压缩决策

配置与 CLI 参数

CCR 在代理中默认启用。基础命令(wiki/ccr.md):

# Proxy with CCR enabled (default)
headroom proxy --port 8787

# Disable CCR entirely: no retrieval markers, no headroom_retrieve tool
headroom proxy --no-ccr

# Disable proactive expansion of previously-compressed content
headroom proxy --no-ccr-proactive-expansion

结合 CLI 定义,这三个开关的完整语义与对应环境变量是:

参数 环境变量 实际行为
(默认) CCR 全开:标记注入 + 工具注入 + 响应处理 + 主动展开
--no-ccr HEADROOM_NO_CCR=1 一次性关闭 CCR 的"每一半":不写检索标记、不注入 headroom_retrieve 工具、不做响应处理(proxy.py 中同时置 ccr_inject_tool / ccr_inject_marker / ccr_handle_responses 为 False)。适合追求最大节省、且客户端无法解析注入工具的流式/非 MCP 场景——代价是有损压缩不可恢复
--no-ccr-proactive-expansion HEADROOM_NO_CCR_PROACTIVE_EXPANSION=1 只关闭 Phase 4 的主动展开,其余 CCR 行为(标记、工具、响应处理)保持开启

与 CCR 相关的两个邻近开关也值得了解:

  • --losslessHEADROOM_LOSSLESS=1):无 CCR 的无损模式,用格式原生无损压缩(及无标记的 SmartCrusher)压缩工具输出,不产生任何检索标记,因此连 MCP 检索工具都不需要;
  • --ccr-inline-resolveHEADROOM_CCR_INLINE_RESOLVE):在响应路径上内联解析 <<ccr:...>> 标记,而不是依赖模型调用 headroom_retrieve。适用于没有工具调用往返来"兑换"标记的调用方(例如 Headroom 作为 LiteLLM guardrail/代理跳板运行时,issue #2509),默认关闭,仅作用于非流式响应。

为什么 CCR 重要

方案 风险 节省
不压缩 0%
传统压缩 数据丢失 70-90%
CCR 压缩 无(可逆) 70-90%

CCR 让你拿到激进压缩的节省幅度,同时风险为零——LLM 随时可以取回原始数据。这就是"压缩-缓存-检索"命名的含义:Compress 省 token,Cache 保原文,Retrieve 兜底准确性。

运行演示与验证

examples/ccr_demo.py 已不在仓库中,当前最接近的可用示例是 examples/test_ccr.py:它构造一组检索器返回的 JSON 工具结果,调用 SDK 的 compress() 压缩后,检查关键概念是否在压缩输出中幸存:

python examples/test_ccr.py

文档中记录的验证输出:

Tokens: 2904 -> 2703 (201 saved)
Transforms: ['router:protected:user_message', 'router:mixed:0.97']

No CCR markers

  reward tampering: FOUND
  sycophancy: FOUND
  ...
6/6 key concepts preserved in compressed output

注意该运行显示 "No CCR markers":examples/test_ccr.py 直接调用 SDK 的 compress() 函数,而该特定负载没有跨越触发 CCR 标记的大小阈值。完整的"压缩-缓存-检索"工具调用闭环(headroom_retrieve、主动展开)只在 headroom proxy 内部运行,而非独立 SDK 调用中。想验证代理侧的完整 CCR 行为,仓库 tests/ 下有一组针对性测试可参考:test_ccr_response_handler.pytest_ccr_context_tracker.pytest_ccr_tool_injection.pytest_ccr_marker_resolution.pytest_ccr_tool_always_on.py(覆盖工具粘性注入)。

实现索引

想继续深入 CCR 源码时,从以下入口出发:

登录后查看全文
热门项目推荐
相关项目推荐