
1. 从本地显卡到 ServerlessStable Diffusion 部署的真实痛点如果你最近在折腾 AI 绘画大概率会遇到一个绕不开的问题本地显卡跑 Stable Diffusion 越来越吃力。一张 3060 12G 勉强能出 512x512 的图想上 768 或者跑个 ControlNet显存直接爆红。更别说换模型这件事每次下载一个 2G 到 7G 的 ckpt 文件硬盘空间肉眼可见地缩水切换模型还要重启 WebUI等个三五分钟是常态。我身边不少做设计的朋友电脑配置其实不差但一提到装 Python 环境、配 CUDA、拉依赖头就大了。光是 xformers 和 torch 版本对不上这一条就能卡住一整天。这时候 Serverless 函数计算的价值就出来了你不需要买显卡不需要配环境按量付费用完就停。把 Stable Diffusion 部署到函数计算上本质上就是把「本地跑不动」和「不想折腾环境」这两个问题一起解决掉。但部署完之后新的问题又来了。你可能会同时用多个模型服务一个跑 SD 出图一个跑对话模型做提示词润色还有一个跑代码模型帮你写调用脚本。每个服务一套 Key、一套鉴权、一套计费管理起来非常碎。这时候如果有一个统一的 Key 入口把不同模型的调用收敛到同一个网关事情会简单很多。TaoToken 做的就是这件事它提供一个统一的 API Key让你在 Serverless 函数里用同一套鉴权去调用不同模型换模型的时候只需要改一个 Model ID不用重新配 Key。这篇文章面向的是想在 Serverless 函数计算上部署 Stable Diffusion、并且希望用统一 Key 管理模型调用的开发者。我会从部署模板、依赖层、环境变量注入一路讲到 TaoToken 统一 Key 的接入示例最后给一次冷启动推理请求的验证动作和返回结果检查点。你跟着做能拿到一个可复制的函数配置并且知道每一步在干什么。2. TaoToken 前置准备统一 Key 与模型调用链路在把 Stable Diffusion 部署到函数计算之前先把 TaoToken 这一层准备好。你可以把它理解成一个「模型调用的统一入口」不管后面接的是 SD 的 API、对话模型还是代码模型前端函数只需要认一个 Base URL 和一个 API Key。这样做的好处是当你想换模型的时候不用去改函数里的鉴权逻辑只改 Model ID 就行。先打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并登录。登录之后进入控制台找到 API Keys 页面创建一个新的 Key。这个 Key 就是你后面要注入到函数环境变量里的凭证。创建的时候建议起一个能区分用途的名字比如serverless-sd-demo方便后面排查问题。拿到 Key 之后你需要确认两件事Base URL 和 Model ID。Base URL 是https://taotoken.net/api注意这个地址不带任何查询参数直接作为请求前缀使用。Model ID 则取决于你要调用的模型比如你想让函数在出图前先润色提示词就可以选一个对话模型如果你只是想让 SD 服务本身通过统一 Key 做鉴权那就把 SD 的 API 路径挂在这个 Base URL 下面。这里有一个容易踩的坑很多人会把 Base URL 写成带/v1或者带其他路径的形式结果请求直接 404。正确的做法是 Base URL 只写到/api具体的模型路径在代码里拼接。比如对话模型的完整地址是https://taotoken.net/api/v1/chat/completions而 SD 的接口则根据你部署的 WebUI 实际暴露的路径来拼。另外TaoToken 的 Key 是敏感信息绝对不要硬编码在函数代码里。函数计算平台一般都有环境变量配置入口把 Key 写进环境变量代码里用os.environ.get(TAOTOKEN_API_KEY)读取。这样即使代码被导出或者分享Key 也不会泄露。如果你用的是阿里云函数计算可以在「函数配置」-「环境变量」里添加如果是其他平台找对应的环境变量设置项即可。还有一点值得提前说TaoToken 的计费和额度是在控制台里统一看的。你可以在控制台里看到每个 Key 的调用量、消耗情况这样当你在函数里同时调用了 SD 和对话模型时账单是合并的不用去几个平台分别对账。对于个人开发者或者小团队来说这种统一管理的方式能省不少心。准备好 Key 和 Base URL 之后就可以进入下一步开始写函数配置了。下面我会给出一份可复制的 JSON 配置你可以直接改改参数就用。3. 可复制配置函数计算部署 Stable Diffusion 与环境变量注入这一节是整篇文章的核心操作部分。我会给出一份函数计算的配置模板包含运行环境、依赖层、环境变量注入以及如何把 TaoToken 的统一 Key 接进去。你可以把下面的 JSON 当成一个起点根据自己的平台做微调。先看函数的基础配置。以阿里云函数计算为例创建一个「容器镜像」类型的函数或者用「自定义运行时」加依赖层的方式。如果你追求部署速度推荐直接用容器镜像因为 Stable Diffusion 的依赖比较重打成层反而麻烦。下面这份 JSON 描述的是一个自定义运行时的函数配置包含环境变量和层引用{ functionName: sd-serverless-demo, runtime: custom-container, handler: index.handler, memorySize: 8192, timeout: 300, instanceConcurrency: 1, environmentVariables: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的实际Key, SD_MODEL_ID: sd-v1-5-inpainting, SD_API_PATH: /sdapi/v1/txt2img }, layers: [ acs:fc:cn-hangzhou:official:layers/Python3-Flask/versions/1 ], code: { ossBucketName: your-bucket, ossObjectName: sd-serverless-demo.zip } }这份配置里有几个关键点。memorySize给到 8192MB是因为 Stable Diffusion 推理对内存有一定要求尤其是加载模型的时候。timeout设成 300 秒是为了应对冷启动时模型加载慢的情况。instanceConcurrency设为 1是因为 SD 推理本身比较吃资源单实例并发太高容易 OOM。环境变量部分TAOTOKEN_BASE_URL固定为https://taotoken.net/apiTAOTOKEN_API_KEY填你在控制台创建的那个 Key。SD_MODEL_ID和SD_API_PATH是给函数内部逻辑用的方便你换模型的时候只改环境变量不用动代码。接下来是函数代码里怎么用这些环境变量。下面是一段 Python 示例展示如何用 TaoToken 的统一 Key 去调用一个对话模型做提示词润色然后再把润色后的提示词传给 SD 的 APIimport os import requests TAOTOKEN_BASE_URL os.environ.get(TAOTOKEN_BASE_URL) TAOTOKEN_API_KEY os.environ.get(TAOTOKEN_API_KEY) SD_API_PATH os.environ.get(SD_API_PATH) def polish_prompt(raw_prompt): url f{TAOTOKEN_BASE_URL}/v1/chat/completions headers { Authorization: fBearer {TAOTOKEN_API_KEY}, Content-Type: application/json } payload { model: gpt-4o-mini, messages: [ {role: system, content: 你是一个提示词优化助手把用户输入改写成适合 Stable Diffusion 的英文提示词。}, {role: user, content: raw_prompt} ] } resp requests.post(url, jsonpayload, headersheaders, timeout30) resp.raise_for_status() return resp.json()[choices][0][message][content] def generate_image(prompt): url fhttp://localhost:7860{SD_API_PATH} payload { prompt: prompt, steps: 20, width: 512, height: 512 } resp requests.post(url, jsonpayload, timeout120) resp.raise_for_status() return resp.json()这段代码里polish_prompt走的是 TaoToken 的统一入口generate_image走的是本地 SD 服务。注意 SD 服务的地址是localhost:7860因为 SD WebUI 和函数代码跑在同一个容器里。如果你把 SD 单独部署成另一个服务就把这个地址换成对应的内网地址。依赖层方面如果你的函数需要requests以外的库比如pillow或者numpy建议提前打好层。层的内容就是一个包含python/lib/python3.x/site-packages/目录的 zip 包。打层的时候注意 Python 版本要和函数运行时一致否则会出现 import 失败。配置写完之后把代码打包上传或者推到 OSS 让函数引用。部署完成后函数会经历一次冷启动这时候模型需要从 NAS 或者镜像里加载时间可能在 30 秒到 2 分钟之间。冷启动完成后后续请求就会快很多。4. 验证请求与成功结果检查点配置部署好之后不要急着写复杂业务先做一次最小化的验证请求。这一步的目的是确认三件事函数能正常启动、TaoToken 的 Key 能通过鉴权、SD 的 API 能返回图片数据。先验证 TaoToken 这一层。你可以直接在函数里加一个测试入口或者用本地 curl 模拟。下面这个 curl 命令是验证对话模型调用的curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的实际Key \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: 用一句话描述一只在雨中散步的猫}] }如果返回的 JSON 里有choices字段并且message.content是一段通顺的中文说明 TaoToken 的 Key 和 Base URL 都没问题。如果返回 401说明 Key 不对或者没带上如果返回 404大概率是 Base URL 写错了检查是不是多写了/v1或者少了/api。接下来验证 SD 的 API。在函数容器里SD WebUI 启动后会监听 7860 端口。你可以用下面这个请求测试出图curl -X POST http://localhost:7860/sdapi/v1/txt2img \ -H Content-Type: application/json \ -d { prompt: a cat walking in the rain, cinematic lighting, steps: 20, width: 512, height: 512 }成功的返回结果里会有一个images数组里面是 base64 编码的图片数据。你可以把这段 base64 解码后保存成 png看看是不是一张正常的图。如果返回{detail:Not Found}说明 SD 的 API 路径不对检查 WebUI 启动时有没有加--api参数。如果返回超时说明冷启动还没完成等一会儿再试。冷启动推理请求的检查点有三个。第一函数日志里能看到 SD WebUI 启动完成的标志通常是Running on local URL: http://0.0.0.0:7860。第二第一次请求的耗时明显比后续请求长这是正常的因为模型加载只在冷启动时发生一次。第三返回的图片 base64 长度应该在几十万字符量级如果只有几百字符说明返回的是错误信息而不是图片。我实测下来冷启动第一次出图大概要 60 到 90 秒之后同样的请求 5 到 10 秒就能返回。如果你发现每次请求都很慢检查一下函数实例是不是被回收了或者instanceConcurrency设得太低导致频繁冷启动。验证通过之后你就可以把这条链路接到自己的业务里了。比如做一个前端页面用户输入中文提示词函数先调 TaoToken 润色成英文再调 SD 出图最后把图片返回给前端。整个过程只需要一个统一 Key换模型的时候改环境变量就行。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节整理几个我在部署过程中真实遇到过的报错以及对应的排查思路。这些报错在 Serverless 场景下比较典型尤其是涉及统一 Key 和本地服务调用的时候。第一个是401 Unauthorized。这个报错通常出现在调用 TaoToken 接口的时候。原因一般有三个Key 没填对、Key 前面少了Bearer、或者环境变量没注入成功。排查的时候先在函数里打印一下os.environ.get(TAOTOKEN_API_KEY)的前几位确认 Key 确实读到了。然后检查请求头里的Authorization字段格式必须是Bearer sk-xxx中间有一个空格。如果 Key 是从控制台复制的注意不要带多余的空格或者换行。第二个是local proxy failed。这个报错一般出现在函数内部调用本地 SD 服务的时候。原因是函数容器里的网络配置或者端口没对上。SD WebUI 默认监听0.0.0.0:7860但有些镜像会改成127.0.0.1:7860这时候函数里用localhost可能连不上。解决办法是在启动 SD 的时候显式指定--listen 0.0.0.0 --port 7860然后在函数里用http://127.0.0.1:7860去调。如果还是不行检查一下函数的安全组或者网络策略有没有限制本地回环。第三个是reading choices相关的报错完整信息可能是KeyError: choices或者list index out of range。这个报错说明你调用的接口返回结构和你预期的不一样。常见原因是 Model ID 写错了或者调用的路径不对。比如你把对话模型的路径写成了 SD 的路径返回的就不是choices结构。排查的时候先把原始返回打印出来看看resp.json()里到底有什么字段。如果返回的是{error: model not found}那就去 TaoToken 控制台确认一下这个 Model ID 是否可用。第四个是OAuth相关的报错比如OAuth token expired或者invalid_grant。这个在 Serverless 场景下通常是因为你用了某种需要 OAuth 刷新的鉴权方式但函数实例长时间运行后 token 过期了。如果你用的是 TaoToken 的 API Key一般不会遇到这个问题因为 API Key 是长期有效的。但如果你在函数里集成了其他需要 OAuth 的服务就要注意 token 刷新逻辑。建议把刷新逻辑放在函数初始化阶段而不是每次请求都去刷。除了这四个还有一个比较隐蔽的问题函数冷启动时 SD 还没完全启动请求就进来了导致连接被拒绝。解决办法是在函数入口加一个健康检查等 SD 的/sdapi/v1/options返回 200 之后再处理业务请求。下面是一个简单的等待逻辑import time import requests def wait_for_sd(timeout120): start time.time() while time.time() - start timeout: try: resp requests.get(http://127.0.0.1:7860/sdapi/v1/options, timeout5) if resp.status_code 200: return True except requests.exceptions.ConnectionError: pass time.sleep(3) return False把这个函数放在业务逻辑之前调用能避免大部分冷启动导致的连接错误。排查报错的时候函数日志是最重要的信息来源。建议在关键步骤都加上日志输出比如请求发出前打印 URL 和 Model ID返回后打印状态码和响应体的前 200 个字符。这样出问题的时候能快速定位是哪一层挂了。6. 统一 Key 接入后的模型切换与长期使用建议把 Stable Diffusion 部署到 Serverless 并且接入 TaoToken 统一 Key 之后最直接的好处是换模型变简单了。以前换模型要改代码、重新部署现在只需要在环境变量里改一下SD_MODEL_ID或者换一个 API 路径函数重启后就是新模型。如果你用的是 TaoToken 的对话模型做提示词润色换模型也只需要改model字段Key 和 Base URL 都不用动。对于长期使用的场景有几个建议可以帮你省事。第一把常用的模型 ID 和对应的用途记在一个配置文件里比如用一个models.json管理函数启动时读取。这样你不需要每次去翻控制台。第二给函数设置合理的超时和内存SD 推理建议至少 8GB 内存、300 秒超时如果跑大图或者高步数适当往上加。第三关注冷启动频率如果请求比较稀疏可以考虑用预留实例或者定时触发保持实例存活但这样会增加成本需要权衡。如果你后面要接入更多模型比如代码模型或者语音模型TaoToken 的统一 Key 依然适用。你只需要在函数里根据不同的任务类型选择不同的 Model ID 和路径。这种架构的好处是鉴权层和业务层解耦了换模型不会影响鉴权逻辑换 Key 也不会影响业务代码。最后提醒一点Stable Diffusion 的模型和 WebUI 本身有各自的开源协议商用之前务必确认清楚。Serverless 平台和 TaoToken 只是提供了运行和调用的基础设施模型版权的责任还是在使用者这边。如果你只是个人学习或者内部测试一般问题不大如果要对外提供服务建议仔细阅读相关协议。按照上面的步骤走下来你应该已经有一个能跑的 Serverless Stable Diffusion 服务了并且用 TaoToken 的统一 Key 把模型调用管理起来了。后面想换模型、加功能都只需要在配置层做小改动不用大动干戈。