返回博客

如何用 ChatGPT 5.5 把一个手绘 Demo,一路补成了能用的 SaaS

作者 约 4 分钟读完

从「输入一句话生成手绘图」到部署上线,踩遍额度、SDK、产品形态的坑,再把假登录、假账单补成真能跑的东西——一个小项目从 Demo 到能用的 SaaS 的完整记录。


如何用 ChatGPT 5.5 把一个手绘 Demo,一路补成了能用的 SaaS

从「输入一句话生成手绘图」到部署上线,中间踩了额度、SDK、产品形态一堆坑,最后把假登录、假账单补成真能跑的东西。记录一个小项目从 0 到「像样」的完整过程。

最近做了一个小项目:HandDraw AI。

一开始它只是个很直接的工具:输入一句描述,选一种手绘风格,生成图片。后来部署、域名、Key、模型额度、产品形态这些问题一个个冒出来,它慢慢从一个生成器变成了 SaaS 雏形。再后来我打开代码才发现,那个 SaaS 只是个空壳——登录、账单、历史全是假的,又花了一轮把它补成真能跑的东西。

这篇把整个过程串起来讲。

缘起:先把核心能力跑通

最初的目标很简单:做一个手绘风图片生成器。技术栈选得很轻:

Next.js App Router
TypeScript
Vercel
Gemini Image API

前端负责输入画面内容、选风格、传参考图;后端用 Next.js API Route 调图像模型。项目结构大致是:

src/app/page.tsx               主页面
src/app/api/generate/route.ts  生图接口
src/lib/styles.ts              风格配置
src/lib/prompt.ts              Prompt 拼装
public/examples/               风格示例图

它内置了 13 种手绘风格——儿童涂色页、蜡笔童涂、xkcd 火柴人、水墨写意、复古像素……每种风格背后都是一段打磨过的提示词模板。

最初版本很像一个「开发者工具」:打开就是编辑器,左边填参数,右边看结果。功能能跑,但产品感很弱。

API Key 与环境变量

AI 应用第一个绕不开的问题就是 Key 怎么放。本地用 .env:

GEMINI_API_KEY=xxx
GEMINI_IMAGE_MODEL=gemini-3.1-flash-image

Key 只在服务端 API Route 读取,前端不暴露。同时 .gitignore 一定要忽略:

.env
.env*.local

这点很重要——AI Key 一旦提交到 GitHub,基本就等于公开了。

模型名能用,不代表有额度

开发中反复测了几个模型:

gemini-3-pro-image-preview
gemini-3.1-flash-image
gemini-3.1-flash-lite-image

一开始遇到最多的是 429:

free_tier_requests limit: 0
free_tier_input_token_count limit: 0

这不是代码错,也不是 Key 格式错。是当前 Google 项目对这个模型没有免费额度。换了新 Key 再测就 OK 了:

OK image: data:image/jpeg;base64,...
model: gemini-3.1-flash-image

调试这类应用,最好先把错误分清楚:

500:服务端配置可能有问题
401/403:Key 或权限问题
429:额度或频率限制
200:调用成功

SDK 版本变更

还遇到一个典型问题:Gemini SDK 旧版本的 Interactions API schema 不再支持。

The legacy Interactions API schema is no longer supported.
Please upgrade your @google/genai JS/TS SDK to version >= 2.0.0

解决方式是升级 SDK,并把图片响应解析从旧的 interaction.outputs 改成新结构的 interaction.steps:

npm install @google/genai@latest

这也是 AI 应用开发的现实:模型和 SDK 变化很快,写完不能不管。依赖升级、接口变更、模型下线,都要预留维护空间。

部署到 Vercel

标准 Next.js,部署很顺:

vercel --prod

关键是配好环境变量(GEMINI_API_KEY、GEMINI_IMAGE_MODEL),Production 和 Development 都要配。上线后还处理了几个细节:

这些看起来不像「写代码」,但都是产品上线的一部分。

从「工具」到「SaaS 外壳」

最初页面的问题是:用户一进来就直面编辑器。这适合我自己调试,但普通用户根本看不懂在填什么。于是做了一次重构,把形态改成 SaaS:

首页:产品介绍
登录:账号入口
价格:Free / Pro 套餐
工作台:项目、额度、生成器

用户路径变成:打开首页 → 了解产品 → 登录 → 选套餐 → 进入工作台 → 生成图片。

SaaS 外壳的用户路径

很多 AI Demo 的能力其实够用,卡住它们的是没有产品外壳——用户不知道它是什么、适合谁、怎么收费、为什么要注册。

把「空壳」补成真能用的产品

SaaS 外壳搭起来后,打开代码会发现——流程几乎全是演示用的空壳:

  • 登录是假的,账号密码是只读占位符;
  • 账单是假的,选套餐不真开通、刷新就没;
  • 项目列表是硬编码的常量;
  • 生成完就丢,刷新即消失,没有任何历史;
  • 移动端菜单按钮是个死键。

全是摆设。于是有了这一章:整体优化页面 + 把功能真正补活。

空壳 vs 真能跑

原来一个文件塞下所有视图、状态、逻辑。这次拆成职责清晰的结构:

src/lib/data.ts        套餐/示例数据、类型、工具函数
src/lib/history.ts     本地持久化 + 生成历史(新增)
src/components/*        Topbar / HomeView / AuthView / BillingView
                       StudioSidebar / StudioView / Footer
src/app/page.tsx       只负责状态编排

page.tsx 从「什么都干」变成「只做编排」,可维护性直接上一个台阶。

拆掉巨型组件

  • 状态持久化:用 localStorage 存登录态和套餐,刷新不掉线。关键是避开 SSR 的 hydration 报错——初始用默认值,挂载后在 useEffect 里再读本地存储。
  • 真实的生成历史:每次生成自动存一条(缩略图、风格、时间、prompt),侧边栏支持点击恢复、删除、下载;首页 dashboard 联动展示真实最近项目。
  • 能用的配额系统:按套餐算额度(Starter 20 / Pro 800),进度条真实反映用量,用完自动禁用并提示。
  • 修复订阅流程:未登录选套餐先引导登录、再自动激活,「当前套餐」正确高亮。
  • 补齐交互:带校验的登录框、真正能展开的移动端导航、重新生成、新建、toast 提示、加载态与空状态。
  • 页面优化:新增风格画廊(点任意风格直达工作台并预选)、页脚,收敛整体视觉。

验证与交付

功能写完不等于做完,三道关卡逐一过:

npm run lint   → 0 错误 0 警告
npm run build  → 类型检查 + 静态生成通过
dev server     → 首页 200、SSR 内容正确、无 hydration 报错

工程化:一份 AGENTS.md

项目还加了 AGENTS.md 作为贡献者指南,记录项目结构、开发命令、代码风格、提交规范、环境变量注意事项。提交信息保持简短祈使式:

Refactor app into SaaS workspace
Update Gemini SDK image response handling
Add persistence, history, and UI polish to workspace

这些小规范,哪怕一个人开发也有用——项目一旦持续迭代,没有基本约定很快就会乱。

从 Demo 到产品差的是什么

这个项目不大,但完整走了一遍 AI 产品的关键环节:核心能力验证 → Prompt 与风格配置 → Key 安全 → 额度调试 → SDK 适配 → Vercel 部署 → 域名与访问控制 → SaaS 重构 → 账号与付费路径预留 → 持久化与历史。

几个具体的感受:

模型调用真的只是第一步。真正要上线,后面还压着一长串:谁来用、怎么进入、怎么付费、怎么限额、怎么隐藏底层复杂度、怎么稳定部署、怎么处理错误。

而「看起来能用」和「真能用」之间,差的往往不是新功能,是状态管理。登录会不会掉、刷新会不会丢、额度用完怎么办——这些边界情况才是分水岭。

还有一点:交付要留痕、能验证。我在收尾时说清了哪些验证过、哪些没跑(依赖真实 Key 的 Gemini 生成没实调,避免消耗额度)。诚实的交付比漂亮的结论有用。

HandDraw AI 现在还是早期版本,但基础搭起来了。生成历史已经有了(本地版),下一步优先补两件事:真实账号系统和真实支付订阅,再把历史从 localStorage 换成后端数据库。