图片生成 OpenAI 兼容 API 集成 媒体持久化 计费安全

图片生成 API 集成:OpenAI 兼容模式

通过 OpenAI 兼容端点集成图片生成与编辑,安全限制 n 和尺寸,并用 gpt-image-2 正确持久化临时媒体。

AveMujica API 3 分钟阅读

图片生成 API 集成,是指产品代码调用稳定的图片端点——通常是 POST /v1/images/generations,并经常包括 POST /v1/images/edits——同时把模型选择、凭证、配额与媒体存储从各个客户端中抽离。在 OpenAI 兼容网关中,应用发送 prompt,以及可选的 model、n、size、quality 和 GPT Image 字段(如 output_format),然后解码 base64 载荷;只有仍支持旧格式的模型才处理 URL 响应。本文基于本仓库已经暴露的网关路由,给出接近生产形态的集成路径,不虚构供应商价格或质量评分。

GPT Image 的最小正确请求形状

使用一个经过认证的 base URL、一把由网关签发的 Bearer token,以及当前 OpenAI GPT Image 契约。官方文档将 gpt-image-2 列为当前主力图片模型;GPT Image 生成返回 data[].b64_json。response_format 参数仅适用于旧版 DALL·E 模型,不适用于 GPT Image。

curl -sS https://YOUR_GATEWAY/v1/images/generations \
  -H "Authorization: Bearer $GATEWAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "A product photo of a ceramic mug on a wooden table, soft window light",
    "n": 1,
    "size": "1024x1024",
    "output_format": "png"
  }' | jq -r '.data[0].b64_json' | base64 --decode > mug.png

应把响应视为临时媒体加计费元数据,而不是持久产品资产。如果产品需要在供应商临时 URL 过期后继续使用图片——适用于旧模型或非 OpenAI 适配器——应立即下载到自有对象存储,或者使用按用户所有权授权的个人图片工作台持久化路径。

始终在当前 OpenAI 图片生成指南 与 Images API 参考 中重新检查模型专属字段。Google 在 ai.google.dev 单独记录 Gemini 原生图片生成。网关不会取代这些文档,它只统一访问方式。

为什么通过网关集成,而不是维护多个 SDK

产品团队在图片集成上失败,通常是运维问题,而不是模型质量问题:

  • 每个服务保存不同的供应商密钥
  • prompt 实验绕过预算控制
  • 把临时 CDN URL 存进数据库,随后链接失效
  • 图片 n 或分辨率在未校验时放大成本
  • 个人沙箱与团队项目共用一把密钥,导致费用归属失真

多供应商 LLM 网关 与 OpenAI 兼容 API 网关 中介绍的模式,可以保持客户端稳定,同时让运维人员把稳定别名映射到通道、实施配额并写入使用日志。对于媒体,同一控制面还必须回答:“之后谁可以下载这项资产?”

关键端点与请求字段

生成

POST /v1/images/generations 是文生图入口。本仓库图片请求 DTO 中常见的字段包括:

字段在 GPT Image / 网关中的作用
model优先使用稳定别名 gpt-image-2(或你的映射别名);旧 gpt-image-1* ID 已进入 OpenAI 向 gpt-image-2 迁移的弃用路径
prompt必填文本指令
n可选数量;指针类型保留 JSON 是否显式出现,但本网关会在计费前把 n=0(或缺省)归一化为 1
size如 1024x1024、1536x1024、1024x1536 的分辨率字符串;GPT Image 2 还记录了更广泛的 WIDTHxHEIGHT 规则
quality模型支持时使用的供应商专属质量 token
output_formatGPT Image 系列支持的 png / jpeg / webp
output_compression对支持压缩的格式设置压缩参数
response_format仅旧版 DALL·E 支持(url 或 b64_json);GPT Image 不支持,并始终返回 base64
background、moderation、stream适配器支持时可用的模型系列扩展
user用于滥用追踪的最终用户标签

不要假设每个字段在每个模型上都有效。应根据环境实际启用的模型和供应商当前文档进行验证。

编辑

POST /v1/images/edits 是编辑/inpaint 表面。集成通常通过 multipart form 发送源图片、可选 mask 和 prompt。产品代码应:

  1. 把用户上传接收到有大小上限的临时存储
  2. 调用网关前统一 MIME type
  3. 与生成请求保持相同鉴权和计费所有者
  4. 绝不在应用日志中记录原始图片字节

如果产品只需要“用更严格的 prompt 重新生成”,只接生成端点可能就够了。只有用户必须保留构图或品牌资产时再加入编辑能力。

计费安全属于集成的一部分

图片生成是乘数密集型 API。恶意或有 bug 的客户端若把 n 设置成荒谬数值,一次 HTTP 请求就可能演变为记账事故。在本代码库中:

  • 请求校验会在配额计算前拒绝超过 MaxImageN(128)的 n
  • n 缺失或为 0 时归一化为 1(“保留指针是否出现”不等于“执行零张图片”)
  • 预扣与结算路径仍必须拒绝负数或饱和后的费用
  • 质量/尺寸比例只有在校验后才能成为乘数

产品规则:

  • UI 中把 n 默认设为 1
  • 不要在没有小于平台上限的产品级 clamp 时接受用户输入的 n
  • 多变体生成前显示预估成本
  • 如果预扣失败,UX 应直接失败;不要“乐观排队”并从另一个钱包扣费

TypeScript:生成、解码并持久化

type ImageGenResponse = {
  data: Array<{ b64_json?: string; url?: string; revised_prompt?: string }>;
};

async function generatePng(prompt: string, model = "gpt-image-2") {
  const res = await fetch(`${process.env.GATEWAY_BASE}/v1/images/generations`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GATEWAY_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model,
      prompt,
      n: 1,
      size: "1024x1024",
      output_format: "png",
    }),
  });
  if (!res.ok) throw new Error(await res.text());
  const body = (await res.json()) as ImageGenResponse;
  const b64 = body.data?.[0]?.b64_json;
  if (!b64) {
    // Legacy/provider-adapter path only: some non-GPT models may still return url.
    if (body.data?.[0]?.url) {
      return { kind: "url" as const, url: body.data[0].url, revised: body.data[0].revised_prompt };
    }
    throw new Error("no image payload");
  }
  const bytes = Buffer.from(b64, "base64");
  // upload bytes to object storage under the billing owner's key prefix
  return { kind: "bytes" as const, bytes, revised: body.data[0].revised_prompt };
}

Python:使用同一契约并显式解码

import base64
import os
from pathlib import Path
import requests

def generate_and_store(prompt: str, model: str = "gpt-image-2") -> Path:
    r = requests.post(
        f"{os.environ['GATEWAY_BASE']}/v1/images/generations",
        headers={"Authorization": f"Bearer {os.environ['GATEWAY_KEY']}"},
        json={
            "model": model,
            "prompt": prompt,
            "n": 1,
            "size": "1024x1024",
            "output_format": "webp",
            "output_compression": 80,
        },
        timeout=120,
    )
    r.raise_for_status()
    b64 = r.json()["data"][0]["b64_json"]
    out = Path("/tmp/out.webp")
    out.write_bytes(base64.b64decode(b64))
    return out

当你控制存储并希望只请求一次时,优先使用 b64_json,这是 GPT Image 默认格式。只有在描述旧模型或供应商适配器行为时才提到 url。绝不要假设 URL 永久有效。

个人图片工作台与团队视频工作台

本仓库提供的图片工作台属于个人 / session / token 作用域:

  • UI:图片工作台
  • /api/user/image-workbench/* 下的用户 API,包括预设、模板、运行、资产、生成与编辑
  • /api/image-workbench/* 下用于生成/编辑/下载的 API-key 变体
  • /api/image-workbench/admin/* 下的管理员模板工具
  • 对象键和历史记录使用用户所有权

当前代码树中不存在团队作用域的 /api/teams/:team_id/image-workbench 路由。除非你的 fork 已添加,否则不要宣称支持团队图片历史或项目作用域图片资产。

相比之下,视频工作台 确实包含 /api/teams/:team_id/video-workbench 下的团队/项目路由。如果产品经理要求“像视频一样共享图片历史”,那是一个功能缺口,不是当前图片工作台已经具备的能力。原始 /v1/images/* 的多租户计费仍跟随令牌的计费所有者;参见 多租户 LLM 网关架构。

当运维人员或设计师需要交互式历史和预设时使用工作台;当后端服务以编程方式生成资产时使用原始 /v1/images/*。两条路径应共用相同的模型别名与使用日志字段,便于后续归因。

媒体生命周期清单

  1. 提交:使用有作用域的网关密钥,而不是供应商密钥。
  2. 校验:发送前校验 n(记住这里的 0 会变成 1)、尺寸和 prompt 长度。
  3. 记录:保存 request ID / usage log ID,供支持排查。
  4. 解码:解码 b64_json(或只获取一次旧版 URL),并按留存策略持久化对象存储键。
  5. 授权:后续下载按所有者鉴权;图片工作台资产归用户,你自行存储的 API 任务归计费所有者。
  6. 过期或重审:产品策略要求时让资产过期或重新审核。
  7. 观测:通过监控栈按模型追踪失败率与成本。

如果跳过第 4 步,产品 bug 会在数周后表现为缩略图损坏,而不是 API 错误。

Prompt 与产品工程注意事项

  • 在应用层分离系统意图(品牌规则)与用户 prompt;Images API 通常只有一个 prompt 字符串,所以需要谨慎组合。
  • 保存最终发送的 prompt,而不仅是 UI 草稿,因为供应商可能返回 revised_prompt。
  • 限制每个用户的并发图片任务;重试很容易意外 fan-out 图片端点。
  • 对 HTTP 429/5xx 使用有界重试,并在技术栈支持时使用幂等客户端键(参见 LLM API 重试与幂等)。
  • 多模型实验在确认尺寸/质量支持后只修改 model 别名,不要 fork 整个客户端。
  • 默认示例优先使用 gpt-image-2;如果通道仍暴露旧别名,应显式映射。

常见集成失败

症状可能原因修复方法
“成功”后没有图片在 GPT Image 响应中查找 url读取并解码 b64_json
配额突然大量消耗n 无上限或重试风暴Clamp n,对重试去重
期望 n=0 表示“试运行”网关把 0 归一化为 1不要发送零,直接不调用
个人账户可用,团队密钥失败密钥缺少项目授权或模型分组错误修复团队令牌授权;不要假设图片工作台是团队作用域
编辑端点返回 400multipart 字段名错误或 mask 尺寸错误对齐供应商/网关 form 契约
找不到模型通道未映射别名在通道配置中将稳定别名映射到上游 ID

相关阅读

来源

失败问题解答

我应该用三个 SDK 分别调用 OpenAI、Gemini 和其他供应商吗?

如果产品只需要一种客户端契约,就不应这样做。生成和编辑优先走 OpenAI 兼容网关路径;只有网关尚未映射的功能才保留原生 SDK。

response_format=url 适合写入生产数据库吗?

它不是 GPT Image 的正确默认值,因为 GPT Image 不支持该参数并始终返回 base64。对于仍返回 URL 的旧模型,只把 URL 当作临时指针,并在用于长期产品页面前重新托管。

n 的安全默认值是多少?

生产 UX 从 1 开始。只有产品明确需要多个变体时才提高,并把产品硬上限设在平台上限之下(本代码库为 128)。不要发送 n=0 并期待 no-op。

图片请求需要 streaming 吗?

通常不需要。只有适配器和 UI 确实能从局部预览中获益时才启用 streaming;否则保持简单的 request/response 路径。

团队如何在不泄漏资产的情况下共享图片历史?

当前网关的图片工作台历史属于个人。共享图库需要在你自行保存的字节之上构建应用层分享;也可以仅用团队项目密钥为 /v1/images/* 计费,同时对持久化对象实现自己的 ACL。

首次生产接线

把一个后端任务接到 POST /v1/images/generations,使用 model=gpt-image-2、n=1、output_format=png,完成 base64 解码并立即上传对象存储。确认使用日志显示预期计费所有者;只有在通道配置中验证尺寸/质量兼容性后,再加入第二个模型别名。