跳转至

myopenclaw v0.2.6

补丁版本(2026-11-12)。三件事:长截图不再整轮失败、兜底模型补上钥匙、 网关隔离的启动时序钉死。两处改动都在镜像里 —— 必须重建 hermes 镜像。

本版变更

长截图超过视觉尺寸上限 → 缩图重试,不再整轮失败

现场(贝小爷 / finance profile):用户发了一张 1220×24948 的安卓长截图,DeepSeek 回

400 .messages[3].image[0]: You have uploaded an unsupported image. Please make sure
    your image is valid and has one of the following formats: webp, png, jpeg, and gif.

这句话里没有任何尺寸词汇 ⇒ 上游 error_classifier 把它判成 format_error (retryable=False)⇒ 直接终止,用户只看到「⚠️ The model provider failed after retries」。

实测结论:格式没问题,是尺寸。同一张图缩到最长边 8000px(391×8000)后返回 200。 而上游本来就有一条「超限 → 缩图重试一次」的恢复链(turn_recovery 的 image-shrink 分支, 默认阈值恰好也是 8000),只是没被触发。

  • 修法:构建期补丁 docker/hermes/patch-image-limit.py 把该措辞加进 _IMAGE_TOO_LARGE_PATTERNS → 分类变成 image_too_large → 走上游自带的缩图重试
  • 锚点手术:上游结构变了就构建失败,不会静默出厂没打补丁的镜像(与飞书卡片补丁同一套约定)
  • 守卫 tests/test_hermes_image_limit_patch.py(11 项:词表与现场原文一致、锚点只命中一次、 幂等、失配非零退出、Dockerfile 真的 RUN 它)
  • 端到端验证:同一张原图现在 23s 出概括,日志可见 image-shrink recovery: re-encoded 1 image part(s)

兜底模型:从"写了但空转"到真能接管

查这条链时发现的真缺口,两层:

  • finance 一个兜底都没配(fallback_providers: []、没有 fallback_model)—— 守卫的 LIVE_CONFIGS 只列了三份 profile,那份配置没人看
  • 另外三个 profile 虽然写了 zai/glm-5.3-flash,容器里却没有凭据,实测直接报 No usable credentials found for provider 'zai'. Set GLM_API_KEY, ZAI_API_KEY, Z_AI_API_KEY. —— 也就是说兜底从来没真正接管过一次

  • compose 给四个 hermes 容器注入 GLM_API_KEY=${GLM_API_KEY:-}(复用 .env 里那把智谱 key)

  • scripts/ensure_hermes_model.py 每次 start.sh 幂等补齐 fallback_providers / fallback_model / agent.reasoning_overrides (glm-5.3-flash 强制思考,缺那行会被智谱 400);只补缺失或 canonical 值, 操作者手选的别的兜底/档位原样保留
  • 守卫 tests/test_hermes_fallback_glm.py 的 LIVE_CONFIGS 扩到四份 profile

网关隔离:启动时序

基础镜像的 s6 会在每个容器里、按共享卷上的 gateway_state.json,给四个 profile 都建 gateway-<profile> 服务。"一个容器只跑自己的 agent"靠 entrypoint-wrapper.sh 的后台清扫维持。 而清扫原来只等 90 秒,这次重建时 stage2 的 chown -R /opt/data/home 在 virtiofs 上跑了 10 分钟 —— 清扫等到超时、一轮没扫,却仍打印「✅ gateway 隔离完成」。后果:

容器现象 用户感知
每容器跑着 gateway-default + gateway-finance,自家的 gateway-coder/gateway-daoyuan exit 78(Another local Hermes gateway is already using this Feishu app_id) 爱码士/道元由"冒充者"在服务;爱玛士完全没反应(同一 app 的 WS 被游荡抢过,杀掉后飞书仍投给已死连接,日志一条错误都没有)
  • GW_SWEEP_WAIT 90s → 900s;循环外重扫(wait=0 时 found 从未赋值)
  • 等不到服务树 → 大声告警,不再假报「✅ 完成」
  • 终检后对本 profile 补一次 s6-svc -u(被游荡抢过 app_id 的自家网关,外部清干净后才能起来)
  • 守卫 tests/test_entrypoint_gateway_isolation.py 增两条:服务树缺失不许报完成 / own 崩了要拉起

文档

  • docs/hermes-topology.md 新增「网关隔离:一个容器一个网关」: 机制、两类故障现场表、处置口诀、重建前备份 lark-cli user token
  • README 补「一个容器一个网关」「模型与兜底」两条
  • docs/kanban.md、docs/hermes-channels.md 交叉链接到该节

升级注意

  1. 重建 hermes 镜像并重建四个容器:
    docker compose build hermes
    docker compose up -d hermes hermes-coder hermes-finance hermes-daoyuan
    
    ⚠️ 不要用 ./scripts/start.sh --build:它给所有镜像加 --build,本机若没克隆 ../yuque_mcp_server,会因构建上下文缺失让整条命令失败。
  2. 重建前备份 lark-cli 的 user token(存容器内 keychain,不在挂载卷上):
    docker compose cp <容器>:/root/.local/share/lark-cli /tmp/lark-cli-<容器>
    # 重建后拷回:docker compose cp /tmp/lark-cli-<容器>/. <容器>:/root/.local/share/lark-cli/
    # 再 chown -R root:root、chmod -R go-rwx
    
  3. 启动会慢约 10 分钟:stage2 的 chown -R /opt/data/home(virtiofs),期间容器 Up 但网关不在线。新的清扫会等最多 900s,等不到会告警。这段时间不要手动 s6-svc -u 去"补网关",会重新引入游荡。
  4. 回滚:镜像回滚 + 保留旧 docker-compose.yml 的 env 即可;配置层的 fallback_* / reasoning_overrides 不碍事(缺凭据时只是不生效)。

验收

  • pytest tests/:1158 passed, 1 skipped(本版新增 25 项守卫)
  • mkdocs build --strict:通过;docker compose config --quiet:通过
  • 真机 · 长截图:原来那张 24498px 的图现在能读,日志 image-shrink recovery
  • 真机 · 网关:四容器各 1 个网关在岗(其余 down),四个飞书 ✓ connected, hermes-coder 独占 dispatcher 锁
  • 真机 · 兜底:四个容器 GLM_API_KEY 已注入,glm-5.3-flash 可用
  • 运维清理:删除 ~/.hermes/logs/mcp-stderr.log(1.2 GB —— codegraph stdio MCP 在 2026-07-20~09-14 间每 4.3 秒失败一次的重试残骸,共 113 万次 FileNotFoundError; logs/ 本就不在备份范围,删除不影响快照)

已知边界

  • 兜底的"真接管"没有做故障注入实测:只验证了凭据可用 + 配置形状正确;真要验证得 人为让主模型失败一次
  • repo-scanner MCP 自 2026-11-11 23:00 起 degraded(每 5 分钟重试 5 次后 parking); 容器内探活返回 200,靠 300s 自探恢复。若反复出现需另查
  • 长截图缩到 8000px 后细节会损失:1:20 的长图宽度只剩 ~391px,只适合"看懂大意"
  • 现在没有 stdio 型 MCP 了,所以 codegraph 那种"重试写满磁盘"的循环没有复发路径; 但没有加"单文件超 500MB 告警"的兜底