Go 推理客户端:重试要懂模型调用的副作用 Go 推理客户端重试要懂模型调用的副作用一、模型调用不是普通 GETGo 后端服务调用模型接口时常会封装重试逻辑。网络抖动、限流、超时都可能触发重试。但模型调用不是普通 GET它有成本、可能产生不同输出也可能触发下游工具调用。重试要懂副作用。盲目重试会放大成本拖长延迟还可能让用户看到不一致结果。二、先区分错误类型flowchart TD A[模型调用失败] -- B{错误类型} B -- C[网络超时] B -- D[限流] B -- E[上下文过长] B -- F[格式错误] C -- G[可重试] D -- G E -- H[不可重试] F -- I[看任务策略]网络短暂失败和 429 限流可以重试上下文过长、认证失败通常不可重试格式错误是否重试要看是否能通过更严格提示词修复。还要控制总时间预算。每个下游重试都不能突破用户请求的整体超时。三、客户端要带策略type RetryPolicy struct { MaxAttempts int BaseDelay time.Duration MaxElapsed time.Duration }重试策略要按功能配置。实时聊天、后台总结、批量任务不能用同一套重试次数。model_client_retry: chat: attempts: 1 max_elapsed_ms: 2500 batch_summary: attempts: 3 max_elapsed_ms: 30000实时请求宁愿快失败也不要让用户等很久。四、幂等和日志很重要如果模型调用背后会触发工具或写入就必须有幂等键。否则重试可能重复执行副作用。日志里要记录 attempt、错误类型、延迟、模型和最终结果。没有这些信息成本和延迟异常时很难排查。客户端还要支持请求级 context。上游请求取消后下游模型调用也应该停止等待。否则用户已经断开连接后端仍在消耗模型资源。ctx, cancel : context.WithTimeout(parentCtx, 2500*time.Millisecond) defer cancel() resp, err : client.Generate(ctx, req)重试时要带抖动避免多个实例同时重试打穿推理网关。指数退避如果没有 jitter在故障恢复瞬间仍然可能形成流量尖峰。retry_backoff: base_ms: 100 max_ms: 1000 jitter: true还要记录重试带来的额外成本。一次用户请求因为重试产生两次模型调用账单和限额系统都应该知道。否则成本分析会低估失败场景。对于流式响应重试更要谨慎。已经向用户输出部分 token 后重新请求可能生成不同后续内容。流式任务通常更适合失败后提示重试而不是自动拼接。客户端还要暴露调用结果分类而不是只返回error。业务层需要知道这是用户输入问题、平台限流、模型超时、网关失败还是输出校验失败。错误分类越清楚上层越容易决定是否降级。type ModelCallResult struct { Retryable bool Category string Attempts int CostTokens int }例如context_too_long可以提示用户缩短内容rate_limited可以进入排队或降级模型output_schema_invalid可以触发一次受控修复。把所有失败都包装成 500只会让调用方写出更粗糙的重试。最后推理客户端应把策略和观测做成默认能力。业务代码只传任务类型和请求上下文不应该在每个服务里复制一套重试判断。复制越多线上行为越不可预测。五、总结Go 推理客户端重试要区分错误类型、功能场景、时间预算和副作用。可靠不是多试几次而是在该重试时重试在不该重试时及时失败。