
最近折腾了产品图片流水线上的视觉语言模型调用优化,踩了几个坑,这篇把问题说清楚。
先说结论:在 VLM(视觉语言模型)步骤前加了 Bifrost 做语义缓存,三周内把账单砍了 62%。GPU 那边的 diffusion 生成从来就不是该操心的地方。
Photoroom 的流水线处理每张产品图要三步。第一步,VLM 读图生成结构化描述。第二步,另一个 LLM(大语言模型)把用户给的提示词改写成 diffusion 模型能好好工作的形式。第三步,SDXL 加上内部 LoRA 在我们的 A100 上跑实际生成。
我们一直在盯着 diffusion 那步。准确说,每个 sprint 都做基准测试和性能分析。所以看到 Q1 账单的时候傻眼了——Claude 和 Gemini Vision 的成本加起来比同等工作负载的 GPU 租赁费还高。VLM 和提示词改写这层占了总推理费用的 58%。
问题在于,之前我们直接从 Python 服务调用这些 provider,没有任何缓存。同样一张产品图、同样一个用户请求,响应结果每次都要重新付钱。
我先看了 LiteLLM 和 Portkey。都是好东西。LiteLLM 要在现有 FastAPI 服务里加个 Python 库,门槛最低,provider 覆盖也很全。Portkey 有精致的托管 UX 和很干净的仪表盘。
最终选了 Bifrost,有三个原因。首先,它是个 Go 二进制, gateway 不会跟我们推理服务抢同一个 GIL 绑定的 CPU。其次,语义缓存是内置功能,不用另外装。第三,OpenAI 兼容的端点意味着我们不需要改任何 SDK 调用。
客观说两句。LiteLLM 的 Python 生态影响力更大,要是你的技术栈以 Python 为主,它的路由配置会更顺手。Portkey 的分析界面,说实话比开箱即用的 Bifrost 好看。
Bifrost 作为 sidecar 跑在提示词改写服务旁边。Caption(描述生成)和 rewrite(改写)两步现在都走 http://bifrost:8080/v1/chat/completions。配置不大。
providers: anthropic: keys: - value: env.ANTHROPIC_KEY_PRIMARY - value: env.ANTHROPIC_KEY_BACKUP google_vertex: keys: - value: env.VERTEX_KEY semantic_cache: enabled: true similarity_threshold: 0.94 ttl_seconds: 86400 fallbacks: - primary: anthropic/claude-3-5-sonnet fallback: - google_vertex/gemini-1.5-pro governance: virtual_keys: - id: vk_caption_team budget_usd_monthly: 800 - id: vk_rewrite_team budget_usd_monthly: 400
三个地方值得说。0.94 的缓存阈值是用 5000 条 captioning 调用做 held-out 测试调出来的。设到 0.97 会漏掉太多明显的重复。设到 0.90 就会返回颜色不对的描述,这对电商场景是致命的。fallback 列表不是摆设。三月份测下来 Anthropic 5xx 错误率 0.4%,按我们的量级,这是真实影响用户的问题。
| 指标 | 之前 | 之后 |
|---|---|---|
| 缓存命中率(描述生成) | 0% | 71% |
| 缓存命中率(提示词改写) | 0% | 49% |
| p95 延迟,描述生成步骤 | 1.8s | 0.31s |
| 月度 VLM + LLM 费用 | 基准值 | -62% |
| 处理的 provider 故障切换事件 | 0(直接返回错误) | 14 |
提示词改写那步缓存命中率低一些,因为用户提示词变化更多。描述生成是真正的赢家,因为同一家商户的产品图在 embedding 空间里高度聚集。我们头部商家大约 70% 在 90 天窗口内上传了 80% 的目录图片。
迁移没什么浪漫的。两行代码。
client = openai.OpenAI( base_url="http://bifrost:8080/v1", api_key=os.environ["BIFROST_VIRTUAL_KEY"],
)
其他全都没动。VLM 团队没改一行代码。改写团队只是切换了一个配置开关。
语义缓存有真实的失效场景。要是下游模型输出本来就应该每次不同(创意生成、采样密集型场景),就别开这个。我们在给编辑提供三个变体的 diffusion 提示词推荐端点里禁用了缓存——缓存会很开心地每次返回同一组三个变体。
Go 二进制又多一个要运维的服务。对小团队来说这不是小事。要是你不需要缓存,LiteLLM 作为库集成移动部件更少。
通过 virtual key 做成本归属是按 key 来的,不是按我们客户的下游商家分的。要是你需要做到商家级别的多租户计费,得自己写点胶水代码。
语义缓存自己用了 embedding 模型。在你默认请求留在你的 VPC 之前,先读一下文档看看它背后是什么。
原文链接:https://dev.to/bifrost/semantic-caching-the-vlm-step-in-our-product-photo-pipeline-4f9k