Skip to content

Nano Banana 系列 ​

模型简介 ​

Google 的 Nano Banana 系列提供图像生成和编辑能力,包括以下模型:

🆕 Nano Banana 2.1 (最新) ​

  • 模型名称: gemini-nano-banana-2.1
  • 版本: 正式版(GA)
  • 特点: 改善图像细节、文字渲染和多轮编辑的一致性
  • 分辨率支持: 1K、2K、4K,默认 1K,不支持 512
  • 参考图片: 单次请求最多 14 张
  • 适用场景: 文生图、图片编辑和多图合成

2.1 不接受 temperature、topP、topK、seed、logprobs 参数。切换型号时需同时检查这些参数,参见 Google Cloud 模型规格。

端点迁移说明 ​

注意

Nano Banana 2 和 Pro 的预览模型已退役,请将模型 ID 更新为下表中的正式版本。模型状态参见 Google 官方退役列表。

预览端点正式端点
gemini-3.1-flash-image-previewgemini-3.1-flash-image
gemini-3-pro-image-previewgemini-3-pro-image

调用方式:Google 原生格式(支持自定义纵横比) ​

💡 模型切换说明

在 Google 原生格式中,模型名称在 URL 路径中指定:

Nano Banana 2.1: /v1beta/models/gemini-nano-banana-2.1:generateContent

Nano Banana 2: /v1beta/models/gemini-3.1-flash-image:generateContent

Nano Banana Pro: /v1beta/models/gemini-3-pro-image:generateContent

Nano Banana: /v1beta/models/gemini-2.5-flash-image:generateContent

使用 POST 请求和 Exchange Token API Key:Authorization: Bearer $API_KEY,请求体为 application/json。全系列共用请求结构,参数支持范围按型号区分。

字段必填性说明
contents必填非空的消息数组。单轮传入当前请求;多轮按顺序传入历史消息和当前请求。
contents[].role可选单轮可省略,通常填 user;多轮历史中用 user 和 model 区分用户与模型消息。
contents[].parts必填非空的内容片段数组。提示词与参考图片分别放在独立的片段中。
contents[].parts[].text可选生成或编辑图片的提示词,建议提供。
contents[].parts[].inlineData.mimeType条件必填使用 inlineData 传入参考图片时必填,须与图片实际格式一致,例如 image/png、image/jpeg。
contents[].parts[].inlineData.data条件必填使用 inlineData 传入参考图片时必填,内容为 Base64 编码,不含 data URL 前缀。Nano Banana 最多 3 张,2.1、2、Pro 最多 14 张。
contents[].parts[].thoughtSignature条件必填多轮编辑时,若上一轮模型响应包含此字段,应随对应内容片段原样回传;首次请求无需自行填写。参见 思考签名说明。
generationConfig.responseModalities可选建议显式指定:["TEXT", "IMAGE"] 返回文字和图片;["IMAGE"] 仅请求图片。
generationConfig.imageConfig.imageSize可选输出分辨率,K 必须大写,512 不带 K。支持范围见下方;Nano Banana 需省略此参数。
generationConfig.imageConfig.aspectRatio可选输出纵横比,例如 1:1、16:9、9:16。
generationConfig.thinkingConfig.thinkingLevel可选2.1:MINIMAL、MEDIUM(默认)、HIGH;2:MINIMAL(默认)、HIGH;Pro:HIGH;Nano Banana 不支持。参见 思考级别说明。

支持的纵横比 ​

类型纵横比选项适用型号
横向21:9(超宽屏)、16:9(宽屏)、4:3、3:2全系列
正方形1:1全系列
纵向9:16(竖屏)、3:4、2:3全系列
其他5:4、4:5全系列
扩展比例1:4、4:1、1:8、8:1、9:21Nano Banana 2.1、Nano Banana 2、Nano Banana Pro

支持的分辨率 ​

ℹ️ 分辨率支持说明

  • Nano Banana 2.1 (gemini-nano-banana-2.1):支持 1K、2K、4K,默认 1K,不支持 512。
  • Nano Banana 2 (gemini-3.1-flash-image):支持 512、1K、2K、4K 四种分辨率。
  • Nano Banana Pro (gemini-3-pro-image):支持 1K、2K、4K 三种分辨率。
  • Nano Banana (gemini-2.5-flash-image):约 1K 级别,具体像素随纵横比变化,不支持 imageSize 参数。

代码示例 ​

完整 Curl 示例(Nano Banana 2.1 - 4K) ​

bash
#!/bin/bash
set -euo pipefail

export API_KEY="sk-xxxx"

curl --fail-with-body -sS \
  "https://api.exchangetoken.ai/v1beta/models/gemini-nano-banana-2.1:generateContent" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{
      "role": "user",
      "parts": [{"text": "A futuristic city skyline at sunset, highly detailed"}]
    }],
    "generationConfig": {
      "responseModalities": ["IMAGE"],
      "imageConfig": {"aspectRatio": "16:9", "imageSize": "4K"}
    }
  }' -o response.json

jq -er '[.candidates[]?.content.parts[]?
  | select(.thought != true and .inlineData.mimeType == "image/png")
  | .inlineData.data][0] // error("No PNG image returned; check response.json")' \
  response.json | base64 --decode > output_4k.png

echo "✅ 图片已保存: output_4k.png"

快速开始 Curl 示例(Nano Banana 2.1 1K) ​

沿用上例的 API_KEY,以下请求使用 Nano Banana 2.1 生成 1K 图片。

bash
set -euo pipefail

# 使用 Nano Banana 2.1(1K)
curl --fail-with-body -sS -X POST "https://api.exchangetoken.ai/v1beta/models/gemini-nano-banana-2.1:generateContent" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{
      "role": "user",
      "parts": [{"text": "a beautiful sunset over mountains"}]
    }],
    "generationConfig": {
      "responseModalities": ["IMAGE"],
      "imageConfig": {"aspectRatio": "16:9", "imageSize": "1K"}
    }
  }' | jq -er '[.candidates[]?.content.parts[]? | select(.thought != true and .inlineData.mimeType == "image/png") | .inlineData.data][0] // error("No PNG image returned")' | base64 --decode > output.png

更多场景代码示例 ​

以下是涵盖单图编辑、多图合成、智能编辑以及 Base64 数据处理工具类的进阶示例。Python 示例使用 Nano Banana 2,依赖 requests 和 Pillow;请将第 4 节工具类保存为同目录下的 Base64ImageHandler.py。

1. 单图编辑示例(Python) ​

对单张图片进行编辑操作。

python
import requests
import json
import base64
from Base64ImageHandler import Base64ImageHandler

def get_image_base64(url):
    """
    【关键步骤】
    本示例下载图片后,使用 Base64 编码的 inlineData 传入
    """
    print(f"[LOG] 正在下载图片以适配原生接口: {url}")
    try:
        headers = {'User-Agent': 'Mozilla/5.0'}
        response = requests.get(url, headers=headers, timeout=30)
        if response.status_code == 200:
            # 获取 content-type,默认为 image/png
            mime_type = response.headers.get('Content-Type', 'image/png').split(';', 1)[0]
            # 转换为 base64 字符串
            b64_data = base64.b64encode(response.content).decode('utf-8')
            print(f"[LOG] 图片下载成功,格式: {mime_type}, 数据长度: {len(b64_data)}")
            return mime_type, b64_data
        else:
            print(f"[ERROR] 图片下载失败,状态码: {response.status_code}")
            return None, None
    except Exception as e:
        print(f"[ERROR] 图片下载异常: {e}")
        return None, None

def creative_merge(image_urls, merge_instruction):
    print("=" * 50)
    print("开始处理图片合并请求 (Gemini 原生协议)")
    print("=" * 50)

    # 1. 准备请求内容 parts
    parts = []

    # 添加文本指令
    parts.append({
        "text": merge_instruction + "\n\n请直接返回生成的图片。"
    })

    # 2. 处理图片:下载并转换为 inline_data 格式
    if image_urls:
        print(f"[LOG] 图片URL数量: {len(image_urls)}")
        for i, url in enumerate(image_urls, 1):
            mime_type, image_b64 = get_image_base64(url)
            if image_b64:
                parts.append({
                    "inlineData": {
                        "mimeType": mime_type,
                        "data": image_b64
                    }
                })

    # 3. 构造 Gemini 原生 Payload (contents 结构)
    # 结构: contents -> parts -> [text, inlineData]
    data = {
        "contents": [
            {
                "role": "user",
                "parts": parts
            }
        ],
        "generationConfig": {
            "responseModalities": ["TEXT", "IMAGE"],
            "maxOutputTokens": 4096 
        }
    }

    # 4. 设置原生接口 URL
    # 注意:这里使用的是 :generateContent 接口
    api_endpoint = "https://api.exchangetoken.ai/v1beta/models/gemini-3.1-flash-image:generateContent"

    headers = {
        "Authorization": f"Bearer sk-xxxxx",
        "Content-Type": "application/json"
    }

    print(f"\n[LOG] 请求URL: {api_endpoint}")
    print(f"[LOG] 发送请求中...")

    try:
        response = requests.post(api_endpoint, headers=headers, json=data, timeout=180)

        print(f"[LOG] HTTP状态码: {response.status_code}")
        # print(f"[LOG] 响应头: {dict(response.headers)}")

        if response.status_code != 200:
            print(f"[ERROR] API请求失败")
            print(f"[ERROR] 响应内容: {response.text}")
            return None, None

        response_json = response.json()
        print(f"\n[LOG] API调用成功,开始解析原生响应...")

        # 5. 解析 Gemini 原生响应结构
        # 图片位于 candidates[].content.parts[].inlineData 中
        response_str = json.dumps(response_json, ensure_ascii=False)

        # 尝试打印回复文本
        try:
            candidates = response_json.get('candidates', [])
            if candidates:
                content = candidates[0].get('content', {})
                res_parts = content.get('parts', [])
                if res_parts and 'text' in res_parts[0]:
                    print(f"[LOG] 模型回复文本片段: {res_parts[0]['text'][:200]}...")
        except Exception as e:
            print(f"[WARNING] 简单解析文本失败: {e}")

        # 使用工具类读取原生响应中的 inlineData
        result = Base64ImageHandler.extract_from_response(response_str)

        if result and result[0]:
            print(f"[LOG] 成功提取图片数据,长度: {len(result[0])}")
        else:
            print(f"[WARNING] 未能提取到base64数据,请检查模型回复内容。")

        return result

    except requests.exceptions.RequestException as e:
        print(f"[ERROR] 请求异常: {e}")
        return None, None
    except Exception as e:
        print(f"[ERROR] 未知错误: {e}")
        import traceback
        traceback.print_exc()
        return None, None

if __name__ == "__main__":
    images = [
        "https://您的图片.png",
    ]

    # 提示词:针对多模态模型的 Image-to-Text-to-Image 技巧
    prompt = (
        "你是一个图像编辑助手。请给图片中的人物带上一个时尚的帽子。\n"
        "【关键】:请直接输出修改后的图片数据,不要只输出描述。\n"
        "如果可以直接生成图像,请返回图像。\n"
    )

    result = creative_merge(images, prompt)

    print("\n" + "=" * 50)
    print("保存图片结果")
    print("=" * 50)

    if result and result[0]:
        base64_data, image_format = result
        print(f"[LOG] 准备保存图片,格式: {image_format}")
        saved_filename = Base64ImageHandler.save_to_file(base64_data, image_format=image_format)
        print(f"[SUCCESS] 图片已保存为: {saved_filename}")
    else:
        print("[ERROR] 流程结束,未保存图片。")

2. 多图合成 - 创意融合 (Python) ​

将多张图片作为输入,通过 Prompt 指令进行创意融合。

python
import requests
import json
import base64
from Base64ImageHandler import Base64ImageHandler
def get_image_base64(url):
    """
    【关键步骤】
    本示例下载图片后,使用 Base64 编码的 inlineData 传入
    """
    print(f"[LOG] 正在下载图片以适配原生接口: {url}")
    try:
        headers = {'User-Agent': 'Mozilla/5.0'}
        response = requests.get(url, headers=headers, timeout=30)
        if response.status_code == 200:
            # 获取 content-type,默认为 image/png
            mime_type = response.headers.get('Content-Type', 'image/png').split(';', 1)[0]
            # 转换为 base64 字符串
            b64_data = base64.b64encode(response.content).decode('utf-8')
            print(f"[LOG] 图片下载成功,格式: {mime_type}, 数据长度: {len(b64_data)}")
            return mime_type, b64_data
        else:
            print(f"[ERROR] 图片下载失败,状态码: {response.status_code}")
            return None, None
    except Exception as e:
        print(f"[ERROR] 图片下载异常: {e}")
        return None, None
def creative_merge(image_urls, merge_instruction):
    print("=" * 50)
    print("开始处理图片合并请求 (Gemini 原生协议)")
    print("=" * 50)
    # 1. 准备请求内容 parts
    parts = []
    # 添加文本指令
    parts.append({
        "text": merge_instruction + "\n\n请直接返回生成的图片。"
    })
    # 2. 处理图片:下载并转换为 inline_data 格式
    if image_urls:
        print(f"[LOG] 图片URL数量: {len(image_urls)}")
        for i, url in enumerate(image_urls, 1):
            mime_type, image_b64 = get_image_base64(url)
            if image_b64:
                parts.append({
                    "inlineData": {
                        "mimeType": mime_type,
                        "data": image_b64
                    }
                })
    # 3. 构造 Gemini 原生 Payload (contents 结构)
    # 结构: contents -> parts -> [text, inlineData]
    data = {
        "contents": [
            {
                "role": "user",
                "parts": parts
            }
        ],
        "generationConfig": {
            "responseModalities": ["TEXT", "IMAGE"],
            "maxOutputTokens": 4096 
        }
    }
    # 4. 设置原生接口 URL
    # 注意:这里使用的是 :generateContent 接口
    api_endpoint = "https://api.exchangetoken.ai/v1beta/models/gemini-3.1-flash-image:generateContent"
    headers = {
        "Authorization": f"Bearer sk-xxxxx",
        "Content-Type": "application/json"
    }
    print(f"\n[LOG] 请求URL: {api_endpoint}")
    print(f"[LOG] 发送请求中...")
    try:
        response = requests.post(api_endpoint, headers=headers, json=data, timeout=180)
        print(f"[LOG] HTTP状态码: {response.status_code}")
        # print(f"[LOG] 响应头: {dict(response.headers)}")
        if response.status_code != 200:
            print(f"[ERROR] API请求失败")
            print(f"[ERROR] 响应内容: {response.text}")
            return None, None
        response_json = response.json()
        print(f"\n[LOG] API调用成功,开始解析原生响应...")
        # 5. 解析 Gemini 原生响应结构
        # 图片位于 candidates[].content.parts[].inlineData 中
        response_str = json.dumps(response_json, ensure_ascii=False)
        # 尝试打印回复文本
        try:
            candidates = response_json.get('candidates', [])
            if candidates:
                content = candidates[0].get('content', {})
                res_parts = content.get('parts', [])
                if res_parts and 'text' in res_parts[0]:
                    print(f"[LOG] 模型回复文本片段: {res_parts[0]['text'][:200]}...")
        except Exception as e:
            print(f"[WARNING] 简单解析文本失败: {e}")
        # 使用工具类读取原生响应中的 inlineData
        result = Base64ImageHandler.extract_from_response(response_str)
        if result and result[0]:
            print(f"[LOG] 成功提取图片数据,长度: {len(result[0])}")
        else:
            print(f"[WARNING] 未能提取到base64数据,请检查模型回复内容。")
        return result
    except requests.exceptions.RequestException as e:
        print(f"[ERROR] 请求异常: {e}")
        return None, None
    except Exception as e:
        print(f"[ERROR] 未知错误: {e}")
        import traceback
        traceback.print_exc()
        return None, None
if __name__ == "__main__":
    images = [
        "https://图片一.png",
        "https://图片二.png"
    ]
    # 示例提示词:针对多模态模型的 Image-to-Text-to-Image 技巧
    prompt = (
        "你是一个图像编辑助手。请给图一中的人物带上一个时尚的帽子。并把图二中的金项链穿戴到图一的人物身上\n"
        "【关键】:请直接输出修改后的图片数据,不要只输出描述。\n"
        "如果可以直接生成图像,请返回图像。\n"
    )
    result = creative_merge(images, prompt)
    print("\n" + "=" * 50)
    print("保存图片结果")
    print("=" * 50)
    if result and result[0]:
        base64_data, image_format = result
        print(f"[LOG] 准备保存图片,格式: {image_format}")
        saved_filename = Base64ImageHandler.save_to_file(base64_data, image_format=image_format)
        print(f"[SUCCESS] 图片已保存为: {saved_filename}")
    else:
        print("[ERROR] 流程结束,未保存图片。")

3. 智能多图处理策略 (Python) ​

沿用前两节的 creative_merge 函数,根据图片数量调整提示词,实现单图编辑或多图创意处理。

python
def smart_multi_image_edit(images, instruction):
    """智能多图编辑,自动处理不同数量的图片"""
    
    if len(images) == 1:
        # 单图编辑
        prompt = f"Edit this image: {instruction}"
    elif len(images) == 2:
        # 双图合成
        prompt = f"Combine these two images creatively: {instruction}"
    else:
        # 多图处理
        prompt = f"Process these {len(images)} images together: {instruction}"
    
    return creative_merge(images, prompt)

4. Base64 数据处理工具类 (Python) ​

用于处理 API 返回的 Base64 数据,支持提取、保存和转换为 PIL Image 对象。

python
import base64
import io
import json
from datetime import datetime
from PIL import Image

class Base64ImageHandler:
    """Base64图片处理工具类"""
    
    @staticmethod
    def extract_from_response(response_content):
        payload = json.loads(response_content) if isinstance(response_content, str) else response_content
        for candidate in payload.get("candidates", []):
            for part in candidate.get("content", {}).get("parts", []):
                if part.get("thought"):
                    continue
                image = part.get("inlineData", {})
                mime_type = image.get("mimeType", "")
                if mime_type in {"image/png", "image/jpeg", "image/webp", "image/heic", "image/heif"} and image.get("data"):
                    return image["data"], mime_type.split("/", 1)[1]
        return None, None

    @staticmethod
    def save_to_file(base64_data, filename=None, image_format="png"):
        """保存base64数据为文件"""
        if not filename:
            filename = f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{image_format}"
        
        image_data = base64.b64decode(base64_data, validate=True)
        with open(filename, 'wb') as f:
            f.write(image_data)
        
        return filename
    
    @staticmethod
    def to_pil_image(base64_data):
        """转换为PIL Image对象"""
        image_data = base64.b64decode(base64_data, validate=True)
        return Image.open(io.BytesIO(image_data))
    
    @staticmethod
    def from_pil_image(pil_image, format='PNG'):
        """从PIL Image转换为base64"""
        buffer = io.BytesIO()
        pil_image.save(buffer, format=format)
        img_str = base64.b64encode(buffer.getvalue()).decode()
        return f"data:image/{format.lower()};base64,{img_str}"