Headroom CCR(Compress-Cache-Retrieve)架构解析:让 LLM 上下文压缩可逆的四阶段机制
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/ 下的四个模块组成,与 模块入口 的文档注释一一对应:
- Tool Injection(tool_injection.py):代理在发生压缩时向请求注入
headroom_retrieve工具 - Response Handler(response_handler.py):拦截响应,自动处理 CCR 工具调用
- Context Tracker(context_tracker.py):跨轮次追踪压缩内容,支持主动展开
- Batch Processing(batch_processor.py、batch_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 压缩工具输出时:
- 原始内容存入 LRU 缓存
- 生成一个用于检索的哈希键
- 在压缩输出中插入标记:
[1000 items compressed to 20. Retrieve more: hash=abc123]
源码层面,标记格式远比文档示例丰富。tool_injection.py 中 CCRToolInjector 维护了一组标记识别模式,覆盖了不同压缩器的输出形态:
# - 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] 的压缩标记中提供"。
注入时机有两层值得注意的机制:
- 哈希归属校验(
verify_ownership):括号标记的"形状"并非 Headroom 独有——其他上下文工具也会产生视觉相同的标记。tool_injection.py 中的verify_ownership()会在扫描标记后、注入工具前,用压缩存储的exists()检查把"本代理从未存过的哈希"剔除,避免模型调用headroom_retrieve后必然落空、重做已有工作(issue #2836 修复)。 - 会话级粘性注入(sticky-on):一旦某会话发生过 CCR,
headroom_retrieve必须留在后续每次请求的tools数组中(inject_tool_definition 的session_has_done_ccr参数)。否则工具列表的字节在会话中途时开时关,会打爆提示词缓存的前缀稳定性。
除工具注入外,inject_into_system_message 还会向 system 消息追加一段"Compressed Context Available"说明,列出当前可用的哈希(最多展示 5 个),告知模型如何调用检索工具;若请求中没有 system 消息则新建一条。
Phase 3:响应处理器
当 LLM 调用 headroom_retrieve 时:
- Response Handler 拦截该工具调用
- 从本地缓存取回数据(约 1ms)
- 把结果作为工具结果加入对话
- 自动继续发起下一轮 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 负责:
- 记住前几轮压缩过什么
- 分析新查询与压缩内容的相关性
- 在 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_key(context_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 相关的两个邻近开关也值得了解:
--lossless(HEADROOM_LOSSLESS=1):无 CCR 的无损模式,用格式原生无损压缩(及无标记的 SmartCrusher)压缩工具输出,不产生任何检索标记,因此连 MCP 检索工具都不需要;--ccr-inline-resolve(HEADROOM_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.py、test_ccr_context_tracker.py、test_ccr_tool_injection.py、test_ccr_marker_resolution.py 和 test_ccr_tool_always_on.py(覆盖工具粘性注入)。
实现索引
想继续深入 CCR 源码时,从以下入口出发:
- 模块总览与组件导出:headroom/ccr/init.py
- 标记识别与工具注入:headroom/ccr/tool_injection.py
- 响应拦截与续接调用:headroom/ccr/response_handler.py
- 多轮追踪与主动展开:headroom/ccr/context_tracker.py
- Batch API 支持:headroom/ccr/batch_processor.py、headroom/ccr/batch_store.py
- MCP 检索服务端:headroom/ccr/mcp_server.py、headroom/ccr/mcp_http.py
- 完整架构背景:wiki/ARCHITECTURE.md 中的 "CCR Architecture: Compress-Cache-Retrieve" 一节
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00