diff --git a/phoneuse/hermes-gateway-ops-notes.md b/phoneuse/hermes-gateway-ops-notes.md new file mode 100644 index 0000000..fd96c12 --- /dev/null +++ b/phoneuse/hermes-gateway-ops-notes.md @@ -0,0 +1,73 @@ +# 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 ` → `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`**——这是一个需要常驻的独立进程,配完凭证不会自动跑起来。 + +**修法**: +```bash +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` 模块就能读,不需要装命令行工具): +```python +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 分钟而不是几次失败后就自己停下来的根本原因。 + +**修法**: +```yaml +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 模式真的生效了,不是配置摆设。