首页
/ Headroom 再对齐工程实录:从"丢消息压缩"到"只压活跃区"的缓存安全重构(REALIGNMENT 全解)

Headroom 再对齐工程实录:从"丢消息压缩"到"只压活跃区"的缓存安全重构(REALIGNMENT 全解)

2026-09-05 21:48:56作者:何举烈Damon

本文以 Headroom 仓库中的 REALIGNMENT/ 文档集为核心(入口为 INDEX.md),完整解读这次由 10 个并行深度审计子代理驱动的重构工程:为什么原有的"压缩 = 从对话历史中选择丢弃"心智模型是错的、9 个阶段 40 个 PR 如何把整个代理栈迁到 Rust 并以"字节忠实 + 只压活跃区(live zone)"重建压缩管线,以及 10 条贯穿所有 PR 的缓存安全不变量。读完后你能掌握一套可复用的方法论:如何在 LLM 代理中做到"压缩不减缓存命中率",并用 SHA-256 字节等价、token 单调不增等属性测试把这套约束固化进 CI。

一、起点:一个错误的心智模型

Headroom 的定位是"在工具输出、日志、文件和 RAG 块到达 LLM 之前压缩它们",以库、代理、MCP 服务器三种形态提供。再对齐(Realignment)工程源于 2026-05-01 对一份 10 代理深度审计的成文,目标是(引自 INDEX.md):

Move the entire codebase to Rust, preserve prefix cache, retain compression value, integrate RTK end-to-end, and gate compression policy by auth mode (PAYG / OAuth / subscription).

问题诊断集中在 00-overview.md。旧架构的旗舰组件 IntelligentContextManager(ICM)会把整个 messages 数组分词、给每条消息打重要性分、然后删除旧消息直到预算满足——并且已被接入 Rust 代理的 /v1/messages 路径,frozen_message_count: 0 是硬编码的。这意味着每一次压缩都从索引 0 开始删消息,把 Anthropic 前缀缓存打得粉碎

审计的量化发现(00-overview.md):

  • 5 个顶级"缓存杀手"缺陷,全部源于同一个错误模型;
  • 约 10K LOC 的架构性过度建设(ICM + scoring + relevance + rolling-window + progressive-summarizer + tool-crusher + cache-aligner 重写路径 + crates/headroom-core/src/{context,scoring,relevance}/ 大部分);
  • 线格式缺口:流式 SSE 解析器缺 thinking_deltasignature_deltacitations_delta,存在 UTF-8 跨包拆分损坏、回退路径按单 \n 切分 SSE 的 bug;
  • Bedrock/Vertex 对等是假的——有损的 LiteLLM Anthropic→OpenAI 转换会丢掉 thinkingredacted_thinkingdocumentsearch_resultimageserver_tool_usemcp_tool_use 块;
  • 没有任何工具定义归一化没有认证模式感知——PAYG、OAuth、订阅 CLI 全部拿到同一策略,同样会泄露指纹的重序列化;
  • X-Headroom-* 请求头泄露到上游,外加 anthropic-beta 被篡改、OpenAI-Beta 自动注入——属于"指纹级"的订阅吊销风险;
  • CCR 标记在 Rust 路径上计算了却从未注入出向请求体;ccr_retrieve 工具逐请求开/关,每次状态变化都 bust 一次 tools 数组。

正确的模型则是完全相反的一句话:"passthrough is sacred;只压缩 live zone——类型感知、哈希键控、位置保持、带旁路元数据。" 缓存热区(system prompt、tools、旧轮次、reasoning/thinking/redacted/compaction 项)永远不碰。

二、完整缺陷清单:P0 到 P6 的 72 项

01-bug-list.md 是这份文档集中信息密度最高的文件:按 P0(cache-killer 铁证)→ P6(测试基建)分级,每条带 file:line 证据、指南章节引用、修复方式和 ROI 估计。汇总如下:

优先级 数量 归属阶段
P0(cache-killer) 7 Phase A
P1(线格式/流式损坏) 10 Phase A + Phase C
P2(架构过度建设) 10 Phase B
P3(缺 Phase 3 缓存稳定化基建) 9 Phase E
P4(OpenAI 长尾 + Bedrock/Vertex) 12 Phase C + Phase D
P5(认证模式 + 可观测性 + 指纹) 14 Phase F + Phase G
P6(测试基建与对等) 10 Phase I(并行)
合计 72

几个 P0 级铁证(每条在源码中都有对应证据,摘选最有代表性的四条):

  1. P0-2:所有 Python 转发器用 httpx ... json=body 重序列化。httpx 默认编码器是 json.dumps(body, separators=(", ", ": "), ensure_ascii=True)——入站字节是 ,/: 加原始 UTF-8,出站却变成 , /: \uXXXX 转义,上游收到的字节永远不等于客户端发的字节。
  2. P0-3:Rust 代理忽略客户的 cache_control 标记frozen_message_count: 0 硬编码,配注释 TODO: detect provider prefix-cached messages...,叠加 ICM 后每次压缩都从索引 0 删。
  3. P0-5:数字精度经 serde_json::Value 往返丢失——Value::Numberi64|u64|f641.0 会变成 1,大于 2^53 的整数丢精度。
  4. P0-6:memory 工具注入逐请求开关tools 列表大小在请求之间翻转,同时 anthropic-beta 被加塞 context-management-2025-06-27——典型的会话中途缓存破坏。

P1 级则覆盖了流式 SSE 的字节级问题:errors="ignore" 解码会静默丢弃跨 TCP 读拆分的 emoji/CJK 字节;解析器只 switch 了 text_deltainput_json_delta;LiteLLM 桥在上游缺 tc.id 时会伪造 toolu_<uuid>,导致下一轮 tool_result 引用假 ID、工具配对断裂。

三、九个阶段、40 个 PR:工程化路线图

再对齐被拆成 9 个阶段、40 个 PR,串行约 13 周、2-3 人并行约 8 周(INDEX.md "Phase totals" 表):

阶段 PR 数 LOC 变化(估计) 日历(串行) 内容
A — Lockdown 8 -200 / +400 1 周 止血:/v1/messages 改纯透传、停改 system prompt、Python 转发器改 content=raw_bytes、Rust 端尊重 cache_control、剥 x-headroom-*anthropic-beta 会话粘滞、SHA-256 字节等价往返测试
B — Live-zone engine 7 -10,000 / +1,500 2 周 删除 ICM 全家桶(~10K LOC);建 Rust live-zone 块分发器;CCR 加固(持久后端 + 常驻 ccr_retrieve 工具注册)
C — Rust proxy paths 5 -2,000 / +5,000 3 周 字节级 SSE 解析状态机;/v1/chat/completions/v1/responses(HTTP 与流式);按 item 类型的透传保持(V4A patch、local_shell_call.action.command argv、Codex phase 字段、MCP 项、compaction)
D — Bedrock/Vertex native 4 -800 / +2,500 2 周 删除 LiteLLM 有损转换器;建原生 /model/.../invoke(AWS SigV4)与 /v1beta1/projects/.../streamRawPredict(GCP ADC)路由
E — Cache stabilization 6 -100 / +900 1 周 工具数组确定性排序、JSON Schema 键递归排序、自动放置最多 4 个 cache_control 断点(Anthropic)、自动注入 prompt_cache_key(OpenAI)、易变内容检测(只告警不改写)、cache-bust 漂移遥测
F — Auth-mode policy 4 -50 / +600 1 周 classify_auth_mode(headers) → `payg
G — RTK + observability 3 -50 / +400 1 周 扩展 wrap CLI(cline、continue、goose、openhands);接通死字段 tokens_saved_rtk;每次调用的 RTK Prometheus 指标
H — Python retirement 3 -15,000 / +200 2 周 删除 headroom/proxy/server.py、全部 handlers、responses_converter.pymemory_handler.pybatch.pysemantic_cache.pyheadroom/transforms/* Python 部分;保留 CLI 包装器、RTK 安装器、evals、learn、memory 写器、tokenizer、TOIN
I — Test infra 并行 +2,000 持续 SHA-256 往返测试;SSE 边角用例 fixture(UTF-8 拆分、ping、全 delta 类型、[DONE]、流中断错);属性测试(SSE 解析不 panic、压缩 token 单调不增);缓存命中率持续指标;make test-parity 升级为每 PR 闸门
合计 40 ~-28,000 / +13,500 串行 ~13 周 / 并行 ~8 周

每个阶段的 PR 在 03-phase-A-lockdown.md11-phase-I-test-infra.md 中按 PR 粒度展开,每个 PR 含分支名、worktree 路径、风险级别、LOC、精确到文件的改动清单、新增测试、验收标准、被阻塞/阻塞关系、回滚方式。以 03-phase-A-lockdown.md 的 8 个 PR 为例:

  • PR-A1realign-A1-icm-passthrough,-180/+30,低风险):停掉 Rust 代理对 /v1/messages 的 ICM 调用,该端点变纯字节忠实透传;删 crates/headroom-proxy/src/compression/icm.rscompress_anthropic_request 保留签名为 no-op 桩,作为 Phase B 的唯一重写目标;新增 SHA-256 往返测试 compression_on_message_passes_body_unchanged_sha256
  • PR-A2:删除 _inject_system_context 路径,memory 上下文只走 _append_context_to_latest_non_frozen_user_turn(追加到最新用户消息尾部);删 cache_aligner 重写路径、保留检测器 + 客户告警。
  • PR-A3(高风险,最高单项收益):所有 Python 转发器从 httpx ... json=body 切到 httpx ... content=raw_bytes;未改动则原样转发 await request.body() 字节,改动过则用 separators=(",", ":") + ensure_ascii=False 重新序列化一次。
  • PR-A4:Rust 端遍历 system/tools/messages 中的客户 cache_control 标记计算 frozen_message_countCargo.tomlserde_jsonarbitrary_precision + raw_value feature,未修改的 messages[*] 以精确字节副本转发。
  • PR-A5_strip_internal_headers 剥除 x-headroom-*(大小写不敏感前缀)后再上游转发。
  • PR-A6/A7merge_anthropic_beta 确定性合并 + betas_seen 会话粘滞;memory 工具注入会话粘滞(注入过就永远注入,同字节定义)。
  • PR-A8:Python 线格式热修集合(SSE 字节缓冲、全 delta 类型、Codex phase 保留、上游 request-id 捕获、413 状态码修正、录制生产 fixture 的 SHA-256 往返测试)。

12-decisions-needed.md 记录了 15 个需要人工拍板的开放问题及推荐结论,例如:ICM 删除范围取 Tier 1+2(约 10K LOC,MessageScorer Rust 移植 PR #338/#343 认定为沉没成本直接删);Phase H 期间用 HEADROOM_PROXY_BACKEND={python|rust} 环境变量做灰度切换、Rust 确认 ≥99.9% 字节等价后默认 rust,Python 保留 30 天作为显式回滚目标;容器镜像策略从 ~500MB(Python + LiteLLM + ONNX)降到 ~50MB 的单一 Rust 二进制(FROM scratch / distroless)。

四、十条贯穿不变量:任何 PR 都不得违反

这是整个工程的宪法性条款(INDEX.md "Cross-cutting invariants",02-architecture.md §2.2 将其细化为 I1-I10 并给出实现方式与测试闸门):

  1. 代理不打算修改的字节必须**字节等价(SHA-256)**到达上游;
  2. 缓存热区——system、tools、旧轮次、reasoning/thinking/redacted/compaction 项——永不被修改;
  3. 压缩是 append-only:只有 live zone(最新用户消息、最新 tool/function/shell/patch 输出)可被重写;
  4. 压缩是确定性的:同输入字节 → 同输出字节;
  5. 工具定义只归一化(排序),永不压缩;
  6. signatureencrypted_contentredacted_thinking.datacompaction.encrypted_content 只透传;
  7. TOIN 从不改变请求时决策,只观察、在两次部署之间发布建议;
  8. 会话只要做过 CCR,CCR 标记和 ccr_retrieve 工具就每次请求都在——从不切换;
  9. Authorization 头字节忠实转发,永不未脱敏地记录或持久化;
  10. 认证模式(PAYG / OAuth / subscription)门控压缩策略;订阅模式运行在隐身态(无 X-Headroom-* 上游、无 beta 漂移、不改 UA、不剥 accept-encoding)。

目标架构(02-architecture.md §2.1)给出的请求生命周期是:classify_auth_mode → 剥 x-headroom-*RawValue 字节缓冲 → 尊重 cache_controlfrozen_message_countlive_zone_compress(识别 live-zone 块、逐块内容类型检测、分发给类型感知压缩器、token 校验失败即回退、CCR 哈希键控存库打标记、块内原位替换)→ tool_def_normalizecache_control_auto_place(Anthropic 最多 4 个 ephemeral 断点)→ prompt_cache_key_inject(OpenAI,仅客户未设时)→ 以原始字节转发 → 字节级 SSE 状态机处理响应 → usage 遥测。

§2.6 还明确列出了这套架构不做的事:永不丢历史消息、永不改 system/tools/旧轮次、TOIN 不参与请求时决策、代理不 shell out 到 RTK、不做 Anthropic↔OpenAI 形状互译(每个 provider 自己的原生 handler)、不改 User-Agent、不压图片和 base64 音频、不改 tool_use.input 键序或 function_call.arguments 字符串内容等。这份"负面清单"和正面清单同等重要,它把未来贡献者的自由度约束在了缓存安全边界内。

五、源码验证:计划已落地为当前代码

文档是计划,仓库是事实。用当前代码库交叉核对,再对齐的核心产物已存在于 Rust 侧:

1. cache_control 标记 walker 已实现。 crates/headroom-core/src/cache_control.rs 中的 compute_frozen_count(parsed: &Value) -> usize 正是 PR-A4 规划的实现:遍历 messages[i].content[*].cache_control,把冻结下限抬到 i + 1(下限独占:messages[i] 本身属于缓存前缀所以冻结);system/tools 中的标记不抬高消息索引下限(它们无条件属于热区,见架构文档不变量 I2);模块头注释还说明了按构建约束"用解析器不用正则"、以及 1h 标记必须前置于 5m 的 TTL 顺序规则(违反只告警不拒绝)。是否启用由 Config::cache_control_auto_frozen(CLI --cache-control-auto-frozen / 环境变量 HEADROOM_PROXY_CACHE_CONTROL_AUTO_FROZEN)门控。

2. live-zone 分发器已就位。 crates/headroom-core/src/transforms/live_zone.rs 的模块文档直接对应 Phase B 的构建史:PR-B2 交付识别 live-zone 块的分发器骨架,PR-B3 接入按内容类型的压缩器(JsonArray → SmartCrusher;BuildOutput → LogCompressor;SearchResults → SearchCompressor;GitDiff → DiffCompressor),PR-B4 加上 token 校验闸门(compressed.tokens >= original.tokens 即回退)与按内容类型的字节阈值(code>2KB、JSON>1KB、logs>500B、plain text>5KB)。它明确声明 live zone 的边界:下限是 frozen_message_count,上限是最新用户消息——最新 assistant 消息同样属于热区,因为它是对话续接点,永不触碰。

3. 认证模式分类器已实现。 crates/headroom-core/src/auth_mode.rs 是 Phase F PR-F1 的产物:一个纯函数把入站 HeaderMap 映射为 Payg | OAuth | Subscription 三类——PAYG 按 token 付费、激进压缩省钱的逻辑全部打开;OAuth 的 per-token 成本对调用方不透明,缓存安全优先,因为 OAuth scope 钉在 (account, model, session) 上、beta 头漂移会使其失效;Subscription 类调用方是 UX 绑定的 CLI/IDE(Claude Code、Cursor、Copilot 等),必须"看起来像上游 agent":保留 User-Agent、永不注入 X-Headroom-*、永不剥 accept-encoding。文档强调该分类器是纯函数、无 I/O、单次调用 <10μs、对畸形头永不 panic(非 UTF-8 值回落到安全的 Payg 默认并打 tracing::warn!)。

4. serde_json feature 已按 P0-5 修复。 工作区 Cargo.toml 第 50 行:serde_json = { version = "1", features = ["preserve_order", "arbitrary_precision", "raw_value"] }——正是 PR-A4 要求的三个 feature 集,注释说明 arbitrary_precision 用于保留源文件中的字面数字 token。

5. 退役清单与仓库现状吻合。 计划中 Phase B/H 要删的组件在当前仓库中已不在位:headroom/transforms/ 目录里没有 intelligent_context.pyrolling_window.pyprogressive_summarizer.pytool_crusher.pyscoring.pycrates/headroom-core/src/ 下没有 context/scoring/ 目录。而被"Preserved primitives" 保留的组件都在:crates/headroom-core/src/transforms/ 下可看到 smart_crusher/code_compressor.rslog_compressor.rssearch_compressor.rsdiff_compressor.rssafety.rstag_protector.rskompress.rs,以及 tokenizer/signals/ccr/compression_policy.rsrollout.rs。从源码结构看,02-architecture.md §2.3 规划的模块布局(transforms/live_zone.rs 为 NEW、safety.rscontext/ 移入 transforms/pipeline/ 收缩为 live-zone 编排器)与当前目录结构基本一致,说明文档与代码是同一时间轴上的"计划—实现"两端。

六、保留什么、退役什么:两条清单

INDEX.md 的两条清单是理解 Headroom 当前技术债与资产分布的最快入口。

保留原语(按用户决策)

  • TOIN(Tool Output Intelligence Network)——重构为严格 observation-only;按租户聚合键。目标形态见 02-architecture.md §2.5:Telemetry trait 只有 record_compression(...),"No request-time hint API. Period.";建议经 cargo run -p headroom-toin-publish -- --auth-mode payg --model claude-3-7-sonnet 在部署间隙发布为 recommendations.toml,压缩器只在启动时读取;
  • CCR(Compress-Cache-Retrieve)——CcrStore trait + SqliteCcrStore(主后端)/RedisCcrStore(可选多 worker);ccr_retrieve 对做过 CCR 的会话每次请求都注册;标记格式 <<ccr:HASH>> 追加到压缩块内容末尾,内容寻址、确定性、可安全重放;
  • Kompress-base——纯文本压缩器,只在 live-zone 用户消息文本 >5KB 时作为最后手段使用;Rust 移植走 ort crate(ONNX);
  • ContentRouter——约 2150 LOC,被审计认定为"架构上正确的部分"(文档特意更正了早期项目记忆中"53K 行"的错误——差了 25 倍);
  • 类型感知压缩器——SmartCrusher(Rust 25 文件)、Code、Log、Search、Diff;
  • signals/ Rust trait 模块、tokenizer/safety.rs(工具对原子性逻辑,Phase B 后移至 transforms/safety.rs)。

退役(~25K LOC):ICM(Python intelligent_context.py + Rust context/manager.rs)、RollingWindowProgressiveSummarizerscoring.pytool_crusher.pycrates/headroom-core/src/{scoring,relevance}/context/ 大部分、crates/headroom-proxy/src/compression/icm.rscache_aligner.py 重写路径(保留检测器 + 告警)、Phase H 达标后删除的 Python 代理全家(server.pyhandlers/anthropic.pyhandlers/openai.pyhandlers/streaming.pyhandlers/gemini.pyresponses_converter.pymemory_handler.pymemory_tool_adapter.pysemantic_cache.pybatch.py)、LiteLLM Bedrock/Vertex 转换器(被 Phase D 原生信封取代)、MessageScorer Rust 移植(PR #338/#343,判定为浪费的工作,Phase B 删除)。

01-bug-list.md P2 段给出了每条的 file:line 证据,例如 ToolCrusher 在 headroom/transforms/tool_crusher.py:106frozen_message_count 检查地遍历所有工具消息;TOIN 在 headroom/telemetry/toin.py:853-927 于调用中变更模式状态并返回偏向同一输入字节决策的 hint;CCR 标记在 crates/headroom-core/src/context/manager.rs:172-185 只被记录而从未写回请求体。

七、工程协作约定:分支、worktree 与 CI 闸门

INDEX.md "Conventions" 一节定义了所有再对齐 PR 的协作契约,值得任何做大型多 PR 重构的团队参考:

  • 分支命名realign-<phase-letter><pr-num>-<slug>,例如 realign-A1-icm-passthrough
  • worktree 隔离:每个 PR 一个独立 git worktree add 检出具(~/claude-projects/headroom-worktrees/...),避免并行 PR 相互污染;
  • 提交前缀:Rust 迁移阶段的提交统一用 fix: 而非 feat:(避免 semantic-release 版本号膨胀);
  • 不加 Co-Authored-By: Claude trailer
  • 推送前闸门make ci-precheck,不许裸推。当前仓库的 Makefileci-precheck 聚合了 ci-precheck-rust(cargo fmt --check + clippy + test)、ci-precheck-pythonci-precheck-commitlint 三道闸,且 test-parity 作为独立 target 存在——对应 Phase I 计划中把对等测试提升为每 PR 闸门(Diff 失败构建、Skipped 允许)的路线。

八、如何继续深入这份文档集

INDEX.md 指定的阅读顺序:

  1. REALIGNMENT/00-overview.md——执行摘要、错误心智模型、五大错误假设("TOIN 可以影响每请求压缩决策"、"CCR 可以按需改缓存热区"、"总结历史轮次是一种策略"、"ToolCrusher 无冻结检查地操作所有历史工具消息"等);
  2. REALIGNMENT/01-bug-list.md——72 条带 file:line 的完整缺陷清单;
  3. REALIGNMENT/02-architecture.md——目标架构:请求生命周期、I1-I10 不变量及各自实现方式与测试闸门(如 I5 的属性测试 tokens(output) ≤ tokens(input))、压缩器模块布局、认证模式策略矩阵(PAYG 允许激进压缩与自动 cache_control 放置,OAuth/Subscription 禁止自动断点注入并各自收紧指纹面)、保留原语的目标代码形态;
  4. 九个阶段文档 0311,每个 PR 都是可执行规格(分支/文件/测试/验收/回滚俱全);
  5. REALIGNMENT/12-decisions-needed.md——15 个待决策项及签核模板。

源码侧对应的验证入口:crates/headroom-core/src/cache_control.rs(冻结下限计算)、crates/headroom-core/src/transforms/live_zone.rs(live-zone 分发器)、crates/headroom-core/src/auth_mode.rs(认证模式分类)、crates/headroom-core/src/transforms/(类型感知压缩器家族)、Cargo.toml(serde_json 精确性 feature);测试侧可关注 tests/test_proxy_byte_faithful_forwarding.pytests/test_cache_aligner_detector_only.pytests/test_anthropic_beta_session_sticky.pytests/test_memory_tool_session_sticky.py 等与 Phase A 验收项一一对应的回归用例。

这份文档集的价值不止于一次重构的档案:它示范了如何用"不变量 + 属性测试 + 字节等价验收"把 LLM 代理的缓存安全从口号变成可执行契约——先锁死透传(Phase A),再在冻结边界内重建压缩(Phase B),最后用认证模式策略矩阵(Phase F)把"省 token"与"不被上游识别为异常客户端"解耦。对任何在代理层做 LLM 上下文优化的工程,这套"passthrough is sacred"的方法论可以直接迁移复用。

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