利用headroom实现模型token用量压缩

背景

我的中转站(基于 new-api)和 Hermes Agent 已经部署很长一段时间了。最近才开始部署试用 headroom-proxy 做上下文压缩,效果还不错,记录一下。

说明:new-apione-api 的活跃 fork,one-api 项目已停止迭代,new-api 在 one-api 基础上持续维护更新。

headroom-proxy 是一个 Go 语言开发的 LLM 代理服务器,支持在模型调用前自动压缩上下文,减少 token 用量。它提供了多种压缩策略,包括语义压缩(kcompress)、前缀冻结、工具输出压缩等,可以在保持对话质量的同时显著降低 token 消耗。

部署架构

graph TD
    subgraph "client"
        A[Hermes Agent]
        B[OpenClaw]
        C[OpenCode]
    end

    subgraph "本地模型链路"
        D[headroom-llamaswap 8788]
        E[llama-swap]
    end

    subgraph "中转站链路"
        F[headroom 中转站 8787]
        G[中转站]
    end

    A -->|请求| D
    B -->|请求| D
    C -->|请求| D
    D -->|代理请求| E

    A -->|请求| F
    B -->|请求| F
    C -->|请求| F
    F -->|代理请求| G

Headroom Proxy 部署踩坑记录

CLI Help 信息压缩问题

headroom-proxy 默认压缩策略(功能全开)会遇到压缩破坏 CLI help 文本的问题,导致 CLI 工具调用受阻。

现象:当模型调用中包含 CLI 工具的 help 信息时,headroom-proxy 的语义压缩会破坏 help 文本的格式和语义,导致 CLI 工具无法正常解析参数说明。

解决方案:在 docker-compose.yaml 中配置排除 CLI help 信息的压缩,具体做法是在 compression.excluded_prefixes 中添加 CLI help 相关的文本前缀。

Kcompress CPU 指令集要求

Kcompress 语义压缩依赖 ONNX 模型,需要 AVX 512 指令集,我的群晖 NAS CPU 指令集不适配,无法在开启 kcompress 的情况下,把 headroom-proxy 部署在群晖 NAS。

分析:群晖 NAS 的 CPU(如 J4125、J5040 等)仅支持 AVX2,不支持 AVX512。Kcompress 的 ONNX 模型编译时使用了 AVX512 指令集,在群晖 NAS 上启动时会报 Illegal instruction 错误。

解决方案:群晖 NAS 上部署 headroom-proxy 时,关闭 kcompress 功能,仅使用前缀冻结和工具输出压缩策略。

Kcompress 计算资源要求

ONNX 推理对 CPU、内存有一定资源占用,也不适合在开启 kcompress 的情况下部署 headroom-proxy 在纯 IO 职责的低资源 VPS 上。

分析:Kcompress 的 ONNX 推理需要消耗额外的 CPU 计算资源和内存,在低资源 VPS 上会导致:

  • CPU 占用率升高,影响其他业务
  • 推理延迟增加,模型调用响应变慢
  • 内存占用增加,可能触发 OOM

解决方案:在低资源 VPS 上,关闭 kcompress 功能,仅使用前缀冻结策略。

AnyLLM Key Fallback 无效

我们尝试过配置 API Key,无鉴权调用 headroom-proxy,发现 AnyLLM backend 的鉴权 fallback 机制目前无效。

现象:配置 headroom-proxy 的 API Key 后,期望在不带 API Key 的模型调用请求中,headroom-proxy 自动添加 API Key。但实际测试发现,AnyLLM backend 的鉴权 fallback 机制无法正常工作。

解决方案:好在带鉴权信息的模型调用请求,API Key 透传是正常的。因此暂时选择弃用 headroom-proxy 的鉴权 fallback 机制,继续在模型调用请求中携带 API Key。

健康检查误打 Anthropic API

现象:一次重启后,docker compose ps 显示两个 headroom 容器都是 unhealthy,但访问 http://127.0.0.1:8787/livezhttp://127.0.0.1:8788/livez 都是正常的。

原因:headroom 的 /readyz 会触发 upstream 检查,而 upstream URL 默认来自 api_targets.anthropic。如果环境变量里没有显式设置 ANTHROPIC_TARGET_API_URL,它会去探测 https://api.anthropic.com。也就是说,即使我们实际只用 OpenAI-compatible backend(new-api 或 llama-swap),健康检查也会因为 Anthropic API 不可用而误判。

解决方案:不使用 Anthropic API 时,在 headroom 容器里加:

1
- HEADROOM_SKIP_UPSTREAM_CHECK=1

同时把 Docker healthcheck 从 /readyz 改成 /livez,避免健康检查被上游慢响应拖死:

1
2
3
4
5
6
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8787/livez"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s

这样 /livez 只检查 headroom 进程是否存活,/readyzHEADROOM_SKIP_UPSTREAM_CHECK=1 后也会把 upstream 视为 ready。

一次模型超时后连接池被打满

现象:某次模型请求卡住后,后续请求开始出现 ReadTimeoutPoolTimeout/v1/chat/completions 返回 502,/readyz 也可能持续 503。

原因:headroom 默认 upstream read timeout 较长,慢请求会长时间占用连接;如果同时开启多次重试,失败请求会叠加放大,最后把 httpx 连接池占满,新请求只能等连接释放,表现为 PoolTimeout

解决方案

  1. 减少重试放大:

    1
    - HEADROOM_RETRY_MAX_ATTEMPTS=1
  2. 给连接/连接池超时一个更明确的值:

    1
    - HEADROOM_CONNECT_TIMEOUT_SECONDS=120
  3. 跳过 Anthropic upstream 健康检查,避免健康检查本身参与制造假故障:

    1
    - HEADROOM_SKIP_UPSTREAM_CHECK=1
  4. 恢复时优先只重启 headroom 容器,不先动 llama-swap 或模型服务:

    1
    2
    cd ~/workspace/docker-compose-confs/headroom
    docker compose up -d
  5. 如果重启 headroom 后仍然卡住,再单独验证 llama-swap 上游:

    1
    curl --max-time 20 -sS http://192.168.31.125:9292/v1/models

    如果上游 llama-swap 自己也不响应,才考虑处理 llama-swap 服务或模型加载问题。

本次验证结果:按上述配置修改 compose 并重启后,两个 headroom 容器均变为 healthy8787 /readyz8788 /readyz 都返回 200;使用 8788 无鉴权实际调用 qwopus3.6:35b-a3b-q5-128k 返回 200,约 20.5 秒完成。

功能配置优化

根据社区讨论和实际踩坑经验,我们对 headroom-proxy 的功能配置做了以下优化。

主要配置调整

  1. 压缩策略配置:根据硬件条件选择压缩策略

    • 群晖 NAS:关闭 kcompress,使用前缀冻结 + 工具输出压缩
    • 低资源 VPS:关闭 kcompress,仅使用前缀冻结
    • 高性能 PC:全功能开启(kcompress + 前缀冻结 + 工具输出压缩)
  2. CLI Help 排除:添加 CLI help 信息的压缩排除规则

  3. 健康检查修正:不使用 Anthropic API 时,设置 HEADROOM_SKIP_UPSTREAM_CHECK=1,Docker healthcheck 改用 /livez

  4. 超时/重试收敛:设置 HEADROOM_RETRY_MAX_ATTEMPTS=1HEADROOM_CONNECT_TIMEOUT_SECONDS=120,避免一次慢请求放大成连接池打满

  5. 鉴权配置:放弃鉴权 fallback,模型调用请求中携带 API Key

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# docker-compose.yaml 关键配置节
services:
headroom:
environment:
- DISABLE_KOMPRESS=1 # 群晖/低资源VPS上关闭
- NO_CCR_INJECT_TOOL=1 # 禁止注入工具调用
- NO_CCR_MARKER=1 # 禁止注入压缩标记
- PROTECT_RECENT=5 # 保护最近5轮对话不被压缩
- COMPRESS_SYSTEM_MESSAGES=false # 系统消息不压缩
- HEADROOM_SKIP_UPSTREAM_CHECK=1 # 不用 Anthropic 时跳过 upstream 健康检查
- HEADROOM_RETRY_MAX_ATTEMPTS=1 # 避免慢请求重试放大
- HEADROOM_CONNECT_TIMEOUT_SECONDS=120 # 连接/连接池超时
- COMPRESSION_STABLE_AFTER_TURN # 开启结构化压缩
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8787/livez"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s

实测数据

指标 8787 端口(中转站 nex-agi/nex-n2-pro:free) 8788 端口(本地 llama-swap qwopus3.6:35b-a3b-q5-128k)
API 请求数 48 495
总输入 token 936,468 21,791,478
总输出 token 41,405 136,511
请求压缩次数 16 353
压缩节省 token 622,160 23,632,292
综合压缩率 39.92% 52.03%
平均压缩率 41.1% 38.2%
最佳压缩 44.4%(97,688 → 54,280 tokens) 88.1%(572,012 → 68,132 tokens)
前缀冻结次数 17 103
前缀缓存命中率 0.5% 98.3%
请求缓存命中率 42.9% 97.1%
平均延迟 31,973ms 15,242ms
最大延迟 217,235ms 282,213ms
平均 overhead 290.09ms 110.93ms

压缩策略分布:

  • 8787 端口:smart_crusher 74 次,text 6 次,log 6 次
  • 8788 端口:smart_crusher 337 次,text 65 次,log 8 次

最佳压缩示例:

  • 8787:97,688 → 54,280 tokens,节省 43,408 tokens(44.4%)
  • 8788:572,012 → 68,132 tokens,节省 503,880 tokens(88.1%)

数据解读

  1. 综合压缩率:8788 端口(本地 llama-swap)综合压缩率 52.03%,8787 端口(中转站)39.92%。本地 llama-swap 的压缩效果更优,可能因为本地模型上下文更长、压缩机会更多。

  2. 最佳压缩:8788 端口最佳压缩达到 88.1%(572,012 → 68,132 tokens),8787 端口最佳压缩 44.4%(97,688 → 54,280 tokens)。长上下文场景下压缩效果显著,8788 端口单次节省近 50.4 万 tokens。

  3. 缓存命中率:8787 端点请求缓存命中率 42.9%,前缀缓存命中率 0.5%;8788 端点请求缓存命中率 97.1%,前缀缓存命中率 98.3%。8788 的缓存命中表现更稳定,说明本地 llama-swap 路径更利于复用上下文。

  4. 压缩策略smart_crusher 是主力压缩策略,8788 端口占 337 次,8787 端口占 74 次。8788 端口还有 text 策略 65 次和少量 log 策略,说明本地 llama-swap 的上下文类型更丰富。

  5. 延迟:8788 端口平均 overhead 110.93ms,8787 端口 290.09ms。本地 llama-swap 的额外开销更小,符合预期。

  6. 压缩策略smart_crusher 是主力压缩策略,8788 端口占 95 次(76.6%),8787 端口占 18 次(85.7%)。8788 端口还有 text 策略(34 次)和少量 logkompress 策略,说明本地 llama-swap 的上下文类型更丰富。

  7. 延迟:8788 端口平均 overhead 89.96ms,8787 端口 192.15ms。本地 llama-swap 的额外开销更小,符合预期。

总结

headroom-proxy 是一个实用的本地 token 压缩代理,但在实际部署中需要根据硬件条件选择合适的压缩策略。主要踩坑点包括 CLI help 文本压缩破坏、kcompress 的 CPU 指令集和资源要求、鉴权 fallback 机制无效、健康检查误打 Anthropic API,以及一次模型超时后连接池被打满等。通过合理配置,可以在保证对话质量的同时有效降低 token 消耗。


利用headroom实现模型token用量压缩
https://bipedalbit.net/2026/06/18/利用headroom实现模型token用量压缩/
作者
Bipedal Bit
发布于
2026年6月18日
许可协议