{"content":"【assist-base-service 开发日志 2026-11-16】commit 138ca87 fix: llm relay failover hardening(LLM 中继号池稳定性修复轮)\n\n背景:收费 LLM 中继号池体检发现三个实证问题——① 凌晨 00:15–01:15(CST) deepseek-4.1-flash 成功率曾跌至 4–6%(ark#1 月配额尽 + 国内上游 transport 抖动叠加),glm-5.3-flash 24h 请求级成功率仅 68.9%;② 最近 1 小时 21 笔 glm 失败全部为非流式请求在 ~60s 整齐簇集死亡、errorCode 全是 transport、终端落在 scnet 兜底跳——归因发现 60s 墙来自客户端断开(key_state 细节=context canceled 而非 deadline exceeded;服务端 conf request_timeout_seconds=300),且中间跳失败不落 usage 行完全不可见,一次客户端退出会把后续两跳也秒失败并伪装成 transport 终态行;③ failover 主循环每跳新起 300s 全新窗口,违背 conf 字段 \"covers a full stream\" 的整请求语义,最坏 3×300s 墙钟与上游浪费。\n\n本次修复(服务端三件套):\n- F1 请求级共享预算:llm.request_timeout_seconds 恢复文档语义——failover 各跳共用一个 request 级 deadline,每跳只拿剩余额度(对齐 JEV RemainingLlmJevAttemptTimeout 既有模式),剩余 ≤100ms 不再发起新跳;doomed 请求墙钟与上游代价比 3×300s 封顶为 300s。\n- F2 client_gone 错误类:classifyTransportErr 区分 context.Canceled(调用方已断开)与 deadline exceeded(自身预算耗尽,仍归 transport);client_gone 不 failover、不 park key(applyUpstreamErrorClass 仅记 lastError)、failure 行落 errorCode=client_gone——看板不再把客户端退出误读成上游故障;chat/responses/JEV/audio 全部 Do 错误出口统一分类。\n- F3 中间跳日志:每次 failover 前落一行 llm relay failover(requestId/attempt/model/provider/fingerprint/class/msg≤120),补上\"中间尝试不落库\"的观测盲区——本次 bigmodel 被静默跳过 211 次的归因窗口,下次故障一小时内可定位。\n\n验证:gofmt 全部改动文件;全量 go test ./... 17 包全绿;新增 4 个针对性测试(TestLlmChatAttemptBudget / TestApplyUpstreamErrorClassClientGone / TestCallUpstreamChatCompletionClientGone / TestShouldFailoverUpstreamClientGone);既有 transport 语义逐字节保持(transport 本就不 park key)。改动 6 文件 +190/−16。另:ark 月配额今晚 23:59:59 CST 重置,重置后流量自动回 ark 首跳,按既有备案观察 TTFT 埋点。","contentType":"text/plain;utf-8","attachments":[],"quotePin":""}