API 可用率测试怎么做:自己测 AI API 的指标、脚本与统计口径
做 API 可用率测试,最简单可靠的办法是:每隔固定时间向接口发一个最小请求,记下状态码和耗时,按“成功 /(成功 + 服务端失败)”算比例。难点不在脚本,而在口径:哪些错误算服务的、哪些算你自己的,样本够不够,测的是入口还是真实推理。本文给出一个能直接跑的脚本,再用 cdnping 和 AGI Plan 两份公开数据说明,同一个服务换一种口径,结论可能差很多。
合成探测和真实请求统计,测的是同一件事吗?
不是。两种常见做法:
- 合成探测:由监控方定时发请求,例如每 5 分钟访问一次 API 入口。优点是间隔均匀,夜里没人用也有数据;缺点是请求很轻,往往只验证到网关能响应,未必走到模型推理。
- 真实请求统计:服务方把用户真实请求的成功、失败计数汇总出来。它反映的是用户实际碰到的情况,但请求量随作息起伏,凌晨样本很少,一小时只有两三个请求时,比例没有统计意义。
cdnping 的 API 状态页 属于前一种:从 Cloudflare 边缘每 5 分钟探测各官方 API 入口,具体做法写在 数据方法。AGI Plan 公开的逐小时数据属于后一种。AGI Plan 是 cdnping 的商业合作方,正好可以把两种口径放在一起看。
两种口径放在一起,差别有多大?
先看合成探测。cdnping 在 2026 年 10 月 17 日 18:05(北京时间)的快照,探测点为丹佛(DEN)。cdnping 当天才开始采集,字段名虽然叫 24 小时,实际只覆盖 16:40 到 18:05 约 85 分钟、242 条探测记录:
| 服务入口 | 中位耗时 p50 | p95 | 窗口内可用率 |
|---|---|---|---|
| api.anthropic.com | 73 ms | 84 ms | 100% |
| api.openai.com | 96 ms | 194 ms | 100% |
| api.deepseek.com | 353 ms | 414 ms | 100% |
| api.moonshot.cn | 375 ms | 1125 ms | 约 94.4% |
| agiplan.dev | 883 ms | 883 ms | 100% |
15 个入口里只有 Kimi 开放平台在这段时间出现过失败,其余都是 100%。
再看真实请求。AGI Plan 公开的逐小时计数,取 10 月 15 日 19 时到 10 月 17 日 18 时(北京时间)共 48 个小时,合计成功 13,963 次、平台失败 2,177 次,按公式算出来约 86.5%。失败高度集中:10 月 16 日 14 时和 15 时这两个小时分别有 722 次和 877 次失败,单小时成功率约 48% 和 34%;凌晨 4 点到 6 点每小时只有 2 个请求,成功率 100% 也说明不了什么。
同一快照里 AGI Plan 按能力组给出的 30 天可用率也不一样:快速组约 81.7%(44,729 次请求),前沿模型组约 94.4%,均衡组约 95.0%,国产模型组约 98.0%。
把两边对上:合成探测窗口内 agiplan.dev 入口 100% 可用,而同一时段(10 月 17 日 16 时到 18 时)的真实请求每小时都有 1 到 7 次失败。这不矛盾,入口能响应只说明网关在线;上游模型返回 5xx 时,入口探测看不到。反过来,只看真实请求数据,又会在请求稀少的时段失去判断依据。所以自己测的时候,两种都要有,至少要清楚手上的数字属于哪一种。
哪些错误应该算进不可用?
可用率的分母怎么定,决定了结果。下面是一个可以直接沿用的分类表:
| 结果 | 例子 | 是否计入可用率 | 说明 |
|---|---|---|---|
| 2xx | 200 | 计为成功 | 流式接口要确认读到了结束事件 |
| 5xx | 500、502、503、504 | 计为失败 | 服务方或网关的问题 |
| 超时、连接失败 | DNS 失败、TLS 握手失败、读超时 | 单独记录,再判断 | 可能是服务,也可能是你的网络 |
| 429 | 限流 | 单独统计 | 和你的额度、并发有关,不宜直接算故障 |
| 其他 4xx | 400、401、403、404 | 不计入 | 参数、密钥或权限问题,是调用方的错 |
AGI Plan 公开数据采用的就是“只有 5xx 算平台失败”的口径。自己测的时候也建议把 4xx 排除在分母之外,但一定要把它们记下来:一个突然大量出现的 401 可能是密钥被轮换,403 可能和地区或账号状态有关,这些要按服务条款处理,不是可用率问题。
超时最难归因。比较实用的办法是同时探测一个对照目标,例如 cdnping 自己的入口。对照目标也超时,多半是本机网络;对照正常、只有 AI 接口超时,再算到服务头上。网络层的排查顺序可以参考 Claude Code 连接超时和报错怎么排查。
动手:一个最小可运行的探测脚本
下面的 Python 脚本只用标准库,兼容 OpenAI 格式的接口(官方 API、云厂商托管或聚合网关都可以)。每次运行发两个请求:一个 GET /models 检查入口,一个 max_tokens=1 的对话请求检查推理链路,结果追加到 CSV。
#!/usr/bin/env python3
# probe.py: 每次运行探测一次,结果追加到 probe.csv
import csv, json, os, time, urllib.request, urllib.error
BASE = os.environ["API_BASE"].rstrip("/") # 例如 https://api.example.com/v1
KEY = os.environ["API_KEY"]
MODEL = os.environ["API_MODEL"] # 你实际在用的模型 id
OUT = os.environ.get("PROBE_OUT", "probe.csv")
def call(kind, method, path, body=None):
req = urllib.request.Request(BASE + path, method=method,
data=json.dumps(body).encode() if body else None,
headers={"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json"})
t0 = time.time(); status, err = 0, ""
try:
with urllib.request.urlopen(req, timeout=30) as r:
r.read(); status = r.status
except urllib.error.HTTPError as e:
status = e.code
except Exception as e: # 超时、DNS、TLS 等
err = type(e).__name__
ms = int((time.time() - t0) * 1000)
return [int(t0), kind, status, ms, err]
rows = [
call("entry", "GET", "/models"),
call("infer", "POST", "/chat/completions",
{"model": MODEL, "max_tokens": 1,
"messages": [{"role": "user", "content": "ping"}]}),
]
with open(OUT, "a", newline="") as f:
csv.writer(f).writerows(rows)
用 cron 每 5 分钟跑一次:
# crontab -e
*/5 * * * * API_BASE=https://api.example.com/v1 API_KEY=sk-xxx API_MODEL=your-model PROBE_OUT=$HOME/probe.csv /usr/bin/python3 $HOME/probe.py
推理请求会消耗少量 token。每 5 分钟一次、每次输出 1 个 token,一天 288 次,成本可以忽略,但仍建议用单独的密钥,方便在账单里区分。不同接口地址的写法可以对照 Claude Code 自定义 API 与 Codex、OpenCode 接口地址配置。
汇总时按上面的口径计算,4xx 不进分母,429 和超时单独列出:
awk -F, '$2=="infer"{
if($3>=200 && $3<300) ok++;
else if($3>=500) fail++;
else if($3==429) rl++;
else if($3==0) to++;
else c4++
} END{
if (ok+fail==0) { print "没有可统计的样本"; exit }
printf "成功 %d 5xx %d 可用率 %.3f%%\n", ok, fail, 100*ok/(ok+fail);
printf "429 %d 超时/连接失败 %d 其他4xx %d\n", rl, to, c4
}' ~/probe.csv
如果超时最后确认是服务方的问题,把它并入 fail 再算一遍,两个数字都留着。
要测多久,样本才够?
可以用算术估一下。每 5 分钟一次,一天 288 个样本,一次失败就让当天可用率掉 0.35 个百分点。想区分 99.9% 和 99.5%,样本至少要上千个:1,000 个样本里,前者约对应 1 次失败,后者约 5 次,再少就分不开。按每 5 分钟一次算,要三到四天起步;按月看需要约 8,640 个样本,这时一次失败约占 0.01%。
几条经验:
- 只跑一天,只能看出有没有大问题,别用来比较两个服务谁更稳。
- 按小时出图,比只看一个总百分比有用。上面 AGI Plan 的 48 小时数据,总数 86.5% 掩盖了一件事:失败主要集中在两个小时里。
- 请求很少的小时不要单独下结论,可以设一个门槛,例如当小时样本少于 10 个就标灰。
- 探测点要和你的用户处在相近的网络环境。从境外机房测出来的结果,不代表国内办公网的情况,延迟差异的成因可以看 AI API 延迟拆解。
怎么拿公开数据和自己的数据对照?
自测最大的问题是不知道失败是自己的网络还是服务方。可以拿几份公开数据交叉看:
- cdnping /status 和 /down:边缘探测、官方状态页同步和用户报告放在一起,适合判断是不是大面积故障。只是怀疑自己网络时,先看 ChatGPT 挂了吗?三步判断是服务故障还是自己的网络问题。
- 官方状态页:事件等级和受影响组件怎么读,见 怎么看 OpenAI 状态页和 Claude 状态页。
- 服务方公开的真实请求数据:以 AGI Plan 为例,不需要登录即可下载逐小时、逐能力组的计数,默认 CSV:
curl -s 'https://agiplan.dev/api/status/requests?month=2026-10' -o agiplan-2026-10.csv
curl -s 'https://agiplan.dev/api/status/requests?month=2026-10&format=json'
cdnping 的 /status/agiplan 页面会镜像展示这份数据。拿到后和自己的 probe.csv 按小时对齐:两边同时出现失败,基本可以归到服务方;只有你这边失败,先查本地网络和出口。
如果你测的是中转站或聚合网关,可用率只是其中一项,计费方式、数据政策和模型一致性同样要查,完整清单见 AI API 中转站怎么选:可用率、计费、数据政策 8 项检查。经过 OpenRouter 这类路由时,同一模型在不同供应商上的可用率也会不同,参考 同一模型换个 OpenRouter 供应商,可用率能差多少。
上线前的检查表
- 探测分两类:入口(
/models)和推理(最小对话请求) - 2xx 计成功,5xx 计失败,429、超时、其他 4xx 分开记录
- 同时探测一个对照目标,用来区分本地网络问题
- 探测点和真实用户处在相近的网络环境
- 至少积累 3 到 4 天数据再做比较,报告时写明窗口和样本数
- 按小时汇总,样本过少的小时不单独下结论
- 使用独立密钥,遵守服务条款,不用探测去试探限流或地区限制
引用任何可用率数字时,都把来源、窗口和口径写在旁边。本文的 cdnping 数据只有约 85 分钟,AGI Plan 的逐小时数据是 48 小时,它们说明的是方法上的差别,不是长期结论。