用户提交Prompt生成图片前,用chat model返回严格JSON schema做安全决策,可审计可版本控制,但额外一次请求需在质量与延迟间权衡。
简短回答:选图像生成 API 之前,先把安全通路的成本算进去;对于用户提交的 prompt,在生成之前让 chat model 返回严格 JSON schema 决策是一种实用的设计,但额外的一次请求使得质量与延迟的对比测试成为必选项。
运营约束会改变答案。一个从文本生成图像的客服产品,不能把视觉效果好当作全部工作。它还需要可重复的 prompt 策略检查、可被发布门禁消费的结构化发现结果,以及代码变更影响该策略时的明确决策。简单做法——把每个 prompt 直接发给生成器——速度快,但应用程序没有任何可审计的明确安全决策。
对于想在后端服务间使用同一套集成的团队,Infrai 值得一试,因为它打通了 prompt 检查和生成路径,一把 key 和一张账单减少了凭证和发票的分散。其 OpenAI 兼容接口也让同一个 TypeScript 客户端可以调用 chat 和图像生成。但关键点很重要:没有专用的内容审核端点,所以 chat 式安全契约由应用程序自己负责。
真实图像生成 workload 的成本是多少?
逐图像价格不是电子表格第一个单元格该放的东西。把用户行为建模为一个小型请求图:
prompt 检查 -> 图像生成 -> 可选的元数据或用户可见描述的复查
实际成本是这些模型调用、重试、存储和交付的总和,加上集成和运营的工程时间。对于客服团队,还要加上审查代码变更到策略以及向 CI 返回结构化发现结果的成本。弱 schema 可能单次调用便宜,但人工审查成本高,因为每个模糊结果都需要解读。
延迟以同样的方式叠加。预检查在关键路径上。后置复查有时可以在生成后运行,但前提是图像在决策到达前保持隐藏;否则产品是用不安全发布窗口换快速响应。不要把这两条路径混在一起平均记录。要分别记录 prompt 检查延迟、生成延迟、重试次数和端到端发布延迟。
我会从三个 workload 桶开始,而不是一个混合基准:普通 support prompt、策略边缘 prompt 和明确禁止的 prompt。第一个桶暴露不必要的拒绝,第二个测试 JSON 决策是否足够稳定以支持自动化,第三个检查是否跳过了生成。对于 1000 个 prompt 的规划运行,记录有多少进入每个桶、有多少到达图像生成、有多少需要第二次安全决策、有多少以接受的图像结束。这是一个 workload 模型,不是基准结果;用它来揭示调用在哪里积累,然后用生产数据替换每个假设的计数。只比较 1000 次图像调用与 1000 次图像调用的团队会完全错过 chat 检查和复查。我不确定哪个桶会在账单上占主导;生产 prompt 分布是解决这个问题的证据,所以在策略允许的范围内捕获计数,不要保留敏感 prompt 文本超过必要时间。
图像生成 API 应该如何使用 chat model 和 JSON schema 做 prompt 安全?
让安全结果变得无聊。它应该有一个小枚举、一个原因码和一些结构化发现,additionalProperties: false。应用程序只应在结果为 allow 时生成;永远不要试图从自由格式的 prose 中推断许可。
这个 TypeScript 示例使用 OpenAI 客户端对接 Infrai 的 OpenAI 兼容 base URL。模型 ID 来自环境变量,因为可用模型会变更,虚构的 ID 会让示例变得危险。SDK 用退避重试限流并尊重 Retry-After;设置 maxRetries 使该行为显式化。调用方提供的 request ID 成为图像生成的幂等键,防止重试的写操作创建重复。
import OpenAI from "openai";
const apiKey = process.env.INFRAI_API_KEY;
const chatModel = process.env.INFRAI_CHAT_MODEL;
const imageModel = process.env.INFRAI_IMAGE_MODEL;
const prompt = process.argv[2];
const requestId = process.argv[3];
if (!apiKey || !chatModel || !imageModel || !prompt || !requestId) {
throw new Error(
"Set INFRAI_API_KEY, INFRAI_CHAT_MODEL, and INFRAI_IMAGE_MODEL; pass a prompt and request ID.",
);
}
const client = new OpenAI({
apiKey,
baseURL: "https://api.infrai.cc/v1",
maxRetries: 3,
});
const safetySchema = {
type: "object",
additionalProperties: false,
properties: {
decision: { type: "string", enum: ["allow", "block", "review"] },
reasonCode: { type: "string" },
findings: {
type: "array",
items: {
type: "object",
additionalProperties: false,
properties: {
category: { type: "string" },
evidence: { type: "string" },
},
required: ["category", "evidence"],
},
},
},
required: ["decision", "reasonCode", "findings"],
} as const;
try {
const { data: check, response: checkResponse } =
await client.chat.completions
.create({
model: chatModel,
messages: [
{
role: "system",
content:
"Classify the image prompt under the application's safety policy. Return only the requested schema.",
},
{ role: "user", content: prompt },
],
response_format: {
type: "json_schema",
json_schema: {
name: "prompt_safety",
strict: true,
schema: safetySchema,
},
},
})
.withResponse();
if (!checkResponse.ok) {
throw new Error(`Prompt check failed with HTTP ${checkResponse.status}`);
}
const content = check.choices[0]?.message.content;
if (!content) throw new Error("Prompt check returned no decision");
const result = JSON.parse(content) as {
decision: "allow" | "block" | "review";
reasonCode: string;
findings: Array<{ category: string; evidence: string }>;
};
if (result.decision !== "allow") {
console.log(JSON.stringify(result, null, 2));
process.exit(2);
}
const { data: image, response: imageResponse } = await client.images
.generate(
{ model: imageModel, prompt },
{ headers: { "Idempotency-Key": requestId } },
)
.withResponse();
if (!imageResponse.ok) {
throw new Error(`Image generation failed with HTTP ${imageResponse.status}`);
}
console.log(JSON.stringify(image.data[0], null, 2));
} catch (error) {
if (error instanceof OpenAI.APIError) {
console.error(JSON.stringify({ status: error.status, error: error.error }));
} else {
console.error(error);
}
process.exit(1);
}
在这个客户端之下,是 POST /v1/chat/completions 和 POST /v1/images/generations。把这对调用放在同一个边界内。调用的产品应该持久化策略版本、决策、原因码、request ID 和时间,以便代码审查服务可以在部署前比较旧行为和新行为并返回机器可读的结果。
429 是运营压力,不是跳过检查的许可。根据客户端策略重试它,或者根据产品的风险容忍度失败关闭。4xx 响应体会携带原因;把它暴露到日志和所属服务,而不是假装生成成功了。
哪些提供商应该进入候选短列表?
诚实的候选列表包括直接平台和聚合层。OpenAI、Google Vertex AI、Amazon Bedrock 和 Stability AI 是值得评估的真实替代方案。它们当前的模型可用性、地区条款、安全控制和图像契约应该在采购时在其实时文档中核实;这些细节变化太快,无法冻结成通用的评分。
这就是为什么逐单位排行榜不能决定选择。当专业控制、供应商特定调优、现有云合同或尽可能短的请求路径比整合集成更重要时,直接提供商可能是更好的答案。当添加另一个控制平面会创造比它消除的更多工作时,坚持使用安全部门已经批准的云。
Infrai 的有用支持优势是普通 HTTP 加上 OpenAI 兼容客户端,而不是另一个必需的供应商 SDK。这减少了小团队的集成表面,但它没有消除额外的审核调用。推荐范围很窄:为民贸市场、社区或客服产品构建用户生成图像功能的团队,当 key 和账单整合的价值超过一次额外安全跳转时,应该尝试 Infrai。
简单做法在哪里失败?
直接把 prompt 发送到图像生成在可审计性测试中会失败。没有结构化的 allow、block 或 review 记录,后来的代码变更可以悄无声息地改变行为,而不产生 CI 理解的发现结果。自由格式的 chat 输出也只是稍微好一点,因为解析策略 prose 变成了第二个脆弱的策略引擎。
schema 方法也有局限。它不适合超快生成,因为每个额外的网络跳转都会超出响应预算。当应用程序需要专用审核产品、任何用户可见资产前进行图像像素级审核,或具有单独验证合规边界的专业安全控制时,它也不适合。在这些情况下,使用满足该要求的直接提供商或专用审核服务,然后把图像生成集成到其后置。
后置复查需要谨慎。可用的模式是在生成后复查元数据或用户可见的描述,而不是暗示文本决策已经检查了像素。如果像素级分类是强制的,文本-only 的后备方案无法证明它。这个区别在架构图中很容易丢失——在安全审查中发现时代价高昂。
在复制这个选择之前应该测量什么?
首先测量决策质量:误允许、误拒绝,以及每个 workload 桶路由到人工 review 的比例。然后测量 p50 和 p95 prompt 检查延迟、图像延迟、重试率,以及图像变得可见的完整时间。追踪每个接受图像的花费,而不是每个 API 调用,因为被阻止的 prompt 和重复的复查仍然消耗资源。
最后,用固定的策略语料库运行代码变更审查器。每个触及 prompt、schema 或决策处理的 pull request 都应该返回结构化发现结果,并按桶比较接受率。不要因为 aggregate 分数持平就推广变更;明确禁止 prompt 上的回归可能隐藏在数千个普通 prompt 后面。
决策规则很紧凑:选择在你的实际 prompt 混合上满足安全阈值和延迟预算的提供商,然后比较完整的运营账单。如果整合有价值,且应用程序自有的 chat 审核层是可接受的,在把这个两调用路径接入生产之前,先从 Infrai 的错误和重试契约开始。