Skip to main content

01 - SGLang-Omni 总览

把听、想、说拆成三个进程之后,会凭空长出五个跟模型无关的新问题 —— 本章索引那张图列了它们。这一篇给出解法的全景。

先看骨架。六层,从 HTTP 一路到模型前向:

判断一层归谁管,只用一条判据:它认不认识模型

职责认识模型吗换个模型要不要改
HTTP / WebSocketOpenAI 兼容的请求响应格式、SSE 分帧、错误码不改
Client把 HTTP 请求降成内部请求、聚合结果、编码音频不改
Coordinator请求生命周期、投给入口阶段、合并多个终点、广播中止不改
Stage收发控制消息、读写 relay、扇入、流路由不改
Scheduler批选择、KV cache、把活分发下去部分选一种,不写新的
ModelRunner前向、采样、模型特有钩子只改这里

只有最下面一层认识模型 —— 这是整套设计的支点。接一个新模型时你写的代码全部落在那一层加它的配置里,上面五层一行不动。

一、它做什么,不做什么

先划清楚边界,不然很容易把它当成「又一个推理引擎」,然后拿它跟 vLLM 比吞吐 —— 那是两个层面的东西。

它不做的事:KV cache 怎么分页、一个批里该挑哪些请求、显存不够时踢谁、怎么把解码步录成 CUDA Graph。这些是推理引擎的活,SGLang 已经做得很好,它直接拿来用。

它做的事:上面开场列的那五个问题。流水线长什么样、每个阶段什么时候生、张量怎么在阶段之间搬、新模型怎么接进来、请求怎么进来结果怎么出去。

用一句话概括就是:它管编排,不管单段怎么算。下面这张图把两边的分工摊开。

SGLANG-OMNI 自己拥有编排层TOPOLOGY流水线拓扑与阶段生命周期TRANSPORT阶段间的控制面与数据面INTEGRATION模型接入层与自动注册SERVINGOpenAI 兼容端点与路由器SCHEDULERS三种调度器:自回归 / 无状态 / 流式声码器接合面直接复用 SGLANG执行层BATCHING批选择与连续批处理MEMORYKV cache 分页与抢占CACHEradix 树缓存与前缀复用GRAPHCUDA Graph 捕获与回放RUNTIME模型执行、权重加载、注意力后端分界线的位置是刻意选的:凡是「一个自回归引擎内部的事」都归 SGLang,凡是「多个引擎之间的事」都归 SGLang-Omni。这条线一旦模糊 —— 比如开始读写 SGLang 调度器的内部属性 —— 组合就变成了事实上的分叉,升级成本会失控。10 篇有三条具体纪律。
右侧那五格是过去几年 LLM 推理优化的全部积累。SGLang-Omni 的核心判断是:语音和 omni 模型的自回归段跟文本 LLM 同构,所以这些积累不该重写一遍,该原样接上。

二、数据在各层之间变成了什么

上面那张图画的是「谁调用谁」。但读代码时更容易懵的是另一件事:同一份数据,在每一跳上叫什么、长什么样

一个请求在链路上换了八次形态HTTP JSON文本 + 参考音频 URL校验GenerateRequest校验过的内部请求降级OmniRequest带 request_id,协调器认它投递StagePayload阶段间唯一流动的容器Scheduler 组批ForwardBatch模型真正吃的东西前向码本 id 张量[帧数, 码本数]跨阶段波形张量24 kHz float编码PCM / WAV 字节或 SSE 分片胶囊形状表示「流过的数据」,不是模块。红棕那一格是身份的起点:request_id 从 OmniRequest 开始固定,一路带到最后。码本 → 波形那一跳可能跨卡:Qwen3-Omni 上是 GPU 1 到 GPU 1,别的模型可能是 GPU 0 到 GPU 1,走哪条通路由拓扑推导。青绿三格都是张量形态 —— 只有它们需要走 relay 或 CUDA IPC,其余几格是普通对象,跟着控制面走。
这条链原来画成一张 2052×83 的横排 Mermaid 图,在正文列宽下会被压成一条 39px 高的线,只能横向滚动才看得清。改成折成两行的胶囊链之后,同样八个环节在 980×268 里放得下。

三个容易懵的点,看这条链就清楚了:

  • StagePayload 是阶段之间唯一流动的东西。 它是个容器,张量挂在它的 data 里。跨进程时张量被抽出来走 relay、剩下的部分序列化走控制面 —— 04 篇讲这个拆包过程。
  • request_idOmniRequest 开始就固定了,一路带到最后。中止、清理、缓存键全靠它,所以任何以它为键存在阶段之外的东西都必须被清理干净(06 篇的三条竞态)。
  • 码本变波形那一跳可能跨卡。上图里 D6 到 D7 那条边,在 Qwen3-Omni 上是 GPU 1 到 GPU 1、在别的模型上可能是 GPU 0 到 GPU 1,走哪条通路由拓扑推导。

还有一条平行的线:请求本身的状态。协调器给每个 request_id 记一个状态,它跟数据流是两回事:

全部终点都报完成」这一条是 omni 特有的:Qwen3-Omni 有文本和音频两个出口,任何一个没报完成,这个请求就还不算完。纯文本服务只有一个出口,压根不需要这个概念。

三、一个请求的完整旅程

官方给了一张把五层和三个阶段画在同一张图上的端到端图,用的是最简单的 Fish Audio S2-Pro 三阶段 TTS 流水线。左列是请求生命周期(HTTP → Client → Coordinator 再回来),右列是三个阶段,每个阶段标出它的调度器、模型运行器,以及阶段之间「ZMQ 走控制信号、relay 走张量」的分工:

SGLang-Omni 中一个 /v1/audio/speech 请求经过 Client、Coordinator 与三个阶段的端到端流程
出处:sglang-omni docs/design/diagram/s2pro_example_diagram.svg。图里 Request flow / ZMQ (signals) / Relay (tensors) 三种线型是理解这套架构的关键 —— 控制和数据走的是两条完全独立的通路。

按这张图把五层的职责写清楚,重点是每一层认不认识模型

职责认识模型吗细讲
HTTP / WebSocketOpenAI 兼容的请求响应 schema、SSE 分帧、HTTP 错误06 篇
ClientGenerateRequestOmniRequest、结果聚合、音频编码06 篇
Coordinator生命周期、入口阶段投递、终点结果收集、中止广播02 篇
Stage控制面 IO、relay 读写、扇入、流路由、调度器收发桥接02 篇
Scheduler每个阶段的执行循环与失败传播部分03 篇
ModelRunner前向准备、前向分发、输出抽取03 篇

「Stage 不认识模型,也不认识调度器类型」是最关键的一条不变量。 Stage 的代码里没有任何 if isinstance(scheduler, OmniScheduler) 这样的分支 —— 三种调度器对外呈现完全相同的接口。把分支收敛在这一层,是这套架构能不断加新模型而不塌的原因。

四、四个模型的流水线形状

「多阶段」到底长什么样,看四个真实模型的对照最直接。行是阶段类别,列是模型,格子里是这个模型在这一类上的实际阶段名与调度器:

复杂度从上到下渐进,而每多一个阶段都对应一个具体需求

模型比上一个多了什么为什么必须多这一段
Fish S2-Pro最小形状预处理、生成码本、还原波形,三段是下限
Higgs Audio v3多一个 audio_encoder参考音频要跑多码本编码,重到值得单独占一次调度
MOSS-TTS-Local阶段数没变复杂度在阶段内部:主干与 1+12 微步分别捕 CUDA Graph
Qwen3-Omni多五个三种模态(两个塔+扇入)、两个自回归段串联、同时吐文本和音频

三类阶段的调度器类型是固定的:预处理与编码器用 SimpleScheduler(无 KV cache),自回归引擎用 OmniScheduler(有 KV cache),声码器用批量 SimpleScheduler 或流式 Code2WavScheduler(无 KV,但流式有跨块状态)。

最下面那个 Qwen3-Omni 有两个终点,是纯文本服务里完全不存在的东西 —— 协调器必须能等两条终点都完成。这一条需求单独撑起了 Coordinator 这一层。

五、目录地图

源码按「框架层」和「模型层」严格分开,接新模型时只碰后者:

sglang_omni/
├── pipeline/ # 阶段编排、Stage、Coordinator、多进程运行器 ← 02 篇
├── scheduling/ # 三种调度器循环与收发消息类型 ← 03 篇
├── model_runner/ # AR 阶段的模型运行器基类与共享实现 ← 03 篇
├── comm/ relay/ # 传输选择与各后端实现 ← 04 篇
├── config/ # PipelineConfig、StageConfig、拓扑与放置 ← 02 · 05 篇
├── serve/ client/ # HTTP 服务与 OpenAI 兼容适配 ← 06 篇
├── proto/ # 请求、载荷、阶段、控制面消息类型
└── models/ # 每个模型一个子包,自动发现 ← 06 篇

下一篇02 - 流水线与阶段