在一台只有 20G 系统盘的腾讯云服务器上跑 Hermes,真正让人头疼的往往不是某一个报错,而是几个问题连续出现。磁盘先被打到 100%,清理缓存之后,Hermes 又在启动时卡住,Gateway 恢复后,手机端继续提示 Provider authentication failed。这个过程很适合当成一次完整的排障案例来看,因为每一步都能看到实际数据、实际命令和实际结果。
20G 磁盘为什么会突然满掉
先看服务器最直接的结果。
df -h
当时看到的是:
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 20G 20G 28K 100% /
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 3.8G 24K 3.8G 1% /dev/shm
tmpfs 1.6G 163M 1.4G 11% /run
这里最关键的不是 20G 这个总容量,而是最后的 28K。
根分区只剩 28KB,相当于服务器已经没有什么可以继续写入磁盘的空间了。这个状态下,不管是日志、缓存、安装依赖还是程序运行时写文件,都可能受到影响。
排查磁盘问题时,第一件事不要马上删除文件,先看看空间到底被谁占走。
可以直接执行。
du -sh /* 2>/dev/null | sort -hr
得到的结果很集中。
9.3G /usr
9.2G /root
980M /var
475M /opt
163M /run
104M /boot
25M /etc
940K /tmp
56K /home
到这里,排查范围已经缩小很多了。
/usr 占了 9.3G,/root 占了 9.2G,两边加起来已经超过 18G。20G 磁盘之所以这么快见底,原因基本就在这里。
/root 里面究竟是什么占掉了 9.2G
继续把 /root 拆开。
du -xh /root --max-depth=1 2>/dev/null | sort -hr
结果是。
9.2G /root
3.7G /root/.hermes
2.3G /root/.cache
2.0G /root/go
808M /root/.npm
387M /root/.rustup
127M /root/.local
20M /root/.cargo
这里已经出现了几个非常明确的目标。
.hermes 有 3.7G。
.cache 有 2.3G。
go 有 2.0G。
.npm 有 808M。
也就是说,单看 /root,这几个目录就已经吃掉了大部分空间。
这时候不要先猜哪个目录能删,继续往下拆。
/root/.cache 里的 2.3G 能不能删
先看详细结构。
du -xh /root/.cache --max-depth=2 2>/dev/null | sort -hr | head -30
结果非常典型。
2.3G /root/.cache
1.5G /root/.cache/go-build
736M /root/.cache/uv
637M /root/.cache/uv/archive-v0
127M /root/.cache/go-build/54
98M /root/.cache/uv/simple-v21
96M /root/.cache/pip/http-v2
96M /root/.cache/pip
这里最显眼的是两个地方。
/root/.cache/go-build 占 1.5G。
/root/.cache/uv 占 736M。
还有一个 96M 的 pip 缓存。
这些内容都是缓存性质的数据,所以可以优先清理。
当时采用的是分别清除这些缓存。
rm -rf /root/.cache/go-build
rm -rf /root/.cache/uv
rm -rf /root/.cache/pip
这种清理方式的特点很简单,删掉的是缓存,不去碰 Hermes 的虚拟环境。
这一点很重要,因为服务器上同时存在两个容易混淆的路径。
缓存目录是。
/root/.cache
Hermes 虚拟环境是。
/root/.hermes/hermes-agent/venv
两者不是一回事。
后面排查 Hermes 时也验证了,edge-tts 实际安装在 Hermes 自己的虚拟环境里,所以清理 pip 和 uv 缓存没有把这个依赖删除。
/root/go 的 2.0G 又是什么
继续执行。
du -xh /root/go --max-depth=2 2>/dev/null | sort -hr | head -30
结果是。
2.0G /root/go
1.8G /root/go/pkg/mod
1.8G /root/go/pkg
145M /root/go/bin
4.0K /root/go/pkg/sumdb
真正占空间的是。
/root/go/pkg/mod
它有 1.8G。
这里有一个容易踩的坑。
服务器上虽然存在 /root/go,但不代表一定安装了 go 命令。
后面实际执行。
go clean -modcache
得到的结果是。
bash: go: command not found
所以这里不能继续假设系统里已经装了 Go。
如果只是为了释放空间,可以直接处理缓存目录,而不是为了清理 1.8G 再专门安装一套 Go。
当时真正需要保守处理的是 /root/go/bin,这个目录只有 145M,所以没有必要为了多释放一点空间把里面的二进制一起删掉。
Hermes 自己占了 3.7G,哪些东西不能乱删
磁盘清理到这里,最大的单个应用目录变成了 /root/.hermes。
继续看。
du -xh /root/.hermes --max-depth=2 2>/dev/null | sort -hr | head -50
得到的结构很有代表性。
3.7G /root/.hermes
1.9G /root/.hermes/hermes-agent
926M /root/.hermes/hermes-agent/venv
577M /root/.hermes/node
530M /root/.hermes/hermes-agent/.git
406M /root/.hermes/state-snapshots/20260811-045212-pre-update
406M /root/.hermes/state-snapshots
327M /root/.hermes/sessions
269M /root/.hermes/hermes-agent/web
这里有几个目录千万不要因为看到数字大就直接删除。
比如。
/root/.hermes/hermes-agent/venv
/root/.hermes/node
一个有 926M,一个有 577M。
它们都是 Hermes 自己的运行环境组成部分。
真正适合优先确认的是 state-snapshots。
目录名称已经把信息写得很直白。
20260811-045212-pre-update
这个目录有 406M。
它属于更新前状态快照。既然 Hermes 当前已经可以继续运行,这类旧快照才是比较值得检查的清理对象。
另外还有。
/root/.hermes/sessions
占 327M。
会话目录和运行环境不一样,不能只看容量就直接删除,所以当时把它放在后面处理。
磁盘满的时候最重要的一条经验就是,先清缓存和明确的旧备份,再动程序运行目录。
磁盘从 100% 降到 94% 后,为什么 Hermes 还是有问题
清理完成后,再看磁盘。
df -h
结果变成。
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 20G 19G 1.4G 94% /
从 100% 降到了 94%。
可用空间恢复到 1.4G。
服务器暂时脱离了“磁盘彻底写满”的状态。
这一步完成之后,问题开始转向 Hermes 本身。
此时执行。
hermes
结果没有直接进入聊天,而是在启动过程中卡住了。
最后按下 Ctrl+C 后,看到完整调用栈。
其中最值得关注的一段是。
check_tts_requirements()
_import_edge_tts()
_lazy_ensure("tts.edge")
_venv_pip_install(missing)
这条调用链已经说明,Hermes 在启动时检查 TTS 依赖。
检查过程中,它进入了 lazy dependency,也就是运行到某个功能时再尝试补依赖的机制。
随后 Hermes 判断存在缺失依赖,于是尝试调用 pip 安装。
明明已经安装了 edge-tts,为什么 Hermes 还要尝试安装
这个问题需要用命令验证,不能只看报错猜。
先检查。
/root/.hermes/hermes-agent/venv/bin/python -m pip show edge-tts
实际结果是。
Name: edge-tts
Version: 7.2.8
Location: /root/.hermes/hermes-agent/venv/lib64/python3.11/site-packages
Requires: aiohttp, certifi, tabulate, typing-extensions
Required-by: hermes-agent
这条结果很关键。
edge-tts 并没有消失。
它明确存在于 Hermes 自己的 Python 虚拟环境里面。
再看 pip 本身。
/root/.hermes/hermes-agent/venv/bin/python -m pip --version
结果是。
pip 23.3.1 from /root/.hermes/hermes-agent/venv/lib64/python3.11/site-packages/pip
所以问题不能简单归结成“pip 被删了”。
接着检查服务器访问 PyPI 是否正常。
curl -I --max-time 10 https://pypi.org
返回。
HTTP/2 200
这又排除了一个常见原因。
服务器当前能够访问 PyPI。
所以这次真正值得继续验证的是 Hermes 自己的依赖检测过程,以及它实际使用的 Python 环境。
这里有个容易误判的地方。
刚才删除的是。
/root/.cache/uv
/root/.cache/pip
而 edge-tts 安装位置是。
/root/.hermes/hermes-agent/venv/lib64/python3.11/site-packages
删除缓存不会自动把虚拟环境中的 edge-tts 一起删除。
因此,不能看到 Hermes 进入 pip install 就马上判断成“之前清缓存把依赖删掉了”。
Gateway 重启报错,和模型认证失败是一回事吗
这两个问题也要分开。
执行。
hermes gateway restart
服务器返回。
✗ User systemd not reachable:
Linger was enabled, but the user D-Bus socket did not appear.
这里的问题是 systemctl --user 无法在当前 shell 中访问用户级 systemd 和 D-Bus 会话。
这个报错属于 Gateway 的启动管理方式。
它和后面手机里看到的 Provider authentication failed 属于不同层级的问题。
这一点可以通过实际运行结果确认。
因为手机端已经能够收到 Hermes 的回复,所以 Gateway 实际上已经在工作。
这意味着当前并不存在“手机完全连不上 Hermes”这个问题。
手机端提示 Provider authentication failed,到底卡在哪里
手机端收到的错误信息比较完整。
第一条是。
Provider authentication failed.
Check the configured credentials; raw provider details are in the gateway logs.
随后又出现。
Primary model failed — switching to fallback:
minimax-m2.5-highspeed via custom
然后再次出现。
Authentication failed and could not be refreshed
把这三条信息连起来看,就能定位到模型调用阶段。
流程大致是。
手机端发送消息
↓
Hermes Gateway
↓
Primary Model
↓
Provider authentication failed
↓
切换 fallback
↓
minimax-m2.5-highspeed
↓
Authentication failed
也就是说,Gateway 已经收到了消息,Hermes 也已经开始执行模型调用。
当前真正需要继续查的是 Provider 的认证凭据。
为什么直接 grep 整个 .hermes 没有得到有价值的信息
当时尝试了。
grep -RniE "minimax|m2.5|custom|provider|api.?key|base.?url" /root/.hermes 2>/dev/null | head -100
结果几乎全部被 Node.js 的头文件占满。
比如。
/root/.hermes/node/include/node/v8-platform.h
/root/.hermes/node/include/node/v8-local-handle.h
/root/.hermes/node/include/node/openssl/...
这些文件中自然存在 custom、provider 之类的英文单词,但它们和 Hermes 的模型配置没有关系。
所以这种排查方式范围太大。
更合理的做法是只查配置文件和日志,避开 node、venv、.git 这些目录。
配置文件可以先找。
find /root/.hermes -maxdepth 3 -type f \
\( -name "*.json" -o -name "*.yaml" -o -name "*.yml" -o -name "*.toml" -o -name "*.env" -o -name "*.conf" \) \
-not -path "*/node/*" \
-not -path "*/venv/*" \
| sort
日志则只查。
grep -RniE "401|403|unauthorized|authentication failed|invalid.*key|invalid.*token|refresh|credential|minimax" \
/root/.hermes/logs 2>/dev/null | tail -100
这样才能真正看到 Gateway 为什么认为 Provider 认证失败。
这里真正应该盯住哪个字段
手机截图里有一句很有价值。
minimax-m2.5-highspeed via custom
minimax-m2.5-highspeed 是模型名,custom 则说明它走的是一个自定义 Provider。
所以后续检查的重点应该落在这个 Provider 的实际配置上。
最直接的几个字段就是。
Provider
Model
Base URL
API Key
认证方式
当前日志已经明确告诉我们“认证失败”,但仅靠手机界面还不知道具体是 401、403、Token 过期,还是自定义 Provider 的地址配置问题。
因此下一步应该直接看 Gateway 日志,而不是继续删除 Hermes 文件。
这次排障里,哪些东西可以放心动,哪些东西不要碰
整个过程下来,目录可以大致分成几类。
缓存类。
/root/.cache/go-build
/root/.cache/uv
/root/.cache/pip
这类优先清。
Go Module 缓存。
/root/go/pkg/mod
如果这台服务器不是拿来持续编译 Go 项目,也可以单独清理。
应用更新旧快照。
/root/.hermes/state-snapshots/20260811-045212-pre-update
需要先确认当前版本运行正常,再决定是否删除。
Hermes 运行环境。
/root/.hermes/hermes-agent/venv
/root/.hermes/node
这类目录不要因为占空间大就直接删除。
会话数据。
/root/.hermes/sessions
先看具体内容,再判断处理方式。
真正排障时,目录大小只是第一层信息,目录的用途才决定能不能删。
最后回到现在这个状态
服务器已经从。
20G 20G 28K 100%
恢复到。
20G 19G 1.4G 94%
Hermes 的 edge-tts 7.2.8 也已经确认存在于自己的虚拟环境中,PyPI 访问返回 HTTP/2 200。
手机端还能正常收到 Hermes 的模型错误提示,说明 Gateway 这一层已经在工作。
所以后面的排查范围其实已经很小了。
现在最该做的是从 /root/.hermes/logs 里面找到 Provider 的原始认证错误,再去对应检查 minimax-m2.5-highspeed 所使用的自定义 Provider 配置。

