手摸手给 Claude Code 换芯:GLM + Headroom + cc-switch 省钱接入指南
摘要:把 Claude Code 的 API 调用链路换成国产智谱 GLM,同时用 Headroom 压缩上下文进一步降低 token 消耗。本文记录完整的搭建过程与踩坑经验。
为什么要这么折腾
Claude Code 用起来确实爽,但 Claude API 的价格摆在那里------尤其是长上下文对话,token 跑得飞快。国内智谱 GLM 提供了 Anthropic 兼容接口,可以完全替代 Claude 的模型层,价格低了一大截。(当然你想继续用 Claude 也可以,这个方案同样兼容。)
但光替换模型还不够。Claude Code 默认会把完整对话历史每次都原封不动发上去,如果能在中间加一层压缩代理,把冗余上下文剪掉,token 消耗还能再省一笔。
所以最终的链路设计是这样的:
arduino
Claude Code → Headroom (:8787,上下文压缩)
→ cc-switch (:15721,注入 GLM key)
→ https://open.bigmodel.cn/api/anthropic (glm-5.2)
三个组件各司其职:
- Headroom:HTTP 代理,负责压缩发往上游的上下文内容,省 token。
- cc-switch :GUI 应用,本地代理
:15721,保管真实的 GLM API key,并把 Claude 的各模型名映射成glm-5.2。 - GLM:智谱开放平台提供的 Anthropic 兼容接口。
前置条件
- macOS(Apple Silicon / Intel 均可)
- 已安装 cc-switch(
/Applications/CC Switch.app)并配置好一个 GLM provider(本文使用 Anthropic 兼容模式) - 已有智谱 API key(在 open.bigmodel.cn 获取)
- 系统自带 Python 3.9 不够用,Headroom 需要 Python ≥ 3.10,所以需要用
uv单独建一个 Python 3.12 的虚拟环境
第一步:安装 uv
Headroom 通过 uv 安装。如果还没装 uv:
bash
curl -LsSf https://astral.sh/uv/install.sh | sh
安装后 uv 在 ~/.local/bin/uv,确认一下:
bash
~/.local/bin/uv --version
如果提示找不到命令,把 ~/.local/bin 加进 PATH,写入 ~/.zshrc:
bash
export PATH="$HOME/.local/bin:$PATH"
第二步:用 uv 创建 Python 3.12 环境并安装 Headroom
系统自带的 Python 3.9 太旧了,用 uv 自带的 Python 管理功能创建一个新环境:
bash
uv venv ~/.headroom-venv --python 3.12
source ~/.headroom-venv/bin/activate
uv pip install headroom
验证安装:
bash
~/.headroom-venv/bin/headroom --version
# 期望输出类似:headroom, version 0.28.0
第三步:配置 cc-switch
打开 CC Switch.app,添加一个 provider,类型选 Anthropic 兼容:
- Base URL :
https://open.bigmodel.cn/api/anthropic - API Key:你的智谱 key
- 模型映射 :把 Opus / Sonnet / Haiku / Fable 全部映射到
glm-5.2
然后开启本地代理 (enableLocalProxy: true),cc-switch 会在 127.0.0.1:15721 启动一个代理。点应用后,cc-switch 会写入 ~/.claude/settings.json,内容大致长这样:
json
{
"env": {
"ANTHROPIC_AUTH_TOKEN": "PROXY_MANAGED",
"ANTHROPIC_BASE_URL": "http://127.0.0.1:15721",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-8",
"ANTHROPIC_DEFAULT_OPUS_MODEL_NAME": "glm-5.2",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6",
"ANTHROPIC_DEFAULT_SONNET_MODEL_NAME": "glm-5.2",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5",
"ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME": "glm-5.2",
"ANTHROPIC_DEFAULT_FABLE_MODEL": "glm-5.2",
"ANTHROPIC_DEFAULT_FABLE_MODEL_NAME": "glm-5.2"
}
}
注意 ANTHROPIC_AUTH_TOKEN: PROXY_MANAGED 是 cc-switch 的占位符------真实的 key 存在 cc-switch 内部的 SQLite 数据库里(~/.cc-switch/cc-switch.db),不会以明文出现在 settings.json 中。
此时链路已经是 Claude Code → cc-switch → GLM 了,但还没有压缩。下一步把 Headroom 插到中间。
第四步:编写 Headroom 启动脚本
新建 ~/.headroom-start.sh:
bash
cat > ~/.headroom-start.sh <<'EOF'
#!/usr/bin/env zsh
source "$HOME/.headroom-venv/bin/activate"
# Upstream = cc-switch 的本地代理(负责注入 GLM key)
export ANTHROPIC_TARGET_API_URL="http://127.0.0.1:15721"
export HEADROOM_HOST=127.0.0.1
# 输出压缩:默认关掉,设为 1 启用
export HEADROOM_OUTPUT_SHAPER="${HEADROOM_OUTPUT_SHAPER:-0}"
# 跳过上游可达性检查------cc-switch 是 loopback 代理,
# Headroom 的 /readyz 探活可能误报
export HEADROOM_SKIP_UPSTREAM_CHECK=1
exec headroom proxy --port 8787 --host 127.0.0.1
EOF
chmod +x ~/.headroom-start.sh
几个重要的环境变量:
ANTHROPIC_TARGET_API_URL:指向 cc-switch(:15721),而不是直连 GLM------key 注入仍由 cc-switch 负责,Headroom 只管压缩和转发。HEADROOM_SKIP_UPSTREAM_CHECK=1:cc-switch 是 loopback 代理,Headroom 对它做/readyz探活会误报,关掉换手动验证。HEADROOM_OUTPUT_SHAPER:默认0(不压缩输出),想连模型回包一起压缩就设1。
先手动跑一次,确认能起来:
bash
~/.headroom-start.sh
# 另开终端验证:
curl -s http://127.0.0.1:8787/livez
确认无误后 Ctrl+C 停掉,下一步交给 launchd 托管。
第五步:用 launchd 设置开机自启
创建 ~/Library/LaunchAgents/com.headroom.proxy.plist:
bash
cat > ~/Library/LaunchAgents/com.headroom.proxy.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.headroom.proxy</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>__HOME__/.headroom-start.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/tmp/headroom-proxy.log</string>
<key>StandardErrorPath</key>
<string>/tmp/headroom-proxy.log</string>
<key>ProcessType</key>
<string>Background</string>
</dict>
</plist>
EOF
sed -i '' "s|__HOME__|$HOME|" ~/Library/LaunchAgents/com.headroom.proxy.plist
加载并启动:
bash
launchctl load ~/Library/LaunchAgents/com.headroom.proxy.plist
launchctl list | grep headroom
看到 com.headroom.proxy 出现在列表里就对了。plist 的几个关键配置说明:
| 配置项 | 作用 |
|---|---|
RunAtLoad=true |
开机或加载时自动启动 |
KeepAlive=true |
进程意外退出自动拉起 |
ThrottleInterval=10 |
崩溃重启最小间隔 10s,防止重启风暴 |
日志统一写到 /tmp/headroom-proxy.log |
方便排查 |
日常管理命令:
bash
# 停止
launchctl unload ~/Library/LaunchAgents/com.headroom.proxy.plist
# 重启
launchctl unload ... && launchctl load ...
# 看日志
tail -f /tmp/headroom-proxy.log
第六步:把 Claude Code 的 base URL 指向 Headroom
cc-switch 默认会把 ANTHROPIC_BASE_URL 写成 http://127.0.0.1:15721(直连 cc-switch)。要插入 Headroom,需要改成 :8787:
bash
python3 - <<'EOF'
import json, pathlib
p = pathlib.Path.home() / ".claude" / "settings.json"
cfg = json.loads(p.read_text())
cfg.setdefault("env", {})["ANTHROPIC_BASE_URL"] = "http://127.0.0.1:8787"
p.write_text(json.dumps(cfg, indent=2, ensure_ascii=False))
print(p.read_text())
EOF
改完后必须重启 Claude Code------当前会话用的还是改之前的 base URL,不重启不生效。
第七步:验证整条链路
直接请求 Headroom,确认能拿到 GLM 的回复
bash
curl -s -X POST http://127.0.0.1:8787/v1/messages \
-H "x-api-key: PROXY_MANAGED" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"glm-5.2","max_tokens":16,"messages":[{"role":"user","content":"say ok"}]}'
正常应该返回带 msg_... id 的 JSON,content 是 GLM 的回复内容。
Headroom 自检
bash
~/.headroom-venv/bin/headroom doctor --port 8787
注意 doctor 的 /livez 探针对 Headroom 自己的端口有效,不要让它去探 cc-switch 的 15721。
检查 launchd 状态
bash
launchctl list | grep headroom
第二列是 0 表示运行正常;非零说明崩了,去看 /tmp/headroom-proxy.log 排查。
⚠️ 一个必踩的大坑
cc-switch 有一个很坑的行为:如果你在 GUI 里切换 provider 或重新点「应用」,它会重写 ~/.claude/settings.json,把 ANTHROPIC_BASE_URL 改回 http://127.0.0.1:15721,直接绕过了 Headroom。
解决办法:
- 每次在 cc-switch 里动过之后,重新执行第六步把 base URL 改回
:8787,然后重启 Claude Code。 - 或者干脆配好之后就不要再动 cc-switch,只在 GLM key 过期需要更新时才进 GUI。
文件清单
| 路径 | 作用 |
|---|---|
~/.local/bin/uv |
uv 安装器 |
~/.headroom-venv/ |
Headroom 专用 Python 3.12 venv |
~/.headroom-start.sh |
Headroom 启动脚本 |
~/Library/LaunchAgents/com.headroom.proxy.plist |
launchd 自启配置 |
/tmp/headroom-proxy.log |
Headroom 运行日志 |
~/.claude/settings.json |
Claude Code 配置(base URL 指向 :8787) |
~/.cc-switch/ |
cc-switch 数据库与配置(含真实 GLM key) |
常见问题
Q: Claude Code 启动报连接 :8787 失败?
A: Headroom 没起来。launchctl list | grep headroom 看进程在不在,不在就 launchctl load;在但崩了就看 /tmp/headroom-proxy.log。
Q: 改了 cc-switch 后 Claude Code 不压缩了?
A: 检查 ~/.claude/settings.json 里 ANTHROPIC_BASE_URL 是不是被改回 :15721 了,改回 :8787 并重启 Claude Code。
Q: 想连模型输出也压缩?
A: 编辑 ~/.headroom-start.sh,把 HEADROOM_OUTPUT_SHAPER 改成 1,然后 launchctl unload && load 重启 Headroom。
Q: 怎么临时停掉压缩直连 GLM?
A: 把 ANTHROPIC_BASE_URL 临时改回 http://127.0.0.1:15721 并重启 Claude Code,用完再改回来。Headroom 进程本身不用停。
最后的效果
全部配好之后,每次 Claude Code 发请求都会经过 Headroom 压缩再转发到 cc-switch,最后由 cc-switch 注入 GLM key 送到智谱。实际使用下来,上下文压缩能省掉相当可观的 token 量,再加上 GLM 本身比 Claude 便宜很多,这套链路长期用下来确实能省不少。
唯一需要留意的就是那个 cc-switch 覆写 base URL 的坑,记住每次动过 cc-switch 后改回来就行。如果只是稳定用同一个 GLM provider,配好之后几乎不用再管它。
关于作者
本文作者 tenxiaodao,专注 AI 工程化与开发者工具效率实践。更多技术文章可访问:tenxiaodao.top