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 小時,它們說明的是方法上的差別,不是長期結論。