工程實作 19 min read

免費生 100 張遊戲卡牌插圖:FLUX.1-schnell + Pollinations 零成本實戰(沒綁信用卡)

一款 Splendor 網頁版要 90 張發展卡 + 10 張貴族卡插圖,預算 $0、手上沒綁信用卡,不能讓任何一步不小心被扣款。最後用 Hugging Face 的 FLUX.1-schnell 當主力(免費帳號每月 $0.10 額度)、額度用完自動 fallback 到完全免費的 Pollinations,再用 sharp 後製依寶石色染色,100 張圖一次跑完、整副風格一致、總花費 $0。這篇把 token 怎麼拿、額度怎麼算、bash 怎麼封裝、批次 seed 怎麼固定保證可重現、schnell 不吃顏色怎麼用後製補色,全寫清楚,附可直接抄的 bash / Node.js 程式碼。

本文目錄 · 11
TL;DR:我要幫一款 Splendor 網頁版生 90 張發展卡 + 10 張貴族卡的插圖,預算是 $0、手上沒綁信用卡。最後用 Hugging Face 的 FLUX.1-schnell 當主力(免費帳號每月 $0.10 額度)、額度用完自動 fallback 到完全免費的 Pollinations,再用 sharp 後製依寶石色染色,100 張圖一次跑完、整副風格一致、總花費 $0。這篇把 token 怎麼拿、額度怎麼算、bash 怎麼封裝、批次怎麼重現、後製怎麼補色全寫清楚。

📌 目錄

FLUX.1-schnell 免費生圖產出的 Splendor 卡牌插圖在遊戲畫面中

🎨 為什麼選 FLUX.1-schnell 當免費生圖主力

我在做一款 Splendor(璀璨寶石)的網頁版,引擎、React UI、測試都寫完了,剩一件事:90 張發展卡 + 10 張貴族卡的插圖。商業圖庫授權貴、找美術不現實、自己畫一百張更不可能。生成式 AI 是唯一可行解,但有一條硬限制——這專案的圖預算是 $0,而且我手邊沒有綁信用卡的生圖帳號,不能讓任何一步「不小心被扣款」。

我自己一開始也以為「免費生圖」等於畫質爛、等於 watermark、等於只能玩玩。實際跑下來發現,black-forest-labs/FLUX.1-schnell 這顆模型本身就是 Black Forest Labs 開源、走 Apache-2.0 授權的 distilled 版本,4 步就能出圖、速度快、品質夠用在遊戲卡面。它在 Hugging Face 上可以直接透過 Inference Providers 呼叫,免費帳號就有額度。

schnell 在德文是「快」的意思,這顆就是 FLUX 家族裡為速度優化的版本:步數少、延遲低,特別適合「同一套 prompt 批次出幾十上百張」這種場景。

💰 先搞懂 HuggingFace 免費額度到底有多少

動手前先把錢的事算清楚,免得跑到一半被擋。根據 Hugging Face 官方 Inference Providers 定價頁(2026-06 查證):

帳號類型每月免費額度超額
免費註冊用戶$0.10(官方註明 subject to change)需先購買 credit 才能續用
PRO 用戶($9/月)$2.00可 pay-as-you-go
Team / Enterprise每 seat $2.00可 pay-as-you-go,額度組織內共用
計費邏輯是 compute time × 硬體單價,不是「每張固定價」。官方文件給的例子是:一個 FLUX.1-dev 請求跑 10 秒、GPU 每秒 $0.00012,這張就billed $0.0012。換算下來,免費的 $0.10 大概只夠 每月數十張 FLUX 圖。

這就是關鍵矛盾:我要 100 張,免費額度只夠數十張。所以策略不是「想辦法塞進 $0.10」,而是 「HF 額度先榨乾、用完自動切到完全免費的 Pollinations」——這樣 HF 出高品質的那幾十張,剩下的由 Pollinations 補滿,整體還是 $0。

重點:免費帳號超額不會自動扣款,它會直接擋下請求(要先 purchase credit 才會續用)。對「絕不能被扣款」的需求來說,這反而是保險——沒綁卡、超額就停,不會有意外帳單。

🔗 一個 token 的取得與權限設定

整套流程只需要一個 Hugging Face token。取得步驟:

  • huggingface.co/settings/tokens 建立 token

  • 權限一定要勾「Make calls to Inference Providers」(inference 權限)——這是最常踩的坑,少勾這個,呼叫 router 會直接 401

  • 把 token 存到 ~/.cache/huggingface/token,並 chmod 600 鎖權限;或 export HF_TOKEN= 走環境變數
  • # 存 token(注意:token 是機密,別 commit、別貼公開地方)
    mkdir -p ~/.cache/huggingface
    printf '%s' 'hf_你的token' > ~/.cache/huggingface/token
    chmod 600 ~/.cache/huggingface/token
    

    # 驗證:能不能讀到
    test -r ~/.cache/huggingface/token && echo "token ok"

    呼叫的 endpoint 是 router,不是舊的 Inference API:

    https://router.huggingface.co/hf-inference/models/black-forest-labs/FLUX.1-schnell

    ⚡ bash 封裝:HF 主力 + Pollinations fallback

    我把生圖封成一支 free-image.sh,介面就一行:free-image.sh "" [width] [height] [seed]。它的 provider 優先序是 HF FLUX.1-schnell → Pollinations,前者失敗就掉到後者,兩者都掛才非 0 退出。

    核心有三個設計值得抄:

    1. 圖檔 round-trip 自驗

    生圖 API 失敗時常常回的是 HTML 錯誤頁或 JSON error,但 HTTP 200,檔案也寫進去了——如果不驗,你會得到一堆「看起來存在、其實打不開」的壞檔。所以寫檔後一定要驗它真的是圖:

    verify_image() {
      local path="$1"
      [[ -s "$path" ]] || return 1
      local ftype
      ftype="$(file -b --mime-type "$path" 2>/dev/null)"
      if [[ "$ftype" == image/* ]]; then
        return 0
      fi
      rm -f "$path"   # 不是圖 → 刪掉避免殘留誤導
      return 1
    }

    2. 尺寸先 floor 到 8 的倍數

    FLUX 要求寬高是 8 的倍數,傳 768 沒事,但若參數化傳了奇怪的值會報錯。封裝層先除整:

    WIDTH=$(( (WIDTH / 8) * 8 ))
    HEIGHT=$(( (HEIGHT / 8) * 8 ))

    3. 用 python3 安全組 JSON body

    prompt 裡常有逗號、引號、換行,用字串拼 JSON 一定爆。交給 python3 -c 組:

    build_hf_body() {
      python3 - "$PROMPT" "$WIDTH" "$HEIGHT" "$SEED" <<'PY'
    import sys, json
    prompt, w, h, seed = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), sys.argv[4]
    params = {"width": w, "height": h}
    if seed != "":
        params["seed"] = int(seed)
    print(json.dumps({"inputs": prompt, "parameters": params}))
    PY
    }

    HF 失敗時 fallback 到 Pollinations 走的是匿名路徑——免 key、把 prompt URL-encode 進路徑 https://image.pollinations.ai/prompt/?width=...&height=...&seed=...&model=flux,直接 GET 回圖。Pollinations 完全免費、無明確月額度上限,匿名 tier 高併發時可能 rate limit,但對「補滿剩下幾十張」綽綽有餘。

    FLUX.1-schnell 批次生成的 Splendor 卡牌與貴族插圖風格一致

    🧠 批次生 100 張:固定 seed 換風格一致

    封裝層搞定後,上面再疊一支 gen-art.mjs 做批次。整副 100 張要看起來像同一套牌,靠三件事:

    共用 style suffix

    所有圖共用同一段風格尾巴,確保畫風統一:

    Renaissance fantasy gemstone trade illustration, ornate baroque detail,
    rich jewel tones, dramatic chiaroscuro lighting, painterly oil texture,
    centered composition, no text, no letters, no watermark, square format

    no text, no letters, no watermark 這幾個負面詞很重要——生圖模型很愛在圖上亂塞看不懂的假文字,明確排除能少修很多。

    固定 seed(由 id 推導)

    同一張卡每次重生都要長一樣,否則補圖時整副風格會飄。我用 FNV-1a hash 把卡片 id 轉成穩定 seed:

    function seedFromId(id) {
      let h = 2166136261;
      for (let i = 0; i < id.length; i++) {
        h ^= id.charCodeAt(i);
        h = Math.imul(h, 16777619);
      }
      return Math.abs(h) % 2_000_000_000;
    }

    同 id → 同 seed → 同 prompt → 結果可重現。這招讓「HF 額度回補後再跑一次補齊高品質版」變得安全:已存在的檔會被跳過,沒生成的才補。

    prompt 結構:顏色 motif 放最前面

    FLUX 對開頭 token 權重最高。一開始我把場景寫前面、顏色寫後面,結果所有寶石都被畫成透明鑽石。把顏色詞前置並重複(vivid GREEN emerald gemstones glowing bright green)才壓得住:

    const prompt = ${theme.motif}, displayed in ${TIER_SCENE[c.tier]}, ${theme.palette} color scheme dominates the image. ${STYLE};

    tier 越高場景越豪華(T1 是工匠小作坊、T3 是皇家寶庫),讓三階牌一眼能分辨等級。

    🎨 schnell 不吃顏色?用 sharp 後製上色

    即使 prompt 把顏色寫到頂,schnell 因為步數少,顏色控制力還是不如 dev——綠寶石卡常常出來偏灰、紅寶石不夠紅。硬要靠 prompt 救,得反覆重生、燒額度。

    我的解法是 方案 B:生完用 sharp 依卡片 bonus 色後製染色。$0、100% 正確、可重現,不用跟模型的隨機性纏鬥:

    const TINT = {
      emerald: { r: 120, g: 180, b: 130 },
      sapphire: { r: 110, g: 140, b: 200 },
      ruby: { r: 200, g: 110, b: 120 },
      diamond: null,                      // 不染,保留透明白
      onyx: { r: 90, g: 85, b: 110 },     // 偏暗紫黑
    };
    

    async function recolor(rawPath, finalPath, bonus) {
    const tint = TINT[bonus];
    let img = sharp(rawPath);
    if (tint) {
    img = img.tint(tint).modulate({ saturation: 1.3 });
    }
    await img.jpeg({ quality: 88 }).toFile(finalPath);
    }

    tint() 套色相、modulate({ saturation: 1.3 }) 補回飽和度避免染完發灰。diamond 維持中性(它本來就該無色)。下面這張是同一張原圖套不同 tint 的效果——最左是原圖,後面依序套 emerald / ruby / sapphire:

    sharp tint 後製上色:同一張 FLUX 生圖套不同寶石色的對比

    最後 100 張全是 768×768、quality 88 的 JPEG,cards 約 9.3 MB、nobles 約 1.3 MB,全副風格統一、顏色正確。

    📝 踩坑紀錄

    壞檔當成功檔:沒驗就直接跳下一張

    第一版沒做 verify_image,跑完看到 100 個檔很開心,結果有幾張是 HF 排隊時回的 JSON、副檔名卻是 .jpg。遊戲一載入就破圖。加上 mime-type 驗證、不是圖就刪掉重生後才乾淨。「檔案存在」不等於「圖生成成功」,一定要 round-trip 驗它打得開。

    顏色 token 放後面 → 整副變鑽石

    前面提過,FLUX 對開頭 token 權重最高。場景寫前面、顏色寫後面時,模型把「gemstone」當主體、顏色當裝飾,結果五色全畫成透明鑽石。顏色詞前置 + 重複 + 大寫強調才壓得住。

    schnell 顏色救不動,別硬燒額度

    我一度想靠加強 prompt + 重生把綠寶石救綠,燒掉好幾次額度都沒穩定。停下來改走 sharp 後製,一次解決還可重現。模型不擅長的事,丟給確定性的後製,比跟隨機性纏鬥划算。

    token 少勾 inference 權限 → 401

    建 token 時沒勾「Make calls to Inference Providers」,router 直接擋。重建 token 勾對權限即可。

    ⚖️ 免費生圖管道比較

    維度HF FLUX.1-schnellPollinations商業 API(DALL·E / Midjourney)
    費用免費額度 $0.10/月,超額需購 credit✓ 完全免費✗ 按張付費
    需信用卡✗ 不需(超額自動擋)✗ 不需✓ 需綁卡
    需 API key△ 需 token(免費申請)✗ 匿名可用✓ 需 key
    畫質✓ 高△ 中(匿名 tier)✓ 高
    月額度上限數十張(免費)無明確上限看付費方案
    適合場景主力出高品質圖補量 / fallback商業專案有預算
    我的組合策略:HF 出主力品質、Pollinations 補量,兩者疊起來 $0 出滿 100 張。

    ❓ 常見問題 FAQ

    Q1:FLUX.1-schnell 免費可以商用嗎?
    模型本身走 Apache-2.0 授權(Black Forest Labs 開源的 schnell 版本),生成的圖可商用。但每個 Inference Provider 的服務條款不同,正式商用前還是去確認你呼叫的那個 provider 的 ToS。

    Q2:免費的 $0.10 額度用完會自動扣款嗎?
    不會。免費帳號超額會直接擋下請求,要先在 billing 頁 purchase credit 才能續用。沒綁卡就不會有意外帳單——這對「絕不能被扣款」的需求反而是保險。

    Q3:為什麼不直接全用 Pollinations,免 key 又免費?
    可以,但 Pollinations 匿名 tier 在高併發時會 rate limit、畫質也略遜 FLUX.1-schnell。我的策略是 HF 出主力品質的那幾十張,Pollinations 補剩下的量,兼顧品質與成本。

    Q4:schnell 和 dev 差在哪?該選哪個?
    schnell 是 distilled 速度版,4 步出圖、快、適合批次;dev 步數多、品質與顏色控制更好但較慢、計費也更高。批次大量出圖選 schnell + 後製,少量精修選 dev

    Q5:seed 固定真的能完全重現嗎?
    同一個 provider、同 model、同 prompt、同 seed 下結果穩定,這是「同 id 重生不變」的基礎。但跨 provider(HF vs Pollinations)不保證一致,所以補圖時要用「跳過已存在檔」而不是「整副重生」。

    🔗 延伸資源

    author
    陳彥彤

    AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

    support

    覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。

    related

    相關文章

    [工程實作] · 17min
    Pollinations 免費生圖 API:不用註冊、不用 key、一行 URL 就出圖(附實測坑)
    Pollinations 是我看過門檻最低的生圖 API —— 不用註冊、不用 API key、不用綁信用卡,把 prompt 塞進一個 URL、curl 一下就回一張圖。這篇把它「URL 即 API」的設計、實測坑(/models 清單跟文件對不上、傳不存在的 model 不報錯而是靜默 fallback)、seed 決定性、匿名與註冊 tier 的差別、2025 之後的 watermark 政策、Referrer / Bearer 兩種認證都寫清楚,附可直接抄的 curl 與 Node.js 範例。本文 2 張配圖全是用 Pollinations 匿名 API 當場生的。
    [工程實作] · 15min
    用 Claude Code 從零做一款遊戲:不是會 prompt,是會這三件工程紀律
    我用 Claude Code 從零做了一款 Splendor(璀璨寶石)網頁版 —— 規則引擎、React UI、單元測試、E2E、連 100 張卡牌插圖都生好,全程沒手寫幾行 code。但能跑得順的關鍵不是 prompt 寫得好,而是三件工程紀律:規則引擎跟 UI 徹底分離、用「不變量測試 + fuzzing」逼出 AI 看不到的暗 bug、E2E 走完整 round-trip 而不是繞過引擎。這篇拆解這套協作方法,附這個專案的真實程式碼。
    [工程實作] · 19min
    我怎麼把別人的 skill 吸進自己產線,做成更強的 skill:一次語意標註實作覆盤
    看到好用的 skill,大部分人裝來用;如果你有自己的產線,更值錢的做法是拆它的零件焊進你的流程。我原本有一套截圖審查+手冊產線(/screenshot-review),但手冊圖是「乾圖」沒有圖上標註——這正是 app-screenshots 的 annotate.js 補得上的洞。本文覆盤:我怎麼把它拆進產線,過程撞到一個架構級的坑(標註截不進圖=OVERLAY GONE:agent-browser 每個 CLI 指令是獨立 CDP 呼叫,eval 注入的 overlay 下一指令就蒸發),查根因、用 batch 把 open→eval→screenshot 綁進同一 session 解掉、封裝成 annotate-shot.sh、寫回自己的 skill。全程真的跑真的驗,附 batch 解法的真實成果圖與踩坑總表。