fix: 首字停摆自动重试 — init 后首个 token 停摆时自动重发 query#265
Conversation
收到 system:init 后,首个 assistant token 因网关/API 瞬时抖动迟迟不到, 300s 后空闲超时直接判失败,让用户白等 5 分钟还收到报错。这是可重试的 瞬时故障,不该当作终态错误。 - executor 跟踪 firstTokenSeen(assistant 消息 / stream_event / 工具活动 任一到达即置位)与 timeoutKind(idle / hard) - catch 分支识别「idle 超时 + 从未收到首个 token」组合,带退避递归重发 query(保留 resume,会话状态未推进),上限取 config - 新增 CLAUDE_FIRST_TOKEN_STALL_RETRIES 配置(默认 2,设 0 关闭) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
用 fake timers 模拟 init 后停摆,覆盖四个场景: - 停摆一次后重试成功 - 超过 firstTokenStallRetries 上限后返回 idle timeout 错误 - 首个 token 已到达的普通空闲超时不重试 - firstTokenStallRetries=0 时关闭重试 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @lishuceo's task in 7m 13s —— View job Review: 首字停摆自动重试 (PR #265)✅ Approved — clean, well-scoped fix with solid regression coverage.
What the PR doesDistinguishes Correctness — looks good ✅
Observations (non-blocking, no change required)
Tests — comprehensive ✅The 4 fake-timer cases cover the matrix well: stall-then-recover, exhaust-retries, no-retry-after-first-token (the key guard), and |
背景
土豆儿等 bot 偶发
Query idle timeout after 300s with no activity (total elapsed: 303s)。查日志确认失败模式:即:收到
system:init后,首个 assistant token 因网关/API 瞬时抖动迟迟不到,300s 后空闲超时直接判失败 —— 让用户白等 5 分钟还收到报错。这是可重试的瞬时故障,不该当作终态错误。改动
firstTokenSeen—— assistant 消息 /stream_event/ 工具活动任一到达即置位timeoutKind—— 区分idle(空闲,可能首字停摆)与hard(硬性总超时,不重试)idle超时 + 从未收到首个 token」组合,带退避(1.5s)递归重发 query。保留resume(会话状态未推进,重发安全),上限取 config。CLAUDE_FIRST_TOKEN_STALL_RETRIES配置(默认2,设0关闭)。只在「首字停摆」时重试 —— 一旦有任何 token / 工具活动产出,即视为普通空闲超时,不重试(避免重复已产出的工作)。
测试
src/claude/__tests__/executor.test.ts新增 4 个回归用例(fake timers 模拟停摆):firstTokenStallRetries=0时关闭重试全量回归:
1688 passed(唯一失败memory/quality.test.ts > 当前工作重点在 base 上同样失败,是本工作区缺DASHSCOPE_API_KEY导致的环境问题,与本 PR 无关)。🤖 Generated with Claude Code