Headroom Proxy 新手導引
本文將 Headroom 壓縮 proxy 設定成「常駐服務」,讓 OpenCode 與 Claude 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-proxyflag。--no-proxy是wrap的選項,誤加會導致 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_CONTENTenv 注入三層: 1. 覆寫原生anthropic/openaiprovider 的 baseURL → 原生模型流量直接指到 8787。 2. transport plugin:patch opencode 的fetch/http,把**所有** provider 流量導到 proxy(含 Gemini/Copilot/中途新增的)。 3. 備援的headroomprovider(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。
原理:
HeadroomPlugin做 transport 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/health皆healthy - port 8787/8789 由排程任務的 python process 綁定
-
headroom-opencode已裝於~/.config/opencode且plugins/headroom.ts存在 -
HEADROOM_PROXY_URLUSER env =http://127.0.0.1:8787 - 實際測試:
opencode run "Reply with exactly: ok"後,8787/stats的by_method出現 POST、by_path含/v1/chat/completions、api_requests > 0 - Claude Code
ANTHROPIC_BASE_URL=http://127.0.0.1:8789(且無 Bedrock 衝突) - 重開全新 opencode(含 herdr 重啟)後流量照常進 8787