1. 先确定它解决的是什么问题

普通多模态文本服务可以把图像、音频或视频编码为输入 token,随后仍由一个自回归模型输出文本。any-to-any 模型则可能在一次交互里先理解视频与音频,再生成思考文本、离散语音码,最后以声码器或扩散模型合成波形、图像或视频。不同阶段的计算形态、批处理方式、显存状态和设备偏好并不相同。

官方架构概览把 vLLM-Omni 的目标定义为支持图像、音频、视频和 action 等非文本输出,并把自回归(AR)与非自回归的 DiT 工作负载纳入同一服务框架。它并不宣称每一个多模态 checkpoint 都可无需适配地运行;模型须有对应的流水线定义、输入输出处理器和部署配置。

2. 六个组件的职责边界

从外向内看,入口层包括 OpenAI 风格 API、同步 Omni 和异步 AsyncOmni。异步入口把一个逻辑请求交给 AsyncOmniEngine;引擎再把控制面请求送进 Orchestrator。Orchestrator 维护各个 stage 的客户端和请求路由,而 AR stage 与扩散 stage 分别由对应执行客户端驱动。

官方图中的 OmniRouter 负责把模型及运行参数解析到合适的流水线;OmniConnector 则是数据面的抽象,用于搬运 embedding、hidden state、音频 chunk 等大对象。因而「收到 HTTP 请求」和「把上游 tensor 真正送到下游设备」是两条不同的路径,前者不能替代后者的带宽、所有权和回收设计。

控制面

入队、取消、stage 选择、输出完成判定和请求关联。它应传递小的元数据与通知,而非默认复制所有中间 tensor。

数据面

hidden state、编码结果、codec token 或扩散条件等载荷。它由本地共享内存或远端 connector 传送,性能和故障语义需单独验证。

3. 模型图与部署图不是同一个配置

模型流水线描述的是「哪些 stage 存在、边如何连接、何处为最终输出」;部署配置描述的是「每个 stage 用什么设备、并行策略、批量上限和 connector」。官方概览列出三类常见结构:以 DiT 为主并使用 AR 文本编码器的 Qwen-Image 类;以 AR 为主、DiT 负责生成的 BAGEL 类;以及把 AR 与 DiT/专用生成器串联的 Qwen-Omni 类。

vLLM-Omni 论文中 Qwen2.5-Omni 的媒体编码、Thinker、Talker 与 vocoder stage graph
论文原图:vLLM-Omni Figure 4 把 Qwen2.5-Omni 的媒体编码、Thinker、Talker 与 DiT vocoder 展开为可分别组批的 stage graph,并标出 hidden state 和 codec token 的交接。它说明框架的多阶段执行方式,不代表本页后面读取的 Qwen3-Omni 适配代码采用完全相同的字段和物理放置。

源码中的 pipeline registry 将已知模型类型映射到流水线配置,而不是运行时凭 checkpoint 名称猜测图结构。这个分层使同一图可在不同硬件上换部署策略,也意味着仅复制 YAML 并不能让未登记模型获得正确的中间表示、停止条件和输出处理。

4. 一个逻辑请求从 AsyncOmni 开始

AsyncOmni.generate() 是异步调用面的关键入口。调用者提交一个逻辑请求,并通过异步迭代取得最终输出;对扩散模型,一个逻辑请求仍只对应一个 prompt,但多个兼容的在途逻辑请求可以由调度器组成批次。这个约束避免把「应用层的一次生成」误解为「必须独占一个设备批次」。

应用程序
  -> AsyncOmni.generate(...)
  -> AsyncOmniEngine.add_request(...)
  -> 输入处理器 + OutputProcessor[0]
  -> Janus 请求队列
  -> Orchestrator
  -> stage 0 客户端
  -> 后续 stage 与最终输出队列
  -> AsyncOmni._process_single_result(...)
  -> async for result in generate(...)

上述顺序来自官方异步架构说明async_omni.py。它说明返回给应用的对象已走过该模型定义的终点,而不是每个 stage 的内部 token 流都直接暴露为用户输出。

5. 主进程、编排线程与 stage 进程

当前异步设计把用户侧主进程与 Orchestrator 线程分开,并用 Janus 队列跨同步/异步边界传递控制消息。每个实际计算 stage 则通过 StageEngineCoreClientStageDiffusionClient 与后台的 AR StageCoreProc 或 DiffusionEngine 通信;官方文档明确标注这些客户端使用 ZMQ。

因此,单个 Python API 调用不表示全部工作都在同一事件循环或同一 GPU 上进行。排查卡顿时,至少应分别观察:入口是否入队、编排器是否取到请求、stage 是否接受请求、上游是否产出、路由是否把输出交给下游、最终队列是否被消费。把这些状态混成一个「模型慢」指标会掩盖队列背压和传输等待。

6. Orchestrator 的轮询与路由

orchestrator.py 持有 stage pool,并先把新请求投给 stage 0。它在编排循环中轮询已运行 stage:语言模型路径调用异步输出获取,扩散路径使用非阻塞读取;拿到结果后交给对应 output processor,再根据流水线边调用 _route_output_forward_to_next_stage

这是一种按 stage 分治的调度结构,不是把整个端到端图压成一个单一 vLLM 请求。优点是每个 stage 可以有各自的 engine 和批处理策略;代价是阶段间的消息、张量存放和请求关联成为显式系统问题。若下游未就绪,上游输出的暂存位置与回压策略就会影响端到端尾延迟。

7. Qwen3-Omni 的三阶段实例

在该 tag 的 Qwen3-Omni pipeline 中,stage 0 是 Thinker:它是多模态 AR stage,拥有 tokenizer,并可产生最终文本。stage 1 是 Talker:它从 Thinker 接收信息,以 AR 方式生成语音相关的 codec token。stage 2 是 Code2Wav:它把来自 Talker 的码流变为最终音频。

输入(文本 / 图像 / 音频 / 视频)
  -> Thinker(stage 0,LLM_AR)
      -> 最终文本
      -> Thinker2Talker 中间表示
  -> Talker(stage 1,LLM_AR)
      -> codec token
  -> Code2Wav(stage 2,LLM_GENERATION)
      -> 最终音频

这张调用链不是模型结构论文的抽象重画,而是该版本注册的服务流水线。它也解释了为什么同一个用户提示可能有文本与音频两条产物路径,以及为什么一个 stage 的完成并不必然等于整个逻辑请求完成。

8. 中间表示如何进入下游

Qwen3 输入处理器显示了这不是仅转发文本 token 的接口。Thinker 到 Talker 的完整负载包含第 0 层 embedding、第 24 层 hidden state、token id、说话人和语言相关信息;异步路径会把负载 detach 到 CPU,再交由传输与下游处理。Talker 到 Code2Wav 的处理器则收集 codec 输出并按配置组织 chunk。

这些字段是 Qwen3 适配实现的事实,不应外推为任意模型都需要第 24 层隐藏状态。正确的工程做法是把「模型特定的语义」保留在 stage input/output processor,把「如何交付该载荷」保留在 connector 和编排器中,避免把模型协议硬编码进通用队列。

9. async_chunk:重叠,而非无成本并行

该版本支持 async_chunk。对 Qwen3 的异步 Thinker-to-Talker 路径,处理器可以让 Talker 先得到用于准备的 prompt bookkeeping,而较大的上游载荷随后经 connector 交付。adapter 中的 construct_next_stage_streaming_input_prompt 会更新 prompt 记录与 block hash,用于下游的异步预热。

因此 async_chunk 的语义是尝试让下游准备工作与上游持续生成重叠,而不是承诺零拷贝、零等待或任何模型都能逐 token 流式消费。是否收益取决于 chunk 大小、连接器、下游 prefill、可用 KV 空间和模型处理器;应在目标模型与硬件上测量首个音频/视频片段以及最终完成时间。

10. 最终输出与取消不能只看 stage 0

OutputProcessor 既处理 stage 输出,也决定是否将其作为中间结果路由还是写入最终队列。异步入口的最终 handler 从面向请求的输出队列读取结果,再通过 _process_single_result 交给调用者。因而取消、超时和客户端断开都必须沿逻辑请求关联传播到仍在运行的 stage;只停止 Thinker 不能自动证明后面的语音或扩散作业已被释放。

公开架构文档展示了这种端到端控制流,但不提供一份跨所有 connector、所有模型和所有失败点的 exactly-once 或事务性保证。部署方应把重复输出、延迟到达的 chunk、下游拒收和取消竞态当作需要集成测试的状态,而非由 API 形状自动消除的问题。

11. 实验性全双工走的是另一条扩展路径

v0.26.0 将 MiniCPM-o 4.5 full duplex 标为 experimental preview。其 设计文档给出的链路是 WebSocket、会话处理器与 DuplexSession,经 DuplexRequestClient 和控制面进入扩展后的 StagePool,再经 MiniCPM stage、TTS/Token2Wav 和 realtime 投影回浏览器。

该文档只把 stage 0 的会话 KV 连续性、stage 1 TTS/Token2Wav、模型拥有的 listen/speak 决策、连续 PCM 与播放确认列为当前范围。它明确未承诺调度器原生的 KV append、确定性 VAD 打断、生产级多会话公平性与恢复、长会话有界 KV、视频输入或 A/V 同步。不要把 preview 的状态图当成生产会话 SLA。

12. 从哪几个文件开始读源码

阅读顺序可按控制流展开:先看 入口 了解用户拿到什么;再看 AsyncOmniEngine 和 Orchestrator 了解队列与循环;随后看 StagePool、Qwen3 pipeline 和 input processor,最后检查 connector adapter。这样能避免从某段模型适配代码误推全局调度语义。

下一章把这些结构落到调度、KV、connector 和扩散执行模式上:哪些状态可以跨 stage 交接,哪些仅是当前实现的 best-effort 优化,以及哪些并行组合在官方文档中明确不兼容。

13. 一手来源

来源本页用途
v0.26.0 releasetagged source版本、发布日期与实验性特性边界。
Architecture Overview组件、模型类型与总架构。
Async Omni Architecture请求队列、线程/进程和异步输出流程。
Qwen3 pipelineinput processor三阶段和模型特定中间表示。
MiniCPM-o full-duplex design实验性会话链路及明确未覆盖的能力。