diff --git a/docs/goose-v149-103-cutover-runbook.md b/docs/goose-v149-103-cutover-runbook.md index d43469f..9962f31 100644 --- a/docs/goose-v149-103-cutover-runbook.md +++ b/docs/goose-v149-103-cutover-runbook.md @@ -1,6 +1,6 @@ # Goose v1.49 · 103 生产切换 Runbook -> **状态:** 草案(本地 v1.49 证据已齐;**未授权**在 103/105 执行任何操作) +> **状态:** 已执行。103 自 **2026-09-10 15:57** 起 9 实例运行 `releases/goose-20260910-v149-m1`(sha256 前缀 `0204c12a085b094b`,构建于开发机,非 103 本机);1.41 回滚二进制 `goosed-20260729-v141-m1` 与备份 `backups/pre-v149-pool-20260910-153230` 保留。上线后审计与后续修复见 [incidents/2026-09-23-goose-v149-prod-log-audit.md](./incidents/2026-09-23-goose-v149-prod-log-audit.md)。后续二进制替换仍须单独批准,且必须从干净 commit 构建并记录 sha。 > > **适用范围:** 103(`114.85.107.50` / Mac Studio arm64)将 goosed 运行时从 **1.41 native** 升级到 **v1.49 TKMind 补丁树** > diff --git a/docs/incidents/2026-09-23-goose-v149-prod-log-audit.md b/docs/incidents/2026-09-23-goose-v149-prod-log-audit.md new file mode 100644 index 0000000..d92edd2 --- /dev/null +++ b/docs/incidents/2026-09-23-goose-v149-prod-log-audit.md @@ -0,0 +1,50 @@ +# 2026-09-23 Goose v1.49 上线后生产日志审计 + +103 自 2026-09-10 15:57 起 9 实例(`:18006`–`:18014`)运行 `releases/goose-20260910-v149-m1`。本文记录 2026-09-10 至 2026-09-23 的 goosed 日志审计结果、根因与修复位置。 + +## 现象与根因 + +| # | 现象(次数) | 根因 | 修复 | +|---|---|---|---| +| 1 | `platform_extensions/web.rs` panic(16,`:65` 与 `:78`) | 按字节截断到 50000 切在多字节字符中间;`to_lowercase()` 后的下标用于切原字符串,Unicode 小写改变字节长度后越界。`parse_ddg_results` / `extract_attr` 同类问题 | tkmind_go:字符边界截断 + ASCII 小写(长度不变)。见 `fix/v149-prod-log-fixes` | +| 2 | reply 任务 panic 后用户等满 Portal 超时(15/20 分钟) | `RequestGuard` drop 时只清理请求,不发 Error / Finish | tkmind_go:panic 时 guard 补发 `Error` + `Finish{reason:"aborted"}` | +| 3 | `Harness remember failed`(6368,每轮都失败) | 103 上 `harness/bin/mysql2-cli.mjs`(未进 Git 的 Docker 时期脚本)写死 `/opt/portal/node_modules/mysql2`,native 部署下不存在 | Memind:`scripts/goosed-harness-mysql2-cli.mjs` 随 Portal runtime 下发并用 `createRequire` 解析;tkmind_go:harness 连续失败 3 次熔断 10 分钟、stderr 只记尾部 600 字符 | +| 4 | 扩展加载 `Too many open files`(118,2026-09-22 19:00 至 09-23 01:00,含 `sandbox-fs`) | launchd `maxfiles` 软上限 256 | Memind:`run-goosed-native.sh` 启动前 `ulimit -n 65536`(103 `kern.maxfilesperproc=122880`);`scripts/goose-v149-pool-hardening-103.sh` 负责现网应用 | +| 5 | `Tool 'write_file' was not advertised for this model turn`(41,每天都有,主要 `write_file`) | Portal 提示词与 skills 用裸名 `write_file`,生产只开放 `sandbox-fs__write_file`;v1.49 名称纠正只处理 `ext.tool`,不处理丢前缀 | tkmind_go:`recover_mangled_tool_name` 支持裸名映射到唯一的 `ext__name`(有歧义不纠正;在权限检查前完成,经典 loop 与状态机共用) | +| 6 | provider 403 `SUBSCRIPTION_NOT_FOUND`(54)、429 `API_KEY_QUOTA_EXHAUSTED`(22)、401(7)、空回复放弃(13) | `api.0d000721.cv` 中转订阅 / key 失效(配置在模型中心,不在代码) | 09-16 之后未再出现,已通过配置处理;无代码改动 | + +同期 loop 守卫日志均为 0(max turns、重复、上下文超限、compact),原因是:重复检测实际未启用(`RepetitionInspector::new(None)` 且 `inspect(&self)` 不保存状态),Finish 统一为 `reason:"stop"`,无从区分。 + +## Loop 守卫补强(同分支) + +- `RepetitionInspector` 改为按 session 保存状态,新的真实用户消息重置;阈值读 `GOOSE_MAX_TOOL_REPETITIONS`(未设或 0 关闭)。 +- Finish `reason` 区分 `stop` / `max_turns` / `empty_response` / `cancelled` / `error` / `aborted`,并打 `Reply finished` 日志。 +- Portal `consumeSessionEventsUntilFinish` 返回 `finishReason`,写入 `h5_agent_run_events.session_finished`。 +- Portal 空闲超时 `MEMIND_SESSION_REPLY_IDLE_TIMEOUT_MS`(默认关,最小 30s):仅在没有进行中的工具调用时计时,只由 Message / Notification / UpdateConversation 重置;超时码 `SESSION_REPLY_IDLE_TIMEOUT` 可走交付物恢复。 + +建议 canary 值:`GOOSE_MAX_TURNS=100`、`GOOSE_MAX_TOOL_REPETITIONS=5`、`MEMIND_SESSION_REPLY_IDLE_TIMEOUT_MS=300000`。取值需先按生产每轮 turn 分布复核。 + +## 可追溯性问题 + +- 运行中二进制 sha256 前缀 `0204c12a085b094b`,与开发机 `tkmind_go-v149/target/release/goose`(2026-09-10 15:51)一致,不是 103 上构建的(103 构建产物为 `4fdec430…`,未使用)。 +- 构建时工作区含未提交的 `crates/goose-providers/src/openai.rs`(DashScope `max_tokens` 上限)。该补丁在 `fix/v149-prod-log-fixes` 上单独提交,之后的二进制须从干净 commit 构建并记录 sha。 +- 103 上 `/Users/john/Project/tkmind_go-v149` 是断开的 worktree(指向不存在的 `.git/worktrees`),无法在 103 查源码版本。 + +## 排查入口 + +```bash +# 结构化日志(v1.49,每实例一份 XDG_STATE_HOME) +ssh john@114.85.107.50 'ls /Users/john/Project/tkmind_go-native/state/*/state/goose/logs/cli/' +# panic 与启动日志 +ssh john@114.85.107.50 'grep -ah "panicked at" ~/Library/Logs/goosed-native-180*.log | sort | uniq -c' +# 运行中二进制 +ssh john@114.85.107.50 'shasum -a 256 /Users/john/Project/tkmind_go-native/releases/goose-*' +``` + +## 上线顺序与回滚 + +1. Portal runtime 发布(带 `scripts/goosed-harness-mysql2-cli.mjs` 与 finish reason / 空闲超时代码;空闲超时默认关)。 +2. `goose-v149-pool-hardening-103.sh --dry-run` → `--yes`(ulimit + `CODEX_HARNESS_MYSQL`,逐实例重启并健康检查)。回滚:`--rollback`。 +3. 新 goosed 二进制(从干净 commit 构建)按 cutover runbook 替换,旧二进制保留在 `releases/`。 + +每一步都需单独批准。