Headroom Proxy 新手導引

本文將 Headroom 壓縮 proxy 設定成「常駐服務」,讓 OpenCodeClaude Code 的 LLM 流量自動流經 headroom,達到 token 節省。 整套流程包含:背景概念 → 前置檢查 → 建立常駐 proxy → 客戶端設定 → 驗證 → 疑難排解。


0. 背景概念(先搞懂再動手)

Headroom 是一個「Context 壓縮層」,放在你的 LLM 客戶端(OpenCode / Claude Code)與上游 API 之間:

客戶端 ──► headroom proxy ──► 上游 API (Anthropic / OpenAI / Bedrock)
            (壓縮、cache)
  • 它把 request 中的重複內容(log、RAG、工具 schema、程式碼)壓縮,減少送上游的 token → 省錢
  • /p/<project> 路徑**是「dashboard 專案歸屬」用的,由**客戶端 base URL 決定(例:.../p/opencode),proxy 本身不決定它。
  • 重要:proxy 有自己的**上游**。若上游是 https://api.anthropic.com(Anthropic),而客戶端用 Bedrock 登入,兩者會衝突(見 §5)。

省多少? - 「穩定小幅」(cache mode,coding profile)= 每輪省幾 %。 - 「省 50%+」需要「大量重複內容」場景 + token mode 高壓縮 profile(如 agent-90),代價是打爆 provider prefix-cache、變慢。


1. 前置檢查

1.1 確認 headroom 已安裝

headroom --version

本機安裝於 C:\Users\paul_fang\.local\bin\headroom.exe(透過 uv tools 安裝,版本 0.37.0)。 若要用 AST 程式碼壓縮(--code-aware),需額外裝 extra:

pip install "headroom-ai[code]"

1.2 確認 port 沒有被佔用

Get-NetTCPConnection -State Listen -LocalPort 8787,8789 -ErrorAction SilentlyContinue
# 若出現 OwningProcess,代表被舊 proxy 佔用(見 §5 疑難排解)

2. 建立常駐 proxy(排程任務)

headroom wrap <tool> 的 proxy 會綁在該 session 上,CLI 一結束 proxy 就死。 要讓 herdr 之類會自動重啟 agent 的工具穩定吃到 headroom,需把 proxy 獨立成常駐 daemon

2.1 用工作排程器(Task Scheduler)建兩個常駐任務

需要**管理員權限**(會彈 UAC)。建立一個 setup_headroom_tasks.ps1 並以系統管理員執行:

$exe = "C:\Users\paul_fang\.local\bin\headroom.exe"

$task8787 = @{
    TaskName  = "Headroom_Proxy_8787_opencode"
    Action    = (New-ScheduledTaskAction -Execute $exe -Argument "proxy --port 8787" -WorkingDirectory "C:\Users\paul_fang")
    Trigger   = (New-ScheduledTaskTrigger -AtLogOn)
    Settings  = (New-ScheduledTaskSettingsSet -StartWhenAvailable -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit (New-TimeSpan -Days 0))
    Principal = (New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Highest)
    Force     = $true
}

$task8789 = @{
    TaskName  = "Headroom_Proxy_8789_claude"
    Action    = (New-ScheduledTaskAction -Execute $exe -Argument "proxy --port 8789" -WorkingDirectory "C:\Users\paul_fang")
    Trigger   = (New-ScheduledTaskTrigger -AtLogOn)
    Settings  = (New-ScheduledTaskSettingsSet -StartWhenAvailable -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit (New-TimeSpan -Days 0))
    Principal = (New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Highest)
    Force     = $true
}

Register-ScheduledTask @task8787
Register-ScheduledTask @task8789

或直接在 PowerShell 用 schtasks(需管理員):

schtasks /Create /TN "\Headroom_Proxy_8787_opencode" /TR "\"C:\Users\paul_fang\.local\bin\headroom.exe\" proxy --port 8787" /SC ONLOGON /RL LIMITED /F
schtasks /Create /TN "\Headroom_Proxy_8789_claude" /TR "\"C:\Users\paul_fang\.local\bin\headroom.exe\" proxy --port 8789" /SC ONLOGON /RL LIMITED /F

⚠️ 注意:proxy 子命令沒有 --no-proxy flag--no-proxywrap 的選項,誤加會導致 exit code 2。正確常駐指令就是 headroom proxy --port <port>(在前景執行,由排程任務常駐)。

2.2 port 用途

Port 用途
8787 OpenCode
8789 Claude Code

2.3 啟動並確認任務

schtasks /Run /TN "\Headroom_Proxy_8787_opencode"
schtasks /Run /TN "\Headroom_Proxy_8789_claude"
schtasks /Query /TN "\Headroom_Proxy_8787_opencode" /FO LIST
# 期望 Status: Running

3. 客戶端設定

3.1 OpenCode → 8787

設定檔:C:\Users\paul_fang\.config\opencode\opencode.jsonc

provider 加入 headroom proxy(baseURL 指到 8787 的 /v1):

{
  "provider": {
    "headroom": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Headroom Proxy",
      "options": {
        "baseURL": "http://127.0.0.1:8787/v1"
      },
      "models": {
        "gpt-4o":  { "name": "GPT-4o",  "limit": { "context": 128000,   "output": 16384 } },
        "gpt-4.1": { "name": "GPT-4.1", "limit": { "context": 1048576,  "output": 32768 } }
      }
    }
  }
}

⚠️ 手動加 provider 的坑(為何 wrap opencode 有效、你手動無效)

wrap opencode 不只加 provider.headroom,它透過 OPENCODE_CONFIG_CONTENT env 注入三層: 1. 覆寫原生 anthropic / openai provider 的 baseURL → 原生模型流量直接指到 8787。 2. transport plugin:patch opencode 的 fetch/http,把**所有** provider 流量導到 proxy(含 Gemini/Copilot/中途新增的)。 3. 備援的 headroom provider(headroom/gpt-4o 等)。

只加 provider.headroom 而**不選 headroom/* 模型、不覆寫原生 provider**,原生流量根本不會碰 8787 —— 所以 Log 只有 GET 沒有 POST。

3.1b 推薦做法:official plugin + 常駐 proxy(herdr 友善)

若用 herdr 這類會重啟 agent 的工具,推薦用**官方 headroom-opencode plugin** 做 in-process 攔截,與常駐 proxy 搭配。

Step 1 — 裝套件(在 opencode 全域 config 目錄):

cd C:\Users\paul_fang\.config\opencode
npm i headroom-opencode

Step 2 — 建立 plugin 檔 C:\Users\paul_fang\.config\opencode\plugins\headroom.ts (opencode 會自動載入 ~/.config/opencode/plugins/ 下所有 plugin)

import { HeadroomPlugin } from "headroom-opencode";

export default async function plugin(input) {
  return HeadroomPlugin(input, {
    proxyUrl: process.env.HEADROOM_PROXY_URL ?? "http://127.0.0.1:8787",
  });
}

Step 3 — 設恆存 env(herdr 重啟也吃;plugin 有 fallback,即使沒設也預設 8787):

[Environment]::SetEnvironmentVariable("HEADROOM_PROXY_URL","http://127.0.0.1:8787","User")

Step 4 — 重開 opencode 生效後,8787 會收到 POST /v1/chat/completions

原理:HeadroomPlugintransport interception(in-process),在 opencode 內部攔截所有發出的 API 請求導向 proxy。不用選模型、不用覆寫 provider baseURL、全通吃。

3.2 Claude Code → 8789

Claude Code 透過 **ANTHROPIC_BASE_URL 環境變數**控制上游,不是 provider 物件。

方式 A — 全域 ~/.claude/settings.json:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://127.0.0.1:8789"
  }
}

方式 B — 專案 ./.claude/settings.json: 同樣結構,放專案根目錄。


4. 驗證

4.1 Health 端點

Invoke-RestMethod http://127.0.0.1:8787/health   # 期望 "status":"healthy"
Invoke-RestMethod http://127.0.0.1:8789/health   # 期望 "status":"healthy"
健康輸出重點: - config.savings_profile: coding(cache mode) - kompress.backend: onnx - cache: true - pid: 對應排程任務綁定的 process

4.2 確認 port 由正確 process 綁定

Get-NetTCPConnection -State Listen -LocalPort 8787,8789 | Select LocalPort, OwningProcess
# 8787 / 8789 需各自有 OwningProcess(排程任務起的 python: headroom.cli proxy)

4.3 確認排程任務狀態

schtasks /Query /TN "\Headroom_Proxy_8787_opencode" /FO LIST
# Status: Running

4.4 確認真的有流量走過(plugin / 覆寫是否生效)

起一個 headless opencode 觸發一次呼叫,再查 8787 的 stats:

$env:HEADROOM_PROXY_URL = "http://127.0.0.1:8787"
opencode run "Reply with exactly: ok"
# 等 1~2 秒後
$s = Invoke-RestMethod -Uri "http://127.0.0.1:8787/stats" -TimeoutSec 5
$s.proxy_inbound.by_method          # 應出現 "POST"
$s.proxy_inbound.by_path            # 應含 /v1/chat/completions
$s.summary.api_requests             # 應 > 0

判斷點:若 by_method 只有 GET(health/stats/dashboard)、沒有 POST,代表 opencode 沒把請求送到 8787(見 §5.3 / 5.5)。


5. 疑難排解

5.1 排程任務「上次結果 = 2」

指令錯誤:proxy 不認得 --no-proxy

Error: No such option '--no-proxy'. (Did you mean one of: '--http-proxy','--no-ccr',...?)
解: 改用 headroom proxy --port <port>,重新 Register-ScheduledTask(Force)。

5.2 排程任務「上次結果 = 3」/ 綁不到 port

port 被舊 proxy 佔用。舊 proxy 其實是 python 程序,不是 headroom.exe:

# 找佔用 port 的程序
Get-NetTCPConnection -State Listen -LocalPort 8787,8789 | Select LocalPort, OwningProcess
# 確認是不是 python -m headroom.cli proxy
Get-Process -Id <PID> | Select CommandLine
# 殺掉舊 proxy
Stop-Process -Id <PID> -Force

也一併把舊的 headroom.exe 程序清掉:Get-Process -Name headroom | Stop-Process -Force ⚠️ 但 headroom mcp serve 是 MCP server(不是 proxy),不要誤殺。

5.3 port 明明有 proxy,但客戶端沒吃到

  • 確認各端 base URL port 配對:
  • OpenCode → opencode.jsonc = ...:8787/v1
  • Claude Code → ANTHROPIC_BASE_URL = ...:8789
  • Claude Code 改 env 後需**重開 session** 才生效。

5.5 OpenCode 設了 /v1 但 8787 沒收到 POST(無 POST,只有 GET)

原因:只新增 provider.headroom 而不選 headroom/* 模型、不覆寫原生 provider,原生流量不會走 8787。

解法(擇一): - 推薦:改用 official plugin(§3.1b)做 in-process 攔截,全通吃。 - 或把原生 provider baseURL 也覆寫到 8787:

"provider": {
  "anthropic": { "options": { "baseURL": "http://127.0.0.1:8787/v1" } },
  "openai":    { "options": { "baseURL": "http://127.0.0.1:8787/v1" } },
  "headroom": { "npm": "@ai-sdk/openai-compatible", "options": {
      "baseURL": "http://127.0.0.1:8787/v1", "apiKey": "headroom-local" },
    "models": { "gpt-4o": {...}, "gpt-4.1": {...} } }
}
- 或**直接選模型** headroom/gpt-4o(baseURL 已指 8787)。

5.6 OpenCode 使用前提(plugin 路線)

plugin 已裝後,「自動走 8787」須 3 條件同在: 1. 常駐 proxy 8787 活著(Task Scheduler 登入自啟)。proxy 一死,plugin 導過去的請求會連不上而失敗——最大單點。 2. opencode 有載入全域 plugins 目錄(正常皆然;--pure 會跳過)。 3. opencode 是新啟動的(plugin 安裝前就開的 session 不生效)。

5.4 Claude Code + Bedrock 衝突(關鍵!)

proxy 的上游是 https://api.anthropic.com。若你的 Claude Code 用 Bedrock 登入(CLAUDE_CODE_USE_BEDROCK=1 + Bedrock ARN model),與 ANTHROPIC_BASE_URL 指向 headroom 衝突(兩者都控制 upstream)。

解套(擇一): - 改用 Anthropic API key 登入(移除 Bedrock env),再指到 8789。 - 或不用 headroom 管 Claude(Bedrock 流量直連,不走 8789)。


6. 額外省錢調整(optional)

在常駐 proxy 的環境變數設定(加入 Task 的 proxy 啟動前 env),herdr 重啟不受影響:

變數 作用
HEADROOM_OUTPUT_SHAPER=1 補上**輸出端**壓縮(預設只壓輸入)
HEADROOM_TOOL_SEARCH=1 工具 schema 壓縮
HEADROOM_TARGET_RATIO=0.15 更用力壓(保持 cache mode,不打爆 prefix-cache)
HEADROOM_SAVINGS_PROFILE=agent-90 換成高壓縮 token mode(省多但變慢、打爆 cache)

7. 檢查清單

  • headroom --version 正常
  • 兩個排程任務存在且 Status: Running(\Headroom_Proxy_8787_opencode\Headroom_Proxy_8789_claude)
  • http://127.0.0.1:8787/health...:8789/healthhealthy
  • port 8787/8789 由排程任務的 python process 綁定
  • headroom-opencode 已裝於 ~/.config/opencodeplugins/headroom.ts 存在
  • HEADROOM_PROXY_URL USER env = http://127.0.0.1:8787
  • 實際測試:opencode run "Reply with exactly: ok" 後,8787 /statsby_method 出現 POST、by_path/v1/chat/completionsapi_requests > 0
  • Claude Code ANTHROPIC_BASE_URL = http://127.0.0.1:8789(且無 Bedrock 衝突)
  • 重開全新 opencode(含 herdr 重啟)後流量照常進 8787