批量跑任务最烦的不是报错,而是卡住:几十个请求里总有两三个转圈转到超时,整批结果被迫重来。其实超时是 API 调用里最常见的「正常故障」——上游排队、网络抖动、模型生成长回复,都会让个别请求偶尔变慢。这篇讲清超时发生在哪一段、上限该怎么设,以及重试的正确姿势。
超时不是「网络差」,先定位卡在哪一段
一次 API 请求要过三段路:你的程序连上服务器(建连)、请求发出去等模型生成(等待)、结果完整传回来(传输)。任何一段超过等待上限,客户端就会主动掐断并报 timeout。「超时」不等于「服务挂了」——很多时候只是那一次的排队时间超过了你设的上限。诊断第一步看错误类型:连接超时(connect timeout)多半是网络或代理问题;读取超时(read timeout)是服务器收了请求但迟迟没吐完结果,这才是生成式 AI 场景的主战场。
三层超时上限,谁最短谁说了算
你的程序、中间代理、网关和上游模型各自有超时设置,整条链路里最短的那段说了算。给客户端设置时可以参考:
- 连接超时:建连阶段的等待上限,设 5~10 秒就够,太长只会让真正的网络故障暴露得更慢
- 读取超时:等响应的时间。生成式任务建议给足 120~180 秒——模型排队或长文生成都可能超过一分钟,设太短会把本来能成功的请求掐死在半路
- 总时长上限:有些框架还有整体 deadline,注意把生成长回复的时间算进去,别让总时长比读取超时还短
还有个常被误解的点:流式输出本身不改变总耗时,但连接会一直有数据流动,能「喂」住那些按空闲时间计时的中间层。所以长回复任务优先开流式(stream: true),既边生成边看到内容,也大幅降低被中途掐断的概率。AIGC微风网关对流式响应内置了心跳保活,空闲期也会定期下发心跳,就是为了减少这种误伤。
重试:指数退避,且只重试「安全」的请求
抖动既然正常,重试就是标配。但无脑重试会把小问题放大成雪崩——服务端正在过载时,立刻重发只会火上浇油。记住两条铁律:
- 只重试「值得重试」的错误:429(限流)和 5xx(服务端故障)可以重试;400/401/403 这类参数或密钥错误,重试一万次也不会通,先改请求再说
- 指数退避加随机抖动:第一次等 1 秒、第二次 2 秒、第三次 4 秒,再叠一点随机偏移,避免整批任务在同一瞬间集体重发
- 设重试上限并记录半途状态:批量任务给每个请求记状态,超时的先标记、整批跑完再统一补跑,别让一个请求卡死整条流水线
容易踩的坑:非流式请求被客户端掐断后,服务端很可能仍在生成。此时立刻重试等于同一问题问两遍、花两遍钱。批量任务优先用流式或给足读取超时,把「超时重试」留给明确的 429/5xx。
常见问题
流式请求中途断了,已生成的内容还算数吗?
先把已收到的部分留好,别整段丢弃。断流前的内容已经生成完毕,可以基于它续写或补跑剩余部分。AIGC微风网关对出错的中断流有兜底处理,按实际产出结算;如果你的客户端一读到流错误就全部重来,先改成「保留已收到内容、只补后半段」的策略,能省不少冤枉消耗。
重试会不会重复扣费?
分两种情况:请求被明确拒绝(429、5xx,还没进模型)时重试不产生额外消耗;请求已经进入生成、只是客户端没等到结果就掐断,重试就是两次生成、两份消耗。所以「先看错误码判断失败在哪一段,再决定要不要重试」,比无脑重试三次省钱得多——日志里的错误类型就是钱。接口地址统一走 https://api.aigcbreeze.cn/v1/chat/completions,错误码语义与 OpenAI 兼容。
批量任务怎么安排并发和超时?
三件套:控制并发(比如同时 5~10 个请求)、每个请求独立设置超时与重试参数、失败任务落盘待补跑。先拿几十条样本跑通全链路,观察真实耗时分布(p95 往往远大于平均值),再据此定超时上限,比拍脑袋写个 30 秒靠谱得多。