网页端能开,API 却报错:差别在三处
用浏览器打开 AI 网页端,一次请求完成就结束,连接由浏览器统一复用,失败了大不了刷新重来。API 调用不是这个模型:SDK 在后台持续发请求,流式输出要维持几十秒甚至更久的长连接,多个任务还会同时打进来。三处差别最容易被忽略,也最容易在批量任务真正跑起来之后集中暴露。
| 关注点 | 网页端 | API 调用 | 出问题的典型表现 |
|---|---|---|---|
| 出口 IP | 变化基本无感 | 要求尽量固定、少跳变 | 401 / 403、区域限制提示 |
| 并发连接 | 由浏览器连接池统一管理 | 按任务数同时建立多条连接 | 连接超时、429、EOF |
| 超时设置 | 页面加载完即结束 | 要覆盖流式输出的整个时长 | read timeout、连接被重置 |
排查顺序建议从出口开始:出口不对,后面两项的验证结果都会被污染。
出口 IP:API 比网页端更在意固定
网页端靠 Cookie 或登录态识别身份,出口地址换一个,通常只是重新验证一次。API 侧不一样,平台会按 API Key 绑定地域与风控策略,出口地址频繁跳变容易被判定为异常来源,轻则要求重新验证,重则直接拒绝请求。
共享出口与固定出口
- 共享出口:多个用户共用一个落地地址,同一出口上的行为会互相影响,排查时也更难定位。
- 固定出口:IEPL 专线通常提供稳定的落地出口,适合需要把地址加进平台白名单的场景。
- 中转线路的出口由调度决定,是否固定要在自己的环境里实测确认,不要只看说明。
验证出口是否稳定
- 在本地开发机和跑任务的服务器上分别打印出口地址,记录结果。
- 间隔一段时间重复几次,看记录是否一致;批量任务最好在真实运行时段再测一轮。
- 确认域名解析走的是哪一侧,别让解析结果和出口地址对不上。
- 把要调用的 API 域名写进代理规则,确保请求真的经过代理,而不是被分流规则放行成了直连。
# 打印当前出口地址
curl -sS https://ipinfo.io/ip
# 打印连接耗时与首字节耗时,用来区分"连不上"和"连上但很慢"
curl -sS -o /dev/null \
-w "connect=%{time_connect} start=%{time_starttransfer}\n" \
-H "Authorization: Bearer $API_KEY" \
https://api.openai.com/v1/models
DNS 解析走哪一侧
如果代理只接管了 TCP,域名解析仍在本地完成,平台侧看到的解析位置与出口地址可能对不上,部分接口会返回区域错误。在客户端里开启远端解析,或者确认 API 域名命中了代理规则,再往下排查。
并发:先看清三个上限
并发上不去,通常不是本地带宽不够,而是三个上限里有一个先到了。
- 客户端上限:进程的文件描述符数量、HTTP 连接池大小、线程或协程数量。
- 线路与 NAT 会话上限:同一个出口地址能同时维持的会话数是有限的。
- 平台侧限流:同一 API Key 或同一出口地址的并发请求数限制,通常以 429 返回。
HTTP/2 多路复用不是万能
HTTP/2 允许一条 TLS 连接承载多个请求,省掉了反复握手。但服务端会用 SETTINGS_MAX_CONCURRENT_STREAMS 限制单条连接上的并发流数,超出的部分被排队,而不是立刻失败。表现出来就是没有报错,但等待时间越来越长,日志里看不到明显异常,最后只能靠打点数据发现。
并发怎么调
- 用信号量或连接池把同时在飞的请求数限制在可观测的范围内,从小的并发开始往上加。
- 重试要带指数退避和随机抖动,避免失败之后所有任务在同一秒集体重试。
- 需要更高并发时,把任务拆到多条线路上,而不是把单条线路压满。
- 把并发数、失败率、平均等待时间打点上报,出问题时才有依据。
超时:流式输出按空闲间隔设,不是总时长
超时不是一个数字,而是三类,各自管的事情不同。
- 连接超时(connect):从发起到握手完成。可以设得短一些,超时基本说明线路不通或出口不可达。
- 读超时(read / idle):等待下一个数据块的最长时间。流式输出必须按两个数据块之间的最长间隔来设。
- 总超时(total):整个请求的硬上限。只适合非流式请求,流式请求设总超时会误杀正常的长输出。
不少 SDK 的默认读超时对短请求够用,遇到推理阶段长时间不吐 token 的模型就偏紧:连接看起来静默,其实并没有断,结果被客户端主动掐掉,日志里只剩一条 read timeout。
重试的代价
流式输出一旦已经开始返回内容,再重试可能产生重复计费,上下文也会乱。建议把重试限制在连接建立阶段,以及明确的 5xx 与 429 上;429 要退避之后再试。
一个可用的超时配置思路
import httpx
from openai import OpenAI
client = OpenAI(
base_url="https://api.openai.com/v1",
# read 按"两个数据块之间的最长间隔"设置,不是按整个请求时长
timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=5.0),
max_retries=2,
)
流式接口只保留空闲判断,把总超时放宽到业务允许的上限;非流式的批量任务反过来,给一个明确的总超时更安全。
线路怎么选:IEPL 专线、中转与直连
三类线路不是谁替代谁,而是对应不同的调用形态。
| 线路类型 | 路径特征 | 适合的调用形态 | 上手前要确认 |
|---|---|---|---|
| IEPL 专线 | 端到端专线承载,不走公网绕行 | 长连接流式输出、需要固定出口 | 出口地址是否固定、能否加进白名单 |
| 中转 | 先接入中转节点再出境 | 并发较高、对成本敏感的批量任务 | 出口是否随调度变化 |
| 直连 | 直接连海外节点 | 调试、轻量请求 | 公网路径受拥塞影响更大 |
VPNFN 覆盖 120+ 国家 / 地区、180+ 线路,三类线路都在可选范围内,隐私策略是不记录日志。比较稳妥的顺序是:先用直连把功能跑通,再把需要稳定出口的调用切到 IEPL 专线,把可以重试的批量任务放到中转线路上,让不同形态的请求各走各的。
套餐与流量:按调用量挑档位
月订阅分 60GB / 250GB / 500GB 三档。API 调用的流量开销集中在两处:长上下文请求的请求体会明显放大上行流量,流式输出的下行按实际生成的 token 计。估算方法是把单次请求的上下行大小乘以每天的调用次数,再乘 30 天,然后留出余量。
- 调试阶段与个人脚本:先用最小档,把出口、并发、超时三项跑通再谈流量。
- 长时间批量任务:先按上面的方法算月流量,再决定是升档还是叠加流量包。
- 用量波动大的场景:流量包用完为止、永久不过期,适合放在月订阅之外兜底。
- 设备不限台数,开发机、服务器和手机可以同时在线,不用为每台机器单独准备一份。
结论:先用月订阅的最小档验证出口是否固定、并发能跑到多少、超时该怎么设;三项都确认之后,再按实际用量升档,或者用流量包接住突发量。
上线前自检清单
把下面几项过一遍,基本能覆盖 AI API 调用里最常见的连接类故障。
- ✅ 连续多次打印出口地址,确认开发机与服务器看到的是同一个出口。
- ✅ 给 SDK 显式设置连接超时与读超时,不用默认值。
- ✅ 流式请求按空闲间隔设置读超时,并单独处理推理阶段静默较久的模型。
- ✅ 重试只覆盖连接建立阶段与 5xx、429,并带指数退避与随机抖动。
- ✅ 把 API 域名写进代理规则,同时确认域名解析走的是远端。
- ❌ 不要在代码里写死某一条线路的地址,订阅链接要能整体更新。
- ❌ 不要把 API Key 与代理配置一起提交进代码仓库。
一句话:AI API 的稳定性问题,大多不在调用代码本身,而在出口地址是否稳定、并发是否被限、超时是否覆盖了流式输出这三件事上。先把这三项验证完,再谈线路与套餐。