Nano-Banana实战手册:与Notion API集成实现结构图自动归档工作流
你是不是也遇到过这样的烦恼?用Nano-Banana生成了一大堆精美的产品结构图,它们散落在电脑的各个文件夹里,时间一长,连自己都忘了哪个图对应哪个项目。想找一张之前做的耳机分解图做参考,得翻半天硬盘。
对于设计师、产品经理或硬件工程师来说,这些结构图不仅是作品,更是重要的设计资产和灵感库。手动整理效率低下,而且缺乏统一的描述和标签,后期检索非常困难。
今天,我就带你解决这个问题。我们将把Nano-Banana这个强大的“结构拆解实验室”,与Notion这个“万能数字工作台”连接起来,打造一个全自动的图片归档工作流。生成即归档,描述自动写,标签自动打,让你的每一份创意都井井有条。
1. 为什么需要自动化归档?
在深入代码之前,我们先看看手动管理AI生成图片的几个典型痛点:
- 资产分散:图片保存在本地,与项目文档、设计思路分离,形成信息孤岛。
- 元数据缺失:生成图片时用的提示词(Prompt)、参数设置这些“创作基因”没有记录下来,导致无法复现或优化。
- 检索困难:靠文件名和记忆查找图片,效率极低,无法进行“风格”、“物体类型”等维度筛选。
- 协作壁垒:本地文件难以与团队实时共享和讨论。
而Notion作为一个集成了数据库、Wiki、任务管理功能的平台,恰好能完美解决这些问题。我们可以把Notion数据库当作一个专业的“数字影集”,每张图片都是一条记录,附带完整的元数据。
我们这个实战项目的目标很简单:在Nano-Banana生成图片的一瞬间,自动将图片和它的所有生成信息(提示词、参数、时间)保存到指定的Notion数据库中。
2. 工作流架构与核心组件
整个自动化流程可以看作一个“生成-捕获-推送”的管道。下图清晰地展示了数据是如何流动的:
flowchart TD A[Nano-Banana 生成图片] --> B[Python 捕获脚本] B --> C{图片&元数据} C --> D[上传图片至图床<br>(如OSS/S3)] C --> E[格式化数据] D --> F[获取图片公开URL] E --> F F --> G[调用 Notion API] G --> H[Notion 数据库<br>新增一条记录] H --> I[完成自动归档]要实现这个流程,我们需要准备三样东西:
- 一个Notion数据库:用来接收和存储我们的图片数据。
- 一个Notion集成(Integration):相当于我们脚本访问Notion的“钥匙”。
- 一段Python脚本:负责捕获Nano-Banana的信息并调用Notion API。
下面我们一步步来设置。
2.1 第一步:在Notion中创建数据库
首先,在你的Notion工作区新建一个页面,然后添加一个“Database - Full page”或内联数据库。
我们需要为数据库设计一些属性(Properties),来匹配图片的元数据。建议至少包含以下列:
| 属性名 (Name) | 属性类型 (Type) | 说明 |
|---|---|---|
| Name | Title | 图片标题,我们可以用提示词的前几个字自动生成。 |
| Image | Files & media | 用于存放图片本身。 |
| Prompt | Text | 完整的生成提示词。 |
| Style | Multi-select | 风格标签,如Knolling,Exploded View。可以手动或根据提示词关键词自动添加。 |
| Object | Select | 物体类型,如Clothing,Electronics,Footwear。 |
| LoRA Scale | Number | 记录生成时使用的LoRA权重。 |
| CFG Scale | Number | 记录生成时使用的CFG规模。 |
| Date Created | Date | 图片生成日期。 |
| Status | Status | 状态,如Archived,In Review。 |
创建好后,记住这个数据库的ID。打开数据库页面,浏览器地址栏的URL中,在www.notion.so/后面、?v=或?pvs=前面的那一长串字符就是数据库ID。例如:https://www.notion.so/yourworkspace/1234567890abcdef1234567890abcdef?v=...,其中加粗部分就是数据库ID。
2.2 第二步:创建Notion集成并获取密钥
- 访问 Notion Developers 页面,点击 “+ New integration”。
- 为你的集成起个名字,比如 “Nano-Banana Auto-Archiver”。
- 选择关联的工作区。
- 点击 “Submit” 创建。创建成功后,你会看到“Internal Integration Token”。这个令牌(Token)就是你的API密钥,务必妥善保存(我们称之为
NOTION_TOKEN)。 - 接下来,需要把这个集成邀请到你刚才创建的数据库。打开你的数据库页面,点击右上角的
···,选择Add connections,然后在列表中找到你刚创建的 “Nano-Banana Auto-Archiver” 集成,点击添加。
现在,你的脚本就有权限向这个数据库写入数据了。
2.3 第三步:理解Nano-Banana的信息捕获点
Nano-Banana基于Streamlit构建。在标准部署中,生成的图片和参数都存在于Streamlit的会话状态(st.session_state)或前端组件中。为了捕获这些信息,我们需要对Nano-Banana的源码进行小幅修改,或者在其生成流程结束时触发我们的脚本。
一个简单可行的思路是:在Nano-Banana的生成函数执行完毕后,将关键信息(图片路径/字节流、提示词、参数)写入一个临时文件(如JSON文件),然后由另一个独立的守护进程(或计划任务)读取这个文件并执行Notion上传逻辑。
这样做的好处是解耦,不影响Nano-Banana本身的稳定性。下面我们主要编写这个独立的“归档器”脚本。
3. 核心脚本编写:Notion归档器
假设Nano-Banana在生成图片后,会在/tmp/nano_banana_job.json路径下生成一个任务文件。我们的脚本就监听这个文件的变化,或者定期扫描它。
首先,安装必要的Python库:
pip install notion-client requests pillow接下来是完整的脚本notion_archiver.py:
import json import os import time from datetime import datetime from pathlib import Path import requests from notion_client import Client from PIL import Image # 配置信息 - 请替换成你自己的 NOTION_TOKEN = "你的Notion集成Token" DATABASE_ID = "你的Notion数据库ID" JOB_FILE_PATH = "/tmp/nano_banana_job.json" # Nano-Banana输出任务信息的路径 IMAGE_UPLOAD_URL = "https://你的图床服务/upload" # 可选,如果需要先上传图片到图床 IMAGE_UPLOAD_AUTH = {"Authorization": "Bearer 你的图床密钥"} # 可选 # 初始化Notion客户端 notion = Client(auth=NOTION_TOKEN) def upload_image_to_notion(image_path): """ 将图片上传到Notion。 Notion API要求图片必须先有一个公开可访问的URL。 这里提供两种方案: 方案A:如果你有图床(如OSS、S3、Imgur API),先上传到图床,返回URL。 方案B:如果图片在本地,且脚本与Notion在同一网络环境(通常不现实),可尝试直接传文件(更复杂)。 本例以方案A为例,假设我们有一个上传接口。 """ # 方案A: 上传到自己的图床服务 if IMAGE_UPLOAD_URL: with open(image_path, 'rb') as f: files = {'file': (os.path.basename(image_path), f, 'image/png')} response = requests.post(IMAGE_UPLOAD_URL, headers=IMAGE_UPLOAD_AUTH, files=files) if response.status_code == 200: return response.json().get('url') # 假设返回JSON包含'url'字段 else: print(f"图床上传失败: {response.status_code}, {response.text}") return None else: # 方案B: 本地文件演示(实际使用需要解决公网访问问题) print("未配置图床,无法上传图片。请配置IMAGE_UPLOAD_URL。") return None # 注意:Notion也支持直接上传二进制文件,但需要更复杂的multipart/form-data处理, # 且受限于Notion API的文件大小限制。对于高清图,推荐先上传到图床。 def extract_style_tags(prompt): """从提示词中提取风格标签。""" tags = [] prompt_lower = prompt.lower() if 'knolling' in prompt_lower: tags.append("Knolling") if 'exploded view' in prompt_lower: tags.append("Exploded View") if 'flat lay' in prompt_lower: tags.append("Flat Lay") if 'instructional diagram' in prompt_lower: tags.append("Instructional") return tags def extract_object_type(prompt): """从提示词中提取物体类型。""" prompt_lower = prompt.lower() if any(word in prompt_lower for word in ['shirt', 'jacket', 'dress', 'clothing', 'garment']): return "Clothing" elif any(word in prompt_lower for word in ['camera', 'phone', 'laptop', 'headphone', 'electronic']): return "Electronics" elif any(word in prompt_lower for word in ['shoe', 'sneaker', 'boot', 'footwear']): return "Footwear" elif any(word in prompt_lower for word in ['bag', 'backpack', 'handbag']): return "Bag" else: return "Other" def create_notion_page(job_data, image_url): """在Notion数据库中创建新页面。""" # 准备属性 prompt = job_data.get('prompt', '') title = prompt[:50] + "..." if len(prompt) > 50 else prompt properties = { "Name": {"title": [{"text": {"content": title}}]}, "Prompt": {"rich_text": [{"text": {"content": prompt}}]}, "Style": {"multi_select": [{"name": tag} for tag in extract_style_tags(prompt)]}, "Object": {"select": {"name": extract_object_type(prompt)}}, "Date Created": {"date": {"start": datetime.now().isoformat()}}, "Status": {"status": {"name": "Archived"}}, # 假设默认归档 } # 添加数字参数 if 'lora_scale' in job_data: properties["LoRA Scale"] = {"number": job_data['lora_scale']} if 'cfg_scale' in job_data: properties["CFG Scale"] = {"number": job_data['cfg_scale']} # 准备子节点(图片) children = [] if image_url: children.append({ "object": "block", "type": "image", "image": { "type": "external", "external": {"url": image_url} } }) # 调用API创建页面 try: new_page = notion.pages.create( parent={"database_id": DATABASE_ID}, properties=properties, children=children ) print(f" 成功归档到Notion! 页面ID: {new_page['id']}") return True except Exception as e: print(f" 创建Notion页面失败: {e}") return False def process_job_file(): """处理任务文件。""" if not os.path.exists(JOB_FILE_PATH): return False try: with open(JOB_FILE_PATH, 'r') as f: job_data = json.load(f) print(f"读取到任务数据: {job_data.keys()}") # 1. 上传图片 image_path = job_data.get('image_path') image_url = None if image_path and os.path.exists(image_path): image_url = upload_image_to_notion(image_path) if not image_url: print("图片上传失败,跳过本次归档。") return False else: print("任务中未找到有效的图片路径。") # 可选:如果Nano-Banana直接输出图片base64,可以在这里处理 # image_data = job_data.get('image_base64') # 2. 创建Notion页面 success = create_notion_page(job_data, image_url) # 3. 处理成功后,删除或备份任务文件 if success: backup_path = JOB_FILE_PATH + f".backup_{int(time.time())}" os.rename(JOB_FILE_PATH, backup_path) print(f"任务文件已备份至: {backup_path}") return success except json.JSONDecodeError as e: print(f"任务文件JSON格式错误: {e}") except Exception as e: print(f"处理任务文件时发生未知错误: {e}") return False def main(): """主循环,监听任务文件。""" print(" Nano-Banana Notion归档器已启动...") print(f"监听文件: {JOB_FILE_PATH}") print("等待生成任务... (按 Ctrl+C 退出)") last_mod_time = 0 while True: try: if os.path.exists(JOB_FILE_PATH): current_mod_time = os.path.getmtime(JOB_FILE_PATH) # 如果文件被修改(新任务产生),并且不是我们刚刚备份的 if current_mod_time != last_mod_time: print("\n检测到新任务,开始处理...") last_mod_time = current_mod_time process_job_file() time.sleep(3) # 每3秒检查一次 except KeyboardInterrupt: print("\n👋 归档器已停止。") break if __name__ == "__main__": main()4. 在Nano-Banana中触发任务生成
现在,我们需要修改Nano-Banana的源码(通常是app.py或主Streamlit脚本),在图片生成成功的回调函数中,将信息写入任务文件。
找到Nano-Banana中生成图片并显示后的代码位置。例如,在Streamlit中,可能有一个点击“Generate”按钮后的处理块。
修改示例(需根据实际代码结构调整):
# 假设在Nano-Banana的某个生成函数中 def generate_image(prompt, lora_scale, cfg_scale, ...): # ... 原有的图片生成逻辑 ... # image 是生成的PIL Image对象 # output_path 是图片保存的路径 # 在生成成功后,写入任务信息 job_data = { "prompt": prompt, "lora_scale": lora_scale, "cfg_scale": cfg_scale, "image_path": output_path, # 图片保存的完整路径 "timestamp": datetime.now().isoformat(), # 可以添加更多参数,如seed、steps等 } job_file_path = "/tmp/nano_banana_job.json" with open(job_file_path, 'w') as f: json.dump(job_data, f, indent=2) print(f"任务信息已写入: {job_file_path}") # 原有的返回或显示图片的逻辑... return image5. 部署与运行
- 配置脚本:将
notion_archiver.py中的NOTION_TOKEN、DATABASE_ID和IMAGE_UPLOAD_URL替换成你自己的值。 - 启动归档器:在服务器上,使用
python notion_archiver.py或nohup python notion_archiver.py &在后台运行此脚本。 - 修改Nano-Banana:按第4步修改Nano-Banana源码,确保它能正确写入任务文件。
- 重启服务:重启Nano-Banana的Streamlit服务。
现在,当你在Nano-Banana中生成一张新的结构图时,稍等几秒钟,刷新你的Notion数据库,就会发现一条包含图片和所有详细信息的新记录已经自动创建好了!
6. 总结
通过这个实战项目,我们成功地将Nano-Banana的创意生成能力与Notion的知识管理能力无缝衔接。这个自动化工作流带来的价值是显而易见的:
- 效率提升:彻底告别手动保存、重命名、复制粘贴提示词的操作。
- 资产沉淀:所有生成物及其上下文都被结构化地保存下来,形成了可搜索、可复用的设计资产库。
- 流程标准化:确保了每一张输出图片都带有完整的元数据,便于团队协作和项目复盘。
- 灵感激发:在Notion中浏览按风格、物体分类的图库,本身就是绝佳的灵感来源。
你可以在此基础上继续扩展,例如:
- 添加“生成失败”的状态记录。
- 将Notion数据库与团队评审流程(如添加
In Review、Approved状态)结合。 - 在归档后自动触发一个Slack通知,告知团队有新设计图入库。
- 使用更复杂的AI模型来自动分析生成图片的内容,添加更丰富的标签。
希望这份实战手册能帮你打造一个更高效、更有序的AI辅助设计工作流。解构万物之后,也让你的创作过程本身,变得清晰而优雅。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。