# 09 · ComfyUI 实战配方

前面几篇是选型，这篇是"手怎么动"。四条可直接照抄的配方，从低配到高配。

> ComfyUI 版本迭代快，节点名和参数位置会变。以下配方给的是**节点类型名 + 连接顺序 + 关键参数**，具体面板上的位置以你那一版的模板（Workflow → Browse Templates）为准。

---

## 0. 环境准备

```bash
# Python 3.11 + CUDA 12.4
git clone https://github.com/comfyanonymous/ComfyUI && cd ComfyUI
python -m venv venv && source venv/bin/activate
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
pip install -r requirements.txt
pip install huggingface_hub
```

**目录约定**（务必分开，模型很占地方）

```
ComfyUI/models/
├── diffusion_models/   # 主模型（unet/dit），单个 7–28 GB
├── text_encoders/      # umt5-xxl 约 11 GB（fp8 约 5.5 GB）
├── vae/                # 1 GB 以内
├── clip_vision/        # 图生视频参考图编码
└── loras/
```

**首次启动**

```bash
python main.py --listen 0.0.0.0 --port 8188
# 或者加加速（装好 SageAttention 之后）
python main.py --use-sage-attention --fast
```

---

## 配方 A：Wan2.1-1.3B 文生视频（8 GB 显存起步）

**适用**：8–12 GB 卡、批量出草稿、验证流程是否通。

**模型下载**

```bash
hf download Comfy-Org/Wan_2.1_ComfyUI_repackaged --include "split_files/*" --local-dir ./ckpt_wan21
# 把需要的文件移到 ComfyUI/models 对应目录：
#   diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors
#   text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors
#   vae/wan_2.1_vae.safetensors
```

**节点链**

```
CLIPLoader(type=wan) ──► CLIPTextEncode(正向)  ─┐
                     └─► CLIPTextEncode(负向)  ─┤
UNETLoader ──► ModelSamplingSD3(shift=8.0) ────┼──► KSampler ──► VAEDecode ──► CreateVideo ──► SaveVideo
VAELoader ──────────────────────────────────────┤
EmptyHunyuanLatentVideo(w×h×帧数) ──────────────┘
```

**关键参数**

| 参数 | 建议值 | 说明 |
|---|---|---|
| 分辨率 | 832×480 或 480×832 | 1.3B 上限，别硬上 720P |
| 帧数 | 81（≈5 秒@16fps） | 必须是 4n+1 |
| steps | 30 | 25 以下质量掉得明显 |
| cfg | 6.0 | 太高会过饱和 |
| sampler | uni_pc / euler | 1.3B 用 uni_pc 更稳 |
| shift | 8.0 | 影响运动幅度，6–10 之间调 |

---

## 配方 B：Wan2.2-TI2V-5B 图生视频（12–24 GB，主力方案）

**适用**：单张 4090/3090，要 720P 和可控的首帧构图。**本目录最推荐的性价比档。**

**模型下载**

```bash
hf download Comfy-Org/Wan_2.2_ComfyUI_Repackaged --include "split_files/*" --local-dir ./ckpt_wan22
#   diffusion_models/wan2.2_ti2v_5B_fp16.safetensors
#   text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors
#   vae/wan2.2_vae.safetensors   ← 注意：2.2 的 VAE 与 2.1 不通用
```

**节点链（图生视频）**

```
LoadImage ──► ImageScale(短边 720) ─┐
CLIPTextEncode(正向) ───────────────┤
CLIPTextEncode(负向) ───────────────┼──► Wan22ImageToVideoLatent ──► KSampler ──► VAEDecode ──► SaveVideo
UNETLoader ──► ModelSamplingSD3 ────┤
CLIPLoader(type=wan) ───────────────┘
```

**关键参数**

| 参数 | 建议值 |
|---|---|
| 分辨率 | 1280×704 或 704×1280（必须 32 的倍数） |
| 帧数 | 121（5 秒@24fps） |
| steps | 30 |
| cfg | 5.0 |
| sampler | euler / res_multistep |
| 首帧权重 | 别把首帧当"必须一模一样"，留 0.7–0.85 给模型自由度 |

**踩坑**：2.2 的 VAE 与 2.1 不通用，放错会直接报形状错误；分辨率不是 32 的倍数会出现边缘撕裂。

---

## 配方 C：Wan2.2-A14B 高质量双阶段（24 GB，需量化）

**适用**：确定的镜头要出终稿。A14B 是 MoE 双模型（high noise + low noise），**必须两段采样**。

**模型下载**

```bash
# high/low 两个噪声专家 + 图生视频变体
#   diffusion_models/wan2.2_t2v_high_noise_14B_fp8_scaled.safetensors
#   diffusion_models/wan2.2_t2v_low_noise_14B_fp8_scaled.safetensors
#   diffusion_models/wan2.2_i2v_high_noise_14B_fp8_scaled.safetensors
#   diffusion_models/wan2.2_i2v_low_noise_14B_fp8_scaled.safetensors
```

**节点链（双 KSampler 串联）**

```
UNETLoader(high_noise) ──► ModelSamplingSD3 ──► KSampler#1(steps=10/总步数, end_at=0.5)
                                                    │
UNETLoader(low_noise)  ──► ModelSamplingSD3 ──► KSampler#2(start_at=0.5, steps=总步数)
                                                    │
                                              VAEDecode ──► SaveVideo
```

**关键参数**

| 参数 | 建议值 |
|---|---|
| 总步数 | 20（high 10 步 + low 10 步） |
| cfg | 3.5–4.5（A14B 比 5B 更吃低 cfg） |
| shift | 12.0（高噪声段）/ 3.0（低噪声段） |
| fps | 16 或 24，按提示词的意图给 |
| 显存 | fp8 双模型常驻需 ~28 GB；24 GB 卡建议 nunchaku 4-bit 或分段 offload |

**踩坑**：两个 KSampler 的 `start_at/end_at` 必须互补（0→0.5、0.5→1），否则会出现"前段糊、后段糊"或者整体噪点。

---

## 配方 D：对口型收尾（LatentSync / InfiniteTalk）

**LatentSync（只改嘴，18–20 GB）**

```
LoadVideo ──► LoadAudio ──► LatentSyncNode ──► VHS_VideoCombine(带原音轨)
```
参数：`inference_steps=20`、`guidance_scale=1.5`，输出分辨率跟随原视频。

**InfiniteTalk（长口播，24 GB 量化）**

```
LoadImage(或 LoadVideo) ──► InfiniteTalkNode(audio=wav, chunk 长度 81 帧)
        ──► KSampler ──► VAEDecode ──► 拼接(循环块) ──► SaveVideo
```
参数：每块 81 帧、块间用上块尾帧做参考；音频先切成等长片段，否则长口播会累积漂移。

---

## 收尾：放大 + 补帧

```
VAEDecode ──► UpscaleModelLoader(RealESRGAN_x4plus) ──► ImageUpscaleWithModel
          ──► ImageScale(降到 1080P/2K) ──► RIFE VFI(×2) ──► VHS_VideoCombine
```

**顺序不能反**：先放大再补帧，否则补帧会把噪点也放大成"果冻"。
**别拉太狠**：480P 直放 4 倍到 1080P 会明显塑料感，先补到 720P 再适度锐化更真实。

---

## OOM 排查表

| 现象 | 原因 | 处理 |
|---|---|---|
| 一开始就 OOM | 模型没装下 | 换 fp8/GGUF Q4，或 `--lowvram` |
| 采样到一半 OOM | 序列太长（分辨率×帧数） | 降帧数或降分辨率，帧数优先降 |
| 最后一步 OOM | VAE 解码爆显存 | tiled VAE（`VAE Decode (Tiled)`），tile 256 |
| 跑几批后越来越慢/崩 | 显存碎片 | 每批之间重启 worker，或加 `--disable-smart-memory` |
| 速度只有别人的 1/3 | 没开 Sage/注意力后端 | 装 SageAttention，加 `--use-sage-attention` |
| 首帧和输入图差太多 | 首帧权重/编码问题 | 提高首帧约束，或用 I2V 专用模型而非 T2V |

---

## 批量生产：用 API 而不是点鼠标

ComfyUI 的 `/prompt` 接口接受 workflow 的 **API 格式 JSON**（菜单里 Workflow → Export (API)）。

```python
# scripts/batch_generate.py 的核心逻辑
import json, requests, time
wf = json.load(open("recipe_b.json"))          # API 格式
for i, prompt in enumerate(prompts):
    wf["6"]["inputs"]["text"] = prompt          # 6 是正向 CLIPTextEncode 的节点 id
    wf["3"]["inputs"]["seed"] = 1000 + i
    r = requests.post("http://127.0.0.1:8188/prompt", json={"prompt": wf}).json()
    pid = r["prompt_id"]
    while True:                                  # 轮询完成状态
        h = requests.get(f"http://127.0.0.1:8188/history/{pid}").json()
        if pid in h: break
        time.sleep(3)
```

配套脚本放在 `scripts/` 目录：`batch_generate.py`（批量提交 + 轮询 + 失败重试）、`install-comfy.sh`（装环境和插件）、`download-models.sh`（按配方拉模型）。

**生产环境的四条经验**
1. 别让前端直接打 ComfyUI，中间加一层队列（Redis/文件队列都行），否则并发一高就崩。
2. 每条任务记录 `seed + 参数 + 模型版本`，出片好能复现。
3. 失败自动重试一次，再失败写进死信队列人工看——OOM 和显存碎片是主要失败原因。
4. 输出目录按日期分桶，单目录文件超过几千个时 ls 都会卡。
