
之前先后部署过不少推理服务但把多模态理解的推理框架和视频生成模型放在同一个环境里、对外统一提供接口这还是头一回。项目标题里写的是 vLLM-Omni Wan2.1-T2V-1.3B 部署文档实际做完之后我觉得更像是搭了一个中间层——底层是 vLLM-Omni 这个多模态推理运行时负责管理模型加载、显存调度和请求排队旁边再挂一个 Wan2.1-T2V-1.3B 这样的小视频生成模型用任务队列的方式让两边协作最终只需要对业务方暴露一个 HTTP 接口。整套部署过程不算难但中间有不少细节非常容易翻车写这篇文档把关键步骤、参数和踩过的坑都留档给后面要做同样组合的人一个参考。先说结论这套方案的硬件门槛不高单张 24GB 显存的卡就能跑起来但如果想做得稳最好给视频生成任务留更大的显存余量。1.3B 参数的视频生成模型在显存占用上比想象中克制真正的瓶颈在于 vLLM-Omni 本身加载多模态模型时的显存开销以及两套服务同时运行时对显存、内存和磁盘 IO 的争抢。所以这篇文章从头到尾都会围绕一条主线如何让理解模型和生成模型在同一个环境里协作并且把排队、超时、显存分配这些问题在部署阶段就解决好。1. 这套组合解决什么问题理解与生成共用一套服务1.1 单卡同时跑理解和生成问题的本质是显存和时间片vLLM-Omni 本身负责的是多模态理解任务也就是把文本、图像、音频、视频这些输入交给大语言模型去做推理典型场景是图文对话、音视频内容问答。Wan2.1-T2V-1.3B 则是纯生成侧的模型输入一段文字描述输出一段视频。两者单独部署都很简单各起一个服务、各占一个端口就行。但一旦业务方提出先理解、再生成这种串联需求比如先让模型分析一段视频内容、再根据分析结果生成一段新视频事情就开始变麻烦了。麻烦点不在模型本身而在工程结构。分开跑的话你需要自己写代码去调两个 HTTP 服务管理两个进程的生命周期、两套 token 鉴权、两套超时策略还要处理两个服务之间数据格式不一致的问题。更重要的是进程一多显存分配就变成零和博弈一个服务占多了另一个就会 OOM。很多人的第一反应是各用各的卡但现实是能拿到两张卡的人毕竟是少数单卡跑多模型才是常态。把 Wan2.1-T2V-1.3B 挂到 vLLM-Omni 这套体系里面本质上是把多模型管理这件事交给一个统一运行时。vLLM-Omni 内部已经有很成熟的显存管理器、调度器、连续批处理逻辑虽然这些机制主要是为了优化 token 级别的推理但稍微改造一下用来做不同模型之间的粗粒度任务排班也非常合适。简单说就是让专业的人做专业的事理解任务归理解框架管生成任务挂一个后端进去对外只暴露一个入口。1.2 直接同步调用为什么不行这里必须先说清楚回应一个常见的错误认知视频生成不是普通的大模型推理它不走输入 token、输出 token那条路。Wan2.1-T2V-1.3B 输入的是文本序列经过文本编码器之后进入 DiT 主干网络做迭代去噪最后还要过 VAE 解码器把隐空间张量还原成像素级视频帧。整个流程耗时通常在几十秒到几分钟量级具体取决于分辨率、帧数和步数。如果中间层直接用同步 HTTP 请求去调用这个流程后果是灾难性的。假设生成一段 5 秒、24fps、480P 的视频需要 120 步去噪单卡上大约要 40 到 60 秒那么业务方的请求在这段时间内会一直占着一个 HTTP 连接代理层、网关层、负载均衡的默认超时时间大多在 30 秒左右连接被切断几乎是必然的。就算你手动把超时时间调得很长同步调用也意味着一台机器同时只能处理一个生成任务后面的请求全部排队阻塞吞吐量低到没法用。所以正确做法是引入异步任务队列。业务方提交任务之后立刻拿到一个 task_id服务端在后台慢慢跑业务方通过轮询或者回调去拿结果。这个思路在很多部署文档里都是一笔带过但实际落地时任务队列的设计直接决定了整个服务是否可用。后面我会专门用一个章节来讲这部分实现。1.3 分工之后各自负责什么在我这套部署方案里两套系统有明确分工可以先看这张表模块负责内容对外能力vLLM-Omni多模态理解推理、请求鉴权、任务排队、结果缓存/v1/chat/completions、/v1/tasksWan2.1-T2V-1.3B 后端文生视频推理负责文本编码、DiT 去噪、VAE 解码内部 gRPC/HTTP 生成服务中控网关任务状态机、异步任务队列、模型可用性探测对外统一 API理解模型的输出会作为生成模型的输入但两者之间不需要直接通信中控网关负责传递。这样设计的好处很明显每个模型挂了都可以单独重启不会像一条链路全挂那样难排查。实际部署时vLLM-Omni 和 Wan 后端可以放在同一个进程里也可以分开进程跑我的建议是分开进程后面会解释原因。2. 部署前的硬性条件硬件、驱动、CUDA 环境与依赖清单2.1 显存预算怎么算这是整个部署过程里最容易算错的一笔账。很多人看到 Wan2.1-T2V-1.3B 只有 1.3B 参数就以为它只需要很低显存结果一跑发现 OOM然后开始怀疑模型是不是有问题。实际上视频生成模型的显存占用大头不在参数权重而在中间激活值和 VAE 解码时的显存峰值。我实测下来在不开启任何量化的情况下Wan2.1-T2V-1.3B 在 BF16 精度下模型权重大约占 2.6GB 到 3GB 显存但是生成一段 480P、5 秒、24fps 的视频时DiT 去噪过程中的临时显存峰值能冲到 8GB 到 10GB。vLLM-Omni 加载一个 7B 到 8B 量级的多模态理解模型权重加上 KV cache 预分配大约占 16GB 到 20GB 显存。中控网关和任务队列本身不占显存但占内存一般 500MB 到 1GB 内存足够。所以单卡 24GB 显存是一个能跑但不宽裕的状态。如果视频生成任务和 vLLM-Omni 的推理同时进行显存大概率会爆。解法有三个方向错峰执行、量化、以及把 KV cache 预分配值调小。后两者我在第 7 章会展开讲真正可用的参数组合。2.2 系统环境要求我在这台测试机上用的是 Ubuntu 22.04 系统驱动版本 535CUDA 12.2PyTorch 2.3 版本实测可以稳定跑通。如果你用的是更新的系统需要注意一点CUDA 版本太新反而容易出问题因为 vLLM-Omni 里有一部分 CUDA 算子需要编译而编译期工具链对新版本 CUDA 的适配往往滞后于稳定版。不要一味追求最新选一个经过验证的稳定组合更实际。环境隔离强烈建议用 conda而不是直接用系统 Python 环境。因为 vLLM-Omni 编译时会拉一堆依赖包括 flash-attention 这种编译时间极长的库一旦把系统 Python 环境搞乱了恢复成本很高。conda create -n omni-venv python3.10 -y conda activate omni-venv2.3 依赖清单与验证顺序依赖安装顺序建议严格按照下面这张表来不要先装 torch 再装其他东西因为 vLLM-Omni 对 torch 版本敏感版本不匹配会导致算子编译失败日志里只显示一段很抽象的undefined symbol让人排查半天。依赖项版本区间用途与备注CUDA Toolkit12.1 或 12.2编译算子必需运行时只需要 driverPyTorch2.3.0 或 2.4.0核心训练/推理框架不能太新transformers4.40 以上加载多模态模型结构diffusers0.30 以上加载 Wan 模型视频生成专用accelerate0.30 以上设备映射与 offloadflash-attention编译版或预编译版加速长序列注意力计算装完所有依赖后先跑一下环境自检确认 GPU 驱动和 CUDA 都能正常访问再继续往下走python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0)) python -c from vllm import LLM; print(LLM) # 确认 vllm 可导入这一步能过滤掉大约三成的环境问题。很多人在这一步没检查就跑去起服务后面报错才开始怀疑 GPU 驱动其实环境问题在前面提前 30 秒就能发现。3. vLLM-Omni 的编译安装与最小启动验证3.1 为什么走源码编译而不是 pip 安装vLLM-Omni 官方仓库会提供 pip 包但版本更新频率很高pip 包往往滞后于主线。如果你只是跑最简单的功能pip 安装省事但要做本文这种挂一个视频生成后端进去的改造你基本绕不开源码编译因为需要修改或扩展它的调度逻辑。建议直接拉源码编译。git clone https://github.com/vllm-project/vllm-omni.git cd vllm-omni pip install -e .编译过程比较耗时主要是在编译 flash-attention 和 vllm 自带的自定义算子。在 8 核机器上大概要 10 到 20 分钟如果机器比较老40 分钟也很正常。编译时注意看日志里有没有Failed to build flash-attn这样的字样如果出现大概率是 CUDA 路径没配置好。export CUDA_HOME/usr/local/cuda-12.2 export PATH$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH$CUDA_HOME/lib64:$LD_LIBRARY_PATH3.2 启动前必须检查的两个参数vLLM-Omni 启动时会分配显存默认行为是占满所有空闲显存这在我们这套方案里是不能接受的因为旁边还要跑 Wan。所以启动参数里必须显式限制显存占用。我用的是这套组合vllm serve model_path \ --port 8000 \ --gpu-memory-utilization 0.55 \ --max-num-seqs 8 \ --enforce-eager--gpu-memory-utilization 0.55表示只让 vLLM-Omni 最多占用 55% 的显存剩下的留给 Wan 模型。--max-num-seqs 8限制并发序列数避免多个请求同时进来时 KV cache 膨胀失控。--enforce-eager是关闭 CUDA graph 捕获能省一点点显存代价是推理速度略微下降在这里性价比很高。还有一个很容易被忽略的参数叫--device默认是cuda但如果你的机器有集成显卡vLLM 有时会错误地选中它导致启动报错建议显式指定--device cuda:03.3 多模态请求的最小验证服务启动之后用 OpenAI 兼容的接口格式传一个多模态请求验证基础能力。这里我用一张图片加一段文字作为输入应该能返回对应的描述内容curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: omni-model, messages: [ { role: user, content: [ {type: text, text: 描述这张图片的内容}, {type: image_url, image_url: {url: file:///path/to/test.jpg}} ] } ] }如果这一步返回正常的 JSON 文本回复说明 vLLM-Omni 的基础链路是通的。这一步一定要在挂 Wan 后端之前完成否则后面出了问题很难定位是理解侧还是生成侧的锅。3.4 显存实际占用观察服务起来之后用nvidia-smi看一下显存占用应该在预期范围内。如果没限制gpu-memory-utilization你会看到显存几乎被占满后面 Wan 模型加载的时候大概率直接 OOM。这一步的数字建议记录下来作为后面调参的基准。这里有个小经验启动 vLLM-Omni 之后权重加载完到服务 ready 之间通常有几秒到几十秒的延迟别急着去请求等日志出现类似Application started的提示再测试。4. Wan2.1-T2V-1.3B 的模型获取与基线推理4.1 权重文件组织方式与校验Wan2.1-T2V-1.3B 的权重发布形式一般是分片文件包含文本编码器、DiT 主干网络、VAE 解码器几个子目录。下载的时候要注意不要只下 DiT 那部分三个子目录都要完整否则加载的时候会报missing keys。下载完成后强烈建议做一次完整性校验。很多模型下载工具默认不校验哈希文件损坏时 diffusers 加载会报一个不直观的错误比如某个维度对不上。提前用sha256sum校验一遍能省掉后面至少半小时的排查时间。sha256sum -c checksums.txt4.2 用 Diffusers 跑基线推理先不接 vLLM-Omni单独用 diffusers 跑一次生成确认模型本身没问题。这段代码也是后面接入中控队列时核心生成逻辑的原型import torch from diffusers import WanPipeline pipe WanPipeline.from_pretrained( /path/to/wan2.1-t2v-1.3b, torch_dtypetorch.bfloat16, ) pipe.to(cuda:0) prompt 一只橘猫在窗台上晒太阳镜头缓缓推进 negative_prompt 画面模糊文字水印 video pipe( promptprompt, negative_promptnegative_prompt, height480, width832, num_frames49, num_inference_steps30, guidance_scale5.0, ).frames[0]这里有个参数细节需要注意num_frames设为 49 通常对应 2 秒的视频配合 24fps 帧率。如果业务方要 5 秒视频需要推算帧数不是简单地把 49 改成 120因为 Wan 2.1 内部会按 4 的倍数处理帧数。常见组合是 33 帧约 1.3 秒、49 帧约 2 秒、81 帧约 3.4 秒超过这个长度需要手动处理长视频切分这是后面进阶功能不在本文范围内。4.3 推理耗时与峰值显存记录跑一次 30 步、480x832、49 帧的生成我在这张卡上的实测数据是项目数值模型加载时间8 秒30 步去噪耗时22 秒VAE 解码耗时4 秒峰值显存9.1GB输出视频尺寸480x832x49 帧这个数据告诉我们两件事第一单次生成从提交到返回大约 35 秒远超 HTTP 同步请求的合理超时所以异步任务队列是刚需第二9.1GB 的峰值显存意味着 vLLM-Omni 最多只能占 14GB 左右不然必须做量化。4.4 关键推理参数速查Wan2.1-T2V-1.3B 有四个参数对生成质量影响最大这里直接给出我验证过的稳定区间参数推荐区间影响num_inference_steps20 到 50步数越高细节越好但 50 步以上收益递减明显guidance_scale4.0 到 6.0数值越高越贴合提示词但过高会出现颜色过饱和height/width480P 到 720P分辨率越高显存翻倍注意宽高须能被 16 整除num_frames33 / 49 / 81帧数增加时显存线性增长且不能随意取中间值如果你想做批量测试建议从 480x832、30 步、49 帧这一档起步确认画质和速度都能接受之后再逐步提高分辨率。5. 连通两套服务统一网关与异步任务队列5.1 为什么不能直接把生成请求转发给 Wan假设已经完成了上面两步现在有两个服务vLLM-Omni 在 8000 端口Wan 在 8001 端口。你可能会想中控层直接把请求转发给 Wan 不就行了不行。原因就是之前算过的那笔时间账——视频生成单次要几十秒直接转发会超时同时没有人管理多任务并发如果同时来三个生成请求Wan 后端要不排队等死要不直接 OOM。异步任务队列解决的不只是超时问题它还把请求频率和生成耗时解耦了。一个提交视频生成请求的动作耗时只要几毫秒重活全在后台异步执行。这样即使前端一秒来了几十个请求系统也只是把它们塞进队列慢慢消化不会出现雪崩。5.2 一个足够用且不过度设计的状态机任务状态建议保持最小集合够用就好不要一开始就设计十几个状态后期维护会很痛苦。五个状态足够覆盖绝大多数情况pending已提交等待被 Worker 拾取running正在执行生成succeeded生成完成结果可下载failed生成失败错误信息附在任务记录里canceled任务已被用户取消我在实现时用 Python 字典 文件锁做的内存版队列代码很短适合中小规模场景。如果业务量大了再替换成 Redis 队列接口设计上尽量隔离后面换存储不至于把业务代码全重写。5.3 最小网关代码框架这里给一个可运行的极简实现思路。核心是TaskWorker线程不断从队列里取任务调用 Wan 后端的推理函数最后把结果写回状态存储import threading import uuid import time from typing import Dict class TaskQueue: def __init__(self): self.tasks: Dict[str, dict] {} self.lock threading.Lock() def create_task(self, payload: dict) - str: task_id uuid.uuid4().hex with self.lock: self.tasks[task_id] { status: pending, payload: payload, result: None, error: None, created_at: time.time(), } return task_id def update_status(self, task_id: str, **kwargs): with self.lock: self.tasks[task_id].update(**kwargs) def get_task(self, task_id: str) - dict: with self.lock: return self.tasks.get(task_id)然后是一个简单的 Worker 主循环import queue class TaskWorker(threading.Thread): def __init__(self, task_queue: TaskQueue, gen_fn): super().__init__(daemonTrue) self.task_queue task_queue self.gen_fn gen_fn def run(self): while True: task_id self.pending_queue.get() self.task_queue.update_status(task_id, statusrunning) try: result self.gen_fn(self.task_queue.tasks[task_id][payload]) self.task_queue.update_status(task_id, statussucceeded, resultresult) except Exception as e: self.task_queue.update_status(task_id, statusfailed, errorstr(e))gen_fn就是封装 Wan 推理函数的那一层里面把prompt、height、num_frames这些参数传给模型。为了可扩展性建议gen_fn内部还要负责任务粒度的显存锁定——同一时间只允许一个生成任务调用 Wan 模型避免并发调用直接 OOM。5.4 超时、重试与鉴权细节异步化之后有几个同步调用里不存在的细节需要处理。第一是超时。任务没有连接超时的概念但不能让它无限期 pending。我在创建任务时给了一个timeout字段pending超过 10 分钟直接置为failedrunning超过 15 分钟也置为failed防止模型卡死之后任务永远挂在队列里。第二是重试。failed状态的任务不会自动重试但如果错误原因是显存不足这种资源性问题可以在任务里加一个retry_count字段让 Worker 在下一轮调度时再尝试一次。显存不足这类错误重试之前必须先释放上一次占用的显存否则重试只会再次失败。第三是鉴权。内部服务之间简单用一个共享 token 就够了不必上完整的 OAuth。中控网关对外开放的接口一定要校验 token否则任何人都可以提交任务消耗你的显存资源。实测下来这个环节是很多人会漏掉的服务一起端口对外一开结果被别人刷了几百个生成任务直接把卡打爆。中控层对外暴露三个接口就够用POST /v1/tasks # 提交生成任务返回 task_id GET /v1/tasks/{id} # 查询任务状态与结果 POST /v1/tasks/{id}/cancel # 取消任务再配合 vLLM-Omni 原本的/v1/chat/completions接口整个服务就形成了闭环理解请求走原接口生成请求走任务接口两边互不干扰。6. 部署过程中最容易翻车的几个坑完整排查链路6.1 坑一CUDA 算子编译失败现象pip install -e .编译到一半报Failed to build flash-attn或者编译完成后启动 vLLM-Omni 直接报ModuleNotFoundError: No module named flash_attn。排查先看编译器日志多数情况是 CUDA 路径不对。conda 环境里默认的 CUDA 路径经常指向 conda 自带的目录而不是你系统装的/usr/local/cuda。检查方式python -c import torch; print(torch.version.cuda) nvcc --version如果 torch 里的 CUDA 版本和nvcc对不上编译出来的算子加载时会报undefined symbol。解决办法是让nvcc与 torch 使用同一套 CUDAexport CUDA_HOME$CONDA_PREFIX export PATH$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH$CUDA_HOME/lib64:$LD_LIBRARY_PATH修复验证重新编译后导入 flash_attn 不报错即可。6.2 坑二Wan 生成结果全是纯色或黑屏现象视频生成流程正常走完日志没有任何报错但输出的视频文件打开是纯黑或纯灰画面。排查这个问题几乎不是模型的问题而是 VAE 解码端的精度问题。Wan2.1-T2V-1.3B 这个模型对 VAE 解码的数值稳定性比较敏感。我一开始用torch.float16加载整个 pipeline结果出来的全是黑屏。后来换成了torch.bfloat16问题立刻消失。原因是 VAE 的某些中间层在 FP16 下会数值溢出。修复加载模型时统一使用torch_dtypetorch.bfloat16如果你必须用 FP16可以单独把 VAE 部分换回 FP32但这样会导致显存增加约 1GB。6.3 坑三同时请求时出现Out of Memory但单跑都正常现象vLLM-Omni 和一个 Wan 生成任务同时执行报CUDA out of memory。排查先看是不是显存真的不够用nvidia-smi看两张表的对比。注意这里有个容易误判的点nvidia-smi显示的显存占用不包含 CUDA 上下文预留部分。CUDA 上下文默认会预留约 300MB 到 500MB 显存两个进程各自预留累计起来可能就有 1GB 的隐性开销。所以如果单进程各自剩余显存加起来刚够实际跑并发的时候大概率 OOM。修复两种方向。第一种是把 vLLM-Omni 的--gpu-memory-utilization从 0.55 降到 0.45给 Wan 留出更多空间。第二种是为 Wan 的推理函数加一个互斥锁确保同一时间只有一个生成任务在执行避免两个生成任务同时挤占显存。这两种方法不冲突建议都做。6.4 坑四任务提交后长时间无响应现象提交生成任务后任务状态一直是pending过很久才进入running看似卡住。排查先看后台 Worker 线程是不是没有启动。很多人只写了TaskWorker类忘了在服务启动时调用worker.start()。这是个很低级的错误但出现频率很高。再看是不是任务队列的get方法没有设置超时导致 Worker 在空队列上一直阻塞新任务来了也拿不到。用queue.Queue.get(timeout1)可以避免这个问题Worker 每秒醒一次检查新任务。修复启动流程里显式创建 Worker 实例并.start()同时在get()里面设置超时。6.5 坑五生成结果文件无法访问现象任务状态是succeeded但业务方下载结果文件时报 404 或 403。排查如果你的生成结果只是存在本地临时目录中控层的静态文件服务没有配置对这个目录的访问权限就会出现这种问题。另一个常见原因是任务结果里存的是相对路径而业务方用拼接之后的完整 URL 去下载。建议在任务结果里直接存绝对路径或完整下载 URL不要留相对路径让下游自己拼。7. 显存不够时的量化与 offload 参数7.1 三种节省显存方案的实际效果对比当 24GB 显存真的不够用时有这三种常见方案我全部实测过效果差异很大方案显存节省代价适用场景BF16 加载 CPU offload约 30%生成时间拉长约 1.5 倍显存刚好差一点Wan 模型量化为 INT8约 50%画质略微下降偶尔出现伪影对画质不敏感的任务vLLM-Omni 侧 KV cache 压缩约 15%长文本理解能力下降输入以短文本为主三种方案可以叠加但不要同时全用否则生成质量会明显劣化。7.2 我实测推荐的折中配置如果你和我一样是单卡 24GB建议采用这个组合兼容性和质量都比较平衡vLLM-Omni 侧--gpu-memory-utilization 0.50关闭 CUDA graph 捕获--max-num-seqs 4Wan 侧torch_dtypetorch.bfloat16加载后立即pipe.enable_model_cpu_offload()调度侧生成任务全局互斥同时只跑一个视频生成任务这套配置下vLLM-Omni 大约占 12GBWan 生成时峰值约为 10GB加起来 22GB 左右留出 2GB 余量基本不会 OOM。代价是 Wan 的生成时间从 35 秒增加到大约 60 秒但换来的是稳定的服务这个交易很划算。7.3 量化时最容易忽略的细节如果你决定把 Wan 模型量化到 INT8有几个细节必须注意。第一量化后的模型在加载时要额外传入quantization_config否则 diffusers 不知道权重的量化格式会直接报错。第二量化模型并不总是比 BF16 省显存因为反量化操作会临时把权重转回高精度导致峰值显存可能不减反增。实测下来 INT8 的显存优势主要体现在权重加载阶段去噪阶段反而不明显。vLLM-Omni 侧做 KV cache 压缩时--kv-cache-dtype fp8_e5m2这类参数在部分模型上会引起精度损失表现为回答变得简短或者不连贯。建议压缩前先跑一批测试集对比压缩前后的输出差异再决定是否启用。7.4 offload 与任务队列的配合enable_model_cpu_offload()会在每次推理时把模型从 GPU 挪到 CPU 再挪回来这个机制和异步任务队列配合时必须注意一个问题如果两次生成任务间隔很短第一次的卸载还没完成第二次的加载又开始了会造成显存和内存的抖动。解决方案是在任务队列的 Worker 里加一个模型不可用标志位卸载完成之前不接收新任务self.model_busy True try: result gen_fn(payload) finally: self.model_busy False这个简单的标志位就能避免模型切换时的资源竞争。很多部署文档不会写这一层但实际运行中这个细节决定了并发场景下会不会出现偶发的 OOM。8. 一点经验与后续扩展方向整套部署下来我最深的体会是把两个不同领域的模型放在一起跑难点从来不在模型本身而在资源调度和状态管理。vLLM-Omni 做理解推理时那套优雅的 KV cache 管理到了视频生成场景基本帮不上忙——视频生成的显存峰值和步数挂钩不跟 token 挂钩你必须自己在外面套一层任务队列用老办法排队限流去保护底层模型。这就是为什么我会反复强调互斥锁、状态机、超时这些看着很不 AI的东西它们恰恰是系统稳定运行的关键。再分享一个实操小技巧整个服务部署完成之后我建议做一次连续 50 个任务的压力测试再上线同时把 nvidia-smi 的显存曲线记录下来。很多问题在单次测试里完全看不出来但一旦并发量上来就会暴露比如显存碎片化导致的任务失败。提前用脚本打一轮压力测试比自己上线后半夜被报警电话叫醒要划算得多。后面如果要继续扩展我会优先做两件事。一是把任务队列从内存版换成 Redis 版这样服务重启后任务不会丢失也方便水平扩展多个 Worker。二是给 Wan 后端加检索增强能力比如先让 vLLM-Omni 理解用户的历史偏好再让 Wan 生成更符合口味的视频。这条路子做通了这套服务的价值就不只是能生成视频了而是真正具备了理解和生成闭环的能力。