fix(tier2): 控制面 SSE 等待防永久卡死(read 超时;008 实证)

cp-smoke-008 实证:控制面在 attempt 0 卡死 60min。根因 = _wait_for_turn_end 的总/空闲超时检查写在
aiter_lines 的 line 循环【体内】,而 httpx read=None;当 SSE 流【完全静默】(008:服务端 3 次 redis
超时打断了事件发布 → 既无 data 也无心跳)时 aiter_lines 永久 await,没 line 进来 → 超时检查永不触发 →
控制面无限挂。修:read=idle_timeout_s(300s),静默超它即 ReadTimeout → except 兜住 → 据 on-disk 评门继续。
不误杀正常回合(agent 内部 run_gates chrome ~60s << 300s)。

注:008 的不收敛是【基建+本 bug】所致,非生成质量——收敛修复(driver)已由 007 现成游戏重跑独立坐实
(accept/九门+富游戏三门全绿)。008 另暴露:工作室设计团队 >240s 超时 degrade 回单 agent(仍产出
design_text);redis 偶发超时(mini-desktop→mini-infra 链路抖动)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zizi 2026-06-24 13:53:04 +00:00
parent 3cd4769f6b
commit fdc9ecd50b

View File

@ -170,8 +170,12 @@ async def _wait_for_turn_end(base_url: str, agent_id: str, session_id: str, *,
print(f"[tier2-control] 订阅 SSE 等本回合结束: session={session_id} agent={agent_id} "
f"(总超时 {timeout_s:.0f}s / 空闲超时 {idle_timeout_s:.0f}s)", flush=True)
try:
# read=None:逐行读流不受单次读超时打断;总时长 / 空闲由本函数显式判(下方 elapsed/idle)。
timeout_cfg = httpx.Timeout(connect=15.0, read=None, write=15.0, pool=15.0)
# read=idle_timeout_s(2026-06-24 cp-smoke-008 实证修复):此前 read=None,SSE 流【完全静默】时
# (008:服务端 3 次 redis 超时打断了事件发布 → 既无 data 也无心跳)aiter_lines 永久 await,
# 而总/空闲超时检查写在 line 循环【体内】、无 line 进来就永不触发 → 控制面卡死 60min(实证)。
# 把 read 设成 idle_timeout_s:静默超过它即 httpx.ReadTimeout → 下方 except 兜住 → 据 on-disk 评门继续。
# idle_timeout_s=300s 远大于 agent 内部 run_gates(chrome ~60s)的正常静默间隙,不会误杀正常回合。
timeout_cfg = httpx.Timeout(connect=15.0, read=idle_timeout_s, write=15.0, pool=15.0)
async with httpx.AsyncClient(timeout=timeout_cfg) as http:
async with http.stream("GET", url, params=params, headers=headers) as resp:
if resp.status_code >= 300: