Nano Banana 系列
模型简介
Google 的 Nano Banana 系列提供图像生成和编辑能力,包括以下模型:
gemini-nano-banana-2.1(Nano Banana 2.1)gemini-3.1-flash-image(Nano Banana 2)gemini-3-pro-image(Nano Banana Pro)gemini-2.5-flash-image(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-preview | gemini-3.1-flash-image |
gemini-3-pro-image-preview | gemini-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:21 | Nano 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)
#!/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 图片。
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)
对单张图片进行编辑操作。
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 指令进行创意融合。
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 函数,根据图片数量调整提示词,实现单图编辑或多图创意处理。
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 对象。
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}"