你与模型之间通常存在三个超时限制——SDK 的超时、中间层的超时,以及服务商针对较长非流式调用制定的规则——其中最短的限制胜出。只提高 SDK 超时,正是你以为已经修复后问题仍会发生的原因。
错误
# Rejected before generating, for a long non-streaming request:
{"type":"error","error":{"type":"invalid_request_error",
"message":"Streaming is strongly recommended for operations that may take
longer than 10 minutes."}}
# Or the client-side companion, with no response at all:
APITimeoutError: Request timed out.原因和解决方法一览
| 原因 | 解决方法 |
|---|---|
| 较长的非流式生成 | 使用流式传输。长响应应当流式传输,而不是等待其一次性返回。 |
| SDK 客户端超时短于生成时间 | 提高它——但也要提高中间层的限制,否则不会有任何变化。 |
| 代理、负载均衡器或无服务器函数限制了请求 | 找出链路中最短的限制;那就是你实际遇到的限制。 |
| 连接已接受,但之后始终没有响应 | 这是挂起,而不是响应缓慢。应单独限制首字节时间。 |
任何可能运行超过几分钟的请求都使用流式传输
除了避免触发限制外,流式传输还提供了存活信号:令牌持续到达意味着模型仍在工作,因此可以区分挂起和缓慢进展。使用单次阻塞调用时,直到超时触发前,这两种情况看起来完全相同。
提高链路中的每个超时,而不只是 SDK 的超时
人们几乎总是只修改客户端就停下了。如果反向代理或无服务器平台将请求限制在你新的客户端超时以下,该限制仍然胜出,症状也不会改变。
from anthropic import Anthropic
# Client timeout is only one of the limits in play.
client = Anthropic(api_key=KEY, timeout=600.0)
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=8192,
messages=[{"role": "user", "content": prompt}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)将首字节时间与总时长分别限制
这是两种不同的故障,需要两种不同的解决方案。较短的首字节截止时间可以快速发现失效连接;宽松的总时长截止时间则允许真正耗时较长的生成完成。一个合并的超时无法同时做到这两点。
添加重试前先确保请求安全
你这边已超时的请求可能已经在上游完成。如果任务存在副作用,或你按调用次数计费,请先在自己的层面加入幂等机制,再添加重试循环。
如果你通过 Kunavo 调用
Kunavo 会公布自己的数字,而不是让你自行摸索:它最多等待 240 秒以获取上游响应头;非流式响应通常只有在完成后才会发送响应头,因此需要超过这一时间的非流式调用无法通过网关完成——请改用流式传输。240 秒限制仅适用于获取响应头的时间,响应头到达后立即释放,因此永远不会截断长流;网关没有其他限制会限制流的长度。真正会结束流的是静默:上游连续 300 秒没有发送任何字节,或你与网关之间的连接连续 600 秒没有收到任何字节。同步图像、视频和音乐路由会在渲染完成期间保持连接最长 540 秒,处于 600 秒窗口之内,因为渲染确实可能需要几分钟。如果请求在响应头到达前超时,则会被视为通道故障;配置了备用通道时,会在模型的下一个通道上重试,并记录为零成本。
常见问题
请求超时后会计费吗?
在 Kunavo 上不会——失败请求会按零成本记录。直接向服务商付费时,则取决于是否实际发生了生成。
为什么流式传输可以避免该限制?
响应会在几秒内开始,连接会保持活跃,因此不会出现足以触发超时的长时间静默。
客户端超时应设置为多少?
应长于最差情况下的真实生成时长,并配合一个较短的独立首字节截止时间。一个很长的合并超时会把每次挂起都变成持续数分钟的停顿。