第十篇:部署与运维
概述
将 Hermes Agent 从开发环境推向生产环境,需要考虑部署方式选择、日志管理、会话生命周期、监控告警和故障排查等一系列运维问题。与传统的 Web 应用不同,AI Agent 的运维有一些独特的挑战:长时间运行的对话会消耗大量内存,上下文压缩可能在关键时刻失败,API 调用可能因为 rate limit 而被拒绝,Cron 任务可能在无人值守时执行失败。
本篇覆盖从本地开发到生产部署的完整运维体系,提供实际可操作的配置示例和排查方法。每个问题都会给出症状、原因和具体的解决步骤。
1. 部署方式
1.1 本地开发部署
最简单的部署方式,直接从源码运行:
# 克隆仓库
git clone <repo-url> /opt/hermes-agent
cd /opt/hermes-agent
# 安装依赖(推荐使用 uv,比 pip 快 10-100 倍)
uv pip install -e ".[all]"
# 初始化配置(交互式引导)
hermes setup
# 启动 CLI 模式(交互式对话)
hermes chat
# 启动 Gateway 模式(后台服务,支持多平台连接)
hermes gateway run
配置文件位置:
~/.hermes/
├── .env # 环境变量(API Key 等,权限 600)
├── config.yaml # 主配置文件
├── SOUL.md # Agent 人格定义(可选)
├── memories/ # 记忆存储
│ ├── MEMORY.md # Agent 笔记
│ └── USER.md # 用户画像
├── skills/ # 技能目录
│ └── software-development/
│ └── code-review/
│ └── SKILL.md
├── sessions/ # 会话数据库(SQLite)
├── logs/ # 日志目录
│ ├── gateway.log
│ ├── agent.log
│ └── cron.log
├── cron/ # 定时任务数据
│ ├── jobs.json # 任务定义
│ ├── outputs/ # 任务输出
│ └── .tick.lock # 调度锁文件
├── checkpoints/ # 文件系统检查点(Shadow Git)
└── bin/ # 自动安装的二进制(如 tirith)
环境变量配置(~/.hermes/.env):
# 必需:至少一个 LLM provider 的 API Key
OPENAI_API_KEY=sk-...
# 或
ANTHROPIC_API_KEY=sk-ant-...
# 可选:模型和 provider
HERMES_MODEL=gpt-4o
HERMES_INFERENCE_PROVIDER=openai
# 可选:平台配置
TELEGRAM_BOT_TOKEN=...
DISCORD_BOT_TOKEN=...
1.2 Docker 容器部署
Hermes Agent 支持 Docker 容器化部署,适合需要环境隔离或快速部署的场景:
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
python3 python3-pip python3-venv sqlite3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . .
RUN mkdir -p /data
EXPOSE 5000
ENV FLASK_APP=app.py
ENV DATABASE_PATH=/data/database.db
CMD ["python3", "app.py"]
Docker 部署注意事项:
- 数据持久化:使用 volume 挂载
~/.hermes,否则容器重启后所有数据(记忆、技能、会话)丢失docker run -v ~/.hermes:/root/.hermes ... - 自动免审:容器内命令执行自动免审(
env_type == "docker"),因为容器本身提供了环境隔离 - 网络配置:确保容器可以访问 LLM API 端点和消息平台 API
- 内存限制:根据模型上下文长度设置合理的内存限制(128K context 的模型可能需要 4GB+)
1.3 Systemd 用户服务
生产环境推荐使用 Systemd 用户服务管理 Gateway。这是最稳定的部署方式——自动重启、日志管理、开机自启:
# ~/.config/systemd/user/hermes-gateway.service
[Unit]
Description=Hermes Agent Gateway
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/hermes gateway run
Restart=on-failure
RestartSec=5
Environment=HERMES_GATEWAY_SESSION=1
Environment=HERMES_HOME=%h/.hermes
# 资源限制
LimitNOFILE=65536
MemoryMax=4G
[Install]
WantedBy=default.target
启用用户服务持久化(确保用户注销后服务仍运行):
# 关键步骤:启用 loginctl linger
loginctl enable-linger $(whoami)
# 启用服务
systemctl --user daemon-reload
systemctl --user enable hermes-gateway
systemctl --user start hermes-gateway
# 查看状态
systemctl --user status hermes-gateway
# 查看日志(实时)
journalctl --user -u hermes-gateway -f
# 查看最近的错误
journalctl --user -u hermes-gateway -p err --since "1 hour ago"
loginctl enable-linger 的作用是让 systemd 用户实例在用户注销后继续运行。没有这个设置,用户注销时所有用户服务会被停止。这在 SSH 远程管理场景下特别重要——管理员 SSH 登录启动服务后注销,服务需要继续运行。
1.4 Nginx 反向代理
当需要 HTTPS、自定义域名或负载均衡时,使用 Nginx 作为反向代理:
server {
listen 443 ssl http2;
server_name hermes.example.com;
ssl_certificate /etc/letsencrypt/live/hermes.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hermes.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 长连接超时——Agent 响应可能需要几分钟
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# 请求体大小限制(用于文件上传)
client_max_body_size 50M;
}
}
WebSocket 支持对于实时平台(Telegram long polling、Discord gateway)的连接是必需的。Upgrade 和 Connection 头确保 WebSocket 连接正确代理。
2. 运维实践
2.1 日志管理
Hermes Agent 的日志存储在 ~/.hermes/logs/ 目录下。
日志级别配置:
# config.yaml
logging:
level: INFO # 全局日志级别
file_level: DEBUG # 文件日志级别(更详细,用于事后分析)
console_level: WARNING # 控制台日志级别(更简洁,减少干扰)
或通过环境变量:
export HERMES_LOG_LEVEL=DEBUG
日志轮转:推荐使用 logrotate 管理日志文件大小,防止磁盘被日志填满:
# /etc/logrotate.d/hermes
/home/*/.hermes/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}
copytruncate 是关键选项——它复制日志文件内容后截断原文件,不需要重启进程。对于不支持信号重载日志文件的应用(如 Hermes Gateway),这是唯一的选择。
关键日志文件及用途:
| 文件 | 内容 | 典型查询 |
|---|---|---|
gateway.log | Gateway 启动、请求处理、平台事件 | grep "error" gateway.log |
agent.log | Agent 迭代、工具调用、压缩事件 | grep "compression" agent.log |
cron.log | 定时任务执行、投递结果 | grep "FAILED" cron.log |
2.2 会话管理
Hermes Agent 提供多个会话管理命令,用于控制对话生命周期:
| 命令 | 作用 | 实现细节 |
|---|---|---|
/new | 创建新会话,重置所有状态 | 调用 on_session_reset(),清空压缩计数、token 计数、记忆 nudge 计数 |
/reset | 重置当前会话,清空对话历史 | 保留配置,只清空消息列表 |
/compact [focus] | 手动触发上下文压缩 | 可选指定聚焦主题,分配 60-70% 摘要预算给聚焦内容 |
/resume | 恢复上次中断的会话 | 从 SQLite 会话数据库加载历史消息 |
会话数据库:会话数据存储在 SQLite 数据库中(~/.hermes/sessions/),支持通过 session_search 工具跨会话检索历史对话。这使得 Agent 可以在当前会话中引用过去会话的内容。
会话超时:Gateway 场景下,会话可能因不活跃而过期。Agent 的活动追踪器(get_activity_summary())监控最后一次活动时间:
activity_summary = {
"seconds_since_activity": 45.2, # 距上次活动的秒数
"last_activity_desc": "terminal: ls -la", # 上次活动描述
"current_tool": "terminal", # 当前正在执行的工具
"api_call_count": 12, # 已执行的 API 调用次数
"max_iterations": 90, # 最大迭代次数
}
2.3 配置热更新
部分配置可以在不重启的情况下更新:
# 重新加载 .env 文件(每次 Cron 任务执行时自动重载)
# Gateway 模式下,新的会话会读取最新配置
# 更新模型配置
hermes setup model
# 更新工具配置
hermes setup tools
# 更新终端配置
hermes setup terminal
配置热更新的边界:
| 配置项 | 热更新 | 说明 |
|---|---|---|
| API Key | 是(新会话) | .env 文件每个 Cron 任务自动重载 |
| 模型 | 是(新会话) | config.yaml 中的模型配置 |
| 审批模式 | 部分 | Smart Approval 模式切换需要新会话 |
| 技能 | 是 | 下次会话自动扫描新技能 |
| 记忆 | 是 | 下次会话加载最新记忆文件 |
| 平台配置 | 否 | 需要重启 Gateway |
| 端口 | 否 | 需要重启 Gateway |
2.4 更新流程
标准更新流程(参考 MEMORY.md 中的项目更新步骤):
# 1. 拉取最新代码
cd /opt/hermes-agent
git pull origin main
# 2. 更新依赖(关键步骤——新版本可能引入新的依赖)
uv pip install -e ".[all]"
# 3. 重启服务
systemctl --user restart hermes-gateway
# 4. 验证版本
hermes --version
hermes status
# 5. 检查日志确认无错误
journalctl --user -u hermes-gateway --since "5 minutes ago"
更新时注意事项:
- 记忆文件(
MEMORY.md/USER.md)不会被更新覆盖(它们在.hermes/memories/目录下,不受 git 影响) config.yaml不会被覆盖,但新版本可能引入新配置项(需要手动添加)- 检查 CHANGELOG 或 release notes 了解破坏性变更
- 强烈建议更新前备份
~/.hermes/config.yaml
3. 监控与告警
3.1 Gateway 状态监控
# 查看整体状态(模型、工具、平台连接等)
hermes status
# 查看 Gateway 进程状态
systemctl --user status hermes-gateway
# 实时日志监控(推荐用于排查问题)
journalctl --user -u hermes-gateway -f
# 查看活跃连接
ss -tlnp | grep 5000
# 查看进程资源使用
ps aux | grep hermes
3.2 进度通知
对于长时间运行的任务,可以通过 Gateway 的进度通知机制了解 Agent 状态:
- 平台适配器回调:
tool_progress_callback将工具调用事件推送到聊天界面 - 心跳机制:
_touch_activity()定期更新活跃时间戳 - 子 Agent 进度:
_build_child_progress_callback()批量上报工具调用
3.3 资源监控
关键资源指标和阈值建议:
| 指标 | 阈值建议 | 检查方式 | 超阈值处理 |
|---|---|---|---|
| 磁盘空间 | > 80% 使用告警 | df -h ~/.hermes | 清理旧日志和检查点 |
| 会话数据库 | > 100MB 需清理 | du -sh ~/.hermes/sessions/ | 清理旧会话记录 |
| 日志目录 | > 500MB 需轮转 | du -sh ~/.hermes/logs/ | 配置 logrotate |
| 检查点数据 | > 1GB 需清理 | du -sh ~/.hermes/checkpoints/ | 清理旧检查点 |
| 内存使用 | 根据模型上下文调整 | `ps aux | grep hermes` |
| CPU 使用 | 长时间 > 80% 需关注 | top -p $(pgrep -f hermes) | 检查是否有死循环 |
3.4 Cron 任务监控
# 查看所有定时任务
hermes cron list
# 查看特定任务的历史执行记录
hermes cron history <job_id>
# 手动触发一次调度(不等待定时)
hermes cron tick
# 查看任务输出文件
cat ~/.hermes/cron/outputs/<job_id>_*.md
# 查看调度锁状态(如果有残留锁)
ls -la ~/.hermes/cron/.tick.lock
4. 生产环境 Checklist
4.1 API Key 安全
- API Key 存储在
~/.hermes/.env中,文件权限设为600 -
.env文件不被版本控制(确认.gitignore包含.env) - 生产环境使用专用 API Key,不与开发环境共享
- 定期轮换 API Key(建议每 90 天)
- 使用凭证池(credential pool)分散 rate limit 风险
# 设置正确的文件权限
chmod 600 ~/.hermes/.env
chmod 700 ~/.hermes/
# 验证权限
ls -la ~/.hermes/.env
# 期望输出:-rw------- 1 user user ... .env
4.2 审批模式
- 生产环境配置
approvals.mode: smart或off - 审批超时设为合理值(Gateway 建议 300 秒)
-
command_allowlist定期审计(检查是否有过于宽泛的永久审批) - 了解 YOLO 模式的影响范围(会话级,非全局)
# config.yaml
approvals:
mode: smart # 智能审批(推荐)
timeout: 60
gateway_timeout: 300
4.3 网络安全
- Gateway 端口不直接暴露到公网(使用 Nginx 反向代理)
- 启用 HTTPS(Let's Encrypt 或自签证书)
- 配置防火墙规则限制入站流量
- 平台 Webhook URL 使用 HTTPS
- 考虑 IPv4 强制偏好(某些 LLM API 不支持 IPv6)
# config.yaml
network:
force_ipv4: true # 强制 IPv4(解决某些环境的 IPv6 连接问题)
4.4 日志轮转
- 配置 logrotate 或类似工具
- 日志保留期限符合合规要求(建议 30 天)
- 敏感信息不出现在日志中(redact 机制已内置)
- 日志格式便于 ELK/Grafana 等工具解析
4.5 备份策略
| 数据 | 备份频率 | 恢复方式 |
|---|---|---|
config.yaml | 每次修改后 | 直接复制回 |
memories/ | 每日 | 直接复制回 |
skills/ | 每次修改后 | 直接复制回或 git 同步 |
sessions/ | 每日 | SQLite 备份恢复 |
checkpoints/ | 不需要备份 | 临时性质,可以重建 |
5. 常见问题排查
5.1 认证失败
症状:Agent 无法启动或 API 调用返回 401/403 错误码
排查步骤:
# 1. 检查 API Key 是否配置
cat ~/.hermes/.env | grep API_KEY
# 或使用内置命令
hermes status
# 2. 检查 .env 文件格式(常见格式错误)
# 正确格式:KEY=VALUE(等号两边无空格,值无引号)
# OPENAI_API_KEY=sk-abc123
# 错误格式:
# OPENAI_API_KEY="sk-abc123" (引号会被当作值的一部分)
# OPENAI_API_KEY = sk-abc123 (等号两边有空格)
# OPENAI_API_KEY=sk-abc123 (行尾有不可见空格)
# 3. 检查 API Key 是否有效(直接调用 API 验证)
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
https://api.openai.com/v1/models
# 期望输出:200
# 4. 检查 provider 配置是否正确
hermes setup model
常见原因及解决方案:
| 原因 | 症状 | 解决方案 |
|---|---|---|
| API Key 过期或被撤销 | 401 错误 | 重新生成 API Key |
.env 文件编码问题 | 启动时报 UnicodeDecodeError | 使用 UTF-8 无 BOM 编码重新保存 |
| Provider 配置错误 | 连接超时或 404 | 检查 base_url 是否正确 |
| Rate limit | 429 错误 | 配置凭证池或降低并发 |
5.2 端口冲突
症状:Gateway 启动失败,报 "Address already in use" 错误
排查步骤:
# 1. 检查端口占用
ss -tlnp | grep 5000
# 或
lsof -i :5000
# 2. 查找并终止占用进程
kill -9 <PID>
# 3. 或使用不同端口启动
HERMES_PORT=5001 hermes gateway run
# 4. 如果是僵尸进程(进程已死但端口未释放)
# 等待 60 秒让操作系统回收端口(TCP TIME_WAIT 状态)
# 或设置 SO_REUSEADDR
5.3 上下文溢出
症状:Agent 回复截断、出现 context_length 错误、或行为异常(忘记之前的对话内容、重复提问)
排查步骤:
# 1. 检查压缩配置
grep -A 5 "context:" ~/.hermes/config.yaml
# 2. 查看压缩日志(确认压缩是否正常工作)
grep "Context compression" ~/.hermes/logs/agent.log | tail -20
# 3. 手动触发压缩(在当前会话中)
/compact
# 4. 检查系统提示词大小(可能过大)
# 查看日志中的 prompt_tokens 值
grep "prompt_tokens" ~/.hermes/logs/agent.log | tail -5
常见原因及解决方案:
| 原因 | 症状 | 解决方案 |
|---|---|---|
| 系统提示词过大 | 压缩后仍溢出 | 精简 SOUL.md、AGENTS.md 内容 |
| 压缩阈值过高 | 压缩触发太晚 | 降低 threshold_percent 到 0.40 |
| 工具输出过大 | read_file 读取了大文件 | 限制单次读取的行数 |
| 技能索引过大 | 系统提示词占用过多 | 禁用不需要的技能 |
调优建议:
# config.yaml
context:
engine: "compressor"
threshold_percent: 0.40 # 降低触发阈值,更早压缩
protect_first_n: 3
summary_target_ratio: 0.20
5.4 Agent 卡死
症状:Agent 长时间无响应,无工具调用也无回复
排查步骤:
# 1. 检查进程是否存活
ps aux | grep hermes
# 2. 检查 CPU 和内存使用(内存不足时系统可能 OOM Kill)
top -p $(pgrep -f hermes)
# 3. 查看 Gateway 日志中的活动追踪
grep "activity" ~/.hermes/logs/gateway.log | tail -20
# 4. 检查是否有等待审批的命令
# Gateway 场景:检查是否有 /approve 待处理
# 日志中搜索 "approval_required"
# 5. 检查 API 连接(API 请求可能挂起)
curl -v --max-time 10 https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
常见原因及解决方案:
| 原因 | 症状 | 解决方案 |
|---|---|---|
| API 请求挂起 | 无日志输出 | 检查网络连接,设置请求超时 |
| 工具执行阻塞 | 日志显示长时间在同一工具 | 检查终端命令是否等待输入 |
| 审批等待超时 | 日志显示 "approval_required" | 用户通过 /approve 或 /deny 响应 |
| 死循环 | 日志显示重复工具调用 | 检查 max_iterations 设置 |
| 内存不足 | 进程消失(OOM Kill) | 增加内存或使用更小的模型 |
恢复措施:
# Gateway:通过平台发送中断命令
# CLI:Ctrl+C
# 如果完全无响应,强制重启
systemctl --user restart hermes-gateway
# 检查检查点(可以回滚到之前的状态)
ls -lt ~/.hermes/checkpoints/
5.5 Cron 任务执行失败
症状:定时任务不执行或执行失败
排查步骤:
# 1. 检查任务列表(确认任务存在且 schedule 正确)
hermes cron list
# 2. 查看任务执行历史
hermes cron history <job_id>
# 3. 查看输出文件(包含详细的错误信息)
cat ~/.hermes/cron/outputs/<job_id>_latest.md
# 4. 手动触发测试
hermes cron tick
# 5. 检查 Cron 日志
grep "cron" ~/.hermes/logs/gateway.log | tail -20
常见原因及解决方案:
| 原因 | 症状 | 解决方案 |
|---|---|---|
| Cron 表达式格式错误 | 任务不执行 | 验证表达式(使用 hermes cron validate) |
| 投递目标错误 | 执行成功但未收到消息 | 检查 deliver 配置和平台连接 |
| 脚本路径错误 | "Script not found" 错误 | 确认脚本在 ~/.hermes/scripts/ 中 |
| API Key 过期 | "Authentication failed" | 更新 .env 中的 API Key |
| 不活跃超时 | "Cron job timed out" | 增大 HERMES_CRON_TIMEOUT |
| 锁文件残留 | 任务不执行(上次 tick 崩溃) | 删除 ~/.hermes/cron/.tick.lock |
# 清理残留锁(安全的操作)
rm ~/.hermes/cron/.tick.lock
5.6 内存消耗过高
症状:系统内存不足,OOM Killer 终止进程,系统变慢
排查步骤:
# 1. 监控内存使用趋势
watch -n 5 'ps aux | grep hermes | head -5'
# 2. 检查上下文大小(代理估算)
grep "prompt_tokens" ~/.hermes/logs/agent.log | tail -5
# 3. 检查并发会话数(Gateway)
grep "active sessions" ~/.hermes/logs/gateway.log | tail -5
# 4. 检查系统内存压力
dmesg | grep -i "oom"
调优建议:
- 限制
max_iterations(默认 90)减少长会话的内存累积 - 配置更积极的压缩阈值(
threshold_percent: 0.40) - 限制 Gateway 并发会话数
- 使用较小的模型(减少每 token 的内存开销)
- 增加系统 swap 空间作为紧急缓冲
6. 多环境配置
6.1 Profile 系统
Hermes Agent 支持 Profile(配置文件隔离),每个 Profile 有独立的:
- 配置文件(config.yaml)
- 记忆文件(MEMORY.md / USER.md)
- 技能目录(skills/)
- 环境变量(.env)
- 会话数据库(sessions/)
# 创建新 Profile(用于生产环境)
hermes profile create production
# 切换 Profile
hermes profile switch production
# 查看所有 Profile
hermes profile list
# 在特定 Profile 下运行
hermes --profile production chat
Profile 的实际目录位于 ~/.hermes/profiles/<name>/,切换 Profile 只是改变 HERMES_HOME 环境变量的指向。
6.2 环境变量参考
关键环境变量及其作用:
| 环境变量 | 作用 | 默认值 |
|---|---|---|
HERMES_HOME | 数据目录路径 | ~/.hermes |
HERMES_MODEL | 默认模型 | config.yaml 中的值 |
HERMES_INFERENCE_PROVIDER | 推理 provider | auto(自动检测) |
HERMES_YOLO_MODE | 跳过所有审批 | 未设置(不跳过) |
HERMES_LOG_LEVEL | 日志级别 | INFO |
HERMES_PORT | Gateway 端口 | 5000 |
HERMES_CRON_TIMEOUT | Cron 任务不活跃超时 | 600(10 分钟) |
HERMES_CRON_SCRIPT_TIMEOUT | Cron 脚本执行超时 | 120(2 分钟) |
HERMES_GATEWAY_SESSION | 标记 Gateway 会话模式 | 未设置 |
HERMES_IPV4 | 强制 IPv4 | false |
HERMES_CHECKPOINT_TIMEOUT | 检查点 git 操作超时 | 30 秒 |
TIRITH_ENABLED | 启用/禁用 Tirith 扫描 | true |
TIRITH_BIN | Tirith 二进制路径 | tirith(自动查找) |
DELEGATION_MAX_CONCURRENT_CHILDREN | 最大并行子 Agent 数 | 3 |
调试指南
本节列出部署与运维中最关键的故障排查方法。
Gateway 启动失败
症状:hermes gateway run 执行后进程立即退出,或 systemctl --user status hermes-gateway 显示 failed。
排查步骤:
# 查看服务状态和退出码
systemctl --user status hermes-gateway
# 关注 Active 字段和 Exit Code
# 查看启动失败日志
journalctl --user -u hermes-gateway -n 100 --no-pager
# 检查端口占用(常见原因:5000 端口已被占用)
ss -tlnp | grep 5000
# 或
lsof -i :5000
# 检查配置文件语法
python3 -c "import yaml; yaml.safe_load(open('/root/.hermes/config.yaml'))"
常见原因:端口 5000 被其他进程占用(如 Flask 开发服务器)、config.yaml 语法错误(YAML 缩进问题)、缺少必要的 API Key、Python 依赖未安装完整。对于端口冲突,可使用 HERMES_PORT=5001 hermes gateway run 临时切换端口。
飞书连接断开
症状:飞书平台不再接收和响应消息,Gateway 日志中出现 WebSocket 连接错误。
排查步骤:
# 检查飞书配置
grep -E "FEISHU_APP_ID|FEISHU_APP_SECRET" ~/.hermes/.env
# 测试网络连通性(飞书 API 端点)
curl -s -o /dev/null -w "%{http_code}" https://open.feishu.cn/open-apis/bot/v2/hook/
# 查看 WebSocket 重连日志
grep -i "feishu\|websocket\|reconnect" ~/.hermes/logs/gateway.log | tail -30
# 检查 Gateway 进程是否存活
ps aux | grep hermes
常见原因:FEISHU_APP_ID 或 FEISHU_APP_SECRET 配置错误或过期、网络防火墙阻断了 WebSocket 长连接、飞书应用权限未正确配置。Gateway 的平台适配器支持自动重连,但如果凭证无效,重连会反复失败。确认飞书开放平台后台的应用状态是否正常。
服务异常退出
症状:Gateway 进程突然消失,没有正常的关闭日志。
排查步骤:
# 检查 OOM killer 日志(最常见原因)
dmesg | grep -i "oom\|killed process" | tail -10
# 或
journalctl -k | grep -i "oom" | tail -10
# 检查 coredump
ls -la /var/lib/apport/coredump/ 2>/dev/null
ulimit -c # 检查 core dump 是否启用
# 查看 systemd 服务退出详情
systemctl --user status hermes-gateway
# 关注 Exit Code:
# 137 = SIGKILL(通常为 OOM killer)
# 139 = SIGSEGV(段错误)
# 1 = 应用层错误
# 检查系统内存使用历史
sar -r 2>/dev/null | tail -20 # 需要 sysstat 包
常见原因:OOM killer 终止(上下文过大的会话消耗过多内存)、段错误(Python C 扩展崩溃)、systemd 因 MemoryMax=4G 限制杀掉进程。解决方案包括增大 MemoryMax 限制、降低 agent.max_turns、配置更积极的压缩阈值。如果频繁 OOM,考虑拆分为多个 Profile 分散负载。
更新后异常
症状:执行 git pull 和 pip install 更新后,Gateway 启动报错或功能异常。
排查步骤:
# 检查是否有未提交的本地更改(可能导致合并冲突)
cd /opt/hermes-agent
git status
# 查看更新内容
git log --oneline -10
# 检查依赖冲突
pip check 2>&1 | head -20
# 查看具体错误日志
journalctl --user -u hermes-gateway --since "5 minutes ago"
常见原因:本地有未提交的修改导致 git pull 合并冲突、新版本引入了不兼容的配置格式变更、pip install 依赖冲突(如两个包要求不同版本的同一依赖)。更新前应先 git stash 保存本地更改,更新后用 git stash pop 恢复。如果依赖冲突严重,尝试 uv pip install -e ".[all]" --force-reinstall。更新后务必检查 CHANGELOG 了解破坏性变更。
思考题
-
Systemd 用户服务 vs 系统服务:为什么推荐使用 Systemd 用户服务(
systemctl --user)而非系统级服务(systemctl)?考虑权限隔离(用户服务以当前用户身份运行,可以访问用户文件)、文件访问(.hermes/目录的所有权和权限)和用户注销后的行为(linger 机制)。 -
日志中的敏感信息:尽管 Hermes Agent 内置了输出脱敏(
redact_sensitive_text),哪些信息可能仍然泄露到日志中?考虑工具调用的参数(如write_file的文件路径中可能包含用户名)、API 响应中的元数据、以及错误消息中的堆栈跟踪。如何设计一个更完善的日志脱敏策略? -
多实例部署的会话管理:如果部署多个 Gateway 实例(负载均衡场景),会话状态(审批队列、压缩状态、YOLO 模式)如何共享?当前架构中
_session_approved和_gateway_queues是进程内全局变量,不能跨进程共享。考虑使用 Redis 或数据库作为共享状态存储。 -
Cron 任务的幂等性:
advance_next_run()在执行前就推进了next_run_at。如果任务执行失败,这个设计会导致跳过调度吗?对于"每隔 5 分钟检查一次"这种任务,如果某次执行失败了,下一次执行会在下一个 5 分钟周期正常触发。但对于"每天早上 9 点发送报告"这种任务,如果执行失败,报告会延迟到第二天。如何确保关键任务不丢失? -
配置热更新的边界:哪些配置可以在运行时更新而无需重启?哪些必须重启才能生效?如果需要实现"零停机"配置更新,应该采用什么策略?考虑 Gateway 的滚动重启(启动新实例、等待旧实例处理完当前请求、关闭旧实例)。
本篇涉及的源文件和配置:
Dockerfile— Docker 容器构建run_agent.py— Agent 初始化与生命周期管理gateway/run.py— Gateway 启动与配置加载cron/scheduler.py— Cron 调度、文件锁、不活跃超时hermes_constants.py— 环境变量、路径常量、IPv4 偏好hermes_cli/config.py— 配置文件管理、热更新