执行摘要
本 PR 为 Qwen 迄今最大的开源模型 Qwen3.8-2.4T-A95B 新增配置驱动的 cookbook 页面,覆盖 NVIDIA(H200/B200/B300/GB300)与 AMD(MI300X/MI350X/MI355X)7 平台共 18 个启动配方,并将投机解码的 NEXTN 与 DSpark 策略纳入其中。除了新增页面(+1305 行),还做了两处刻意且必要的配套改动:扩展 Playground 引擎使 PD 角色可携带专属 flags/env(_playground.jsx +18),以及修复 cookbook 脚手架模板的 router 端口硬编码缺陷。全 PR 不触碰模型或推理代码,属于纯文档与文档工具链变更,但因其部署矩阵的规模和架构特殊性(GDN 线性注意力主导),对用户部署决策有直接且重要的指导价值。
功能与动机
PR body 的 Motivation 指出:Qwen3.8-2.4T-A95B 没有 cookbook 页面,而它是 Qwen 最大的开源权重模型(2.4T 总参 / 95B 激活,92 层由 23 组『3 × (Gated DeltaNet → MoE) → 1 × (Gated Attention → MoE)』构成),部署方式与已有页面覆盖的稠密注意力模型完全不同:
- 三分之二的层是线性注意力,限制并发的不是 KV cache,而是 GDN 循环状态池;
- 2.4T 参数下 FP8 无法装入页面中任何平台的单节点,拓扑受权重体积决定;
- 推理不可关闭,必须显式携带 reasoning parser。
因此该页面不是对既有模板的简单复制,而是针对混合线性注意力架构重新设计的部署指南。
实现拆解
-
新增模型配置数据(核心)
docs/src/snippets/configs/Qwen/qwen3.8.jsx(+939)是整页数据源:定义 4 种量化(BF16/FP8/NVFP4/MXFP4)× 4 策略(Low Latency/Balanced/High Throughput/DSpark)× 4 档节点数的部署矩阵、各平台 Docker 镜像(含 ROCm 7.00/7.20 区分)、多节点 IB/NCCL 绑定提示,以及 Playground 各卡片能力——Attention 并行(TP 到 64、DP-Attention 到 64)、MoE backend(DeepEP/MegaMoE/FlashInfer MXFP4/Marlin)与 WideEP(EP 到 64)、解析器(qwen3 / qwen3_coder)、投机解码(EAGLE/NGRAM/DSpark)、PD 分发(Mooncake/NiXL)与层级 KV cache。关键设计是 DSpark 选项的 disable 条件与 _handle_dspark(python/sglang/srt/arg_groups/speculative_hook.py)的硬约束一一对应。
-
扩展 Playground PD Disagg 引擎
docs/src/snippets/_playground.jsx(+18)允许 modes[] 条目声明角色专属 flags / env(如 prefill 的 --load-balance-method、decode 的轮询间隔)。新增符号 modeMeta 在角色分支内查找当前角色,先按 flag 头剥离 base cell 同名 flag 再追加,env 按值去重。由于 applyAllDeltas 每次 render 从 baseFlags 重新 seed,该改动对现有 8 个配置是 no-op。
-
新增 cookbook 页面并注册
docs/cookbook/autoregressive/Qwen/Qwen3.8.mdx(+315)承载架构介绍、配置要点(GDN 状态槽比例 1/3/5、PP 关闭 overlap scheduler 时为 4;NEXTN 48 并发默认;SM90 与 SM100 线性注意力后端差异)并嵌入 Deployment/Playground 组件。docs/docs.json 注册页面,intro.mdx 卡片链接转向 Qwen3.8,Qwen3.6.mdx 移除 tag: NEW。
-
修复脚手架模板端口硬编码
.claude/skills/cookbook-add-model/templates/config.jsx.tmpl 原先硬编码 router 的 prefill/decode 端口 30000/30001,而引擎 decode 实际监听 30100;改为 {{PREFILL_PORT}} / {{DECODE_PORT}} / {{ROUTER_PORT}} 占位符,并在 authoring-reference.md 中明确『字面端口不会跟随引擎』的告诫。
-
测试与校验配套
无模型/内核代码改动,无对应测试文件;作者声明 mint validate 与 mint broken-links 均通过;benchmark 以空桩形式发布(页面显示 pending),避免未经验证的性能数字上页面,同时 benchmarkCommands 提供复现命令供用户自行测量。
docs/src/snippets/configs/Qwen/qwen3.8.jsx
整页部署配置的核心数据源(+939 行),定义 7 平台 × 4 量化 × 4 策略的 18 个 launch recipe,以及 Playground 各卡片能力、DSpark 约束建模、PD 角色 flag 声明。
// ----- Card: "Speculative Decoding" -----
// DSpark 是训练好的草稿模型,替换 NEXTN 作为独立 strategy 芯片,
// 而不是吞吐 / 延迟曲线上的另一个档位(不同解码器,不是不同操作点)。
speculative: {
options: [
{ id: "current", label: "Inherited from base" },
{ id: "off", label: "Off (greedy)" },
{ id: "mtp", label: "EAGLE / MTP",
flags: ["--speculative-algorithm EAGLE", "--speculative-num-steps 3",
"--speculative-eagle-topk 1", "--speculative-num-draft-tokens 4"] },
// _handle_dspark(python/sglang/srt/arg_groups/speculative_hook.py)
// 在约束不满足时直接 raise 而不是降级:pp_size 必须为 1,
// DP-Attention 下还要求 --enable-dp-lm-head、--moe-a2a-backend none
// 且无 context parallel。因此下面的 disable 条件与 hook 的硬约束
// 一一对应,保证 Playground 不会生成注定启动失败的命令。
{ id: "dspark", label: "DSpark",
flags: ["--speculative-algorithm DSPARK",
"--speculative-draft-model-path RadixArk/Qwen3.8-Max-DSpark"],
disable: [
{ when: { dpAttnOn: [true] },
reason: "DSpark with DP-Attention additionally requires " +
"--enable-dp-lm-head, the built-in TP MoE " +
"(--moe-a2a-backend none) and no context parallel." },
{ when: { hw: ["h200", "mi300x"] },
reason: "DSpark requires pp_size == 1 and this recipe is " +
"pipelined (TP x PP across nodes)." },
{ when: { hw: ["b200", "b300"], quant: ["fp8"] },
reason: "DSpark requires pp_size == 1 and the B200/B300 FP8 " +
"recipes are TP8 x PP2." },
] },
{ id: "ngram", label: "NGRAM",
flags: ["--speculative-algorithm NGRAM",
"--speculative-num-draft-tokens 16",
"--speculative-ngram-max-bfs-breadth 10"],
disable: { dpAttnOn: [true] } },
],
},
docs/src/snippets/_playground.jsx
共享渲染引擎的唯一源码改动(+18),让 modes[] 条目可携带角色专属 flags/env,是本次唯一影响所有既有 cookbook 页的变更点。
// 确定当前 PD 角色:config 声明了 modes[] 时以 Playground 选中值为准,
// 否则回落到 Deploy 面板上的 pdMode(该角色在 Deploy 面板选择)。
const mode = (fc.modes || []).length ? value.mode : ((sel && sel.pdMode) || "off");
if (mode === "prefill" || mode === "decode") {
const backend = value.transferBackend || (backends[0] || {}).id || "mooncake";
const adds = [
`--disaggregation-mode ${mode}`,
`--disaggregation-transfer-backend ${backend}`,
];
// 回填 bootstrap 与 IB device flag(顺序与 base cell 保持一致)。
if (bootstrapPort) adds.push(`--disaggregation-bootstrap-port ${bootstrapPort}`);
if (value.ibDevice && value.ibDevice !== "auto") {
adds.push(`--disaggregation-ib-device ${value.ibDevice}`);
}
// 角色专属 flags:modes[] 条目可声明只属于 prefill / decode 的 flags
//(如 prefill 的 --load-balance-method、decode 的轮询间隔)。先按
// flag 头剥离 base cell 中同名 flag,让角色专属值胜出而不是重复输出。
// 该剥离只发生在角色分支内:applyAllDeltas 每次 render 从 baseFlags
// 重新 seed,角色关闭时不存在残留 base flag 需要清理。
const modeMeta = (fc.modes || []).find((m) => m.id === mode);
if (modeMeta && modeMeta.flags && modeMeta.flags.length) {
flags = h.stripFlagsByFirstToken(
flags, modeMeta.flags.map((f) => f.split(/[\s=]/)[0]));
adds.push(...modeMeta.flags);
}
// 单机无需 --dist-init-addr:prefill / decode 按 PD_PORTS 从各自
// --port 推导 ZMQ 端口(间隔 100),范围不重叠;多机仍由渲染器
// 注入跨节点 --dist-init-addr。
flags = h.insertBeforeTail(flags, adds);
// 角色专属 serving 端口,使 router 的 prefill / decode 目标对齐。
const servePort = PD_PORTS[mode].serve;
flags = flags.map((f) =>
f.split(/[\s=]/)[0] === "--port" ? `--port ${servePort}` : f);
// 后端 env 按 envWhen 硬件门控追加,保留 base cell 原有 env。
const meta = backends.find((b) => b.id === backend);
if (meta && meta.env && meta.env.length) {
const gate = meta.envWhen;
const ok = !gate || Object.keys(gate).every(
(k) => (gate[k] || []).includes(sel[k]));
if (ok) env = [...env, ...meta.env.filter((e) => !env.includes(e))];
}
// 角色专属 env 同样按值去重;apply 是纯函数、每次从 baseEnv 重建,
// 不会跨 render 残留上一个角色的 env。
if (modeMeta && modeMeta.env && modeMeta.env.length) {
env = [...env, ...modeMeta.env.filter((e) => !env.includes(e))];
}
}
return { flags, env };
评论区精华
该 PR 无独立 review 评论,两位 reviewer(JustinTong0323、wisclmy0611)均直接 APPROVED。最有价值的讨论来自作者在 PR body『Notes for reviewers』中主动澄清的设计决策:
『模板硬编码 30000 / 30001 而引擎 decode 服务在 30100,字面端口会让 router 打向 decode 进程从未监听的端口。』—— 这是本次唯一被顺带修复的真实缺陷,而非风格问题。
『dspark 是模型专属策略 ID,因为 DSpark 是另一套投机解码器,不是吞吐/延迟曲线上的不同操作点。』—— 解释了为什么它在策略矩阵中作为第四块芯片而非第四档。
『GB300 Balanced 和 High Throughput 档的 --moe-a2a-backend deepep_v2 上游尚不存在。』—— 明确标注了与上游发布的时序耦合。
『--kv-cache-dtype fp8_e4m3 是逐格携带而非页级默认;B300 NVFP4 和 GB300 BF16 刻意以模型精度服务 KV。』—— 避免一刀切默认值误导不同硬件组合。
风险与影响
风险:
- 共享渲染引擎回归风险:
_playground.jsx 被所有 cookbook 页面共用,本次 +18 改动无对应测试;作者声明对现有 8 个配置是 no-op 且逐一核验,但后续 config 结构变更若触发 modeMeta 分支,可能静默改变所有页面的命令输出。
- GB300 配方依赖未发布 flag:
--moe-a2a-backend deepep_v2 上游尚不存在,旧版本 SGLang 上直接运行 Balanced/High Throughput 命令会失败。
- 基准数据全部 pending:18 个 benchmark cell 均为空桩,用户无法据此对比硬件/量化选型。
- Docker 镜像 tag 依赖发布时序:
lmsysorg/sglang:qwen38 与 ROCm 镜像 tag 为发布期 pin,镜像未同步推送时 docker pull 会失败。
- 脚手架模板修复的传播范围:端口占位符改动影响后续所有新建 cookbook,但已按旧模板生成的页面需逐个确认是否仍携带 30001 字样。
影响:
- 用户:获得 Qwen3.8 的 day-0 部署配方,尤其是 GDN 状态池作为并发瓶颈的配置指引(状态槽比例、NEXTN 并发默认、SM90/SM100 差异)是此前页面从未覆盖的架构维度。
- 团队:脚手架 skill 的端口缺陷修复使后续所有模型页面生成受益;Playground 引擎能力扩展为 PD 角色专属 flag 提供了标准表达位置。
- 系统:纯文档变更,无推理路径、无运行时影响;
_playground.jsx 的幂等 delta 设计保证对既有页面零行为变化。
关联脉络
- PR#34590(Qwen3.8-Max-DSpark 重命名为 Qwen3.8-2.4T-A95B-DSpark)与本 PR 同属 Qwen3.8 文档线:本页 DSpark 配方仍引用
RadixArk/Qwen3.8-Max-DSpark,后续需随重命名同步更新 repo id。
- PR#34379 / #34497 展示了同一条 config 驱动 cookbook 工作流(MDX 页面 +
snippets/configs 配置 + docs.json 注册)在 GLM、Cosmos 等模型上的持续复用,本 PR 还回馈了工作流本身——修复模板端口并扩展 Playground 契约。
- 结合近期多个 diffusion cookbook 与 AMD/NPU 支持 PR,可见 SGLang 正在把『配置驱动的部署指南』沉淀为标准化交付物,并让脚手架与渲染引擎随模型需求持续演化。
参与讨论