hermes-gateway-ops-notes.md 7.2 KB

Hermes Agent 在 Pixel 上的运行期踩坑记录(root 失效 / gateway / feishu / 卡顿排查)

记录 2026-06-25 同一台 Pixel 10 Pro Fold 上,在 hermes-termux-install-notes.md(装机)之后,日常使用/运维阶段踩的坑。跟装机笔记的区别:这次问题都是"装完之后过了一段时间才暴露"的运行期问题。按重要性排序。

1. 系统自动 OTA 升级会让 root 静默失效

现象:adb shell su -c id 突然报 su: inaccessible or not found,但 Magisk App 还在、bootloader 解锁状态还在。

真因:Android 在后台自动下载并安装了系统更新(ro.build.fingerprint 的 build 号变了,如 CP1A.260405.005 → CP1A.260505.005)。A/B 分区机制下,OTA 会把新系统刷进另一个 slot 再切过去,新 slot 的 init_boot 是官方原版、没有 Magisk patch,root 因此跟着没了。不会影响 /data 分区,app 和数据都完好。

修法:重新走一次 root 流程,跟最初 root 这台设备完全一样:

  1. 查当前 ro.build.fingerprint,去 developers.google.com/android/images 下对应 build 的官方出厂镜像。
  2. 从压缩包里提取 init_boot.img(不是 boot.img)。
  3. 推到手机,用 Magisk App「选择并修补一个文件」patch。
  4. adb reboot bootloader → fastboot flash init_boot <patched文件> → fastboot reboot。
  5. 注意 fastboot flash 默认刷的是当前激活的 slot(这次是 init_boot_a,上次首次 root 时是 init_boot_b)——这是正常的,OTA 切了 slot,不用手动指定。

没找到办法:Pixel 原生系统的"系统更新"设置页(android.settings.SYSTEM_UPDATE_SETTINGS,由 com.google.android.gms 处理,不是独立 app)只有"检查更新"按钮,没有"自动下载/自动安装"开关。OTA 逻辑焊在 GMS 里,关掉整个 GMS 代价太大(牵连 Play 商店/推送/定位等)。唯一现实的应对:OTA 弹"立即重启安装"提示时别点,root 不会无故消失;万一升级触发了(比如设备闲置+充电时的夜间自动安装),重新走一遍上面 5 步即可,10-20 分钟能搞定。

2. hermes gateway run 才是真正监听消息平台的进程——hermes chat -q 测试不代表能收发消息

现象:飞书 App ID/Secret 配置好、hermes doctor 显示 ✓ feishu、hermes chat -q 也能正常出结果,但在飞书里发消息给 bot 完全没反应。

真因:hermes doctor/hermes chat -q 只验证凭证和模型链路,不会启动任何消息平台的监听器。真正负责连飞书 WebSocket 长连接、接收/回复消息的是 hermes gateway run——这是一个需要常驻的独立进程,配完凭证不会自动跑起来。

修法:

cd ~/hermes-agent
setsid nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &

看日志确认 [Feishu] Connected in websocket mode + ✓ feishu connected 才算真正接通。

坑:hermes gateway stop 对这种手动 nohup 起的进程不一定杀得干净——它按自己的 pidfile 记账,跟手动起的进程脱节,执行后会显示"✓ Stopped"但 ps 一查进程其实还在。每次重启 gateway,stop 之后务必 ps -A | grep "hermes gateway" 确认真的没残留,否则会有两个进程同时连着同一个飞书 WebSocket,导致消息被重复处理两次。

3. 卡死的 cron 任务才是真正拖慢整机的元凶(不是模型、不是编译)

现象:用户在 Termux 里直接跟 hermes 交互(hermes --yolo),感觉整体卡顿、同一个动作反复重试很多次。第一反应怀疑是模型能力不够、或者代码编译有问题。

真因:恢复设备备份带过来的一个定时任务(wechat-group-digest cron job)在 gateway 启动时被触发,调 terminal 工具跑 sqlite3 ... 查数据库,但 Termux 里根本没装 sqlite3 命令行程序(hermes-agent 自己只用 Python 内置的 sqlite3 模块,没装独立二进制)。每次调用都失败,Agent 没有意识到这是同一个失败在反复重演,傻乎乎重试了 151 次,每 ~10 秒一次,持续了 45+ 分钟,期间一直在发真实 API 请求、占用设备 CPU——这才是让整机变卡、让用户那边感觉"动作重复很多次"的真凶,跟模型水平、跟我们编译的代码都没关系。

排查方法(比直接读 gateway.log 更可靠,因为日志级别可能不打印每条消息细节):直接查 ~/.hermes/state.db(SQLite,Python 内置 sqlite3 模块就能读,不需要装命令行工具):

import sqlite3, time
conn = sqlite3.connect("~/.hermes/state.db")
conn.row_factory = sqlite3.Row
cur = conn.cursor()
# 找进行中(未结束)的会话,看 tool_call_count 是否异常高
cur.execute("SELECT id, model, tool_call_count, started_at FROM sessions WHERE ended_at IS NULL")
# 找某个 session 里哪个工具被反复调用
cur.execute("SELECT tool_name, COUNT(*) FROM messages WHERE session_id=? AND tool_name IS NOT NULL GROUP BY tool_name", (sid,))

sessions.tool_call_count 异常高(大几十到上百)、ended_at IS NULL 持续很久不变化,基本就是卡死的信号。

修法:

  1. pkg install sqlite 把缺的二进制装上。
  2. 重启 gateway(杀掉卡死会话所在的进程),确认旧 session 在 DB 里被标成 end_reason: cron_complete,且新消息时间戳不再增长。

4. tool_loop_guardrails.hard_stop_enabled 默认是关的——同一失败会无限重试

真因:config.yaml 里 tool_loop_guardrails 区块默认 hard_stop_enabled: false,只有 warnings_enabled: true——也就是说连续失败只会在日志里打警告,不会自动放弃。这正是上面 #3 那个 cron 任务能空转 45 分钟而不是几次失败后就自己停下来的根本原因。

修法:

tool_loop_guardrails:
  hard_stop_enabled: true   # 原来是 false

默认阈值:连续 5 次完全相同的失败、或同一工具失败 8 次、或 5 次"无进展"就自动放弃。改完要重启相关进程(gateway / 当前 CLI 会话)才生效,config.yaml 是启动时读一次,不是热加载。

5. Kimi Coding Plan 的模型标识符 / Thinking 模式确认

  • kimi-for-coding 就是官方文档里第三方 agent 该用的"统一模型标识符",不存在单独叫"kimi 2.7 code"的型号需要切换过去——别被这种命名怀疑搞乱配置。
  • 官方文档提了一句容易踩坑的点:"K2.7 Code 模型需要开启 Thinking 模式,否则请求会被路由到 K2.6"。hermes-agent 的 plugins/model-providers/kimi-coding/__init__.py 默认逻辑:只要 config.yaml 里 agent.reasoning_effort 是空字符串(默认值),就会自动在请求里带上 extra_body.thinking = {"type": "enabled"};只有显式设了 reasoning_effort: low/medium/high 才会改用 top_level.reasoning_effort 而不带 thinking 字段。
  • 验证方法:直接拿 .env 里的 key 调一次 https://api.kimi.com/coding/v1/chat/completions,带上 extra_body.thinking.enabled,看响应里 message.reasoning_content 字段有没有真实的推理文本——有内容就说明 thinking 模式真的生效了,不是配置摆设。