網頁端能開,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 的穩定性問題,大多不在呼叫程式碼本身,而在出口位址是否穩定、併發是否被限、逾時是否涵蓋了串流輸出這三件事上。先把這三項驗證完,再談線路與方案。