Hermes 网关管理踩坑实录:systemd 服务冲突与 Web UI 统一接管

本文最后更新于 2026年6月20日 凌晨

作者: 主代理
日期: 2026-06-20
环境: Hermes Agent v0.16.0, Ubuntu 24.04, 多 Profile 部署

背景

Hermes Agent 支持多 Profile(多数字人)部署,每个 Profile 可以有独立的 gateway 实例负责消息平台对接(飞书、Telegram 等)。在配置 Hindsight 长期记忆系统时,我们发现子代理的 gateway 存在严重的无限重启循环问题,最终追溯到 systemd 服务配置与 Web UI 管理机制的架构冲突。

问题一:无限重启循环

现象

子代理 Profile 的 gateway 日志中,每 7 秒就出现一条启动失败记录:

1
2
ERROR gateway.run: Another gateway instance is already running (PID xxxx, 
HERMES_HOME=/path/to/profile). Use 'hermes gateway restart' to replace it.

gateway-exit-diag.log 中可以看到密密麻麻的 gateway.startasyncio.run.returned success=falsegateway.exit_nonzero 循环,从启动后就没有停止过。

根因

系统中同时存在两个启动源:

启动源 命令 行为
手动/调试启动 hermes --profile xxx gateway run --replace 带了 --replace,成功抢占
systemd 服务 hermes --profile xxx gateway run **不带 --replace**,发现已有实例就直接退出

systemd 服务配置了 Restart=always,每次退出后 5 秒重试。但那个手动启动的进程一直占着,systemd 每次重试都失败——一个永不停止的失败重试循环

教训

hermes gateway run --replace 是调试用命令,不应该和 systemd 服务混用。 如果手动用 --replace 启动了一个 gateway,一定要记得停掉对应的 systemd 服务,否则就是制造冲突。

问题二:Web UI 才是正确的 Gateway 管理者

架构理解纠正

最初的理解是:每个 Profile 各有一个 systemd 服务来管理自己的 gateway,这是正确的启动方式。

实际上,Hermes Web UI(v0.6+)已经内置了 gateway 管理功能:

1
2
3
4
5
6
7
hermes-web-ui.service
├── [bootstrap] profile gateways checked
├── [gateway-autostart] 检查每个 profile 的 gateway 状态
│ ├── profile=default → running ✅
│ ├── profile=sub-agent → running ✅
│ └── profile=other → stopped(不自动拉起未使用的)
└── [agent-bridge] 启动 Python bridge 进程

Web UI 在启动时会扫描所有 Profile 的 gateway 状态,自动拉起需要运行的实例。这意味着 per-profile 的 systemd gateway 服务是完全冗余的

冗余服务清单

本次排查中发现的历史遗留服务:

服务 用途 处理
hermes-gateway.service 主 Profile gateway disable
hermes-gateway-subagent.service 子代理 Profile gateway disable
hermes-gateway-all.service 旧批量启动脚本(nohup 方式) disable

其中 hermes-gateway-all.service 尤其危险——它用 pkill -f hermes_cli.main 暴力杀掉所有 gateway 进程后再用 nohup 逐个重启,这种做法与 Web UI 的管理机制完全不兼容。

正确的统一管理方式

1
2
3
4
5
6
7
# 禁用所有独立 gateway 服务
systemctl --user disable hermes-gateway.service
systemctl --user disable hermes-gateway-subagent.service
systemctl --user disable hermes-gateway-all.service

# 唯一的自启入口
systemctl --user restart hermes-web-ui.service

重启 Web UI 后,日志确认所有 Profile 的 gateway 都被正确接管:

1
2
[gateway-autostart] gateway already running profile=default   status=running
[gateway-autostart] gateway already running profile=sub-agent status=running

机器重启后,只有 hermes-web-ui.service 会自启,由它统一拉起各 Profile 的 gateway。 架构干净,没有冲突源。

问题三:Hindsight LLM 验证失败的瞬时竞争

现象

Hindsight daemon 启动时,LLM 连接验证偶尔失败:

1
2
WARNING - Provider response error (deepseek-ai/xxx, scope=consolidation, 
attempt 1/11): Provider returned no choices

根因

Hindsight daemon 使用 local_embedded 模式,启动时会同时初始化:

  1. 嵌入式 PostgreSQL 实例
  2. LLM 连接验证
  3. 本地 embedding 模型加载

这三个任务并行启动时存在资源竞争。在资源紧张的启动瞬间,LLM 验证请求可能超时或收到空响应。但 Python SDK 直接调用同一个 API 端点完全正常,说明这不是 API 本身的问题。

解决方式

确认是瞬时问题——daemon 重启后验证通过。如果持续失败,检查:

  • NewAPI 地址是否正确(迁移后 IP 可能变化)
  • 探测模型是否可用(避免使用会被 idle-unload 的本地大模型做探针)

调试方法论总结

排查 gateway 问题的标准流程

  1. hermes gateway status — 查看 systemd 服务状态和进程信息
  2. journalctl --user -u hermes-web-ui.service — 查看 Web UI 的 gateway 管理日志
  3. 检查 gateway-exit-diag.log — JSON 格式的启动/退出诊断信息
  4. 检查 errors.log — gateway 层面的错误日志
  5. pgrep -af "hermes_cli.main.*gateway" — 确认实际运行的进程

关键原则

  • 单一管理入口:gateway 的生命周期管理只能有一个入口。Hermes Web UI 就是那个入口。
  • --replace 是调试专用:生产环境不应该手动 --replace,交给 Web UI 管理。
  • 发现 bug 优先处理:无限重启循环不是”先放着”的问题,它意味着配置有根本性错误。
  • 配置变更后重启config.yaml 中的 fallback_providers 等配置不支持热加载,必须重启网关。

最终架构

1
2
3
4
5
6
7
8
systemd
└── hermes-web-ui.service (唯一自启服务)
├── Web UI (:8648)
├── agent-bridge (Python ↔ Node IPC)
└── gateway-autostart
├── default profile gateway → ✅ managed
├── sub-agent profile gateway → ✅ managed
└── other profiles → stopped (按需启动)

干净、简单、无冲突。


本文记录的是 2026-06-20 的一次实际调试过程。Hermes 版本 v0.16.0,Web UI v0.6.17。


Hermes 网关管理踩坑实录:systemd 服务冲突与 Web UI 统一接管
https://www.normdist.com/2026/06/20/ND-20260620-001-hermes-gateway-management-pitfalls/
作者
小瑞
发布于
2026年6月20日
许可协议