2160 字
11 分钟
在 Mac Mini 上裸装 Hermes Agent:接 DeepSeek V4 Pro、微信、Telegram 的踩坑实录

要把 Nous Research 的 Hermes Agent 跑到家里的 Mac Mini M4 上,长期常驻。需求很具体:大脑用 DeepSeek V4 Pro,入口要微信和 Telegram,浏览器复用系统已装的 Chrome(不想让 Playwright 再下一个 Chromium),而且以后要改它的源码。

一开始是在 DigitalOcean 上用 Docker 隔离测的,跑通后发现两件事决定了最终方案必须是 Mac 本机裸装:一是 computer-use(cua-driver)是 macOS 专属,Linux 容器里根本起不来;二是要改源码,预编译模式不方便。于是整条链路迁到 Mac Mini,用 developer 模式装。

下面是完整记录,最值得看的是中间那个把人绕了半天的 401。

Developer 模式安装#

Hermes 的 install.sh 是预编译路径,不支持 --dev。developer 安装走仓库自带的 setup-hermes.sh

Terminal window
git clone https://github.com/NousResearch/hermes-agent.git ~/hermes-agent
cd ~/hermes-agent
./setup-hermes.sh

这个脚本做的事:检测平台 → uv venv venv --python 3.11 → 可编辑安装 uv pip install -e ".[all]" → 把 hermes symlink 到 ~/.local/bin → 同步 bundled skills。venv 目录名是 venv(在项目根,不是 .venv),-e 模式下改源码直接生效,这正是要的。

装完 hermes --version 正常,74 个 skills 同步完成。但很快踩了第一个坑。

坑一:uv sync --extra all 静默回退,漏装 telegram 依赖#

配 Telegram 时报:

ModuleNotFoundError: No module named 'telegram'

setup-hermes.sh 默认 uv sync --extra all --locked,理论上 all 应该包含 messaging(telegram 的 python-telegram-bot 在这个 extra 里)。但 all 聚合了一些重型 extra(比如 matrixmautrix[encryption]discord[voice]),其中某个在 macOS 上编译失败,触发脚本的 fallback 链,最后装了个不含 messaging 的子集——而退出码还是 0,所以当时没察觉。

单独补装就好:

Terminal window
cd ~/hermes-agent && source venv/bin/activate
uv pip install -e ".[messaging]"
+ python-telegram-bot==22.6
+ discord-py==2.7.1
+ slack-bolt==1.27.0
...

教训:--extra all 成功退出不代表 all 都装上了,关键平台的依赖要单独验证 python -c "import telegram"

接 DeepSeek V4 Pro:custom provider#

Hermes v0.16 的内置 provider 列表里没有 deepseek(有 openrouter / anthropic / gemini / zai / kimi / minimax …… 和 custom)。DeepSeek 完全 OpenAI 兼容,所以走 custom

Terminal window
hermes config set model.provider custom
hermes config set model.base_url https://api.deepseek.com/v1
hermes config set model.default deepseek-v4-pro
hermes config set model.max_tokens 8192
hermes config set model.context_length 1000000 # V4 Pro 原生 1M context

context_length 必须手动设——custom 端点 Hermes 探测不到上下文窗口,不设会过早压缩历史。

key 从本地 .env 注入到 mini 的 ~/.hermes/.env(全程用 shell substitution,值不落进终端回显)。然后 hermes status 显示得很正常:

Model: deepseek-v4-pro
Provider: Custom endpoint
DeepSeek ✓ sk-1...e86d

看着一切就绪。然后测试就翻车了。

坑二:那个把人绕半天的 401#

现象:no final response#

Terminal window
hermes -z "Reply with exactly one word: PONG"
hermes -z: no final response was produced; treating the run as failed.

错误方向:以为是 reasoning 模型 max_tokens 被吃光#

DeepSeek V4 Pro 是推理模型,会先吐 reasoning_content 再吐 content。第一直觉是 Hermes 给的 max_tokens 太小,被 reasoning 吃光、content 没机会产出。直接 curl 验证:

Terminal window
curl -s https://api.deepseek.com/v1/chat/completions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"deepseek-v4-pro","messages":[{"role":"user","content":"say PONG"}],"max_tokens":10}'
{"choices":[{"message":{"content":"","reasoning_content":"We are asked..."},"finish_reason":"length"}],
"usage":{"completion_tokens_details":{"reasoning_tokens":10}}}

max_tokens=10content 确实空(全被 reasoning 占了)。但把 max_tokens 提到 400,content 就是 "PONG"finish_reason=stop——模型完全正常。于是设 max_tokens=8192 重测,还是 no final response。所以根本不是 max_tokens。

关键:错误被 devnull 吞了#

为什么报错这么没信息量?翻源码 hermes_cli/oneshot.py

hermes_cli/oneshot.py
with redirect_stdout(devnull), redirect_stderr(devnull):
response = _run_agent(prompt, ...)
...
if not (response or "").strip():
real_stderr.write("hermes -z: no final response was produced...")

-z(oneshot)模式把 agent 的 stdout/stderr 全 redirect 到 /dev/null(设计是防 cron/SSH 下崩溃刷屏),所以 agent 内部真正的报错全被吞了。换交互模式跑就能看到:

Terminal window
hermes chat -q "Reply with exactly: PONG"
⚠️ API call failed: AuthenticationError [HTTP 401]
Error: HTTP 401: Your api key: ****ired is invalid

HTTP 401。但我 curl 用 .env 里同一个 key 是 200 成功的,而且报错的 key 结尾是 ired,不是我那个结尾 e86d 的 key。

排查:key 来源全是干净的#

挨个查 key 可能被污染的地方,全部排除:

  • ~/.hermes/.envOPENROUTER_API_KEY / OPENAI_API_KEY 各一行,长度 35(DeepSeek key 正确长度),无重复、无尾随 \r
  • 登录环境(zsh -ic env)和 .zshrc / .zprofile:没有任何 *_API_KEY
  • ~/.hermes/auth.json:不存在
  • config.yaml:没有未注释的 api_key

也就是说所有 key 来源都是那个正确的、curl 能用的 key。但 Hermes 发出去的是另一个结尾 ired 的 key。

根因:status 认对 key,请求发错 key#

Hermes 自己写了 request dump,用脚本(脱敏)看它实际发的请求头:

request.url: https://api.deepseek.com/v1/chat/completions
request.headers.Authorization: len=22 tail='ired'
request.body.model: deepseek-v4-pro

Authorization 头只有 22 字符。正确的应该是 Bearer + 35 = 42。也就是说:hermes status 的配置层读到了正确的 key,但 custom provider 在构造实际 HTTP 请求时没把这个 key 传给 OpenAI SDK,SDK 退回到了某个内部 fallback 值。

修复:显式设 config.yamlmodel.api_key#

config.yaml 的 model.api_key 优先级最高(注释里写 “set here instead of .env”)。显式设上:

Terminal window
hermes config set model.api_key <key>
hermes chat -q "Reply with exactly: PONG"
PONG
Duration: 3s

通了。这个 bug 在 developer 模式下以后可以直接去改 custom provider 的 key 传递逻辑——这也是当初选 developer 模式的价值之一。

两个教训值得单独记:一是 -z 把错误 redirect 到 devnull,调 agent 问题要用 hermes chat -q 才看得到底层异常;二是「配置层显示的 key」和「请求实际用的 key」可能不是一回事,dump 请求头是最终的判据。

Telegram:gateway + pairing#

依赖补齐、LLM 通了之后,Telegram 很顺。token 写进 .envTELEGRAM_BOT_TOKEN,装成 launchd 常驻服务:

Terminal window
hermes gateway install # 生成 ~/Library/LaunchAgents/ai.hermes.gateway.plist
[Telegram] Connected to Telegram (polling mode)

第一次给 bot 发消息,它回了个 pairing code——Hermes 内置配对机制,未授权用户发消息会拿到一次性码,owner 批准即可:

Terminal window
hermes pairing approve telegram <CODE>

之后日志确认端到端打通:

inbound message: platform=telegram msg='hi'
response ready: time=5.9s api_calls=1 response=38 chars

5.9 秒是 DeepSeek V4 Pro 的 reasoning + content 输出时间,正常。

微信这块要先搞清楚 Hermes 的两个适配器:weixin.py个人微信,走腾讯 2026 年官方开放的 iLink Bot API(域名 ilinkai.weixin.qq.com,纯 HTTP long-poll,35 秒 hold);wecom.py 是企业微信,走 WebSocket。两者都不需要公网回调,这对家里没公网的机器很关键。

个人微信用 iLink,凭据靠扫码自动获取,不用预先申请:

Terminal window
hermes gateway setup # 选 Weixin → 终端显示二维码 → 微信扫

二维码有 35 秒时效,而 Mac Mini 是远程机,所以这一步直接屏幕共享到 Mini 本地操作最稳。扫完凭据自动写入 ~/.hermes/.envWEIXIN_ACCOUNT_ID / WEIXIN_TOKEN),重启 gateway:

[Weixin] Connected account=xxxxxxxx base=https://ilinkai.weixin.qq.com
Gateway running with 2 platform(s)

发条「你好」,日志确认:

inbound message: platform=weixin msg='你好'
response ready: time=5.3s api_calls=1 response=14 chars
关于 iLink 的安全模型

iLink bot 是1v1 绑定的:接入后是一个独立的 xxx@im.bot 身份,只有扫码绑定的那个微信号(你自己)能 DM 它,别人加不进来也发不了。所以配置里 WEIXIN_ALLOW_ALL_USERS=true 并不构成风险——它天然就只服务绑定者。另外 iLink 协议只能「回复」不能「主动发起」,对方必须先发消息。

浏览器:复用系统 Chrome,不下 Chromium#

Hermes 的 browser 工具默认要么用 Playwright 的 Chromium,要么走 Browserbase 云。要复用系统 Chrome,正确做法是 CDP(Chrome DevTools Protocol),不是网上传的 AGENT_BROWSER_EXECUTABLE_PATH(这个变量在源码里根本不存在)。

源码 tools/browser_cdp_tool.py 里 CDP endpoint 的解析顺序是 BROWSER_CDP_URL 环境变量 → config.yamlbrowser.cdp_url。所以启动一个开了 remote-debugging 的系统 Chrome,再把 cdp_url 指过去即可:

Terminal window
open -na "Google Chrome" --args \
--remote-debugging-port=9222 \
--user-data-dir="$HOME/.hermes/chrome-debug" \
--no-first-run --no-default-browser-check

--user-data-dir 必须是独立目录,否则如果系统已经在用普通模式跑 Chrome,新进程会复用旧进程,9222 不会监听。验证:

Terminal window
curl -s http://127.0.0.1:9222/json/version
# {"Browser":"Chrome/149.0.7827.54", ... "webSocketDebuggerUrl":"ws://127.0.0.1:9222/..."}

配上 cdp_url 后测试 agent 浏览:

Terminal window
hermes config set browser.cdp_url http://127.0.0.1:9222
hermes chat -q "navigate to https://example.com and report the <h1>" -t browser
🌐 navigate example.com 2.2s
The main <h1> is: Example Domain

agent 通过 CDP 连上了系统 Chrome,全程没下 Chromium。

收尾:常驻化#

两件让它真正「常驻」的事:

Chrome 开机自启——把上面的启动命令包成 LaunchAgent(KeepAlive + RunAtLoad),Mini 重启后 9222 自动恢复:

Terminal window
launchctl load -w ~/Library/LaunchAgents/com.user.chrome-cdp.plist

让聊天里也能用 browser——gateway 各平台的 toolset 里要含 browser。Telegram 默认的 hermes-telegram toolset 本身就含 browser;微信默认 hermes-weixin 不确定,显式加上:

~/.hermes/config.yaml
platform_toolsets:
weixin: [hermes-weixin, browser]

经验总结#

  • developer 模式setup-hermes.sh + editable install)值得,尤其当你预期要改源码、或踩到像 custom provider key 传递这种需要改代码才能根治的 bug。
  • -z 模式会吞错误,调 agent 行为问题一律换 hermes chat -q 看底层异常。
  • 「配置显示的 key」≠「请求实际发的 key」,怀疑认证问题时直接看 request dump 的 Authorization 头长度和尾部。
  • --extra all 不等于全装上,关键平台依赖(telegram 的 messaging)要单独 import 验证。
  • DeepSeek V4 Pro 是推理模型,custom 接入要给够 max_tokens、手动设 context_length
  • 家里没公网也能接微信:个人微信走 iLink long-poll、企业微信走 WebSocket,都不需要回调地址。
在 Mac Mini 上裸装 Hermes Agent:接 DeepSeek V4 Pro、微信、Telegram 的踩坑实录
https://blog.lishuyu.app/posts/mac-mini-hermes-agent-deepseek部署/
作者
猫猫魔女
发布于
2026-06-07
许可协议
CC BY-NC-SA 4.0