← 返回目录
ap1.1实操⏱ 约 9 分钟

把一次调用写"结实":哪些错该重试,哪些重试是白烧钱

四类错误的真实响应 + 一张抄进项目的判定表

🔎 最后验证 2026-08📚 来源:本节四类错误响应均为 ECS 实测(2026-08,deepseek-chat,脚本与原始输出见本节代码)🧰 DeepSeek API、Python
为什么学这个

ap0.3 你给调用装了重试。但重试有个反直觉的地方:对一半的错误,重试是纯浪费——浪费时间、浪费额度、还把日志刷满,让真正的问题更难找。

这一节把错误分类讲透:亲手制造四类典型错误,看它们真实的响应长什么样,然后给你一张能直接抄进项目的判定表。

💡 打个比方

医生看急诊要先分诊:失血要立刻手术,感冒回家躺三天,骨折得先拍片。全都当成"再等等看"或全都推进手术室,都是灾难。错误处理同理:先分类,再决定要不要重试

四类错误的真实长相(实测)

跑本节代码,故意制造四种错误,真实响应:

① 正常请求
  状态: 200 | 0.62s | 好呀!今天心情

② 认证失败(key 写错)
  状态: 401 | 0.2s
  {"error":{"message":"Authentication Fails, Your api key: ****0000 is invalid",
   "type":"authentication_error"...}}
  判定: ❌ 别重试——重试一万次也不会对

③ 参数错误(模型名不存在)
  状态: 400 | 0.12s
  {"error":{"message":"The supported API model names are ... but you passed deepseek-not-exist"...}}
  判定: ❌ 别重试——请求本身有毛病

④ 参数错误(max_tokens=-5)
  状态: 400 | 0.1s
  {"error":{"message":"...max_tokens: invalid value: integer `-5`, expected u32..."}}
  判定: ❌ 别重试

⑤ 超时(把 timeout 压到 0.2s)
  状态: timeout | 0.32s | The read operation timed out
  判定: ✅ 该重试——服务可能好好的,只是这次没赶上

注意 ②③④ 的耗时:0.1~0.2 秒就返回了。它们是"服务清清楚楚告诉你:你的请求有问题"。你重试三次,只是把这句话听三遍。

判定表(抄进你的项目)

类别典型怎么办
该重试超时、连接错误、5xx 服务端错误、429 限流指数退避重试(429 必须退避,见 ap1.3)
别重试400 参数错、401 认证失败、402 余额不足、404 模型不存在立刻失败,记日志,让人来修
慎重试任何"重复执行有副作用"的操作:扣费、发消息、写库先做幂等(带唯一请求 id),再谈重试

第三行最容易出人命:第一次其实成功了,只是响应超时——你一重试,就扣了两次费、发了两条消息。工程上的解法叫幂等:每个请求带一个唯一 id,服务端认 id 去重;做不到就至少在你这边记录"这个 id 已发起过"。

升级版调用函数

把 ap0.3 那个函数升级成会分诊的版本(节选,完整版在代码里):

NO_RETRY = {400, 401, 402, 403, 404, 422}    # 请求本身的问题,重试无意义

def ask(msg, timeout=30, retries=3):
    for i in range(retries):
        try:
            return _post(msg, timeout)
        except HTTPError as e:
            if e.code in NO_RETRY:
                raise                              # 立刻失败,别浪费
            if e.code == 429:                      # 限流:退避后重试
                time.sleep((2 ** i) + random.uniform(0, 0.3)); continue
            if 500 <= e.code < 600:                # 服务端错:退避后重试
                time.sleep((2 ** i) + random.uniform(0, 0.3)); continue
            raise
        except (TimeoutError, URLError):           # 网络类:重试的主战场
            time.sleep((2 ** i) + random.uniform(0, 0.3))
    raise RuntimeError("重试耗尽")

🔧 动手做:给你的调用函数装上分诊(12 分钟)

  1. 跑本节代码 error_taxonomy.py,亲眼看四类错误的响应(故意把 key 改错、把模型名写错,看服务怎么回你)。
  2. 把上面的分诊逻辑合进你 ap0.3 存的 llm.py
  3. 验证分诊生效:把模型名改成不存在的,跑一次——它应该 0.1 秒就失败,而不是傻等三轮重试(耗时就是最好的证据)。
  4. 再验证另一边:把超时压到 0.1 秒,确认它老老实实重试了三次才放弃。
  5. 想一想你毕业方向里哪个操作是"重复执行有副作用"的(扣额度?发通知?写订单?),记下来——AP7 做额度系统时会用到。

做到这里你应该有:一个会分诊的调用函数,以及两次亲手验证(该快的快、该重试的重试)。

为什么:没分诊的重试是"看起来很稳,实际在浪费"——线上排障时你会看到大量重复的 401 日志,却找不到真正的故障点。分诊让失败快速暴露、让抖动被悄悄兜住,这是可观测性的第一步。

❓ 测验
你的调用返回 402(余额不足)。带三次重试的代码会怎样,应该怎样?
⚠️ 避坑日志里没有错误码,等于没有日志

线上最痛的时刻:用户说"报错了",你翻日志只看到 Exception: request failed每次失败至少记四样:状态码/异常类型、耗时、第几次重试、请求的唯一 id。有了这四样,你能在一分钟内判断"是我们的问题还是服务商的问题";没有,你只能猜。ap1.6 做成本仪表时,会把这套日志一起建起来。

🤖 让 AI 审你的错误处理
这是我的大模型调用函数:【粘贴】。请按生产标准审:1) 错误分诊是否完整(哪些码该重试/不该重试/需要幂等);2) 超时设置(连接 vs 读取)是否合理;3) 失败日志缺哪些关键字段;4) 有没有「重复执行会产生副作用」的风险点。逐条给改法。
✅ 小结

分诊三句话:网络类和 5xx/429 重试(配退避)、4xx 立刻失败、有副作用的先幂等再谈重试;失败日志记全四样。

下一节换个战场——体验:同一个回答,用户是等 4 秒看到全部,还是 0.5 秒就看到第一个字?实测差 8 倍。

下一节 → 流式输出:让用户 0.5 秒就看到东西
🔎 来源与核验· 2 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「实测四类错误响应:401 认证失败(0.2s)、400 模型不存在(0.12s)、400 参数非法(0.1s)、超时(0.32s);错误类请求均在 0.2s 内返回明确错误信息」
📚 ECS 实测 error_taxonomy.py(2026-08,deepseek-chat)✓ 已核验 2026-08
「重试判定:超时/连接错误/5xx/429 应退避重试;4xx 类请求错误不应重试;有副作用操作需先实现幂等」
📚 本节实测 + API 工程通行实践✓ 已核验 2026-08
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · ap1.2
流式输出:让用户 0.5 秒就看到东西
继续读下一节 →