文章

VLM Serving 生产实战:用视觉 Token 预算稳定图片与视频推理

本文系统讲解多模态大模型服务中图片与视频输入治理、视觉 Token 预算、预处理队列、成本控制和上线检查清单,帮助团队在生产环境中稳定落地 VLM 推理服务。

背景:VLM 推理不是”多传一个 image_url”这么简单

越来越多业务开始把 Vision-Language Model(VLM) 接入生产系统:票据识别、截图问答、质检巡检、商品图理解、客服工单图片分析、视频片段总结、UI 自动化测试、医疗或工业图像辅助分析等。

从 API 形态看,多模态调用似乎只是把文本消息里的 content 从纯字符串换成「文本 + 图片 URL」或「文本 + 视频 URL」。但真正上线后,VLM Serving 的风险点会很快暴露:

  • 同样是 1 次请求,1 张 512px 图片和 6 张高清截图的成本完全不同。
  • 视频输入会带来抽帧、解码、帧数限制、上传大小、预处理排队等额外变量。
  • 高分辨率图片可能显著放大视觉 token 数量,影响延迟、显存和费用。
  • 文本 LLM 的 QPS/RPM 限流不足以描述多模态请求的真实资源消耗。
  • 输入预处理失败、图片格式异常、外链下载慢、视频文件过大,都会让服务尾延迟变差。

因此,VLM Serving 的核心不是”能不能调用模型”,而是能不能建立一套 视觉 Token 预算 + 输入治理 + 多阶段队列 + 成本归因 的生产体系。

关键事实:视觉输入最终也会变成模型上下文成本

多数 VLM 的工程路径可以概括为:

  1. 接收文本、图片或视频输入。
  2. 下载或读取媒体文件。
  3. 对图片做解码、缩放、裁剪、归一化。
  4. 对视频做抽帧或帧序列构造。
  5. 通过视觉编码器或多模态处理器生成视觉表示。
  6. 将视觉表示与文本 prompt 拼接或融合后送入语言模型。
  7. 输出文本回答。

vLLM 文档 中的 multimodal inputs 页面说明,它支持向多模态模型传入图片、视频、音频、embedding 和 cached inputs,并在示例中使用 limit_mm_per_prompt--limit-mm-per-prompt.image 控制每个 prompt 可携带的多模态输入数量。SGLang 的 Vision API 文档 也展示了 OpenAI-compatible 的视觉输入格式,并支持多图和图文交错输入。OpenAI 的图片与视觉文档 则明确给出了 detail 等级、图像缩放和图像 token 成本计算规则。

这说明生产系统需要把 VLM 输入当作一种可计量、可预算、可调度的资源,而不是把图片/视频简单视为普通请求参数。

核心设计:在 Gateway 层建立视觉 Token 预算

VLM Serving 的第一层治理应该放在入口层,而不是等请求进入 GPU worker 后再处理。建议在 LLM Gateway 或 API Gateway 中加入一个 multimodal admission controller

它至少需要做以下检查:

检查项目标处理策略
文件类型阻止非预期媒体输入只允许 jpg/png/webp/mp4 等白名单格式
文件大小防止上传或下载拖垮服务超限直接拒绝或要求压缩
图片数量防止多图请求无限堆叠按模型和租户配置上限
图片分辨率控制视觉 token 成本超限缩放、降 detail 或拒绝
视频时长控制抽帧与预处理成本限制秒数、帧数或只取关键片段
detail 等级平衡质量、成本和延迟默认 low/auto,高精度场景显式申请
租户预算做成本归因和配额治理按视觉 token 估算值扣减预算

一个实用的预算模型可以从粗粒度开始:

estimated_visual_cost = image_count × image_detail_factor × resolution_factor
                      + video_frame_count × frame_detail_factor
                      + preprocessing_cost_factor

这不是最终计费公式,而是用于准入控制和排队调度的工程估算。真正上线后,可以用模型返回的实际 usage、GPU 指标和预处理耗时不断校准这个估算值。

输入治理:先标准化,再进入推理队列

VLM 服务通常不应该让 GPU worker 直接面对原始图片或视频。更稳妥的做法是拆成三个阶段。

第一阶段:媒体接入与安全检查

入口层负责校验 URL、Content-Type、文件大小、媒体格式和租户权限。对于外部图片 URL,建议设置下载超时、重定向限制、私网地址拦截、最大响应体大小和 MIME sniffing,避免 SSRF、慢下载和异常文件拖垮服务。

第二阶段:预处理与视觉预算估算

预处理层负责图片解码、EXIF 处理、透明背景处理、缩放、裁剪、视频抽帧、异常帧过滤和视觉 token 估算。这个阶段可以使用 CPU worker 或独立的预处理池,不要和 GPU 推理 worker 混在一起。

第三阶段:多模态推理队列

只有经过治理后的结构化输入才进入推理队列。例如:

{
  "tenant_id": "enterprise-a",
  "model": "qwen2.5-vl-7b-instruct",
  "text_tokens_estimated": 860,
  "image_count": 2,
  "video_frame_count": 0,
  "detail": "low",
  "visual_tokens_estimated": 1200,
  "priority": "standard"
}

这样,调度器才能根据文本 token、视觉 token、租户优先级和 GPU 状态做更合理的排队,而不是只看请求数。

为什么图片、视频和文本请求要分队列

文本 LLM 服务中,一个请求通常主要由 prompt tokens、output tokens 和 KV cache 占用决定。VLM 服务还多了媒体下载、解码、图像处理、视频抽帧和视觉编码阶段。

如果所有请求进入同一个队列,可能出现以下问题:

  • 大视频请求阻塞普通图片问答。
  • 多图请求拉高排队延迟,影响轻量截图问答。
  • 图片下载失败或慢下载占住推理入口。
  • 视觉编码器成为瓶颈,但 GPU 语言模型仍有空闲。
  • 成本归因只能看到”请求数”,看不到视觉输入导致的资源差异。

更好的策略是分成四类队列:

  1. download queue:处理外链下载和上传文件落地。
  2. preprocess queue:处理解码、缩放、抽帧和预算估算。
  3. vision encoder queue:处理视觉编码或图像 embedding。
  4. llm inference queue:处理最终文本生成。

这四类队列的指标要分开观测。尤其要关注:

指标说明
preprocess p95预处理阶段 P95 延迟
vision encoder queue length视觉编码器排队长度
visual token estimate视觉 token 估算值与实际值的偏差
media fetch failure rate媒体下载失败率
video frame count distribution视频抽帧数分布
detail level distribution各 detail 等级请求占比

detail 等级:不要默认把所有图片都按最高精度处理

OpenAI 图片与视觉文档中把 detail 分为 low、high、original、auto 等层级,并说明 low 适合不需要细粒度视觉细节的快速低成本理解;部分模型会根据 detail 等级和图像尺寸采用不同的缩放、patch 或 tile 计算方式。

这对自建 VLM Serving 有直接启发:

  • 截图大意总结、图片分类、是否包含某类物体:默认 low 或自动降采样。
  • OCR、表格、票据、合同截图:允许 high,但要限制分辨率和张数。
  • UI 点击定位、密集图表理解、工业质检细节:允许更高 detail,但必须走更高预算和更严格限流。
  • 视频总结:先做抽帧策略,不要把所有帧都送入模型。

生产环境里,detail 不应该只是客户端随便传的参数,而应该由服务端根据业务场景、租户等级、预算和模型能力共同决定。

视频输入:真正的成本通常在抽帧策略上

视频 VLM 服务比图片更容易失控。一个 30 秒视频,如果按每秒 1 帧抽取就是 30 帧;如果按每秒 5 帧抽取就是 150 帧。即使模型支持视频输入,工程上也要明确:

  • 最大视频时长。
  • 最大抽帧数量。
  • 抽帧间隔或关键帧策略。
  • 是否允许多视频输入。
  • 视频分辨率上限。
  • 是否异步处理长视频。
  • 是否把长视频任务转成批处理任务。

vLLM 文档中给出了 video input 的用法示例,但”支持传入视频”不等于”生产环境可以无限制传入视频”。上线时要把视频请求当作高成本任务单独治理。

工程落地架构

推荐架构如下:

Client → LLM Gateway → Media Admission Controller → Media Fetcher / Uploader
       → Image & Video Preprocessor → Visual Token Estimator
       → Multimodal Scheduler → VLM Worker Pool → Usage Recorder / Cost Attribution

每一层的职责要清晰:

  • Media Admission Controller:做格式、大小、数量、分辨率、detail 和预算检查。
  • Media Fetcher:下载外部 URL,设置超时、大小限制和安全隔离。
  • Preprocessor:统一图片方向、色彩、缩放、视频抽帧和异常处理。
  • Visual Token Estimator:在进入推理前估算视觉输入成本。
  • Multimodal Scheduler:按视觉 token、文本 token、租户优先级和队列状态调度。
  • VLM Worker Pool:执行真正的视觉编码和文本生成。
  • Usage Recorder:记录实际 token、耗时、失败原因和租户成本。

常见误区

误区一:只按 QPS 做限流

VLM 请求的成本差异远大于普通文本请求。1 QPS 的高清多图 OCR 可能比 20 QPS 的短文本问答更重。限流至少要加入视觉 token 估算、图片数量、视频帧数和 detail 权重。

误区二:把图片预处理放在 GPU worker 里

这会让 GPU worker 同时承担下载、解码、缩放、异常处理和推理逻辑,导致故障边界混乱。预处理应该前置并可水平扩展。

误区三:所有业务共用一个 detail 默认值

不同业务对视觉细节的需求差异很大。客服图片分类和票据 OCR 不应该使用同一套 detail 策略。

误区四:忽视失败输入的成本

坏图、慢 URL、超大图、损坏视频、错误 MIME 类型都会消耗系统资源。失败请求同样要记录成本和频率,否则排障时只会看到”模型慢”。

上线检查清单

上线前至少检查以下项目:

  • 是否限制单请求图片数量、图片大小和图片分辨率。
  • 是否限制视频大小、时长、帧数和抽帧策略。
  • 是否有服务端 detail 策略,而不是完全信任客户端。
  • 是否把下载、预处理、视觉编码和文本生成拆成独立指标。
  • 是否记录 visual_tokens_estimatedactual_tokenspreprocess_latencyvision_encoder_latency
  • 是否能按租户、模型、业务场景统计多模态成本。
  • 是否对外链图片做 SSRF 防护和下载超时。
  • 是否对高成本请求做排队、降级或异步化。
  • 是否有用于回归测试的图片/视频样本集。
  • 是否能在模型版本、processor 版本或抽帧策略变更后对比输出差异。

结论

VLM Serving 的生产难点不只是”模型是否支持图片或视频”,而是如何让图片、视频和文本输入都变成可治理的资源。

一套稳定的多模态推理服务,应当在入口层完成媒体安全检查,在预处理层完成标准化和视觉 token 估算,在调度层区分轻重请求,在观测层记录真实成本与失败原因。只有这样,团队才能在多图、高清截图、视频抽帧和不同租户混跑的情况下,稳定控制延迟、显存、成本和服务质量。

主要参考资料

  1. vLLM Multimodal Inputs
  2. SGLang OpenAI APIs - Vision
  3. OpenAI Images and Vision Guide
  4. LLaVA-OneVision: arXiv:2408.03326
  5. LLaVA-Mini: arXiv:2501.03895
  6. Flamingo: a Visual Language Model for Few-Shot Learning: arXiv:2204.14198

常见问题

为什么 VLM Serving 不能只沿用文本 LLM 的限流规则?
图片和视频会引入视觉编码、分辨率、帧数和视觉 token 预算等变量,同样的请求数可能对应完全不同的 GPU、CPU、网络和存储成本,仅靠 QPS/RPM 限流无法准确描述多模态请求的真实资源消耗。
视觉 Token 预算应该按什么维度控制?
至少应同时控制单图分辨率、每次请求的图片数量、视频抽帧数、detail 等级、总视觉 token 估算值和租户级预算,并随真实用量数据持续校准。
生产环境中 VLM 服务最容易忽视什么?
最容易忽视输入预处理与视觉编码阶段的排队、异常图片处理、超大视频防护、缓存命中率和视觉 token 成本归因,这些环节往往是尾延迟和成本失控的根源。