:從curl到異常處理與工程化落地)
前陣子要在自己的應(yīng)用里接入 AI 視頻生成能力我本以為最難的是“選哪個模型”結(jié)果打開三家視頻生成 API 的文檔發(fā)現(xiàn)光是“把請求發(fā)出去”這件事就能消耗掉一個下午A 平臺要求x-api-keyB 平臺要求Authorization: BearerC 平臺要求把密鑰放在 query 參數(shù)里請求體字段一個叫prompt另一個叫text還有一個叫input返回結(jié)果有的直接給視頻 URL有的只給一個 task_id 讓你輪詢。不同平臺的錯誤碼也各成體系一個 400 背后的原因可能差了十萬八千里。這種時候OpenRouter 視頻生成 API 就會顯得很有吸引力它用一套相對統(tǒng)一的協(xié)議幫你接多家模型把密鑰、模型路由、計費和大部分錯誤處理收斂到一個入口。但“聚合”不是萬能藥它只是把“接入多家模型”這件事變成“接入一個網(wǎng)關(guān)”后面還有配額、超時、異步任務(wù)、內(nèi)容合規(guī)和工程化落地這些硬問題。這篇文章我想從代碼接入的角度按“先跑通、再處理異常、最后工程化”的順序把整個過程拆開講一遍。1. 先搞清這類 API 聚合平臺到底幫你省了什么1.1 為什么多模型接入會變成一場適配噩夢如果你只接一個模型直接看那一家文檔就夠了。真正麻煩的是你要在同一個產(chǎn)品里比較兩家、三家的視頻生成效果或者你想做一個“用戶可以選不同模型”的功能。這時候每個模型的接入方式不同會帶來兩倍的重復(fù)勞動。我經(jīng)歷過一個很典型的場景上游供應(yīng)商臨時說某個模型要下線我需要快速切到另一個模型。如果是直連我得重新讀文檔、改鑒權(quán)頭、改請求體字段、改響應(yīng)解析邏輯、重新測試。如果是走 OpenRouter 這類網(wǎng)關(guān)大部分時候只需要換一個model參數(shù)其他代碼可以保持不變。這個“改動成本”的差距才是聚合平臺最核心的價值。但這里要說清楚OpenRouter 并不是把每個模型的能力都統(tǒng)一成完全相同的樣子。視頻生成模型天然存在差異有的支持圖生視頻有的只支持文生視頻有的限制了視頻時長有的必須異步輪詢。聚合層可以把“請求如何鑒權(quán)、如何計費、如何返回標準錯誤”統(tǒng)一起來但不可能把模型的底層能力差異也抹平。1.2 OpenRouter 的“代碼優(yōu)先”意味著什么“代碼優(yōu)先”不是官方術(shù)語是我自己更偏愛的一種接入姿勢不做太多圖形界面配置先用 curl 調(diào)通一次請求再用 Python 封裝成函數(shù)最后再接入業(yè)務(wù)邏輯。這種姿勢的好處是每一步都能被版本管理、被測試、被回滾。從實際使用看OpenRouter 的 API 風格接近 OpenAI 的 chat completions 協(xié)議這讓很多已經(jīng)寫過 OpenAI 接口的開發(fā)者上手非常快。代碼里你需要的核心要素就三樣接口地址、API Key、模型名。其他都是圍繞這三個要素的參數(shù)和數(shù)據(jù)格式。需要注意的是OpenRouter 聚合的是“能通過 API 訪問的模型”如果你在模型列表里沒有看到視頻生成相關(guān)模型那可能是賬號權(quán)限、地區(qū)或模型上架情況導(dǎo)致的。接入前一定要先打開官方模型列表確認而不是憑熱搜詞里的“MiniMax H3”“DeepSeek V4”等名字直接寫進代碼。2. 接入前必須確認的三件事賬號、額度、模型列表2.1 注冊、API Key 和充值的通用路徑OpenRouter 的注冊流程和大多數(shù)開發(fā)者平臺類似打開官網(wǎng)注冊賬號進入控制臺后創(chuàng)建 API Key。這個 Key 是你調(diào)用所有模型的統(tǒng)一憑證和直連各平臺時的“多把鑰匙”相比確實方便但也意味著一旦泄露別人可能拿著它去調(diào)用你賬號下的所有模型。所以我建議不要把 API Key 硬編碼在代碼里使用環(huán)境變量。在.env文件中保存 Key并確保該文件被.gitignore忽略。創(chuàng)建 Key 時如果平臺支持權(quán)限范圍或額度限制盡量開啟。至于充值OpenRouter 很多模型是按量計費的視頻生成模型通常比文本模型更貴。如果你只是測試先充一小筆錢不要一開始就開大額自動充值。不同模型的價格、計費單位按秒還是按次都可能不一樣具體以模型卡片和官方文檔為準。2.2 怎么判斷一個模型是否支持視頻生成OpenRouter 的模型列表頁一般會提供每個模型的說明、標簽和示例。想找視頻生成模型可以先搜索video、gen等關(guān)鍵詞。真正的判斷標準不是名字里有沒有“video”而是模型卡片里是否明確寫了輸入輸出支持視頻輸入是否支持prompt、image_url、duration、resolution等字段。輸出返回video_url、video_data還是只返回文字描述。是否異步視頻生成通常耗時較長如果響應(yīng)里帶task_id說明需要輪詢。舉個例子熱搜詞里出現(xiàn)過 MiniMax H3 在 ComfyUI 里生成視頻時如何保持人物 ID 不變的問題。如果你真的想用某個模型做圖生視頻、保持人物一致性不要只看它“能不能生成視頻”還要關(guān)注它支不支持輸入?yún)⒖紙D、支持多少張、以及視頻時長上限。這些信息只能從模型文檔里確認OpenRouter 自身不一定會在統(tǒng)一請求層幫你補齊。2.3 把 Key 放進環(huán)境變量而不是硬編碼下面是一個常見的.env示例OPENROUTER_API_KEYsk-or-xxxx在 Python 里讀取import os API_KEY os.environ[OPENROUTER_API_KEY]這樣做的理由很簡單代碼一旦提交到倉庫密鑰就相當于公開了。很多人被自動抓取 GitHub 的爬蟲掃到 Key然后發(fā)現(xiàn)賬單暴漲問題往往就是硬編碼造成的。3. 用 curl 跑通第一個視頻生成請求3.1 先搭一個最小請求體我不建議一開始就去看復(fù)雜參數(shù)。先構(gòu)造一個最簡單的請求能返回結(jié)果就行。下面是一個用 curl 調(diào)用的示意結(jié)構(gòu)curl -X POST https://openrouter.ai/api/v1/chat/completions \ -H Authorization: Bearer $OPENROUTER_API_KEY \ -H Content-Type: application/json \ -d { model: some-provider/video-model, messages: [ {role: user, content: 生成一段10秒的短視頻主題是城市夜景} ] }注意上面的model是占位符具體模型 ID 以 OpenRouter 控制臺里實際顯示的為準。如果該模型在 OpenRouter 上使用的是獨立的視頻生成端點那么請求 URL 也可能不同一切以官方文檔為準。這里要解釋一下視頻生成模型可能也復(fù)用 chat completions 格式因為這種格式可以傳遞文本指令也可能有專門的/video/generations端點。無論哪種Minimal Request 的原則是一樣的先不要加thinking_budget、resolution、duration這些擴展參數(shù)減少變量跑通后再逐步加。3.2 識別同步響應(yīng)和異步任務(wù)視頻生成和文本生成最大的區(qū)別在于一個 HTTP 請求很難等完整個視頻渲染過程。所以大概率會遇到兩種響應(yīng)模式同步模式請求一直掛起直到視頻生成完畢響應(yīng)中直接包含video_url。異步模式請求很快返回響應(yīng)中包含一個任務(wù) ID例如task_id你需要輪詢另一個狀態(tài)接口直到任務(wù)完成。如果你看到響應(yīng)里返回了一個 URL先判斷它是不是最終視頻地址。有些平臺會先返回一個“占位”任務(wù) URL需要等狀態(tài)變?yōu)?succeeded 之后才能真正訪問。假設(shè)是異步任務(wù)輪詢接口的示意結(jié)構(gòu)類似curl -X GET https://openrouter.ai/api/v1/video/generations/{task_id} \ -H Authorization: Bearer $OPENROUTER_API_KEY輪詢時不要每 0.5 秒就請求一次太密集容易觸發(fā)速率限制也會給平臺造成不必要的壓力。常見做法是 2 到 5 秒一次配合最大輪詢次數(shù)。3.3 第一次跑通后的檢查清單第一次請求返回 200 并不代表完事。我一般會按這個清單檢查HTTP 狀態(tài)碼是 200/201還是 2xx 代表已接受響應(yīng)體有沒有error字段有沒有id或task_id視頻文件如果不是直接給 URL而是給 base64需要確認體積別超過內(nèi)存限制。視頻可訪問性URL 是否過期是否需要鑒權(quán)才能訪問計費字段有些響應(yīng)當中會帶cost可以用于核對本次調(diào)用的費用。記錄下請求時間、模型、任務(wù) ID、狀態(tài)碼和耗時這些信息在后續(xù)調(diào)試時非常關(guān)鍵。4. 把 curl 封裝成可復(fù)用的 Python 函數(shù)4.1 用 requests 寫一個最小的視頻生成函數(shù)一旦 curl 跑通就可以用 Python 固化。這里我用requests舉例因為它足夠簡單也容易替換成httpx或異步客戶端。import os import time import requests API_KEY os.environ[OPENROUTER_API_KEY] BASE_URL https://openrouter.ai/api/v1/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } def generate_video(prompt: str, model: str some-provider/video-model) - dict: payload { model: model, messages: [{role: user, content: prompt}], } response requests.post(BASE_URL, headersheaders, jsonpayload, timeout30) response.raise_for_status() return response.json()這里一個很容易踩的坑是timeout。視頻生成請求可能比普通聊天請求慢很多但也不能因此不設(shè)超時否則代碼會無限掛起。更合理的做法是把超時設(shè)置得比你的心理預(yù)期大一些比如 30 秒同時依賴異步任務(wù)機制而不是想著一個請求等到視頻渲染完。4.2 輪詢?nèi)蝿?wù)狀態(tài)與結(jié)果下載如果響應(yīng)里包含task_id就需要寫一個輪詢函數(shù)def poll_generation(task_id: str, max_attempts: int 60, interval: int 5) - dict: status_url fhttps://openrouter.ai/api/v1/video/generations/{task_id} for attempt in range(max_attempts): response requests.get(status_url, headersheaders, timeout10) data response.json() status data.get(status) if status succeeded: return data if status failed: raise RuntimeError(data.get(error, generation failed)) time.sleep(interval) raise TimeoutError(ftask {task_id} timed out)拿到結(jié)果后如果里面是視頻 URL可以用requests.get(video_url, streamTrue)下載def download_video(url: str, save_path: str) - None: with requests.get(url, streamTrue, timeout30) as response: response.raise_for_status() with open(save_path, wb) as f: for chunk in response.iter_content(chunk_size8192): f.write(chunk)下載后可以用os.path.getsize(save_path)檢查文件大小避免下到一個空文件或錯誤頁。4.3 不要忽略異常處理與請求日志視頻生成 API 的失敗率往往比文本模型高因為它涉及計算資源調(diào)度、長時間任務(wù)、冷啟動等。如果只依賴raise_for_status()一旦上游返回 529 或連接中斷調(diào)用方只會看到一堆異常堆棧。一個可參考的做法是在調(diào)用前記錄一條日志包含模型、prompt 長度、請求時間調(diào)用后記錄 task_id、狀態(tài)、耗時、費用如果有異常時記錄錯誤類型和 response body。但要注意不要把完整 prompt 寫入日志尤其當 prompt 包含用戶隱私或業(yè)務(wù)敏感信息時。建議只記錄 prompt 長度或摘要。日志示例: [INFO] generate_video start modelsome-provider/video-model prompt_len42 ts... [INFO] generate_video task_created task_idxxx statuspending [INFO] generate_video success task_idxxx duration12.3 cost0.0002 [ERROR] generate_video failed task_idxxx error529 overloaded這才是代碼接入里真正值錢的部分不是調(diào)用成功而是失敗時你能快速定位是哪一層出了問題。5. 視頻生成 API 的典型錯誤和繞過思路5.1 529 overloaded 是什么意思很多 OpenRouter 使用者都遇到過api error: 529 overloaded. this is a server-side issue, usually temporary這樣的報錯。它表示服務(wù)端臨時過載不是你請求參數(shù)寫錯了也不是 API Key 失效了。處理方式不是立刻重試一萬次而是“后退重試”。常見做法是指數(shù)退避import time import random def request_with_retry(func, max_retries5, base_delay1): for attempt in range(max_retries): try: return func() except requests.HTTPError as exc: if exc.response.status_code 529 and attempt max_retries - 1: delay base_delay * (2 ** attempt) random.uniform(0, 1) time.sleep(delay) continue raise注意最大重試次數(shù)不要設(shè)得太高比如 5 到 6 次就夠了。如果超過這個次數(shù)還在 529大概率是平臺或模型提供方正在經(jīng)歷較大故障再繼續(xù)重試只會浪費額度。5.2 connection lost mid-response 怎么辦另一個常見錯誤是api error: connection lost mid-response. the response above may be incomplete。它意味著客戶端和服務(wù)端之間的連接在響應(yīng)過程中斷開了。排查順序應(yīng)該是是不是網(wǎng)絡(luò)不穩(wěn)定比如公司網(wǎng)絡(luò)、跨地域訪問。是不是請求設(shè)置了過短的讀取超時。模型生成時間較長網(wǎng)關(guān)在中間斷開了連接。是否啟用了流式輸出而流式讀取不穩(wěn)定。對視頻生成這種長任務(wù)我更推薦優(yōu)先使用異步任務(wù)模式而不是同步等待。因為同步等待一旦連接斷開你既拿不到結(jié)果也不知道任務(wù)是否還在后臺運行狀態(tài)變得不可控。異步任務(wù)至少能留下一個 task_id方便恢復(fù)查詢。5.3 400 參數(shù)錯誤先看響應(yīng)體再改代碼400通常是請求體有問題比如熱搜詞里提到的thinking_budget parameter must be a positive integer就說明模型不支持這個非正整數(shù)的值。類似的錯誤還有上下文長度超限比如maximum context length is 1048576 tokens。處理這類參數(shù)錯誤時不要只看狀態(tài)碼要仔細讀響應(yīng)體里的error.message。OpenRouter 作為網(wǎng)關(guān)往往會把上游模型返回的原始錯誤信息透傳出來這能幫你省很多事。如果確認是模型不支持的參數(shù)直接刪除該參數(shù)如果是上下文超長就對輸入做截斷或摘要。這里有一個建議盡量把“請求參數(shù)構(gòu)造”和“業(yè)務(wù)參數(shù)”分開。你在代碼里定義自己的prompt、duration、resolution然后到一個適配層把業(yè)務(wù)參數(shù)轉(zhuǎn)換成模型真正接受的參數(shù)。這樣切模型時只需要改適配層而不是改所有業(yè)務(wù)代碼。5.4 速率限制和費用控制除了平臺可能限流模型提供方也可能有自己的配額。OpenRouter 統(tǒng)一了計費你可以在控制臺看到調(diào)用記錄和費用。但正因為“統(tǒng)一”你可能對每個模型的具體消耗沒那么敏感。我的做法是測試階段每個模型只跑少量樣本先估算成本。生產(chǎn)環(huán)境設(shè)置單次請求的預(yù)檢邏輯比如限制 prompt 長度、限制視頻時長。如果響應(yīng)帶有cost字段記錄到日志定期核對賬單。下面是一個簡化的問題排查表錯誤現(xiàn)象可能原因優(yōu)先排查項處理建議401 UnauthorizedAPI Key 無效或缺失檢查請求頭中的 Authorization重新生成 Key確認沒有多余空格400 Bad Request參數(shù)錯誤或模型不支持某字段讀取響應(yīng)體 error.message去掉不支持參數(shù)裁剪超長輸入429 Too Many Requests觸發(fā)速率限制檢查近 1 分鐘請求頻率退避重試降低并發(fā)529 Overloaded服務(wù)端過載查看平臺狀態(tài)頁指數(shù)退避必要時切換模型connection lost mid-response連接中斷檢查 timeout 和網(wǎng)絡(luò)改用異步任務(wù)增加重試6. 適用邊界什么場景適合用 OpenRouter 視頻生成 API6.1 適合的人和團隊OpenRouter 這類聚合 API 最適合以下場景快速原型驗證你想比較三個視頻生成模型的效果不想每家都注冊一遍賬號。內(nèi)部工具給團隊做一個“輸入描述生成視頻”的內(nèi)部站點統(tǒng)一 API Key 和計費。個人開發(fā)者沒有精力維護多家平臺的 SDK希望用 OpenAI 風格接口快速接入。需要橫跨不同模型做自動切換的自動化流程。在這些場景里統(tǒng)一協(xié)議帶來的收益是實打?qū)嵉拇a結(jié)構(gòu)基本一致切換模型成本低賬單集中。6.2 不適合的場景聚合 API 不是銀彈。下面這些場景我建議你謹慎考慮對延遲極度敏感聚合網(wǎng)關(guān)會引入額外一跳而且長任務(wù)受排隊影響。數(shù)據(jù)必須留在內(nèi)網(wǎng)視頻渲染通常涉及大量數(shù)據(jù)如果合規(guī)要求數(shù)據(jù)不能出境那就不適合。需要深度定制模型行為某些模型的私有參數(shù)、特殊采樣方式或者細粒度的回調(diào)不一定能在統(tǒng)一接口里完全暴露。超大批量任務(wù)如果每天要生成數(shù)萬條視頻聚合平臺的費率和限流可能不如和模型提供方直接簽合同劃算。另外OpenRouter 本身也受上游模型服務(wù)條款約束。如果一個模型在特定地區(qū)不可用或者上線/下線狀態(tài)有變化你可能會在某個時間點突然發(fā)現(xiàn)請求失敗。所以不要把聚合平臺當成“永不改變”的基礎(chǔ)設(shè)施關(guān)鍵業(yè)務(wù)一定要有模型降級方案。6.3 關(guān)于“無限制”“免審核”的誤區(qū)在熱搜詞里我留意到一些類似“無限制無審核生成視頻”的說法。這里必須說清楚無論在哪個平臺使用 AI 視頻生成能力都要遵守平臺服務(wù)條款、模型使用政策以及當?shù)胤煞ㄒ?guī)。所謂“無限制”“免審核”的軟件很多時候要么是假的要么本身就是違規(guī)甚至違法的工具開發(fā)者一旦接入風險極高。即使 OpenRouter 作為聚合層幫你屏蔽了部分差異它也不會幫你規(guī)避內(nèi)容安全責任。如果生成內(nèi)容涉及侵權(quán)、色情、暴力、詐騙等黑灰產(chǎn)場景責任始終在調(diào)用方。這也是我為什么強調(diào)“代碼優(yōu)先”的另一層含義先把合規(guī)邊界寫進代碼比如 prompt 預(yù)檢、生成內(nèi)容標記、用戶舉報機制而不是等出了事再補救。7. 當請求失敗時按這個順序排查7.1 先看現(xiàn)象和響應(yīng)體遇到失敗第一件事不是改代碼而是記錄現(xiàn)場。你需要確認HTTP 狀態(tài)碼是多少。響應(yīng)體里的error字段寫了什么。有沒有request_id或id可以用于追蹤。是第一次失敗還是穩(wěn)定復(fù)現(xiàn)。如果響應(yīng)體里只有一句“internal server error”那大概率是平臺側(cè)問題如果詳細說明了某個參數(shù)不合法那才是自己的問題。7.2 再查請求體和參數(shù)一旦確認是客戶端問題重點檢查這些項目model字符串是否和模型列表完全一致。messages或prompt字段是否為空、格式是否正確。是否傳了模型不支持的額外字段。是否少傳了必填字段比如圖片輸入時少了image_url。視頻時長、分辨率是否超出模型限制。這里最容易讓人困惑的是同一個請求換一個模型就能通過。這不是 OpenRouter 的問題而是模型之間的能力邊界不同。遇到 400先對照該模型的文檔做參數(shù)裁剪而不是盲目調(diào)整重試次數(shù)。7.3 然后查環(huán)境和網(wǎng)絡(luò)如果請求代碼本身沒問題網(wǎng)絡(luò)層也要排查。常見情況包括本機無法訪問openrouter.ai可能需要檢查 DNS 和網(wǎng)絡(luò)連通性。公司防火墻或安全軟件攔截了長連接。本地代理環(huán)境導(dǎo)致請求被路由到異常節(jié)點。容器部署時未正確設(shè)置網(wǎng)絡(luò)代理或HTTPS_PROXY環(huán)境變量殘留。排查時可以用一個最簡單的文本模型接口測試如果文本模型接口正常視頻生成接口失敗那可能是視頻生成服務(wù)的狀態(tài)或參數(shù)問題如果連文本模型都失敗那大概率是網(wǎng)絡(luò)、Key 或賬戶問題。7.4 最后查賬戶、額度和模型狀態(tài)這一步容易被忽略尤其是在“項目昨天還能跑今天突然不行”的時候API Key 是否過期或被重置。賬戶余額是否不足。是否觸發(fā)了月度或分鐘的速率限制。模型是否下線、暫?;蚯袚Q了版本。是否因為內(nèi)容審核策略命中被平臺標記或限制。如果以上都沒有問題那就把日志里記錄的請求 ID 和錯誤信息發(fā)給平臺支持而不是憑感覺“換個 Key 再試一次”。收尾從一次 API 接入到一套可復(fù)用流程寫到這里你會發(fā)現(xiàn)這篇文章并沒有給出某個具體視頻模型的完整代碼因為 OpenRouter 模型列表和接口細節(jié)是會變化的。真正值得沉淀的是一套接入思路第一步最小跑通。用 curl 發(fā)一個最簡單的文字轉(zhuǎn)視頻請求確認鑒權(quán)、模型名、響應(yīng)結(jié)構(gòu)都正常不要在一開始就調(diào)一堆參數(shù)。第二步補齊異常處理。把 529、超時、參數(shù)錯誤、任務(wù)失敗這些常見情況逐個寫進代碼讓失敗變得可觀測、可恢復(fù)。第三步工程化落地。把 Key 放進環(huán)境變量把調(diào)用封裝成函數(shù)把日志和計費記錄接入你的監(jiān)控體系再根據(jù)業(yè)務(wù)需求選擇異步隊列、并發(fā)控制和模型降級策略。這個框架不只適用于 OpenRouter也適用于任何視頻生成 API。聚合平臺能幫你省去重復(fù)適配的麻煩但真正決定一個功能能不能長期跑下去的是你對額度、錯誤、日志和合規(guī)邊界的掌控。如果你正在準備接入建議現(xiàn)在就打開模型列表找一個支持視頻生成的模型把第一段 curl 跑通。之后再看結(jié)果不遲。