欢迎光临
我们一直在努力

大模型 API 调用避坑:重试、超时、流式解析,本地 Mock 实测跑通

线上第一次调大模型接口,十有八九会在三个地方翻车:偶发 500 没重试、慢请求把线程拖死、流式输出拼出乱码。这篇不讲理论,只讲一段本地能跑的代码。注意先说清楚边界:下面没有调用任何真实大模型接口,服务端是本机 Mock,模拟的是“前两次 500、第三次成功”“流式分片”“故意慢 3 秒”这三种线上常见行为。Mock 能验证的是你客户端的容错逻辑,不是某个厂商接口的真实表现,别把 Mock 结果当成厂商 SLA。

## 1. 先搭一个会“使坏”的 Mock 服务

用标准库 http.server 就够了,不需要装任何依赖。Mock 有三个端点:/chat 前两次返回 500、之后返回 200;/stream 按 SSE 格式分三次吐出“你好,这是流式输出”;/slow 故意睡 3 秒。写 Mock 的好处是翻车可复现:线上偶发的问题,在本地每次跑都能稳定触发,改完代码立刻能验证改没改对。很多新手的问题恰恰相反,拿真实接口反复试,时好时坏,最后不知道是自己改对了还是运气变好了。

## 2. 重试:只对 5xx 和超时重试,4xx 不要重试

重试最常见的错法是“不管什么错都重试三次”。400 参数错、401 鉴权错、429 限流之外的 4xx,重试一百次结果也一样,白白浪费配额和时间。正确做法是分层:5xx 和网络超时可以重试,4xx 直接失败,429 要看响应头里的等待时间再决定。退避时间也要递增,第一次等 0.2 秒、第二次等 0.4 秒,而不是固定间隔死磕。实测结果:/chat 前两次 500,客户端一共发了 3 次请求,第三次拿到 200,返回内容完整。这个数字要记下来——重试次数等于“首次加重试”,日志里只看“成功了”是不够的,要看一共发了几次,次数失控就是重试风暴的前兆。

```python

def call(path, timeout=1.0, retries=3):

    last = None

    for i in range(retries):

        try:

            req = urllib.request.Request(f"http://127.0.0.1:8931{path}",

                                         data=b"{}", headers={"Content-Type": "application/json"})

            with urllib.request.urlopen(req, timeout=timeout) as r:

                return r.status, r.read().decode()

        except urllib.error.HTTPError as e:

            last = f"HTTP {e.code}"

            if e.code < 500:

                break

        except Exception as e:

            last = type(e).__name__

        time.sleep(0.2 * (i + 1))

    return None, last

```

## 3. 超时:没有超时就是没有防线

第二个坑是没有超时。/slow 端点故意睡 3 秒,客户端超时设 1 秒、重试 2 次,最终返回 TimeoutError,整个过程可控、可记录。如果不设超时,一个慢请求就能把工作线程占住,高峰期线程池一满,整个服务跟着垮,而且日志里还看不到任何报错——请求只是“还没回来”。超时时间怎么定?看两点:一是接口 P95 延迟,二是你的上游能等多久。超时不是越短越好,设得比正常延迟还短,等于自己制造失败;设得太长,又起不到保护作用。生产上还要区分连接超时和读取超时,本文的演示用的是总超时,够说明问题,落地时建议分开设。

## 4. 流式解析:按事件切分,不要按读到的数据块切分

流式输出最隐蔽的坑在解码层。第一版代码按 16 字节读一次就直接 decode,结果中文被从中间切开,程序直接报解码错误。这不是模型的问题,是客户端把“一次 read 的结果”当成了“一个完整事件”。正确做法有两条:第一,用增量解码器,让不完整的 UTF-8 字节留到下一次再解;第二,按 SSE 的空行切事件,一个事件解析完再拼 delta,最后遇到 [DONE] 才结束。修复后实测:流式端点三次分片完整拼成“你好,这是流式输出”,没有乱码、没有丢字。落地时还要补一条:流式中途断连怎么办?已经拼出来的部分是直接返回、还是标记为不完整?这个策略要在写代码前定好,不能等线上断了再拍脑袋。

## 5. 避坑清单

第一,重试只针对 5xx、超时和明确可重试的 429,4xx 不重试。第二,超时必须显式设置,连接超时和读取超时分开更稳。第三,退避递增,并给重试设总次数上限,防止雪上加霜。第四,流式解析用增量解码加按事件切分,绝不能假设一次读到的数据就是一条完整消息。第五,日志要记录重试次数、最终错误类型和耗时,只记“成功/失败”等于没记。第六,Mock 先行:把 500、超时、断流、乱序分片都在本地造出来,客户端逻辑跑通了再接真实接口。完整代码在 code/llm_api_client.py,运行结果见 run_log.txt,全部用标准库,复制过去就能跑。

关键词:大模型 API、重试、超时、流式输出、SSE、2026、工程化

赞(0)
未经允许不得转载:171主机测评 » 大模型 API 调用避坑:重试、超时、流式解析,本地 Mock 实测跑通
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址