统一 Bifrost 背后的三家视觉提供商的图像输入
统一 Bifrost 背后的三家视觉提供商的图像输入
TL;DR: 我们运行了一个自动化的视觉质量评估步骤,使用来自 OpenAI、Anthropic 和 Google 的视觉大语言模型(vision LLM)对生成的商品图进行评分。每家提供商对图像负载的格式要求各不相同,而且一次速率限制尖峰就可能导致整个批次停滞。在它们前面接入 Bifrost 后,我们得到了一个统一的 OpenAI 兼容图像模式以及自动故障转移,每次调用仅增加约 4ms 的延迟。
在 Photoroom,我负责商品摄影中扩散模型(diffusion model)方面的工作。生成一张干净的影棚级照片的模型只是工作的一半。另一半是自动判断输出结果是否可用,然后才能交付给客户。
因此,我们构建了一个质量评估评分器(QA scorer)。它将每张生成的渲染图发送给一个视觉模型,并请求一个结构化的判决:背景伪影、边缘裁剪、相对于源图的颜色偏差。我们会将同一张图像发送给不止一家提供商,因为不同模型的失败模式各不相同,单个模型的盲点会通过其他方式暴露出来。
这就是混乱的开始。
三家提供商,三种图像模式
这里的微妙之处在于,“兼容 OpenAI 的视觉”并非一个已确立的标准。准确地说,每种提供商的消息信封结构都不同。
OpenAI 需要 image_url 内容部分,你可以传入一个 data: base64 URI 或一个真实的 URL。Anthropic 的原生 API 需要一个包含 type: base64、media_type 和原始数据的 source 块。Google Vertex 则需要带有 mime_type 的 inline_data。我们的评分器最初有三条代码路径、三组大小限制以及三种重试策略,而这些策略在一个月内就出现了不同步。
对于每个商品的两家提供商、每批次 12 张图像的处理来说,这种分支逻辑就是最容易出问题的部分。出问题的不是扩散模型,而是管道。
我们做了哪些改变
我们将 Bifrost 作为网关接入,并将评分器指向一个单一端点。它对外提供统一的 OpenAI 兼容 API,覆盖 23 家以上的提供商,因此图像部分始终以 OpenAI 的方式编写,然后 Bifrost 将其转换为目标提供商所需的格式。
多模态支持(文本和图像)就位于这个通用接口之后。
现在只有一种请求格式。每次调用唯一改变的是 model 字符串。
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4-6",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "请对此渲染图进行边缘裁剪评分。返回 JSON。"},
{"type": "image_url",
"image_url": {"url": "data:image/png;base64,iVBORw0K..."}}
]
}]
}'
将 anthropic/claude-sonnet-4-6 替换为 openai/gpt-4o 或 vertex/gemini-2.5-pro,请求体完全不变。评分器不再需要知道或关心每个提供商如何编码像素。
故障转移,而非批次停滞
第二个问题是吞吐量。当某个视觉提供商在繁忙时段返回 429 错误时,我们的批次队列就会积压,因为评分器持续向同一个密钥发出请求。
Bifrost 的自动回退机制让我们可以声明一个有序列表。如果主提供商返回错误或超时,请求将使用相同的负载移至下一个提供商。评分器代码无需任何改动。
fallbacks:
- openai/gpt-4o
- anthropic/claude-sonnet-4-6
- vertex/gemini-2.5-pro
在 30 天内,故障转移在大约 0.8% 的调用中触发。这个数字很小。但正是这个区别决定了队列是停滞不前还是顺利排空。
我们还从网关中获得了原生的 Prometheus 指标,因此每个提供商的延迟和错误率都显示在我们已用于 GPU 利用率的同一个 Grafana 面板上。在此之前,这些数据分散在三个提供商的控制面板和一个电子表格中。
对比
在最终确定使用 Bifrost 之前,我们考察了 LiteLLM 和 Portkey。以下是我们针对特定多模态使用场景的真实对比。
| 关注点 | Bifrost | LiteLLM | Portkey |
|---|---|---|---|
| 统一图像模式 | 是,OpenAI 兼容转换 | 是,提供商列表非常广泛 | 是,托管网关 |
| 自托管,单一二进制文件 | 是,Go 语言,npx 或 Docker |
是,Python 代理 | 可自托管,但更复杂 |
| 额外延迟 | 我们测试中约 4ms | Python 负载下更高 | 较低,托管在边缘 |
| 提供商覆盖范围 | 23+ 家 | 三者中最广 | 广泛 |
| 护栏/托管云 | 企业版 | 较轻量 | 托管功能集最强大 |
LiteLLM 的提供商覆盖范围最广,如果你使用 Python,其代理的接入速度确实很快。Portkey 的托管护栏和分析功能比开源版 Bifrost 开箱即用的功能更完善。我们选择 Bifrost 是因为它作为一个自托管的 Go 二进制文件与我们的推理集群并行运行,并且在并发图像流量下延迟开销保持稳定。
权衡与限制
这并非没有代价。
你增加了一次网络跳转。我们测量到中位数延迟约为 4ms,相对于 2-3 秒的视觉调用来说微不足道,但并非为零,对于纯文本流式传输,你会更明显地感受到这个延迟。
同时,它也是一个需要运行和修补的服务。如果 Bifrost 在没有冗余部署的情况下宕机,那么所有提供商都将随之宕机,因此你将每个提供商的脆弱性替换为一个现在由你掌控的单点。出于这个原因,我们运行了两个副本,放在负载均衡器后面。
此外,深层的治理功能,如自适应负载均衡和集群,位于企业版中。开源核心满足了我们的故障转移和多模态需求,但在假定免费版本包含特定功能之前,请检查文档。
转换层的质量也取决于其对提供商的覆盖程度。全新的提供商特性可能会落后上游 API 一个发布版本。