技術筆記 / AI

接生成式影片 API 的十個坑(fal.ai 實戰)

每一條都是真的花錢踩過才知道的,不是讀文件想到的。 專案無關 —— 換一家 vendor 大部分還是成立。 情境是一條 AI 短片產線:選題 → 生 clip → 配音效 → 剪成片,跑在 fal.ai 上。


0. 先講最貴的教訓

文件沒寫的事,它照樣會發生;文件寫了的事,你送的欄位名可能根本不對。

這兩句話底下是 2026-08~09 一支片燒掉的學費。下面十條,前四條會直接花錢。


1. 💰 計費單位不是你以為的那個

fal 在 response header 回 x-fal-billable-units。它跟「秒數」不是同一個東西。

模型 要求 實際計費 比例
Wan 3.0 Prime i2v 5s 14 單位 2.8
MiniMax H3 5s(實際 5.184s) 6 單位 1.2(= ceil 秒數)
MMAudio V2 5s 5 單位 1.0

Wan 那條:估 $0.84、實收 $1.96。如果你的預算 gate 是拿「秒數 × 牌價」算的, 它會放行本該擋下的請求 —— 而且是每一次都低估,不是偶爾。

做法:把 header 記進帳本,寫一支工具從帳本反推每個模型的比例, 存進模型設定,估價時乘上去。沒對過帳的模型要明說「這個數字可能低估」。

2. 💰 已承諾 ≠ 已花費

送出時記「估價」是對的(防併發把預算灌爆),但完成後要用實收覆蓋它。 我們的帳本一開始只數 submitted 的估價,completed 的實收從來沒進過帳: 當天 gate 以為花了 $1.68,實際 $3.92 —— 低估 2.33 倍。

一個 request 一筆帳:submitted 掛估價 → completed 用實收蓋掉 → reversed 整筆丟掉。 ⚠️ 逾時不算失敗:job 還在 vendor 那邊跑、照樣扣款,估價要留著。

3. 💰 預設值會咬人

MiniMax H3 的 resolution 預設是 2K($0.13/s),而 768P 是 $0.06/s。 不明送 = 你照 768P 估價、按 2K 付錢。Wan 的 resolution 預設 1080p 也一樣。

凡是影響計價的欄位一律明送,不要讓它吃預設。

4. 💰 配音效會把影像重新編碼

MMAudio 吐回來的是「同一支影片 + 音軌」。同解析度同 fps,但 bitrate 從 14.9 Mbps 掉到 2.2 Mbps(9.3MB → 1.4MB)。檔案照樣能播, 所以沒有人會發現 —— 每配一次音效就悄悄掉一代畫質。

做法:只拿它的音軌(stream copy),影像用原檔。零重編碼。


5. 🔴 schema 沒有那個欄位 ≠ 不會有那個輸出

MiniMax H3 的 OpenAPI schema 沒有 generate_audio,所以我斷定它不產音訊。 第一支真跑:有音軌,峰值 -2.97 dBFS。它預設就配聲音,而且沒有參數關得掉。

schema 描述的是你能控制什麼,不是它會做什麼。 → 產出一定要量(有沒有音軌、幾秒、多大、什麼解析度),不要憑 schema 推斷。

6. 🔴 不認得的欄位會被靜默忽略

送錯名字不會報錯。所以:

語意 Kling Wan MiniMax H3
音訊輸出 generate_audio audio(而且 default=true) (沒有)
首幀 — start_image_url image_url
來源圖 image_url — reference_image_urls(array)

Wan 那個 audio default=true 特別陰:「不送」不等於「關」。

→ 欄位名由每個模型自己宣告,不要跨模型共用一組。 → 模型不吃某個輸入時要當場擋,不能塞進 payload 讓 vendor 忽略 —— 「你以為接上了、實際上沒有,而錢已經花了」。

7. 🔴 dry-run 印的 body 必須等於 live 送的 body

我們的 dry-run 少了三個參數(negative_prompt / start_image_url / end_image_url), 所以它印的 body 跟真的要送的不一樣。dry-run 的唯一用途就是花錢前核對 —— 差一個欄位就是核對了個寂寞。

更糟的是它製造假證據:曾經有人根據 dry-run 的 body 回報「negative_prompt 被靜默丟掉」,那次結論碰巧對,但推論的依據是錯的。

→ 用反射比對兩邊的簽名,加新參數忘了穿過去就紅。

8. 查參數不用花錢也不用讀行銷頁

fal 有公開的 OpenAPI schema:

curl -s "https://fal.ai/api/openapi/queue/openapi.json?endpoint_id=<ENDPOINT_ID>" \
  | python3 -c "import json,sys;[print(k,':',v.get('type'),'—',str(v.get('description',''))[:90]) \
      for n,sc in json.load(sys.stdin)['components']['schemas'].items() if 'Input' in n \
      for k,v in (sc.get('properties') or {}).items()]"

加任何模型進產線前先打這個。 比讀模型頁的行銷文字準,而且零成本。 ⚠️ endpoint id 的前綴會變:alibaba/wan-3.0-prime/… 是 200、fal-ai/wan-3.0-prime/… 是 404。 (價錢 schema 裡沒有,還是要看模型頁。)


9. 🎬 「參考圖」與「首尾幀」是兩種機制,別混

參考圖 reference-to-video 首尾幀 image-to-video
那張圖是什麼 角色 / 風格參考,模型自己畫第一幀 就是第 0 格畫面
拿去中性背景的定裝照餵它 首幀乾淨,角色照樣鎖住 背景原封不動進成品
張數 多張(H3 最多 9,前 5 張免費) 1(+ 尾幀 1)
解的是 跨鏡角色一致 構圖 / 接點精準

我們踩的:拿綠幕定裝照當 start_image → 成品前 1 秒是「綠幕變形成賣場」的過渡。 換成參考圖之後首幀就乾淨了。

兩條通常是同一家的不同 endpoint、同價 —— 不是誰取代誰,是用在不同鏡頭。

10. 🎬 模型不聽話之前,先確認話有送出去

我們的分鏡每一拍都有「機位」欄,作者也認真寫了 (「貼著輸送帶的低角度特寫,淺景深」)。但那一欄只存進資料庫,從來沒進過 prompt —— 於是每一段都生出同一種中景。

看起來像模型不聽話。實際上是話沒送出去。

補上之後,同一個模型立刻給出低角度、淺景深、主體在背景失焦的鏡頭。

同一類:prompt 模板的作用域。我們的「視覺不變量」 (dancing, bright saturated colors, clean solid-color background)寫給某一個頻道, 卻被無條件套用在所有產線上 —— 情境劇因此一直被要求純色背景。 換再多模型也沒用,因為 prompt 自己在要那個東西。


一句話總結

症狀 先查
比預期貴 計費單位(1)、實收沒回填(2)、預設解析度(3)
畫質變差 有沒有被重新編碼(4)
出現沒要的東西 schema 管不到的預設行為(5)
參數好像沒作用 欄位名對不對(6)、dry-run 是不是漏印(7)
模型「不聽話」 話有沒有真的送出去(10)
首幀怪 用錯機制(9)

在 GitHub 看原文