Claude Opus 5.5 已在 SeedRouter 上線

在 Claude Code 中使用 Nano Banana Pro:一個能產生與編輯影像的 skill

為 Claude Code 打造 Nano Banana Pro skill:一支送出請求、輪詢任務並儲存影像的小腳本,加上一個可以用名稱呼叫的 SKILL.md。

以 Markdown 閱讀

Claude Code 可以透過一個 skill 用 Nano Banana Pro 產生與編輯影像:一個包含 SKILL.md 檔案和一支呼叫 SeedRouter API 的簡短腳本的資料夾。你輸入 /nano-banana-pro 並描述想要的影像,Claude 寫好提示詞、執行腳本,由腳本送出請求、輪詢任務,再把影像儲存到你的專案中。同一個資料夾在 OpenAI Codex 中也能使用。SeedRouter 並未提供官方的 skill 或 MCP 伺服器,所以本指南提供一個可以直接照抄的版本。skill 的格式依據 Anthropic 與 OpenAI 的文件,查證於 2026 年 9 月 29 日。

Claude Code 如何用 Nano Banana Pro 產生影像?

Claude Code 本身不會畫圖。它能執行指令,所以可以執行一支呼叫影像 API 的腳本。裝好 skill 之後,一次請求的流程如下:

  1. 你呼叫 skill 並描述影像,例如 /nano-banana-pro a hero image for the pricing page。
  2. Claude 把你的描述改寫成 Nano Banana Pro 提示詞,並選定尺寸與寬高比。
  3. 它執行腳本。腳本把請求送到 POST https://api.seedrouter.ai/v1/images/generations,取得一個任務 ID。
  4. 腳本每隔幾秒輪詢一次 GET /v1/tasks/{id},直到影像完成,再把它下載到 images/ 資料夾。
  5. Claude 告訴你檔案路徑,之後也能直接在你的程式碼中使用這張影像。

用腳本而不只靠指示,能讓 API 呼叫保持精確:不論 Claude 寫出什麼提示詞,請求內容、輪詢迴圈與儲存檔案的方式每次都一樣。

開始之前需要準備什麼?

  • 一把 SeedRouter API Key,在你啟動 Claude Code 的終端機中匯出:
export SEEDROUTER_API_KEY="your-key"
  • 餘額。 每執行一次就是一次付費請求。新帳戶附一小筆免費餘額,失敗的請求不收費。
  • Python 3。 腳本只用標準函式庫,不需要安裝任何東西。

腳本:送出、輪詢與儲存

把下面的程式碼儲存為 skill 資料夾中的 scripts/nano_banana_pro.py:

#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import sys
import time
import urllib.error
import urllib.request

API = "https://api.seedrouter.ai/v1"
AGENT = "nano-banana-pro-skill/1.0"
MIME = {
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".png": "image/png",
    ".webp": "image/webp",
    ".heic": "image/heic",
    ".heif": "image/heif",
}
RATIOS = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"]


def call(method, path, body=None):
    request = urllib.request.Request(
        API + path,
        data=None if body is None else json.dumps(body).encode(),
        method=method,
        headers={
            "Authorization": "Bearer " + os.environ["SEEDROUTER_API_KEY"],
            "Content-Type": "application/json",
            "User-Agent": AGENT,
        },
    )
    try:
        with urllib.request.urlopen(request, timeout=60) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        sys.exit(f"HTTP {error.code}: {error.read().decode()}")


def submit(args):
    parts = [{"text": args.prompt}]
    for url in args.ref:
        suffix = pathlib.PurePosixPath(url.split("?")[0]).suffix.lower()
        if suffix not in MIME:
            sys.exit(f"Cannot tell the image type of {url}; use a .jpg, .png, .webp, .heic or .heif URL.")
        parts.append({"fileData": {"mimeType": MIME[suffix], "fileUri": url}})
    image_config = {}
    if args.ratio:
        image_config["aspectRatio"] = args.ratio
    if args.size:
        image_config["imageSize"] = args.size
    generation_config = {"responseModalities": ["IMAGE"]}
    if image_config:
        generation_config["imageConfig"] = image_config
    body = {
        "model": args.model,
        "contents": [{"role": "user", "parts": parts}],
        "generationConfig": generation_config,
    }
    return call("POST", "/images/generations", body)["id"]


def wait(task_id, out_dir, timeout):
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        task = call("GET", "/tasks/" + task_id)
        if task["status"] == "completed":
            out_dir.mkdir(parents=True, exist_ok=True)
            extension = "jpg" if task["output"]["output_format"] == "jpeg" else task["output"]["output_format"]
            for index, image in enumerate(task["output"]["data"]):
                path = out_dir / f"{task_id}-{index}.{extension}"
                download = urllib.request.Request(image["url"], headers={"User-Agent": AGENT})
                with urllib.request.urlopen(download, timeout=120) as response:
                    path.write_bytes(response.read())
                print(path)
            return
        if task["status"] == "failed":
            sys.exit(f"Task {task_id} failed: {task['error']['message']}")
        time.sleep(3)
    sys.exit(f"Task {task_id} is still processing. Resume with --resume {task_id}")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--prompt")
    parser.add_argument("--ref", action="append", default=[])
    parser.add_argument("--ratio", choices=RATIOS)
    parser.add_argument("--size", choices=["1K", "2K", "4K"])
    parser.add_argument("--model", default="gemini-3-pro-image",
                        choices=["gemini-3-pro-image", "gemini-3-pro-image-official"])
    parser.add_argument("--out", default="images")
    parser.add_argument("--timeout", type=int, default=600)
    parser.add_argument("--resume")
    args = parser.parse_args()
    if args.resume:
        task_id = args.resume
    elif args.prompt:
        task_id = submit(args)
        print(f"task {task_id}", file=sys.stderr)
    else:
        parser.error("--prompt or --resume is required")
    wait(task_id, pathlib.Path(args.out), args.timeout)


if __name__ == "__main__":
    main()

它做了這些事:

  • 只送出一次。 --prompt 是提示詞文字,--ref 加入一張參考影像 URL(需要多張就重複使用),--ratio 與 --size 設定 imageConfig。請求內容是 Google 的 generateContent 格式加上一個 model 欄位,與 Nano Banana Pro API 參考的定義一致。
  • 任務一建立就印出任務 ID,接著才開始等待。
  • 先輪詢,再儲存。 任務完成後,它把每張影像下載到 images/ 並印出路徑;任務失敗時,它會印出失敗原因。
  • 接續查詢,而不是重新送出。 如果等待逾時,用 --resume <task id> 再次查詢同一個任務。再送出一次就是第二次付費請求。
  • 傳送 User-Agent 標頭。 請保留這一行:有些伺服器會拒絕 Python 預設的 User-Agent,少了它下載可能會以 403 失敗。

交給 Claude 使用之前,可以先單獨測試:

python3 scripts/nano_banana_pro.py --prompt "A ceramic teapot on a linen tablecloth, soft window light" --ratio 16:9 --size 2K

skill 本身:SKILL.md 要寫什麼?

Claude Code 的文件把 skill 描述為一個包含 SKILL.md 檔案的目錄:「--- 標記之間的 YAML frontmatter 告訴 Claude 何時使用這個 skill,markdown 內容則是 skill 執行時 Claude 要遵循的指示。」scripts/ 這類輔助資料夾就放在它旁邊。

把下面的內容儲存為同一個資料夾中的 SKILL.md:

---
name: nano-banana-pro
description: Generate or edit an image with Nano Banana Pro (gemini-3-pro-image) through the SeedRouter API and save it into the project. Use when the user asks for an image, illustration, icon, poster or edit of an existing image.
argument-hint: "[what the image should show]"
disable-model-invocation: true
---

Create an image for this request: $ARGUMENTS

1. Write one prompt in full sentences: subject, framing, action, setting and style. Put any text that must appear in the image in double quotes and say where it goes. For an edit, say what changes and what must stay the same.
2. Choose --ratio from 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9 and --size from 1K, 2K, 4K (4K only for print).
3. Reference images must be public HTTPS URLs ending in .jpg, .png, .webp, .heic or .heif, passed with --ref. If the user points to a local file, ask for a public URL instead. Never send base64.
4. Show the user the prompt and options, and wait for approval. Each run is a paid request.
5. Run: python3 ${CLAUDE_SKILL_DIR}/scripts/nano_banana_pro.py --prompt "..." [--ratio R] [--size S] [--ref URL]
6. If it stops with "still processing", run it again with --resume and the same task ID. Never submit the same request twice.
7. Report the saved file paths. If the task failed, explain the reason and suggest a change to the prompt.

這裡有兩個重要欄位:

  • disable-model-invocation: true 表示只有你能啟動這個 skill。Claude Code 的文件建議把它用在「有副作用,或你想自行掌控時機的工作流程」。每次執行都要花錢,所以不該讓 Claude 自行決定去產生影像。
  • ${CLAUDE_SKILL_DIR} 會被替換成 skill 本身所在的資料夾,所以不論你把 skill 裝在哪裡,腳本都能正常執行。

skill 要放在哪裡,如何執行?

Claude Code 主要從兩個位置載入 skill:

位置路徑可用範圍
個人~/.claude/skills/nano-banana-pro/這台電腦上你的所有專案
專案.claude/skills/nano-banana-pro/目前的儲存庫;提交後即可與團隊共用

完成後的資料夾結構如下:

nano-banana-pro/
├── SKILL.md
└── scripts/
    └── nano_banana_pro.py

在已設定 SEEDROUTER_API_KEY 的終端機中啟動 Claude Code,接著輸入 skill 名稱,後面加上你想要的內容:

/nano-banana-pro a 16:9 hero image for the pricing page: a calm desk with a laptop, a notebook and a cup of tea, soft morning light, muted green palette, no text

Claude 顯示提示詞與選項,你核准後,影像就會出現在 images/ 中。除非你已事先核准這個指令,Claude Code 在執行腳本前還會請求權限,這是產生費用之前的第二道確認。

關於提示詞本身,Nano Banana Pro 提示詞指南更深入說明了結構、影像中的文字與參考影像。

Codex 可以使用同一個 Nano Banana Pro skill 嗎?

可以。兩個工具都遵循開放的 Agent Skills 標準。OpenAI 的 Codex 文件說,skill「是一個包含 SKILL.md 檔案以及選用腳本與參考資料的目錄」,而且該檔案「必須包含 name 與 description」,上面的 skill 兩者都有。

Codex 會在其他資料夾中尋找 skill:

位置路徑
個人~/.agents/skills/nano-banana-pro/
儲存庫.agents/skills/nano-banana-pro/

把資料夾複製過去,或建立一個指向它的符號連結;兩個工具都支援以符號連結方式放置的 skill 資料夾。在 Codex 中,執行 /skills 或輸入 $nano-banana-pro 即可呼叫。

有兩點差異需要留意:

  • 腳本路徑。 ${CLAUDE_SKILL_DIR} 是 Claude Code 的替換變數。Codex 會把每個 skill 的檔案路徑列給模型,所以它找得到 SKILL.md 旁邊的 scripts/nano_banana_pro.py;如果它執行的路徑不對,告訴它腳本在哪裡即可。
  • 自動呼叫。 disable-model-invocation 是 Claude Code 的欄位。Codex 的對應做法是在 skill 資料夾中放一個 agents/openai.yaml 檔案,在 policy 下設定 allow_implicit_invocation: false,這樣 Codex 只會在你呼叫時執行這個 skill。

腳本需要網路存取才能連到 api.seedrouter.ai。如果你的 Agent 在無法連網的沙箱中執行,請為這次工作階段開啟網路存取。

如何編輯專案中既有的影像?

Nano Banana Pro 透過參考影像進行編輯,而 API 只接受公開 URL 形式的參考影像。你硬碟上的檔案要先放到某個地方託管,例如你自己的儲存空間,再用 --ref 傳入。

產生出來的影像本身就已經託管好了:每個完成的任務都會以 URL 回傳影像。要在某個結果上繼續修改,就把它的 URL 當作參考影像傳回去,並寫上你想要的變更:

/nano-banana-pro edit the last image: keep the desk and the laptop, change the tea cup to a glass of water, and make the light warmer

Claude 會用 --ref 重複使用上一張影像的 URL,並寫出一個說明哪些部分保持不變的編輯提示詞。如果要進行在輪次之間保留模型自身脈絡的多輪編輯,API 參考說明了如何把先前的輪次送回去;上面的腳本一次只送出一輪。

如果你已經在程式設計 Agent 中使用 GPT Image 2,在 Codex 與 Claude Code 中使用 GPT Image 2 介紹了那套設定,它的核心是一段貼給 Agent 的提示詞。

常見問題

SeedRouter 有官方的 Nano Banana Pro skill 或 MCP 伺服器嗎?

沒有,SeedRouter 並未提供。本指南中的 skill 就是一個普通資料夾,歸你所有、可以自由修改,它用你的 Key 呼叫公開 API。

為什麼腳本要輪詢,而不是一直等影像回傳?

在 SeedRouter 上,Nano Banana Pro 請求是任務:送出後回傳一個任務 ID,任務完成時影像才會出現。輪詢讓每次連線都很短,就算等待被中斷,任務 ID 仍然有效。

同一個 skill 能用於 Nano Banana 2 嗎?

可以,改幾個地方就好。把腳本中 --model 的預設值與可選值改成 gemini-3.1-flash-image,再在 --size 的可選值中加入 512,這個尺寸 Nano Banana 2 支援而 Pro 不支援。Nano Banana 2 vs Pro vs 2 Lite 比較了這幾個模型。

載入 skill 會產生費用嗎?

不會。Claude 讀取 SKILL.md 在 SeedRouter 上不會產生任何費用。腳本每執行一次就是一次請求,依模型頁面上的費率計費,失敗的任務不收費。

從 Claude Code 開始產生影像

建立資料夾、匯出你的 Key,先用 /nano-banana-pro 試一張小影像。目前價格請見 Nano Banana Pro 頁面,所有請求欄位請見 API 參考。

相關指南