Headroom macOS 部署实战:用 LaunchAgent 把 headroom proxy 变成常驻后台服务
本文基于仓库中 examples/deployment/macos-launchagent 目录的部署文档与配套脚本,讲解如何在 macOS 上把 Headroom 代理服务器(headroom proxy)注册为 LaunchAgent 常驻服务:从安装脚本与 plist 模板的逐项解析,到 Shell 集成、端口校验、健康检查与故障排查,完整覆盖“登录即启动、崩溃自动重启”的本地代理运维全流程。读完后你可以独立完成安装、验证、自定义端口与卸载操作,并理解每个 plist 键与 Shell 集成脚本背后的工作机制。
一、为什么用 LaunchAgent 托管 headroom proxy
Headroom 的核心工作方式之一是通过一个本地代理(proxy)拦截并压缩发往 LLM 的请求内容(工具输出、日志、文件、RAG 分块等),再转发给上游 API。对于 Claude 客户端,典型做法是把 ANTHROPIC_BASE_URL 指向本地代理端口,让所有请求“路过”代理完成压缩与缓存优化。
如果代理只在终端里手动 headroom proxy 运行,存在两个问题:关终端就断、崩溃后要手动拉起。examples/deployment/macos-launchagent/README.md 给出的方案是利用 macOS 原生的 LaunchAgent 机制,获得以下能力(来自文档 Features 一节):
- 自动启动:用户登录时服务自动拉起(对应 plist 中的
RunAtLoad); - 崩溃恢复:代理进程崩溃后自动重启(对应
KeepAlive); - 可配置端口:默认 8787,安装时可通过
--port自定义; - 标准日志:日志输出到
~/Library/Logs/headroom/; - Shell 集成:自动为 Claude 客户端设置
ANTHROPIC_BASE_URL,无需每次手动配置。
该方案定位为单用户开发环境(“set and forget”);生产部署仓库建议评估 Docker 或 systemd 等其他形态,详见 wiki/macos-deployment.md。
二、前置要求与安装目录结构
环境要求
文档明确列出的 Requirements:
- macOS 10.13+(High Sierra 或更高版本);
- 已安装带 proxy 支持包的 Headroom:
pip install headroom-ai[proxy]; - 环境中已配置 Anthropic API key。
从源码看,headroom/cli/proxy.py 中的 ensure_proxy_dependencies() 会在启动前逐一导入 fastapi、uvicorn、httpx、openai、mcp、magika、zstandard、websockets、onnxruntime、transformers、watchdog 等模块,缺失任何一个都会提示 Run: pip install headroom-ai[proxy] 并退出——这正是安装脚本校验代理可用性的底层依据。
目录内四个文件
examples/deployment/macos-launchagent 目录包含 4 个文件,各司其职:
| 文件 | 作用 |
|---|---|
| com.headroom.proxy.plist.template | LaunchAgent plist 模板,含三个占位符 |
| install.sh | 自动化安装脚本 |
| uninstall.sh | 自动化卸载脚本 |
| shell-integration.sh | Shell 集成脚本,自动配置 ANTHROPIC_BASE_URL |
三、逐项解析 plist 模板
com.headroom.proxy.plist.template 是整套部署的核心配置。以下按模板中的键逐项说明:
| plist 键 | 模板值 | 含义 |
|---|---|---|
Label |
com.headroom.proxy |
服务标签,必须与安装后的 plist 文件名一致;launchctl 命令均围绕它展开 |
ProgramArguments |
__HEADROOM_PATH__ proxy --host 127.0.0.1 --port __PORT__ |
实际执行的命令。注意 --host 127.0.0.1 写死为回环地址,即代理只在本机可访问,默认不暴露到外部网络 |
EnvironmentVariables.HEADROOM_PROXY_PORT |
__PORT__ |
端口默认值 8787,安装脚本可覆盖 |
WorkingDirectory |
__HOME__ |
进程工作目录设为用户主目录 |
StandardOutPath |
__HOME__/Library/Logs/headroom/proxy.log |
stdout 日志落盘路径 |
StandardErrorPath |
__HOME__/Library/Logs/headroom/proxy-error.log |
stderr 日志落盘路径 |
KeepAlive |
true |
进程退出/崩溃后由 launchd 自动重启 |
RunAtLoad |
true |
用户登录时立即加载并启动服务 |
ProcessType |
Adaptive |
声明为自适应后台进程,允许系统按后台负载策略调度 |
ThrottleInterval |
10 |
两次重启尝试之间至少间隔 10 秒,避免崩溃循环时高频重启 |
模板中有三个占位符,安装脚本会负责替换(手动安装时需自行替换):
__HEADROOM_PATH__:command -v headroom的输出,即headroom可执行文件绝对路径;__PORT__:期望端口,默认 8787;__HOME__:用户主目录路径。
模板 EnvironmentVariables 段还保留了两个注释掉的可选配置:
<!-- ANTHROPIC_API_KEY 推荐放在 shell 环境中;也可在此显式声明 -->
<!-- <key>ANTHROPIC_API_KEY</key> -->
<!-- <string>your-api-key-here</string> -->
<!-- 可选:LLMLingua 压缩(需 llmlingua extra,见模板注释) -->
<!-- <key>HEADROOM_COMPRESSION_PROVIDER</key> -->
<!-- <string>llmlingua</string> -->
需要说明的是,wiki/macos-deployment.md 中提示早期的 LLMLingua-2 相关启动变量(HEADROOM_COMPRESSION_PROVIDER=llmlingua、HEADROOM_LLMLINGUA_DEVICE、headroom-ai[llmlingua] extra)已随 --llmlingua 参数一起退役,当前 ML 压缩请安装 [ml] extra 并参考 wiki/transforms.md。
四、安装脚本 install.sh 的工作流程
文档给出的三种安装方式:
# 快速安装(推荐)
./install.sh
# 自定义端口
./install.sh --port 9000
# 无人值守安装(跳过交互提示)
./install.sh --port 8787 --unattended
install.sh 的完整流程(源码中每一步都有对应实现):
- 平台检查:
uname -s必须为Darwin,否则直接报错“Use systemd on Linux”; - 定位 headroom 可执行文件:通过
command -v headroom查找 PATH,找不到则提示pip install headroom-ai[proxy];随后执行headroom proxy --help验证 proxy 子命令可用,这是“proxy 支持已安装”的实际判据; - 重复安装检测:若
~/Library/LaunchAgents/com.headroom.proxy.plist已存在,先launchctl bootout gui/<uid>/com.headroom.proxy停止旧服务,再询问是否重装(--unattended模式下自动继续); - 端口确定:优先级为
--port参数 >--unattended默认值 8787 > 交互式询问(回车取默认); - 端口合法性校验:正则要求纯数字且范围 1024–65535,非法端口直接终止;
- 端口占用检测:用
lsof -iTCP:<port> -sTCP:LISTEN -t检查,已占用时提示(交互模式下询问是否继续); - 创建日志目录:
mkdir -p ~/Library/Logs/headroom; - 生成 plist:用
sed一次性替换模板中的三个占位符(__HEADROOM_PATH__、__PORT__、__HOME__),输出到~/Library/LaunchAgents/com.headroom.proxy.plist,并chmod 644; - 加载服务:执行
launchctl bootstrap gui/<uid> <plist>;若失败(通常是服务已加载),先launchctl bootout清理再重试一次; - 启动验证:等待 2 秒后
launchctl print gui/<uid>/com.headroom.proxy确认服务存在,再用lsof确认端口在监听,未监听时提示查看proxy-error.log。
脚本结束时打印服务详情(端口、日志路径、Label)以及后续可用的运维命令,例如:
# 查看状态
launchctl print gui/$(id -u)/com.headroom.proxy
# 重启
launchctl kickstart -k gui/$(id -u)/com.headroom.proxy
# 卸载
./uninstall.sh
关于端口,值得补充一个源码事实:代理 CLI 本身的端口参数定义在 headroom/cli/proxy.py,默认值 8787,且支持通过环境变量 HEADROOM_PORT 覆盖(envvar="HEADROOM_PORT")。也就是说 headroom proxy --port 9000 与 HEADROOM_PORT=9000 headroom proxy 等价,LaunchAgent 的 plist 模板同时注入了 HEADROOM_PROXY_PORT 环境变量以便 Shell 集成脚本识别。
五、安装后验证
文档 Verification 一节给出三条标准验证命令:
# 1. 查看 LaunchAgent 状态(预期输出包含 state = running)
launchctl print gui/$(id -u)/com.headroom.proxy
# 2. 确认端口在监听
lsof -iTCP:8787 -sTCP:LISTEN
# 3. 请求健康检查端点
curl http://localhost:8787/health
/health 端点返回聚合健康状态,例如 {"status": "healthy"};代理 CLI 的帮助输出中也将 GET /health Aggregate health 列为内置路由(见 headroom/cli/proxy.py 中 epilog 的路由说明)。
wiki/macos-deployment.md 还给出了一步端到端功能验证:设置 ANTHROPIC_BASE_URL=http://localhost:8787 后用 anthropic Python SDK 发一条最小请求,确认请求确实经代理转发成功:
export ANTHROPIC_BASE_URL=http://localhost:8787
python -c "
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model='claude-3-5-sonnet-20241022',
max_tokens=50,
messages=[{'role': 'user', 'content': 'Hi'}]
)
print(response.content[0].text)
"
六、Shell 集成:让 Claude 客户端自动走代理
文档 Quick Start 的最后一步是把 Shell 集成加入 ~/.bashrc 或 ~/.zshrc:
export HEADROOM_PORT=8787
source /path/to/shell-integration.sh
shell-integration.sh 的运行逻辑(源码逐行可查):
- 端口来源:读取环境变量
HEADROOM_PROXY_PORT,未设置时回退默认 8787; - 防重复加载:若
HEADROOM_SHELL_INTEGRATION_LOADED已置位则直接return 0,保证 bash/zsh 双兼容下只执行一次; - 快速路径:用
lsof -iTCP:<port> -sTCP:LISTEN -t判断代理是否已在监听——是则直接export ANTHROPIC_BASE_URL="http://localhost:<port>"; - 回退路径:若未在监听且
~/Library/LaunchAgents/com.headroom.proxy.plist存在,则执行launchctl bootstrap gui/<uid> <plist>尝试拉起(幂等,已加载时不会失败),成功等待 1 秒后同样导出ANTHROPIC_BASE_URL; - 失败提示:既没在跑也拉不起来时,打印安装指引,提示回到本目录执行
./install.sh; - 命名空间清理:最后
unset -f移除两个内部函数,不污染 shell 环境。
这样做的效果是:新开终端即自动判断代理状态并配置 ANTHROPIC_BASE_URL,Claude 系客户端(CLI、IDE 插件等)无需任何手动配置即可经由 Headroom 代理。若不想依赖集成脚本,也可以直接手动导出:
export ANTHROPIC_BASE_URL=http://localhost:8787
七、日志与日常运维
日志位置与查看
# 标准输出
tail -f ~/Library/Logs/headroom/proxy.log
# 错误输出
tail -f ~/Library/Logs/headroom/proxy-error.log
# 只看最近 50 行(排障常用)
tail -n 50 ~/Library/Logs/headroom/proxy-error.log
这两个路径来自 plist 的 StandardOutPath / StandardErrorPath,如需修改,直接编辑 plist 中对应键后重新加载服务。
服务生命周期操作
除 install/uninstall 之外,日常操作都基于 launchctl:
# 状态
launchctl print gui/$(id -u)/com.headroom.proxy
launchctl list | grep headroom
# 重启(kill 后由 KeepAlive 拉起)
launchctl kickstart -k gui/$(id -u)/com.headroom.proxy
# 手动停止 / 启动
launchctl bootout gui/$(id -u)/com.headroom.proxy
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.headroom.proxy.plist
# 临时禁用而不卸载(重启机器后仍禁用,需 enable 恢复)
launchctl disable gui/$(id -u)/com.headroom.proxy
launchctl enable gui/$(id -u)/com.headroom.proxy
注意:修改 plist 后必须重新加载才生效(launchctl kickstart -k ... 或 bootout + bootstrap)。
八、手动安装与卸载
手动安装(不走 install.sh)
文档 Manual Installation 一节给出四步流程:
# 1. 复制并准备模板
cp examples/deployment/macos-launchagent/com.headroom.proxy.plist.template \
~/Library/LaunchAgents/com.headroom.proxy.plist
# 2. 编辑 plist:
# - __HEADROOM_PATH__ 替换为 `command -v headroom` 的输出
# - __PORT__ 替换为目标端口
# - __HOME__ 替换为用户主目录(`echo $HOME`)
# 3. 创建日志目录
mkdir -p ~/Library/Logs/headroom
# 4. 加载服务
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.headroom.proxy.plist
卸载
# 仅移除服务
./uninstall.sh
# 移除服务与日志
./uninstall.sh --remove-logs
uninstall.sh 的执行顺序为:检查 plist 是否存在(不存在则直接退出)→ launchctl bootout 停止服务 → 删除 ~/Library/LaunchAgents/com.headroom.proxy.plist → 处理日志目录(--remove-logs 直接删除;否则交互模式下询问,--unattended 模式保留)。脚本结束时还会提醒手动清理 ~/.bashrc / ~/.zshrc 中的 Shell 集成配置与手写的 ANTHROPIC_BASE_URL。
对应的纯手动卸载命令为:
launchctl bootout gui/$(id -u)/com.headroom.proxy
rm ~/Library/LaunchAgents/com.headroom.proxy.plist
rm -rf ~/Library/Logs/headroom # 可选
九、故障排查
文档 Troubleshooting 覆盖了三类高频问题,wiki/macos-deployment.md 进一步给出了错误对照表,这里合并整理:
1. 服务无法启动
先查错误日志:
tail -n 50 ~/Library/Logs/headroom/proxy-error.log
常见原因及处理(README + wiki 错误表):
| 现象/错误 | 处理 |
|---|---|
缺少 ANTHROPIC_API_KEY |
在 shell 环境或 plist EnvironmentVariables 中配置 |
ModuleNotFoundError: No module named 'headroom' |
重装:pip install headroom-ai[proxy](或 uv tool install --python 3.13 "headroom-ai[proxy]") |
command not found: headroom |
plist 中 __HEADROOM_PATH__ 未替换正确,用 command -v headroom 校准 |
Address already in use |
换端口或停掉冲突进程 |
2. 端口被占用
# 找出占用者
lsof -iTCP:8787 -sTCP:LISTEN
# 换端口重装
./uninstall.sh
./install.sh --port 9000
3. 登录后未自动启动
# 确认 LaunchAgent 是否已加载
launchctl list | grep headroom
# 未加载则手动 bootstrap
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.headroom.proxy.plist
若仍无效,检查 plist 中 RunAtLoad 是否为 <true/>(grep -A1 RunAtLoad ~/Library/LaunchAgents/com.headroom.proxy.plist),以及 plist 文件权限是否为 644、属主是否为当前用户而非 root。
4. Shell 集成未生效
# 确认代理确实在跑
curl http://localhost:8787/health
# 重新加载 shell 配置
source ~/.zshrc
# 确认集成脚本确实执行过(预期输出 1)
echo $HEADROOM_SHELL_INTEGRATION_LOADED
另外提醒一点:shell-integration.sh 读取的端口变量是 HEADROOM_PROXY_PORT(默认 8787),而 headroom proxy 命令本身识别的环境变量是 HEADROOM_PORT(见 headroom/cli/proxy.py)。两者默认值一致时没有冲突,但若自定义端口,请确保两处分别用对应的变量名设置,避免“装了一个端口、客户端指向另一个端口”的错位。
十、可选进阶:plist 调优方向
wiki/macos-deployment.md 在基础部署之上给出了几个可选调优点,均可通过编辑 plist 实现:
- 关闭自动重启:将
KeepAlive改为<false/>; - 定时启动:增加
StartCalendarInterval(如每日 9:00); - 资源限制:增加
HardResourceLimits(如MemoryMax512 MB); - 多实例:复制模板为
com.headroom.proxy-2.plist,修改Label与端口后单独 bootstrap,可在多个端口并行运行多个代理; - Apple Silicon GPU 卸载:在 plist 的
EnvironmentVariables中设置HEADROOM_EMBEDDER_RUNTIME=pytorch_mps(需安装headroom-ai[pytorch-mps]extra),把记忆嵌入模型从 ONNX CPU 后端切换到 Apple GPU,仅当 MPS 真正可用时生效,属显式 opt-in。
需要强调的边界:这套 LaunchAgent 部署面向单用户开发场景,服务以用户身份运行、随登录启动、绑定 127.0.0.1 不对外暴露;生产环境应评估 Docker 部署、Linux systemd 或其他托管方案(见 wiki 文档的 Production Deployment 一节)。
十一、延伸阅读
- 部署文档主体:examples/deployment/macos-launchagent/README.md
- 完整版 macOS 部署指南:wiki/macos-deployment.md
- plist 模板 / 安装 / 卸载 / 集成脚本:com.headroom.proxy.plist.template、install.sh、uninstall.sh、shell-integration.sh
- 代理 CLI 实现(端口默认值、依赖校验、
/health路由说明):headroom/cli/proxy.py
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00