1. 从“多模态接口”到“Omni serving”
普通多模态接口常把图片、音频或视频作为一次性输入:服务先做媒体预处理和 encoder forward,把得到的特征并入 prompt,再由一个自回归语言模型输出文本。对使用者来说它当然是多模态;但对 runtime 来说,长时间占用 GPU 的主体仍是一条 prefill/decode 循环,媒体处理通常只是开始前的前处理。
Omni serving 的工作负载更宽:输入和输出都可能含文本、图像、音频、视频或动作;输出端可能是自回归 token、连续 latent 的多步去噪、codec code 的生成、波形重建,或这些分支的组合。一次用户请求因而不是一个 sequence,而是一个带状态边的执行图。vLLM-Omni 将这种图称为 stage graph,SGLang-Omni 则把 preprocessing、encoder、AR engine、talker、decoder、vocoder 和 aggregator 当作协调阶段。两种措辞不同,但共同点是调度单位从“单模型 token iteration”扩展成“互相依赖的异构阶段”。
工作定义:若一个 serving 系统需要在同一逻辑请求中,调度至少两类具有不同状态格式或不同进度粒度的推理阶段,并且要把它们的中间结果、终端媒体和取消语义正确连接,就可把它称为 Omni serving。这个定义刻意不以模型宣传的 any-to-any 标签为准。
2. 请求合同:输入不是一段“prompt 字符串”
入口应把用户意图规范化为有顺序的内容块,而不是在 API 边界就把所有媒体强行转成文本。每个块至少需要声明模态、载荷位置、顺序、时间信息和生命周期。文字块可以直接携带 UTF-8 内容;图片、音频和视频块可携带已上传对象的引用或受控的字节流;实时流还需 chunk 序号、采样时间和是否结束。服务端随后才能决定哪些对象要 decode、哪些特征可复用、哪些后续 chunk 仍属于同一会话。
OmniRequest
request_id, tenant_id, trace_id, deadline, cancellation_token
input[]: { modality, order, payload_ref, media_metadata, timestamp_range }
desired_output[]: { modality, stream, format, quality_or_sampling_options }
session: { session_id, turn_id, resume_policy }
policy: { safety, retention, idempotency_key, priority }
这里的结构是教学合同,不是某个仓库的 wire schema。关键是 desired_output 不能退化为一个布尔值:文本流、PCM/Opus 音频流、图像文件和视频片段的完成条件不同;同一模型也可能只对某些组合有权重和后端支持。payload_ref 不应把租户凭据或临时 URL 留进长期日志,且必须受大小、MIME 类型、解码复杂度和超时限制保护。
3. 输出合同:事件、媒体对象与最终结果必须区分
对文本模型,常见输出可以简化为 token SSE 和一个最终字符串。Omni 输出至少要区分三层:过程事件(阶段开始、排队、首 token、错误、背压)、可消费的增量载荷(文本 delta、codec frame、波形 chunk、预览图或视频 segment)和最终对象(带媒体格式、时长、校验和与安全标记的可下载结果)。前两者可以被流式传输,最后一层才适合声明请求完成。
每个增量事件应有稳定的 request_id、output_id、modality 和单调递增的 sequence_no。音频和视频还应携带时间轴信息,避免网络到达顺序被误认为播放顺序。若客户端断开,控制面不能只停止 HTTP 写入;它还要把取消传到尚未开始、正在运行和已向下游转发的阶段,避免产生无人消费的 GPU 工作和媒体对象。
4. 它与 LLM serving、普通 MLLM serving 有何不同
| 维度 | LLM serving | 常见理解型 MLLM serving | Omni serving |
|---|---|---|---|
| 主要执行循环 | 文本 prefill 后反复 decode | 媒体 encoder 加文本 prefill/decode | 多个 AR、非 AR、codec 或媒体阶段按依赖推进 |
| 主要状态 | token、KV cache、采样器 | 再加媒体特征与位置映射 | 再加 latent、codec code、流时间轴、跨阶段缓冲与会话状态 |
| 输出 | 通常是文本或结构化文本 | 通常仍是文本 | 文本、音频、图像、视频或动作,可能并发增量交付 |
| 调度瓶颈 | KV 容量、decode 吞吐、TTFT | 加上媒体 prefill 的长尾 | 每个 stage 的计算、显存、带宽、速率匹配与端到端尾延迟 |
| 错误边界 | 请求或 sequence | 请求、媒体解码与 prompt 绑定 | 请求、stage、边上的 payload、流 segment 与 session 都需要可追踪 |
这不是说普通 MLLM 没有复杂性,而是它常能把复杂性包在一次 prefill 内。只要输出端出现图像去噪、语音 code 预测和 vocoder,或输入输出同时以连续流前进,单一 batcher 不再能正确表达资源和完成语义。
5. Stage DAG:把模型路径写成数据依赖,而不是固定流水线
对一个非双工请求,可以把执行图写为 \(G=(V,E)\):节点 \(v\in V\) 是一个可独立调度的 stage,边 \(e\in E\) 是有类型的 payload 依赖。一个节点的合同至少应说明输入类型、输出类型、批维度、可并行性、设备需求、可取消点和缓存所有者。图是 DAG 的意思不是所有模型都只有一条链;它可以 fan-out 生成文本和语音,也可以 fan-in 把多路媒体特征合入 thinker。
image/audio/video -> decode + encoder -> media features --+
text -------------------------------> prompt assembly -----+--> thinker / AR --> text events
|
+--> talker --> codec codes --> vocoder --> audio events
prompt ------------------------------> diffusion condition ----> denoiser loop --> VAE / decoder --> image object
图上的箭头应携带格式,而不是只写“data”。例如 media_feature、hidden_state、codec_code 与 latent 不可互换,即使它们在物理上都是 GPU tensor。它们有不同的 shape、消费顺序、可缓存条件和保密边界。下一页会把这些状态逐一拆开。
6. 异构执行循环:每个 stage 都有自己的进度单位
AR thinker 的自然进度单位常是 1 个或少数几个 token;媒体 encoder 可能按整段或固定窗口工作;图像 diffusion 以 denoising step 推进;流式 vocoder 以音频帧或小波形块推进。它们的有效 batch shape 也不同,因此“把所有活跃请求放进同一个 batch”通常既浪费又会制造队首阻塞。
成熟的实现应允许每个 stage 有自己的输入队列、batch 选择器和资源配额,同时由编排器维护跨 stage 的逻辑请求生命周期。SGLang-Omni 的公开架构明确把独立 scheduler、inbox/outbox 和共享内存 tensor 传输放在阶段边界;vLLM-Omni 公开的抽象则强调异构 pipeline、stage execution overlap 与跨阶段的动态资源分配。它们不是同一实现,但都反对用一个全局 token scheduler 假装管理全部异构工作。
异构不等于把每个小函数拆成进程。一个 stage 值得独立调度,当它的并行度、批处理规则、显存常驻、失败恢复或硬件放置与相邻阶段明显不同。反之,过细拆分会把 kernel launch、序列化和队列延迟变成主导成本。
7. 流式:首个可消费结果比“模型结束”更早
流式服务的目标不是尽快传任何日志,而是尽快传对客户端有语义且可消费的结果。文本的第一个 delta、音频的第一个可播放片段、图像的首张可显示预览、视频的第一个可解码 segment 是不同指标。一个 codec frame 若必须等多个 frame 才能封装或解码,就不是最终的可消费音频事件。
为避免迟到的 stage 覆盖新状态,流事件需要 epoch 或 generation 编号。续传时客户端应提供最后确认的事件序号,服务端只重放明确缓存的部分;不能把“重跑模型”伪装成无副作用的网络重传。对于随机采样或 diffusion,重试是否等价还取决于种子、采样参数、模型版本和已消费的状态。
背压同样必须跨越 stage:客户端消费慢时,vocoder 的输出 ring buffer、codec code 队列、talker 生成速率和请求 admission 都要有有限上限。无限队列只是把网络问题延后转换成 HBM、主机内存或磁盘耗尽。
8. 双工:输入与输出会同时改变运行中的状态
半双工语音交互是“先收完一轮输入,再生成一轮输出”。全双工则允许用户继续说话或持续发送视频,同时系统仍在输出语音和文本;新的输入可能改变是否继续说、如何转向或是否取消未播放部分。这不是在两个 WebSocket 上各开一次普通生成,而是带时间轴、交错和中断语义的会话执行。
Qwen3-Omni 报告并公开了 Thinker–Talker、多 codebook 和实时语音输出方向;MiniCPM-o 4.5 的官方说明把实时音视频输入与文本/语音输出不互相阻塞、按毫秒时间线同步的 TDM 机制作为模型能力;Moshi 是以流式 Mimi codec 和多流语音对话为代表的另一条路径。这些模型图不相同,runtime 不能把它们都简化为 ASR -> LLM -> TTS。
在系统合同中,双工至少需要:输入 watermark,输出播放水位,允许打断的边界,尚未播放输出的撤销策略,以及 session 的单一所有者。模型也可能只能在 chunk 边界响应新输入;这时应公开该粒度,不能承诺样本级即时打断。
9. 正确性:内容正确之外,还要正确地交付和停止
推理质量属于模型层;系统正确性则至少包括以下不变量。第一,任何 stage 只能消费与自己 schema、版本和请求身份匹配的 payload。第二,因果生成不能读取未来输入 chunk,codec 和缓存不能跨会话串用。第三,终端结果只能在所有必需分支完成或按合同失败后发出。第四,取消、超时和故障必须最终传到所有仍拥有该请求状态的执行者。
一个可操作的状态机可以有 ACCEPTED、RUNNING、STREAMING、CANCELLING、COMPLETED 和 FAILED。阶段局部的成功不等于请求完成;例如 talker 已生成 code,但 vocoder 失败时,最终语音对象仍不可声明成功。反过来,文本支路可在语音支路继续运行时先交付,前提是 API 明确这是一份部分结果。
10. SLO:端到端延迟不是一个 token/s 数字
Omni 工作负载应把 SLO 分成排队、首结果、稳定输出和完成四类。常用测量包括请求接受到首文本 delta 的 TTFT、接受到首个可播放音频片段的 TTFAS、图片/视频首个可显示对象的 TTFM,以及全部指定输出完成的 JCT。对音频还应记录播放侧 underrun 和 chunk inter-arrival jitter;模型端 token/s 不覆盖用户实际听到的语音连续性。
这个式子是系统分解,不是某篇论文的性能模型。它的价值是迫使评测报告指出:模型、输入长度和媒体分辨率是什么,batch 和并发是什么,硬件与网络是什么,首结果还是全完成在计时。不同模型、不同输出模态和不同运行时的数字不能脱离这些条件横向比较。
11. 速率匹配、容量与 admission
每条边都有生产速率和消费速率。令阶段 \(i\) 的有效产出速率为 \(r_i\),下游阶段 \(j\) 的有效消费速率为 \(r_j\)。在持续输入下,若 \(r_i>r_j\),两者之间的 backlog 会增长;若队列有容量 \(C\),背压至少需要在其填满前传回上游。不同单位不能直接比较,例如每秒 text token、每秒 codec frame 和每秒 latent step 必须借助“每个用户输出需要多少单位”的转换关系。
admission 因而至少需要为活跃请求预留各 stage 的状态上界:AR 的 KV、媒体特征、未消费 codec 缓冲、diffusion latent、通信 workspace 和输出缓冲。只按请求数限流会把短文本问答和长视频加语音生成视为相同负载,最终让尾延迟和 OOM 同时失控。
12. 一个可审查的请求走读
考虑“上传一段带画面的音频,要求用文字回答并朗读回答”的请求。规范化层先绑定视频与音频的时间范围,解码/encoder stage 产生特征;prompt assembly 将特征的位置、文本指令和会话上下文交给 thinker。thinker 可先流出文字,同时把需要的控制信息或隐藏状态交给 talker;talker 生成音频 code,codec/vocoder 将 code 变为带时间戳的音频 chunk。任何一个箭头都应能回答是谁生产、谁消费、何时释放和失败后谁负责清理。
这张走读图故意不写死 Qwen3-Omni、MiniCPM-o 或 Moshi 的内部 tensor 名称。模型 A 可能把 thinker/talker 放在同一套权重周围,模型 B 可能在线 encoder 中维护流式状态,模型 C 可能没有独立文本 thinker。系统集成的第一步不是给它们套同一张模型图,而是从官方代码、配置和技术报告中抽出真实的 stage 合同。
13. Runtime 拥有什么,不拥有什么
runtime 应拥有请求生命周期、stage 拓扑、队列、批处理、内存和设备放置、跨阶段传输、流式事件、取消和观测。模型适配层应拥有 tokenizer、媒体 processor、模型权重、采样规则、codec 或 vocoder 的模型特定输入输出。应用层则拥有用户身份、业务提示词、持久化策略和对最终媒体的使用权限。边界清楚,才不会把模型内部 cache 当作跨租户会话记忆,或把一个 HTTP 断开误当作安全的取消。
同样,runtime 的“支持”必须写成模型版本、后端、设备和模式的组合。例如仓库列出某模型不等于其所有输出模态、流式模式和多机传输都经过端到端验证。下一页先判断模型图和状态,再进入后续页面的 vLLM-Omni 与 SGLang-Omni 源码路径。
14. 一手来源与证据边界
本页的系统抽象来自下列一手论文、官方仓库和官方开发文档。它们支持“阶段化、异构调度、流式与模型能力存在”的表述;并不自动证明任意模型、GPU、网络拓扑和线上负载下的性能或稳定性。
- vLLM-Omni: Fully Disaggregated Serving for Any-to-Any Multimodal Models,论文中的 stage graph 与全解耦 serving 抽象。
- vLLM-Omni 官方仓库,异构 pipeline、streaming、实验性 full-duplex 与支持矩阵应以固定版本核验。
- SGLang-Omni 官方仓库 与 官方架构文档,stage scheduler、inbox/outbox、relay 和 OpenAI 兼容接口的边界。
- Qwen3-Omni Technical Report 与 官方仓库,Thinker–Talker、多 codebook 与实时语音方向。
- MiniCPM-o 官方仓库,4.5 的端到端连接、TDM 全双工机制和公开限制。
- Moshi 论文 与 Moshi 官方仓库,流式 Mimi codec 与全双工语音对话参考实现。