MiaAI-Lab/DeepSeek-v4-Flash-DSpark-2x-DGX-Spark

GitHub: MiaAI-Lab/DeepSeek-v4-Flash-DSpark-2x-DGX-Spark

该项目提供了一套在 2x DGX Spark 集群上部署 DeepSeek-V4-Flash 大模型并启用 100 万上下文窗口的 vLLM 配置与基准测试方案。

Stars: 191 | Forks: 23

# 在 2x DGX Spark 上部署 DeepSeek V4 Flash DSpark C12 NVFP4 KV 用于服务 `DeepSeek-V4-Flash-DSpark` 的独立双节点 DGX Spark 部署方案 采用 vLLM TP=2、DSpark speculative decoding,并使用实验性的 `nvfp4_ds_mla` KV-cache 路径,实现 **1M-token** 的默认最大模型长度。

Follow Mia on X

Buy Me a Coffee at ko-fi.com

## 当前运行环境(当前检出版本) 默认 Docker 镜像是预构建的 Anemll GX10/DGX Spark 版本的 vLLM 0.25,原生支持 DSpark / NVFP4 DS-MLA / b12x MoE: ``` ghcr.io/anemll/dspark-vllm-gx10:0.1.1 ``` 来源:[Anemll/dspark-vllm-gx10](https://github.com/Anemll/dspark-vllm-gx10)。 在首次启动前,在**两个**节点上拉取镜像: ``` docker pull ghcr.io/anemll/dspark-vllm-gx10:0.1.1 ``` `docker-compose.dspark.yml` 与该镜像布局保持一致: - 清除了 entrypoint;command 使用 `/usr/local/bin/vllm serve` - CUDA 位于 `/usr/local/cuda` 下(而不是 Stage-C 的 `/opt/env`) - `--moe-backend flashinfer_b12x` - DSpark **内置于镜像中**(无需将 Stage-C 的 `dspark_proposer.py` 通过 bind-mount 映射到 `/opt/env/...`) - 可选的 `vllm_patch_gb10/` 挂载仍保留,用于实验性的混合 NVFP4 - HF cache 位于 `/cache/huggingface`;一旦两个节点都有了完整的本地 hub cache,首选设置 `HF_HUB_OFFLINE=1`(在线重新下载可能会占满 worker 磁盘) 备选方案:设置 `DSPARK_VLLM_IMAGE=vllm-dspark-runtime:dspark-nvfp4-stage-c` 并运行 `./build-dspark-vllm-runtime.sh` 来进行历史多阶段 Stage-C 覆盖构建。Stage-C 方案和覆盖源码保留在 `recipe/` 下。 使用 Stage-C 时,还需要合并 `docker-compose.stage-c.override.yml` 并在 `.env.dspark` 中启用 Stage-C 环境变量块(参见 [`docs/ENVS.md`](docs/ENVS.md))。 本仓库依然内置了 Keys 的 DSpark 并发补丁和 Stage-C 覆盖源码,用于本地镜像构建和文档记录。使用 Anemll 镜像时,该逻辑直接内置在镜像中,而不是作为主机的 bind-mount。 **默认的 agent 服务配置**(`.env.dspark.example` 和 README 默认值): - 镜像:`ghcr.io/anemll/dspark-vllm-gx10:0.1.1` - 模型:`deepseek-ai/DeepSeek-V4-Flash-DSpark`(HF hub id;当设置 `HF_HUB_OFFLINE=1` 时从缓存离线解析) - `max_model_len=1048576`(**1M** —— 保留此作为文档化的默认值) - `max_num_seqs=6` - `max_num_batched_tokens=8192` - `kv_cache_dtype=nvfp4_ds_mla` - `gpu_memory_utilization=0.85` - `MTP_NUM_TOKENS=3` - API 绑定地址 `0.0.0.0:8888` 本地 `.env.dspark` 可以为特定的集群调低 `MAX_MODEL_LEN`(例如 `512000`),而无需更改方案默认值。 本仓库记录了经过验证的 1M NVFP4 agent 配置、历史的 Stage-C 检查点,以及当前的 Anemll 预构建运行环境: - 默认 `max_model_len=1048576` (1M),`max_num_seqs=6`,`kv_cache_dtype=nvfp4_ds_mla` - 默认镜像 `ghcr.io/anemll/dspark-vllm-gx10:0.1.1`(在此集群上约有 ~2.8M-token 的 KV pool) - 历史 Stage-C C12 pool:`3,225,280 tokens` - 在经过验证的 C12 门控上,单流解码速度保持在 `50 tok/s` 以上 - 确定性的直接提示词测试已完成,没有出现中文漂移或重复的垃圾内容 - 2/4/6 个并发的代码门控提示词均顺利完成 (Stage-C C12) - DSpark 并发补丁在 `max_model_len=200000`,`max_num_seqs=16` 下得到验证 (静态 C16 `315.1` / 交错式 C16 `205.0` tok/s 聚合) 如果您之前部署了旧版本并遇到了 agent 乱码、循环、中文漂移,或提示词/工具 XML 泄露到回复中的情况,请保留 C12 NVFP4 配置,并在更改 agent 框架设置之前验证直接的 API 行为。该修复路径不会将生产环境切换到 fp8 或更小的后备模型。 ## 结果 ### 实时 Anemll 镜像测试通道(当前检出版本) 使用预构建的 Anemll 镜像和本仓库的 compose/启动脚本(TP=2,双节点)验证了 worker 优先启动的方式。 运行环境: - 镜像:`ghcr.io/anemll/dspark-vllm-gx10:0.1.1` - 模型 ID:`deepseek-ai/DeepSeek-V4-Flash-DSpark`(位于 `HF_CACHE` 下的 HF cache) - 服务模型名称:可通过 `SERVED_MODEL_NAME` 配置(例如:`deepseek-v4-flash`) - `kv_cache_dtype=nvfp4_ds_mla` - 默认方案:`max_model_len=1048576`,`max_num_seqs=6`, `max_num_batched_tokens=8192`,`gpu_memory_utilization=0.85`,`MTP_NUM_TOKENS=3` - `--moe-backend flashinfer_b12x` - `VLLM_USE_FLASHINFER_SAMPLER=1`,`VLLM_USE_B12X_WO_PROJECTION=1` - 在两个节点都有完整的 hub cache 后,推荐设置 `HF_HUB_OFFLINE=1` - fabric:显式的 `VLLM_HOST_IP` / `WORKER_VLLM_HOST_IP`,以及匹配的 `NCCL_SOCKET_IFNAME` / `TP_SOCKET_IFNAME` / `GLOO_SOCKET_IFNAME` 在此集群上的启动证据(Anemll 镜像,1M max-model-len 配置): ``` Available KV cache memory: 19.03 GiB GPU KV cache size: 2,826,378 tokens Maximum concurrency for 1,048,576 tokens per request: 2.70x Application startup complete. ``` 直接 API 冒烟测试:`/v1/models` 返回 HTTP 200,并且在 head 和 worker 节点上均返回了非空的兼容 OpenAI 的 chat completions 内容。 ### 实际解码速度(第一个 token 之后) 在实时的 Anemll 通道上,使用 **agent / 文件写入**提示词(`max_tokens=512`,temperature 0,每个请求包含唯一的 nonce,3 次试验,按聚合取中位数)进行的流式纯解码基准测试。Prefill 和第一个 token 被**排除在外**。 | 指标 | 公式 | | --- | --- | | 单流解码 tok/s | `(completion_tokens − 1) / (t_last − t_first)` | | 聚合解码 tok/s | `sum(completion_tokens − 1) / (max t_last − min t_first)` | | 并发数 | 成功率 | 聚合解码 tok/s | 平均单流解码 tok/s | 解码窗口 (s) | 解码 tokens | | ---: | :---: | ---: | ---: | ---: | ---: | | 1 | 1/1 | 66.6 | 66.6 | 7.67 | 511 | | 2 | 2/2 | 93.3 | 47.2 | 10.95 | 1022 | | 3 | 3/3 | 92.8 | 31.9 | 16.52 | 1533 | | 4 | 4/4 | 123.8 | 32.8 | 16.51 | 2044 | | 5 | 5/5 | 121.1 | 25.7 | 21.11 | 2555 | | 6 | 6/6 | 153.7 | 26.8 | 19.94 | 3066 | 各次试验聚合结果(解码 tok/s):C1 `[66.5, 69.4, 66.6]`,C2 `[92.1, 95.6, 93.3]`, C3 `[89.4, 92.8, 93.5]`,C4 `[129.1, 123.8, 121.7]`,C5 `[125.0, 121.1, 111.6]`, C6 `[153.7, 148.8, 157.0]`。 **聚合解码 (Agg decode)** 是生成第一个 token 后的集群总生成速度;**平均单流 (mean stream)** 是 token 开始生成后单个并发聊天感受到的速度(单独运行约 67 tok/s,C=6 时约 27 tok/s)。 在多流竞争下,当单流解码速度下降时,C3 ≈ C2 且 C5 ≈ C4(在聚合层面上)。 ### 2026-07-02 Keys C12 NVFP4 检查点(历史 Stage C) 之前在 Tony 的 Stage C NVFP4 镜像上使用 Keys 的 C12 服务配置进行的高并发通道测试(保留用于对比;并非当前的默认镜像)。 运行环境: - 测试 endpoint:`http://100.90.25.78:8888/v1` - 服务模型:`deepseek-v4-flash-dspark` - 镜像:`vllm-dspark-runtime:dspark-nvfp4-stage-c` - 模型路径:`/cache/huggingface/fraserprice/DeepSeek-V4-Flash-DSpark` - `kv_cache_dtype=nvfp4_ds_mla` - `max_model_len=1048576` - `max_num_seqs=6` - `max_num_batched_tokens=8192` - `gpu_memory_utilization=0.85` - `MTP_NUM_TOKENS=3` - `VLLM_USE_FLASHINFER_SAMPLER=1` - `VLLM_USE_B12X_WO_PROJECTION=1` - `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1` - `thinking=false` - `--generation-config vllm` - 无 `--override-generation-config` 启动证据: ``` GPU KV cache size: 3,225,280 tokens Maximum concurrency for 1,000,000 tokens per request: ~3.2x Application startup complete. ``` 代码门控验证: | 并发数 | 成功率 | 服务器生成 tok/s | 接受率 | 错误输出 | | ---: | ---: | ---: | ---: | ---: | | 1 | 1/1 | 52.79 | 0.585 | 0 | | 2 | 2/2 | 79.76 | 0.600 | 0 | | 4 | 4/4 | 134.70 | 0.602 | 0 | | 6 | 6/6 | 127.78 | 0.615 | 0 | | 12 | 12/12 | 230.10 | 0.602 | 0 | 此运行的上游检查点说明未导入到当前检出中;本仓库保留了运行时的更改和验证摘要,但没有保留上游的 benchmark 产出文件夹。 请勿在此 Stage C 镜像上启用 `VLLM_USE_B12X_FP8_GEMM=1`。在测试中,该标志在 DSpark drafter 预热阶段触发了 DeepGEMM 布局断言错误。 ### 2026-06-30 干净的 Agent 服务检查点 在将模型重新交由 Hermes/OpenClaw 类框架处理之前,先在 Asusi/Spark4 上重现了之前保守且干净的 endpoint。 运行环境: - 测试 endpoint:`http://100.90.25.78:8888/v1` - 服务模型:`deepseek-v4-flash-dspark` - 该通道上使用的镜像:`vllm-dspark-runtime:mia-raf-pr1-nvfp4-keys-c` - 模型路径:`/cache/huggingface/fraserprice/DeepSeek-V4-Flash-DSpark` - `kv_cache_dtype=nvfp4_ds_mla` - `max_model_len=1048576` - `max_num_seqs=6` - `max_num_batched_tokens=8192` - `gpu_memory_utilization=0.80` - `MTP_NUM_TOKENS=5` - `thinking=false` - `--generation-config vllm` - `--override-generation-config '{"temperature":0.0,"top_p":1.0}'` - 每个节点显式指定 `VLLM_HOST_IP` 值 启动证据: ``` GPU KV cache size: 1,990,142 tokens Maximum concurrency for 1,048,576 tokens per request: 1.90x Application startup complete. ``` 直接验证: - `/v1/models` 报告 `"max_model_len": 1048576` - 确定性健全性提示词返回 `NVFP4 DSPARK OK` - 五个较长的英文提示词顺利完成,没有出现 CJK 漂移或重复的垃圾内容 - 代码门控服务器平均解码速度:`54.22 tok/s` - 2/4/6 个并发的直接提示词均干净地成功完成 并发情况: | 并发数 | 成功率 | 聚合 tok/s | 稳定性 | | ---: | ---: | ---: | --- | | 2 | 2/2 | 60.95 | 无 CJK/重复垃圾内容 | | 4 | 4/4 | 83.21 | 无 CJK/重复垃圾内容 | | 6 | 6/6 | 104.11 | 无 CJK/重复垃圾内容 | 此运行的上游检查点说明未导入到当前检出中。 ### 1M NVFP4 配置 在 2x DGX Spark 上验证,每个节点一个 GPU,TP=2,单流。 | 用例 | 服务器 tok/s | TTFC | 接受率 | 接受/草稿 | | --- | ---: | ---: | ---: | ---: | | p256/g64 | 54.46 | 0.506s | 0.667 | 3.33 | | p256/g256 | 65.38 | 0.324s | 0.718 | 3.59 | | p512/g64 | 56.26 | 2.738s | 0.625 | 3.13 | | p512/g256 | 54.41 | 0.422s | 0.550 | 2.75 | | p512/g256 warmup1 | 56.73 | 0.417s | 0.585 | 2.92 | 启动日志报告: ``` GPU KV cache size: 2,044,166 tokens Maximum concurrency for 1,048,576 tokens per request: 1.95x ``` API 报告: ``` {"max_model_len":1048576} ``` 此运行的上游检查点说明未导入到当前检出中。 ### DSpark 并发配置 在同样的 2x DGX Spark TP=2 部署上使用 Keys 的 DSpark 并发补丁进行验证,使用 `kv_cache_dtype=nvfp4_ds_mla`,`max_model_len=200000`, `max_num_seqs=16`,`MTP_NUM_TOKENS=5`,以及 `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1`。 补丁来源: - [drowzeys/Keys-Concurrency-Patch-for-DSpark-DeepSeek-V4-Flash](https://github.com/drowzeys/Keys-Concurrency-Patch-for-DSpark-DeepSeek-V4-Flash) - 测试的补丁 commit:`7e4d94bbcec95223550517c0fa9244e59f9f6483` 此处记录的实时修复保留了 `kv_cache_dtype=nvfp4_ds_mla`,并使用来自该 commit 的经过路径调整的 Patch 2b 更新,刷新了仓库中已经内置的 Keys 覆盖代码。在 Patch 2b 中,不规则的 `query_start_loc` 检测不再依赖于 `num_rejected_tokens_gpu`。只有在内置的兼容 OpenAI 的聊天冒烟请求以及 agent 客户端验证在实时服务上通过后,才应将服务视为已验证。 静态同步批次,一个 TP=2 副本: | 并发数 | 最佳聚合 tok/s | 单流 tok/s | 接受率 | | ---: | ---: | ---: | ---: | | 1 | 57.6 | 57.6 | 0.635 | | 4 | 140.8 | 35.2 | 0.619 | | 8 | 252.6 | 31.6 | 0.635 | | 16 | 315.1 | 19.7 | 0.609 | 交错式独立到达,一个 TP=2 副本: | 并发数 | 成功率 | 聚合 tok/s | 接受率 | | ---: | ---: | ---: | ---: | | 4 | 4/4 | 109.2 | 0.544 | | 8 | 8/8 | 147.3 | 0.534 | | 16 | 16/16 | 205.0 | 0.567 | 正确性健全性检查:在系统扰动下,确定性的受害输出保持字节完全相同。一项中等扰动的压缩测试测得接受率为 `0.529`,在整个扰动窗口内速度达到 `99.7 tok/s`。 此运行的上游检查点说明未导入到当前检出中。 ### 历史 60 tok/s DSpark 基线 早先的约 60 tok/s 数据已被复现,但那是一个单独的诊断配置,不是本仓库默认的 1M NVFP4 部署方案: - 镜像由 `rafaelcaricio/vllm#1` commit `3519c3b88` 重新构建 - `max_model_len=262144` - `max_num_seqs=1` - `kv_cache_dtype=fp8` - `MTP_NUM_TOKENS=5` - `thinking=false` - `temperature=0.0`,`top_p=1.0` - 在 `code_completion` 门控上测得 `63.97 tok/s`,DSpark 接受率为 `67.9%` 使用此配置来诊断镜像/运行环境漂移。不要将其与生产环境的 1M NVFP4 路径混淆。此运行的上游检查点说明未导入到当前检出中。 ### 2026-06-29 Full-1M 并发微型基准测试 上面的 200K/16 配置最大化了原始并发。对于想要**完整 1M 上下文上限同时保持并发**的 agent 集群,请使用 `max_model_len=1048576` 以及 `max_num_seqs=6` 运行。每个请求仍然可以增长到 1M,同时最多可以有 6 个会话并发运行,因为真正的限制因素是共享的 KV pool,而不是每个槽位的预留(参见 [KV cache 的工作原理](#how-the-kv-cache-works-why-1m--concurrency-is-safe))。 在 2026-06-29 的代码补全微型基准部署上进行了验证 (NVFP4, `max_model_len=1048576`,`max_num_seqs=6`, `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1`,`VLLM_USE_B12X_WO_PROJECTION=1`): - 启动:`GPU KV cache size: 1,901,239 tokens`,`Maximum concurrency for 1,048,576 tokens per request: 1.81x` - 6 个并发请求:**6/6 成功**,**~182 tok/s 聚合**(每个流约 30 tok/s),无 OOM / 无抢占失败 - 在相同配置下的单流解码:~67 tok/s (代码) 当大多数会话远低于 1M 时(典型的 agent 交互),这是正确的形态,但您仍然希望保留 1M 的上限。对于 Hermes/OpenClaw 框架验证,上面提到的较新的 2026-06-30 agent 稳定性检查点是更值得引用的安全数据。 ## KV cache 的工作原理(为什么 1M + 并发是安全的) 三个经常被混淆的独立参数: | 参数 | 含义 | 当前构建版本 | | --- | --- | --- | | **KV cache pool** | 以 token 为单位的总共享 KV 内存,根据权重加载后的 `gpu_memory_utilization` 进行分配 | Anemll 镜像上约 2.8M tokens(当前检出);历史 Stage-C C12 上约 3.2M | | `max_model_len` | 每个请求的**上限** —— 任何单个请求可以增长到的最大长度 | 默认 **1,048,576 (1M)** | | `max_num_seqs` | **并发上限** —— 调度器同时运行的最大活跃序列数 | 6 | Pool 是**共享并按需分配的**:PagedAttention 在每个请求生成 token 时为其分配 KV 块,并在完成时释放它们。`max_model_len` 和 `max_num_seqs` 是**上限,而不是预留量** —— vLLM 不会预先分配 `max_num_seqs × max_model_len` 大小的 KV。因此,真正的约束条件是: ``` sum(live tokens across all active requests) <= KV pool ``` 在 1M 上限 / 6 个槽位下的计算示例: ``` 6 requests x 50k tokens = 300k fits easily 6 requests x 200k tokens = 1.2M fits in the Anemll / C12 pools 6 requests x 500k tokens = 3.0M near pool capacity depending on image 3 requests x 1M tokens = 3.0M near pool capacity depending on image 6 requests x 1M tokens = 6.0M impossible — excess requests queue/preempt ``` 启动日志中的 `Maximum concurrency for 1,048,576 tokens per request: ~2.7x` (此集群上的 Anemll 镜像)仅意味着可以容纳少量*同时发起的完整 1M* 请求。Agent 交互几乎永远不会接近 1M,因此六个正常长度的会话可以共享 pool,同时为极少出现的长请求保留 1M 上限可用。这正是为什么 `1M + max_num_seqs=6` 有用的原因:您不是在预留 6×1M,而是在高上限下让多个短请求共享同一个 pool。 ## 注意事项:乱码、循环、中文漂移或提示词/XML 泄露 如果模型能启动,且像 `hi` 这样的基本提示词也能正常工作,但真实的 agent 流量随机变成了重复的字符、中文漂移、泄露了工具/schema XML,或者在 Telegram 上可见的垃圾内容,请不要认为是权重损坏。 对于此部署,在责怪权重之前,需进行三项检查: 1. **运行时镜像 + DSpark 路径:**使用 Anemll 镜像时,确认两个节点运行相同的 tag(`docker image inspect $DSPARK_VLLM_IMAGE`),并且 compose 使用的是 `/usr/local/bin/vllm`(而不是 Stage-C 的 `/opt/env` 路径)。对于历史的 Stage-C 构建,还需确保 `recipe/vllm/v1/spec_decode/dspark_proposer.py` 下的 Keys proposer 路径和覆盖源码与您构建的镜像一致。 2. **两个节点上的模型缓存:**head 和 worker 上必须存在 `deepseek-ai/DeepSeek-V4-Flash-DSpark` 的完整离线 HF hub cache(完成后设置 `HF_HUB_OFFLINE=1`)。不完整的缓存或在线重新下载会占满 worker 磁盘并导致 TP=2 启动失败。 3. **解码/后备安全性:**对于较长的兼容 OpenAI 的 agent 提示词,避免不稳定的采样和隐藏的后备切换。服务器保留 `--generation-config vllm` 且不安装服务器端的 `--override-generation-config`;显式的客户端请求参数仍然具有最高优先级。 compose 启动器包含 `--generation-config vllm`,设置了 `thinking=false`,使用 DSpark speculative decoding 以及 `MTP_NUM_TOKENS=3` 和 `draft_sample_method=probabilistic`,并启用了 FlashInfer sampler。若要进行完全确定性的 curl 检查,请在请求体中发送 `temperature: 0`。 在验证期间,还要清除 agent 的后备列表。如果编排层静默发生后备切换、重启会话,或将陈旧的提示词/工具记录重播到可见的消息流中,那么在直接的 vLLM 测试中看起来已修复的模型,仍然可能看起来像中了毒。除非您有意测试该框架,否则请将 OpenClaw/Hermes 的更改与模型运行时验证分开。 实时修复后需运行的验证门控: ``` direct vLLM prompts: clean direct concurrent vLLM prompts: clean agent harness prompts: clean, DeepSeek, no fallback MTP3 probabilistic draft sampling active ``` 这将保留 NVFP4 KV 和 MTP3。除非您有意接受上下文和质量的折衷,否则不要仅仅为了掩盖症状而切换到 fp8 或退回到更小的后备模型。 ## 重要说明 ## 致谢 有关完整的归属和许可证说明,请参见 [`CREDITS.md`](CREDITS.md)。 ### 特别感谢 **[drowzeys ("Keys")](https://github.com/drowzeys/)** —— 如果没有 Keys 的公开成果,本仓库将无法在真实并发下正确运行。Keys 发布了 DSpark 服务器内并发补丁、请求稳定的 主 KV slot 映射、用于混合 prefill/decode 批次的不规则 `query_start_loc` 路径,以及在 DGX Spark 上早期的 `nvfp4_ds_mla` KV-cache 连接方式。我们的覆盖代码、bind-mounted proposer 以及测量的并发数据都直接建立在这一基础之上。 ### 其他贡献者 - **[drowzeys](https://github.com/drowzeys/) / Keys 并发补丁:** [Keys-Concurrency-Patch-for-DSpark-DeepSeek-V4-Flash](https://github.com/drowzeys/Keys-Concurrency-Patch-for-DSpark-DeepSeek-V4-Flash) - **[tonyd2wild](https://github.com/tonyd2wild/)** —— NVFP4 1M 方案脉络、 乱码修复启动器默认值,以及我们合并到运行时 proposer bind-mount 中的非均匀批次防护 - **Rafael Caricio** —— DSpark vLLM 集成和部署工作: [vllm#1](https://github.com/rafaelcaricio/vllm/pull/1), [spark_vllm_docker#1](https://github.com/rafaelcaricio/spark_vllm_docker/pull/1) - **Fraser Price** —— DeepSeek V4 Flash DSpark 模型/运行时工作: [DeepSeek-V4-Flash-DSpark](https://huggingface.co/fraserprice/DeepSeek-V4-Flash-DSpark), [dspark-vllm](https://github.com/fraserprice/dspark-vllm) - **MiaAI-Lab** —— 双节点 DGX Spark 封装和 优先启动操作手册: [DeepSeek-v4-Flash-DSpark-2x-DGX-Spark](https://github.com/MiaAI-Lab/DeepSeek-v4-Flash-DSpark-2x-DGX-Spark) - **[Anemll](https://github.com/Anemll/dspark-vllm-gx10)** —— 预构建的 `ghcr.io/anemll/dspark-vllm-gx10` vLLM 0.25 镜像,适用于带有 NVFP4 DS-MLA 和 b12x MoE 的双节点 GB10 / DGX Spark - **上游基础** —— vLLM, FlashInfer, NVIDIA Blackwell/CUDA/NCCL 工具链,DeepSeek V4 Flash,以及 DeepSeek-AI DeepSpec / DSpark 研究 ### MiaAI-Lab 贡献 MiaAI-Lab 维护着此 fork 中经过验证的 1M NVFP4-KV 方案、Stage A/B/C 运行时打包、干净的双节点启动流程、Keys 补丁集成以及 compose/启动工具。当前检出版本默认使用 Anemll 预构建镜像,同时保留了 Stage-C 构建脚本以供可选的本地重新构建。 ## 许可证说明 仓库脚本和文档发布在本仓库的 `LICENSE` 下。vLLM 覆盖/运行时文件派生自 vLLM,并在存在的地方保留其 Apache-2.0 血缘关系和 SPDX 标头。基础镜像、FlashInfer/TileLang/Triton/CUDA/NCCL 以及模型权重是独立的上游产物,具有各自的许可证和使用条款。 ## 文件 | 路径 | 用途 | | --- | --- | | `docker-compose.dspark.yml` | 双节点 vLLM/DSpark 服务(默认为 Anemll 镜像布局) | | `.env.dspark.example` | 清理后的集群模板;默认镜像 Anemll `0.1.1`,**1M** 上下文 | | [`docs/ENVS.md`](docs/ENVS.md) | Anemll 与 Stage-C 环境变量注册表矩阵(未知 `VLLM_*` 警告) | | `docker-compose.stage-c.override.yml` | 可选的仅 Stage-C 环境变量注入 | | `start-deepseek-v4-flash-dspark.sh` | worker 优先启动和冒烟测试;镜像必须在两个节点上都存在 | | `stop-deepseek-v4-flash-dspark.sh` | 停止 head 和 worker 服务 | | `status-deepseek-v4-flash-dspark.sh` | 显示 head/worker 容器状态 | | `logs-deepseek-v4-flash-dspark.sh` | 滚动查看 head/worker 的 DSpark 日志 | | `smoke-deepseek-v4-flash-dspark.sh` | 直接并发兼容 OpenAI 的冒烟测试 | | `validate-dspark-config.sh` | 渲染并检查本地 DSpark compose/env 配置 | | `prepare-dspark-model-cache.sh` | 下载/校验模型缓存 | | `build-dspark-vllm-runtime.sh` | 可选的 Stage-C 本地镜像构建(Anemll 不需要) | | `recipe/overlay/` | 用于本地镜像构建的 Stage-C DSpark vLLM 覆盖源码 | | `recipe/vllm/v1/spec_decode/dspark_proposer.py` | Stage-C/proposer 参考;启动脚本可能会同步到 worker | | `recipe/nvfp4/Dockerfile.stage-*` | 用于本地构建的 Stage A/B/C NVFP4 镜像层 | | `patches/keys-concurrency.patch` | 完整的经过路径调整的 Keys 并发补丁参考 | | `vllm_patch_gb10/` | 可选的实验性 GB10 混合 NVFP4 vLLM 插件 | | `docs/PATCHES.md` | 通俗易懂的 Patch 1 / Patch 2 / Patch 2b 并发原理解释 | | `scripts/verify-overlay-sources.sh` | 在构建 Stage-C 镜像前检查覆盖源码 | ## 快速开始 在 head 节点上运行。 ``` cp .env.dspark.example .env.dspark ``` 根据您的集群修改以下值: - `WORKER_HOST` - `WORKER_SCRIPT_DIR`(如果 worker 的检出/部署路径与 head 不同) - `MASTER_ADDR` - `NCCL_IB_HCA` - `NCCL_SOCKET_IFNAME`(以及匹配的 `TP_SOCKET_IFNAME` / `GLOO_SOCKET_IFNAME`,或者留空让 compose 继承 NCCL IF) - `NCCL_IB_GID_INDEX`(并不总是为 0 —— 请与您的 RoCE GID 匹配) - `HF_CACHE` - `WORKER_HF_CACHE`(如果 worker 的缓存路径与 head 不同) - `VLLM_HOST_IP` 和 `WORKER_VLLM_HOST_IP`(每个节点的 fabric IP) 集群 fabric 示例值(请根据您的节点进行修改 —— f0 vs f1 和 GID index 会有所不同): ``` WORKER_HOST=10.0.0.2 MASTER_ADDR=10.0.0.1 VLLM_HOST_IP=10.0.0.1 WORKER_VLLM_HOST_IP=10.0.0.2 MASTER_PORT=25000 NCCL_IB_HCA=rocep1s0f1 NCCL_SOCKET_IFNAME=enp1s0f1np1 TP_SOCKET_IFNAME=enp1s0f1np1 GLOO_SOCKET_IFNAME=enp1s0f1np1 DSPARK_VLLM_IMAGE=ghcr.io/anemll/dspark-vllm-gx10:0.1.1 ``` 除非您是有意进行实验,否则请保留这些**默认的** agent 服务参数(不要将临时的本地 `MAX_MODEL_LEN` 覆盖视为方案默认值): - `VLLM_HOST=0.0.0.0`(如果 Hermes/OpenClaw 或其他机器需要访问 API) - `MAX_MODEL_LEN=1048576` (**1M**) - `MAX_NUM_SEQS=6` - `MAX_NUM_BATCHED_TOKENS=8192` - `GPU_MEMORY_UTILIZATION=0.85` - `MTP_NUM_TOKENS=3` - `HF_HUB_OFFLINE=1`(在两个节点都有完整的模型缓存后启用) - `VLLM_USE_FLASHINFER_SAMPLER=1` - `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1` - `VLLM_USE_B12X_WO_PROJECTION=1` - `VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0` 在 **head 和 worker** 上拉取默认运行时镜像: ``` docker pull ghcr.io/anemll/dspark-vllm-gx10:0.1.1 ``` 可选:改为构建历史 Stage-C 镜像: ``` ./build-dspark-vllm-runtime.sh # 然后设置 DSPARK_VLLM_IMAGE=vllm-dspark-runtime:dspark-nvfp4-stage-c # 以及为 prepare-dspark-model-cache.sh 设置 IMAGE_PYTHON=/opt/env/bin/python ``` 在两个节点上准备模型缓存(或者通过 rsync 同步一个已校验的 hub 快照): ``` ./prepare-dspark-model-cache.sh ``` 即使 `.env.dspark` 中设置了 `HF_HUB_OFFLINE=1`,`prepare-dspark-model-cache.sh` 也会在下载步骤强制使用 HF 在线模式(这在缓存预热后提供服务时是正确的)。 在 Anemll 镜像上使用 `IMAGE_PYTHON=/usr/bin/python3`(默认);Stage-C 需要 `IMAGE_PYTHON=/opt/env/bin/python`。 启动服务: ``` ./start-deepseek-v4-flash-dspark.sh ``` 可选的实验性 GB10 混合 NVFP4 插件: ``` ENABLE_VLLM_GB10_PATCH=1 ./start-deepseek-v4-flash-dspark.sh ``` 启用后,启动器会将 `vllm_patch_gb10/` 同步到 worker,将其挂载到两个容器中,使用 `pip install -e --no-deps` 安装它,设置 `VLLM_PLUGINS=gb10_hybrid_nvfp4`,并使用 `--quantization modelopt_gb10_hybrid` 启动 vLLM。默认禁用。使用 `GB10_HYBRID_NVFP4_M_THRESHOLD` 调整调度器阈值;默认为 `128`。 启动脚本会打印解析出的非机密运行时配置,将 compose/env(及相关文件)同步到 worker 路径,验证两个节点上渲染出的 Docker Compose,先启动 worker,然后启动 head,并在等待 API 就绪时跟踪启动日志。如果启动失败,它会在退出前打印最近的 head 和 worker 日志。 API 服务地址: ``` http://HEAD_NODE_IP:8888/v1 ``` 如果仅进行 head 节点测试,请设置 `VLLM_HOST=127.0.0.1`。如果要让 Hermes/OpenClaw 或其他机器使用该 endpoint,请保持 `VLLM_HOST=0.0.0.0` 并在网络/防火墙层控制访问权限。 ## 运行时配置 ### C12 Agent 服务配置(默认:1M 上下文) 核心 vLLM 标志(来自 `docker-compose.dspark.yml`): - 镜像:`ghcr.io/anemll/dspark-vllm-gx10:0.1.1`(使用 `DSPARK_VLLM_IMAGE` 覆盖) - `/usr/local/bin/vllm serve …` - `--tensor-parallel-size 2` - `--distributed-executor-backend mp` - `--nnodes 2` - `--kv-cache-dtype nvfp4_ds_mla` - `--block-size 256` - `--max-model-len 1048576`(**默认 1M**) - `--max-num-seqs 6` - `--max-num-batched-tokens 8192` - `--max-cudagraph-capture-size 24` (`max_num_seqs * (MTP_NUM_TOKENS + 1)` → `6 * 4`) - `--gpu-memory-utilization 0.85` - `--moe-backend flashinfer_b12x` - `--async-scheduling` - `--enable-chunked-prefill` - `--speculative-config '{"method":"dspark","num_speculative_tokens":${MTP_NUM_TOKENS:-3},"draft_sample_method":"probabilistic"}'` - `--generation-config vllm` 关键运行时环境变量: - `DSPARK_VLLM_IMAGE=ghcr.io/anemll/dspark-vllm-gx10:0.1.1` - `HF_HUB_OFFLINE=1`(当两个节点上的 hub 缓存均完整时启用) - `ENABLE_VLLM_GB10_PATCH=0`(默认);设置为 `1` 可加载可选的 `vllm_patch_gb10/` 插件并添加 `--quantization modelopt_gb10_hybrid` - `GB10_HYBRID_NVFP4_M_THRESHOLD=128` - `VLLM_USE_FLASHINFER_SAMPLER=1` - `VLLM_USE_B12X_MOE=1` - `VLLM_USE_B12X_WO_PROJECTION=1` - `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1` - `VLLM_DSPARK_CONFIDENCE_SCHEDULER=off` - `VLLM_DSPARK_LOCAL_ARGMAX=1` - `VLLM_DSPARK_REPLICATE_MARKOV_W1=1` - `VLLM_DSPARK_FUSED_MARKOV_ARGMAX=0` - `VLLM_DSPARK_REFERENCE_KV_QUANT_DEQUANT=0` - `VLLM_DSV4_B12X_COMPRESSED_MLA=0` - `VLLM_DSV4_DSPARK_DEFER_TARGET_CAPTURE=0` - `B12X_W4A16_TC_DECODE=0` - `DG_JIT_NVCC_COMPILER=/usr/local/cuda/bin/nvcc` ### 200k 并发配置 对于 DSpark 并发,请使用包含的覆盖文件加上 Keys 的并发补丁,并设置: - `MAX_MODEL_LEN=200000` - `MAX_NUM_SEQS=16` - `VLLM_USE_B12X_WO_PROJECTION=1` - `VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1` ### 1M 单流传统配置 对于保守的单流测试,请设置 `MAX_NUM_SEQS=1` 以及 `VLLM_USE_B12X_WO_PROJECTION=0`。除非您是有意进行实验,否则请保持 `MTP_NUM_TOKENS=3`;当前本地运行时在 MTP3 下使用概率性的 DSpark draft 采样。 ## 验证 启动后: ``` curl -fsS http://127.0.0.1:8888/v1/models ``` 确认返回的模型条目报告如下: ``` "max_model_len": 1048576 ``` 然后检查日志: ``` docker compose --env-file .env.dspark -f docker-compose.dspark.yml logs vllm-dspark \ | grep -E "GPU KV cache size|Maximum concurrency" ``` 在 1M max-model-len / 0.85 GPU util 下的 Anemll 镜像上,预期大致如下: ``` GPU KV cache size: approximately 2.8M tokens Maximum concurrency for 1,048,576 tokens per request: approximately 2.7x ``` 历史 Stage-C C12 启动报告了约 2–3.2M tokens 以及约 1.9–3.2x 的倍数,具体取决于镜像和利用率;请始终以您节点的实际启动日志为准。 在将 agent 框架指向该 endpoint 之前,请运行包含的冒烟测试: ``` ./smoke-deepseek-v4-flash-dspark.sh ``` 如果直接兼容 OpenAI 的提示词结果是干净的,但 agent 仍然出现乱码,请在责怪 DSpark 权重之前,先调查 agent 会话、后备列表或框架的提示词重播机制。 ## 备注 - 旧的速度检查点是单流,而不是聚合吞吐量。 - 高并发基准测试是聚合吞吐量,并且在 `max_model_len=200000` 下进行了验证,而不是完整的 1M 上下文。 - 完整上下文和高并发会争用同一个 KV pool。C12 1M 配置适用于大多数会话远低于 1M 上限的常规 agent 流量;它并不意味着支持十二个同时发起的完整 1M 请求。 - 要将 DSpark 并发与更长的上下文结合使用,请先选择一个较低的上下文目标,然后在观察启动日志、KV 分配、接受率和请求错误的同时,缓慢提高并发数。 - 1M 作为已启动/已公布的 `max_model_len` 进行了验证,并留有 KV 余量和短提示词的速度探测。本仓库并不声称完成了完整的 1M-token 检索正确性基准测试。 - 测量的探测使用的是带有 g64/g256 的 p256/p512。如果您更改了采样、批处理、上下文长度、WO projection、compressed MLA 或 confidence scheduler,请重新进行基准测试。 - **默认的** agent 服务配置为 `MAX_MODEL_LEN=1048576` (1M),`MAX_NUM_SEQS=6`,`MAX_NUM_BATCHED_TOKENS=8192`,`GPU_MEMORY_UTILIZATION=0.85`,`MTP_NUM_TOKENS=3`,`DSPARK_VLLM_IMAGE=ghcr.io/anemll/dspark-vllm-gx10:0.1.1`,`VLLM_USE_FLASHINFER_SAMPLER=1`,`VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK=1`,`VLLM_USE_B12X_WO_PROJECTION=1`,无 generation override,以及 `VLLM_DSV4_B12X_COMPRESSED_MLA=0`。本地 `.env.dspark` 可以临时降低上下文(例如 512k),而无需更改该方案默认值。 - Worker 优先启动避免了多节点 `mp` 初始化期间的竞态条件,并在启动容器之前验证两个节点上渲染出的 compose。 - 要求两个节点上具有匹配的镜像、正确的 NCCL/RoCE 设置,以及双节点 Blackwell 级/DGX Spark 配置。 - 建议在 DGX Spark 主机上**禁用 earlyoom** (`sudo systemctl stop earlyoom && sudo systemctl disable earlyoom`)。 earlyoom 守护进程可能会在 GPU 内存压力较高时(例如,在并发处理深上下文工作负载期间)OOM-kill vLLM worker 或 head 进程,即使系统有可用的交换空间或 OOM 是暂时的。禁用它可以避免不必要的进程终止和服务中断。 - 示例模板绑定到 `0.0.0.0:8888` 以用于多主机 agent;如果仅进行 head 测试,请设置 `VLLM_HOST=127.0.0.1` 并在防火墙处控制暴露面。 - 接下来可以尝试的最大序列阶梯大约是 1.25M、1.5M,然后是 1.75M,并采用相同的启动/日志/速度门控。仅仅依靠原始的 KV 数学计算是不够的,因为 DeepSeek V4 sparse MLA 也会分配依赖于最大长度的工作区。
标签:DLL 劫持, Docker, Vectored Exception Handling, vLLM, 人工智能, 大语言模型, 安全防御评估, 模型部署, 深度学习推理, 版权保护, 用户模式Hook绕过, 英伟达硬件, 请求拦截, 逆向工具