WebUI 能开却聊不了:集群 Agent Bridge 三类根因与修复

2026-10-09 · 集群运维 / 故障排查 · 约 8 分钟阅读

分布式的一体机集群最坑的一类故障,不是"服务挂了",而是服务看起来没挂。Hermes WebUI 的页面能正常打开、curl 返回 200、进程也在跑,但你在界面上打字、发送,对面永远卡在"正在思考"——没有任何一个监控指标会因此变红。

这类故障的根子在 Agent Bridge:WebUI 只是壳,真正把界面上的对话接到 agent 能力上的是这个独立子进程。桥断了,壳照样在,只是没人接电话。

下面是我们在自己的多节点集群上把这类故障查清、并固化成一套清单的全过程。所有日志串、文件形态、端口号都来自真实排障现场。

一、先认清"能开不能聊"这个信号

症状非常固定,几乎不会认错:

日志里会有两种铁证(journalctl -u hermes-studio-nodeN | grep -i bridge):

[bootstrap] agent bridge failed to start: agent bridge exited before ready code=1 signal=null

或者启动的下一行立刻报端口被占:

agent bridge started
Error: listen EADDRINUSE: address already in use 0.0.0.0:8648
先建立"正常长什么样"的基线,比记住所有报错更有用。正常节点有三个特征:socket 文件 /tmp/hermes-agent-bridge.sock 的属主是运行用户、时间戳是当天;pgrep -af hermes_bridge 有 2 个进程(主进程 + worker);8648 端口的占用者就是 studio 自己的 node 进程。拿这三条去比,异常一眼就能看出来。

二、诊断五连(先查,别急着改)

排障最大的时间浪费是"上来就重启"。下面五条命令按顺序跑一遍,根因基本就浮出来了:

ls -la /tmp/hermes-agent-bridge.sock                 # 属主 + 时间戳
pgrep -af hermes_bridge | grep -v pgrep             # 有几个进程
systemctl is-active hermes-studio-nodeN
journalctl -u hermes-studio-nodeN --since '6 hours ago' --no-pager | grep -i bridge | tail -6
systemctl list-units --all --type=service --no-pager | grep -i hermes

最后一条是最容易漏、命中率却最高的一步:把节点上所有带 hermes 字样的 unit 全列出来。很多"莫名其妙"的故障,源头是历史遗留的第二个服务在抢同一个端口。

三、三类根因与各自的修法

根因 A:陈旧 root 死 socket

特征非常好认——socket 文件属于 root,而且时间戳是旧的:

srwxr-xr-x 1 root root 0 <旧日期> /tmp/hermes-agent-bridge.sock

Bridge 启动时要先 unlink 再 bind。文件属于 root,运行用户没有权限删它,于是 bind 失败,报的就是那个极具误导性的 OSError: [Errno 98] Address already in use——听起来像端口冲突,其实是文件权限。

sudo -n rm -f /tmp/hermes-agent-bridge.sock
sudo -n systemctl restart hermes-studio-nodeN

判定要点:没人用的死 socket 可以直接删,删之前用时间戳和属主确认它是旧的那个,别把正在用的删了。

根因 B:unit 指向了错的 webui 版本

升级 webui 之后最容易出这个:unit 文件里的 WorkingDirectory / HERMES_WEB_UI_HOME / ExecStart 还指着旧布局(~/webui),而实际代码已经搬到新目录(~/webui-new/node_modules/hermes-web-ui)。路径对不上,启动脚本自然起不来。

修法是照抄标准模板。同一套节点上,几个正常节点的 unit 应该完全一致:

[Service]
Type=simple
User=lishouqi
WorkingDirectory=/home/lishouqi/webui-new/node_modules/hermes-web-ui
Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=HERMES_HOME=/home/lishouqi/.hermes
Environment=HERMES_WEB_UI_HOME=/home/lishouqi/webui-new/node_modules/hermes-web-ui
Environment=PORT=8648
Environment=HOST=0.0.0.0
Environment=HERMES_WEB_UI_DISABLE_UPDATE=1
ExecStart=/usr/local/bin/node /home/lishouqi/webui-new/node_modules/hermes-web-ui/dist/server/index.js
Restart=always
RestartSec=3
别照抄 node 路径。我们集群里就有一台机器没有 /usr/local/bin/node,用的是 ~/.local/bin/node。改之前先 ls -la <node路径> 确认存在,否则会把一个故障换成另一个故障。个别节点还会多一条 HERMES_AGENT_ROOT(指向本机 agent 代码根,而不是 site-packages),照抄模板时要保留。

另外,改 unit 之前先备份:sudo -n cp <unit> <unit>.bak-YYYYMMDD,出问题能立刻回滚。

根因 C:重复服务抢 8648 / unit 指向不存在的脚本

两种表现,本质都是"多出来的东西":

sudo -n systemctl stop <垃圾服务> && sudo -n systemctl disable <垃圾服务>
sudo -n systemctl daemon-reload
sudo -n systemctl restart hermes-studio-nodeN

判据:正常节点上只有两个 hermes 相关 unit——网关 + 本节点的 studio。多出来的基本都是历史残留,而不是"某个需要的组件"。

四、修复后验证六步(缺一不可)

这类故障最怕"感觉好了"。重启后服务显示 active、页面也打得开,但 worker 根本没起来,属于半残状态,过一会儿又是老样子。所以验证要逐条过:

systemctl is-active hermes-studio-nodeN                                  # active
ls -la /tmp/hermes-agent-bridge.sock                                     # 属主正确 + 当前时间
pgrep -af hermes_bridge | grep -v pgrep                                  # 应有 2 条
journalctl -u hermes-studio-nodeN --since '2 min ago' | grep -i bridge   # agent bridge started
ss -ltnp | grep 8648                                                     # 占用者应是 studio 自己的 node
curl -s -m 8 -o /dev/null -w '%{http_code}' http://127.0.0.1:8648/       # 200

第 3 条是最容易被跳过的一条:只有 1 个进程就说明 worker 没起来,这时候页面能开、对话不通,跟没修一样。

正常节点上 bridge 的标准命令行形态是这样的,可以用它反推 unit 里的路径是否都对:

<venv>/bin/python3.12 <webui-new>/node_modules/hermes-web-ui/dist/server/agent-bridge/python/hermes_bridge.py \
  --endpoint ipc:///tmp/hermes-agent-bridge.sock --agent-root /home/lishouqi/hermes-agent --hermes-home /home/lishouqi/.hermes

五、六个踩过的坑

  1. 不要用真实的 socket 路径手工起 bridge 做测试。bridge 是"先 unlink 再 bind"的逻辑,手工起一次就会把正常节点的 socket 删掉——我们真的把一台好节点弄坏过,靠重启服务才恢复。要测就用 --endpoint ipc:///tmp/xxx_test.sock。
  2. 判存在性别用 ls xxx && echo Y。ls 会把路径一起输出,污染判断结果。用 test -S(是 socket)或 test -r(可读)。
  3. 排查第一步就列 unit。重复服务是最常见的隐性根因,而它不会出现在任何"这个服务是否正常"的检查里。
  4. 改 unit 前先备份,sudo -n cp 一行的事,回滚靠它。
  5. 看日志时注意 PID 的含义:node[PID]: 里的 PID 是 studio 主进程,bridge 是它的子进程,别拿主进程的 PID 去 ps 里找 bridge。
  6. 修完顺手再跑一次 pgrep -af hermes_bridge,确认是 2 条而不是 1 条。

六、这类故障的通性

把三类根因摊开看,它们其实是同一件事的三个面:启动前的"清场"没做干净。旧 socket 没删、旧路径没改、旧服务没停——桥的启动条件依赖于一个"干净的地面",而这个前提从没被任何一个健康检查覆盖过。

所以真正有价值的不是记住这三条修法,而是把基线写下来:socket 属主与时间戳、bridge 进程数、8648 占用者、hermes unit 数量。有了基线,下一个同类故障的定位时间是分钟级;没有基线,就只能靠重启和运气。

我们站内在售的方言语料资产(苏州话标准词库 3550 条)与数字人素材,也是按同样的"先定基线、再验证"的方式做出来的——附字段说明与示例代码,交付即可用。

需要有人帮你做这类故障排查?看技术服务 → 看看站内已上架的数字资产 →

常见问题

WebUI 页面能打开,为什么对话发不出去?

因为页面由 WebUI 服务提供,而对话要经过独立的 Agent Bridge 子进程。桥挂了,页面照样 200,但没人接对话。先在日志里 grep -i bridge,找 agent bridge failed to start: agent bridge exited before ready code=1 这条;再看 pgrep -af hermes_bridge 是否只有 1 个进程。

报 Address already in use,但端口明明没人用,怎么回事?

先别信报错字面。用 ls -la /tmp/hermes-agent-bridge.sock 看属主和时间戳——如果文件属于 root 且日期是旧的,那是残留的死 socket,运行用户无权 unlink 才报的 EADDRINUSE。sudo -n rm -f 掉再重启即可。真的是端口冲突的话,ss -ltnp | grep 8648 会明确指出占用者。

升级 webui 之后就起不来了,怎么排查?

几乎一定是 unit 指向了旧版本目录。对比一个正常节点的 unit,重点看 WorkingDirectory、HERMES_WEB_UI_HOME、ExecStart 三处是否指向新的 webui-new/node_modules/hermes-web-ui。注意别照抄 node 的解释器路径——不同机器上 node 的位置可能不一样,改前先 ls -la 确认。

重启计数几千次是什么意思?

说明这个 service 一直在崩溃重启,典型原因是 ExecStart 指向了一个不存在的脚本,日志里会有 can't open file 和 status=2/INVALIDARGUMENT。用 systemctl show <svc> -p NRestarts 可以看到次数,确认无用后停用并 disable 掉,避免它持续抢占资源。

修完怎么确认是真的好了,而不是半残?

跑完整六步验证,其中两条最关键:pgrep -af hermes_bridge 必须返回 2 个进程(主 + worker,只有 1 个就是半残),以及 socket 文件的属主正确、时间戳是当前时间。这两条过了,才算真的恢复。