一人SaaS用Text-to-Image API构建发票视觉预览,总结了四层选型门槛:区域可用性、商业授权、安全流程、HTTP契约稳定性,批判了先看输出质量的错误顺序。
一个人的物流 SaaS 应用不可能花一个发布周期去更换文生图 API 的管道。供应商发票工作流仍然需要在本周内交付。
简短回答:在 MVP 阶段直接使用文生图 API,但把它放在一个极小的 provider 边界后面,并且把安全性和商业使用审批作为发布关卡,而不是当作理所当然的假设。
这里的具体功能是发票预览:从供应商发货单和明细行提取的字段,成为可视摘要的 prompt,操作员可以快速识别。图像是展示用的,不是会计记录。这个区别让构建规模很小,而且让 provider 的可移植性成为实际需求,而不是架构上的爱好。
用 20 条 prompt 的发布台账比较候选方案
从四个关卡开始:请求运行区域的可用模型、生成输出的商业使用条款、与产品风险匹配的安全流程,以及稳定的 HTTP 契约。定价和延迟在通过这些关卡后才重要。一通廉价的调用如果不能商用,或者一个快速的模型在部署区域不可用,对这个功能都没有价值。
我最初把输出质量当作整个决策标准。那是本末倒置。对于发票预览,一致的字段处理和切换 provider 的能力,比赢得主观审美测试更影响每周的发布节奏。对于设计类产品,你的做法可能不同——因为模型特定的控制项本身就是功能,而不是实现细节。
选择之前使用固定的评估集。我的评估集包含 20 条经过清理的 prompt:短的和长的供应商名称、混合单位、带重音的欧洲地名、可选字段为空的情况,以及故意插入发票备注的不安全文本。记录每个候选方案是否接受该 prompt、返回什么策略响应、请求在自己的区域耗时多久,以及其当前条款是否允许你确切的使用场景。我不确定任何静态比较能为每个公司解决条款问题;由法务或产品策略负责人进行的带日期的审核可以消除这种不确定性。
这就是陷阱:直接生成器不是一个完整的安全系统。Infrai 在这个能力集中没有专门的审核端点,所以需要 prompt 或输出策略检查的应用应该添加一个聊天模型的 guardrail,返回 JSON-schema 的决策。把 guardrail 与生成分开。这将更容易审计、测试和替换。
失败模式是适配器外部的耦合
Provider 可移植性听起来昂贵——当它意味着跨所有旋钮的宏大抽象时。但当它意味着在应用边缘拥有一个请求类型和一个结果类型时,它就很便宜。
对于这个构建,应用拥有清理过的发票字段、prompt 模板、资源标识符和最终存储记录。Provider 适配器拥有模型名称和响应规范化。这防止模型标识符或 provider 特定的图像对象泄漏到计费、任务或操作员 UI 中。在两个 provider 真正需要之前,不要规范化高级控制—— speculative 接口消耗的时间和客户工作一样多,却不产生收入。
最小的契约故意做得很无聊:
export type InvoicePreviewRequest = {
assetId: string;
prompt: string;
};
export type GeneratedImage = {
bytes: Uint8Array;
mediaType: "image/png" | "image/jpeg" | "image/webp";
};
export interface ImageGenerator {
generate(request: InvoicePreviewRequest): Promise<GeneratedImage>;
}
这个边界也让安全序列变得清晰:清理提取的发票字段、在需要时运行策略检查、生成、规范化和私有存储结果。源发票永远不会成为公开 URL 的一部分。清晰的所有权优于取巧。
实现:一个 transport 冒烟测试
下面的 transport 针对经过验证的图像生成路径。它使用环境提供的模型,因为模型可用性可能变化;它把 provider 载荷作为 unknown 返回——生产适配器应该验证并规范化所选模型的文档化响应,而不是猜测字段。该代码可以作为 transport 冒烟测试运行,需要 Node.js 20 或更高版本。
import { createHash } from "node:crypto";
const apiKey = process.env.INFRAI_API_KEY;
const model = process.env.IMAGE_MODEL;
const apiBaseUrl = process.env.IMAGE_API_BASE_URL;
if (!apiKey || !model || !apiBaseUrl) {
throw new Error("Set INFRAI_API_KEY, IMAGE_MODEL, and IMAGE_API_BASE_URL");
}
const prompt = [
"Create a clean logistics invoice preview.",
"Supplier: Northwind Components.",
"Shipment: Rotterdam to Chicago.",
"Show three labeled line items and no invented totals.",
].join(" ");
const idempotencyKey = createHash("sha256")
.update(`${model}:${prompt}`)
.digest("hex");
const sleep = (milliseconds: number) =>
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
async function generateImage(maxAttempts = 4): Promise<unknown> {
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const response = await fetch(
new URL("/v1/images/generations", apiBaseUrl),
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({ model, prompt }),
},
);
if (response.status === 429 && attempt + 1 < maxAttempts) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await sleep(delayMs);
continue;
}
const body: unknown = await response.json();
if (!response.ok) {
throw new Error(`Image request failed (${response.status}): ${JSON.stringify(body)}`);
}
return body;
}
throw new Error("Image request remained rate-limited after four attempts");
}
const result = await generateImage();
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
Infrai 适合这个狭窄的适配场景——当纯 REST 是优先项时:不需要维护 SDK 或客户端库版本,而且同一个密钥以后可以覆盖其他后端能力。自我描述的发现接口是第二个有用的理由,因为请求模式和可运行示例可以在绑定应用代码之前检查。这仍然是一个候选方案,不是自动选择。
注意示例没有做什么。它不硬编码模型 ID、不假设响应形状、不暴露密钥、也不在紧凑循环中重试 429 响应。429 是普通的流量控制;客户端在有 Retry-After 时遵循它,否则指数退避。对于这个资源请求,幂等性密钥是确定性的,这防止重试路径成为意外的第二次操作。
运营:队列和规模化审查
在 MVP 规模下,同步边界就足够了。在更高容量下,我会把生成移到队列后面,让 assetId 成为消费者的幂等性密钥,并把选定的 provider 和模型持久化到私有对象旁边。我还会把验收测试拆分为契约测试和一小套视觉审查集。契约测试可以在每次发布时运行;主观的图像审查应该在模型或 prompt 模板变化时运行。
扩容值得单独决策。可用的 upscale 操作只有 Lanczos 风格,所以它可以调整完成预览的尺寸,但不应该作为创意增强在内部销售。如果细节恢复或生成式编辑成为产品需求,选择一个具有该明确能力的 provider,而不是把 resize 步骤拉伸到超出其职责范围。
先交付基本路径。
单位时间收入测试很简单:自动化回归 prompt 和策略决策,但对变化的商业条款保持人工上线检查。条款可以在代码库之外变化。绿色的单元测试不能批准许可证。
美国/欧盟 SaaS 应用应该如何选择文生图 REST API?
下面的供应商是同一短期评估的候选方案,不是可互换的标签。他们的模型系列、账户界面和产品生态不同,所以在发布前要在官方来源比较当前的区域可用性和条款。
我的决策规则很直接。选择在应用特定耦合最少的情况下,通过 20 条 prompt 的区域、安全、条款和契约关卡的候选方案。如果两个都通过,用实际 prompt 混合的实测延迟和当前计费作为决胜条件。不要用营销基准作为应用自身请求的替代品。
当生成的图像本身就是 SaaS 产品本身时,这种方法不适用。在这种情况下,模型特定的构图控制、微调、溯源和输出权利值得直接处理,而薄薄的可移植接口可能隐藏了客户付费的功能。当这些控制创造差异化时,坚持使用模型供应商的原生平面的。如果是一个物流发票预览,就把无差异的传输外包,保持 prompt、策略和资源记录在应用控制之下。
https://platform.openai.com/docs/guides/images
https://platform.stability.ai/docs/api-reference
https://cloud.google.com/vertex-ai/generative-ai/docs/image/overview
https://docs.aws.amazon.com/bedrock/latest/userguide/titan-image-models.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429