# 交接文档 · 2026-09-03(最新) > 给下次进来的人(或进程)。**先读这份,再读 `CLAUDE.md`。** > 权威细节在 `llm-setup.md`(整机)、`raylight/README.md`(视频线,2026-08-22起的生产 > 后端)、`h3-console/README.md`(Web 管理台)。时间线在 `CHANGELOG.md`。 > 这里只写「现在是什么状态、下一步该干什么」——下面 2026-08-20 及更早的几节是历史记录, > 架构已经变了(minimax-h3 旧代码已删除,raylight 是现在的生产后端),仍有效的踩坑经验 > 保留,但**状态类的表格/结论以这一节为准,不要用旧的**。 ## 2026-09-03:现在是什么状态 **一句话**:生产 H3 后端是 `raylight/`(ComfyUI + Raylight 插件,4卡当一个 FSDP 集群, 详见 `CLAUDE.md`「当前负载」表),`minimax-h3/` 旧架构代码已经在 8/24 删除只剩权重。 今天这轮会话围绕 raylight + h3-console 做了一批修复/功能,**全部已部署到服务器验证通过、 已提交推送到 git**(最新 `46fa265`),细节见 `CHANGELOG.md` 2026-09-03 那两节: 1. 修了一个真实 bug:参考图/参考视频文件名带中文时,`raylight/server.py` 的 `_fetch_to_input()` 用 `urllib.request.urlopen` 直接崩 `UnicodeEncodeError` (HTTP 请求行必须纯 ASCII,中文没 percent-encode)。 2. **排队优先级/插队**:`Job` 加 `queue_seq`,`Q` 从 `queue.Queue()` 换成 `queue.PriorityQueue()`,新增 `POST /v1/videos/{jid}/prioritize`,console 详情页/ 列表菜单都加了"优先插队"按钮。 3. **重启不再清空排队任务**:这是顺带发现的架构问题——`start.sh` 重启前,排队中的任务 会被无条件标记 `cancelled`(内存队列重启即空,sqlite 里的记录没有任何路径被重新入队)。 现在重启会把排队中的任务原样保留、按序重新入队,只有"正好卡在重启瞬间"那条真运行中的 任务救不回来(这个改不了,GPU 执行状态确实丢了)。**这条改动很重要**:以后改 `raylight/server.py` 要重启,不用再担心把排队任务清空了,只需要挑当前没有任务在跑 的时机(或者接受损失那一条正在跑的)。 4. 补回一个 8/24 提交过、但**从未真正部署到服务器**的 ComfyUI 存活探测(`_comfyui_alive`, 修 `/health` 在 ComfyUI 进程崩溃后仍骗人报 ready 的监控盲区)。顺带把本地长期分叉的 `raylight/server.py`(顶层)和 `raylight/scripts/server.py` 两份文件同步一致。 5. `h3-console` 的 `GET /v1/models` 之前硬编码 3 档过期数据(模型文件名指向已删除的 `api.py`/`gateway.py` 旧架构),改成实时问 raylight 新增的 `GET /v1/resolutions` (`RESOLUTION_CEILINGS` 的镜像),现在正确返回全部 8 档。 6. 任务详情页加了两个排查用字段(默认折叠):**原始参数**(`req` 原样 JSON)、 **ComfyUI History**(懒加载,问 raylight 新增的 `GET /v1/videos/{jid}/comfy_history`, 透传 ComfyUI 自己的 `/history/{prompt_id}`——这份数据在 ComfyUI 内存里,进程重启就没了, 不是 bug)。生成结果视频从页面底部挪到了状态/操作按钮那一行下面。 ### 当前各服务实际状态(2026-09-03 晚实测,不是文档推测) | 服务 | 状态 | 说明 | |---|---|---| | `raylight`(ComfyUI 8188 + server.py 8100) | **在跑**,4卡满载 100% | 16 条排队 + 1 条运行中(正常工作负载,不是卡住) | | `h3-console`(8080,LAN) | **在跑** | `systemctl is-active` → active,今天带新代码重启过 | | `h3-console`(8081,公网 `/console` 子路径) | **在跑** | 同上 | | `llm38` / `whisper` / `m3e-embedding`(docker) | **停**(`Exited`,2周前) | 不是本次操作停的,沿用既有目标状态(这台机器当前只跑 H3) | | GPU 显存 | 4×22528MiB 卡各用 ~11.5G,100% 利用率 | raylight 一直有任务在跑,正常 | | 磁盘 `/` | 81% used,剩 43G | 没报警但在收紧,留意 | | 磁盘 `/data` | 89% used,剩 199G | 同上 | ### 下一步 / 需要留意的点 1. **没有阻塞性待办**——这轮改动都已验证上线,git 已推送。 2. 排队积压 16 条是正常业务量(用户在批量提交任务测试),不是系统故障,仅供下次进来的人 了解上下文,别误判成"卡住了"。 3. `h3-console` 的 `/v1/models` 里 `partitions` 字段的模型文件名这次顺手改成了 raylight 现在实际用的(`minimax_h3_fl2va_pruned_int8_convrot.safetensors` 等),去掉了对不上号 的 `lora` 字段——这块用户没有明确要求改,只是顺手核实清楚了,如果有外部消费者依赖旧字段 形状需要留意。 4. 磁盘两边都过 80% 了,暂时没影响什么,但没有余量报警,紧张起来会跟其它任务一样直接写 失败——跟 `llm-setup.md` 里旧的磁盘提醒是同一类风险,值得找时间清一次。 --- # 交接文档 · 2026-08-20(历史,状态类结论已过时,见上面最新一节) > ⚠️ 下面这节写的时候 `minimax-h3/gateway.py` 还是生产后端,`raylight/` 还没上线。 > 2026-08-22 起 raylight 才是生产默认后端,minimax-h3 代码已在 8/24 删除。看这节纯为 > 保留历史踩坑记录,不要照着下面的服务状态表操作。 ## 2026-08-20 夜:现在是什么状态 **一句话**:`h3-console`(Web 管理台)今天从零做到部署上线并经过好几轮真实使用反馈迭代 (重跑、回收站、任务记录落盘、假运行中修复、公网子路径接入),本地代码已建 git 仓库推到 `git@gitlab-wuwei.hexpm.com:yibo/2080tiserver.git`。**同时发现一个容易踩的坑**: `qwen.wudaima.com` 这个域名现在实际没有在提供 Qwen LLM 服务——见下面「重要」。 ### ⚠️ 重要:qwen.wudaima.com 现在不是 LLM,是 minimax-h3 的 gateway.py - `docker ps` 是空的——**`llm38` 容器现在没有在跑**(不是本次改的,是之前某个时间点被停的, 可能是之前的会话为了腾显存给 H3 四卡停的,没有记录明确是谁什么时候停的)。 - 端口 8000 现在被 `minimax-h3/gateway.py` 占着(`ss -lptn` 能看到是个 python 进程,不是 llama.cpp)。frp 的 `qwen-llm` 代理转发的正是本机 8000 端口,所以**公网 `http://qwen.wudaima.com/health` 现在返回的是 gateway.py 的 JSON(`{"ready":4,...,"backends": [...]}`),不是 Qwen 的 `/health`**,`qwen.wudaima.com/v1/videos` 现在也是能直接无鉴权提交 视频生成任务的(跳过了 h3-console 那层)。 - **要恢复 Qwen LLM 公网服务**:先确认 minimax-h3 的四个 worker 不在关键任务中途, 停掉占着 8000 的 gateway.py(`ss -lptn "sport = :8000" | grep -o "pid=[0-9]*"` 找 PID 再 `kill`——不是 PM2 托管的,直接 kill 有效),再 `docker start llm38`(约 15 秒)。 两者不能共存在同一个 8000 端口上。 ### 当前各服务实际状态(2026-08-20 深夜实测,不是文档推测) | 服务 | 状态 | 说明 | |---|---|---| | `llm38`(Qwen3.8-27B,docker) | **停**(`docker ps` 为空) | 恢复见上面「重要」一节 | | `whisper`(docker,8002) | **停** | 同上,docker 里没有容器在跑 | | PM2 `m3e` / `sd-forge` | **停** | 沿用 2026-08-19 就定的目标状态,没有变化 | | minimax-h3 四个 `api.py` worker(8010-8013) | **在跑** | 分区会被弹性调度器动态 flip,此刻实测是 3 张 fl2va + 1 张 ref2va(GPU0 ref2va,GPU1/2/3 fl2va),不是固定的,正常 | | `minimax-h3/gateway.py`(8000) | **在跑**,占着 `qwen.wudaima.com` | 见上面「重要」 | | `h3-console`(8080) | **在跑** | 局域网直连 `http://192.168.110.67:8080`,开着弹性调度器(`H3_ENABLE_SUPERVISOR=1`) | | `h3-console`(8081,子路径专用) | **在跑** | 公网 `http://qwen.wudaima.com/console`,`H3_CONSOLE_BASE_PATH=/console`,**不开**弹性调度器(两个调度器同时抢会重现"一个请求切三张卡"的老 bug),跟 8080 那个共享同一份 `console.sqlite3`/`assets/` | | GPU 显存(`nvidia-smi`) | GPU0 5.3G / GPU1 21.3G / GPU2 19.4G / GPU3 18.5G(共 88G) | llm38 停着,所以余量看着比 CLAUDE.md 顶部旧表格写的宽松,但四张卡跑满 minimax-h3 也会吃紧,起新负载前还是先看一眼 | ### 今天做的事(详细踩坑记录都在 CHANGELOG.md,这里只列条目) 1. h3-console 任务详情页加「重跑」按钮(第一版直接后台重投,被否决后改成回填表单跳转新建页)。 2. 修复「后端进程重启后任务永远显示假运行中」——`_refresh_from_gateway` 遇到 404 现在正确判 `failed`。 3. `api.py` 的 `frames` 硬顶 124 改成按 `rows`(分辨率×帧数)动态判定,`ROWS_LIMIT=20000` (支持 `H3_ROWS_LIMIT` 环境变量临时覆盖,仅供边界探测用)。 4. `start-all.sh` 冷启动默认分区从「4 张全 ref2va」改成 2/2 固定分裂(GPU0/1 ref2va,GPU2/3 fl2va)。 5. 左下角后端状态行补上 `running×N`/`queued×N`,之前明明有任务在跑也只显示 `ok`。 6. `api.py` 任务记录改成落盘(`jobs_gpu{N}.sqlite3`),弹性调度器 kill+restart 某张卡时不再 把这张卡上正在跑的任务从内存里一起清空。 7. 任务列表加软删除/回收站(`deleted_at` 列 + `?trash=true` + 恢复接口)。 8. 排查清楚 `gateway.wudaima.com` 独立域名为什么公网打不通——缺口在 frp 服务端 `60.205.214.169`,不是这台机器能改的,**没有再等它**。 9. 改走 `qwen.wudaima.com/console` 子路径对外开放 h3-console(`H3_CONSOLE_BASE_PATH` 支持 + 起第二个 8081 实例 + frp `locations=/console` 分流),验证通过、`qwen.wudaima.com` 原有 服务没受影响(因为它现在本来就不是 LLM,见上面「重要」)。 10. 补齐了本地 `minimax-h3/` 镜像里此前一直缺失的 `gateway.py` 等 13 个文件(和服务器漂移的 缺口),本地建了 git 仓库并推到 `git@gitlab-wuwei.hexpm.com:yibo/2080tiserver.git`。 `CLAUDE.md` 里明文写着的 SSH 密码也一起提交了,跟用户确认过、这是有意为之。 ### 下一步(按优先级) 1. **决定 Qwen LLM 要不要恢复**——现在公网 `qwen.wudaima.com` 实际在裸露 minimax-h3 的 gateway API(无鉴权),如果这不是有意的,需要判断是先恢复 llm38 还是先给 gateway.py/ h3-console 补鉴权。这是产品/安全决策,需要用户拍板,不要擅自停 gateway.py 起 llm38 ——四个 `api.py` worker 现在正被 `gateway.py` 管着,直接停可能打断正在跑的生成任务。 2. **`gateway.wudaima.com` 独立域名**:如果还想要这条线(而不是永久用 qwen 子路径顶替), 需要找到能登录 `60.205.214.169` 的人补一条 nginx vhost 转发规则。 3. **鉴权**:h3-console 和 gateway.py 现在都无鉴权,公网可直调。`h3-console/DESIGN.md` 问题3 有现成方案(`Authorization: Bearer` + 环境变量),跑通了再加,一直没排上。 4. 磁盘 `/` 只剩 29G,`/data` 83% 满——暂时没出问题,但没有余量报警,真紧张时会和其它任务一样 直接写失败。 --- # 交接文档 · 2026-08-18(历史,状态类结论已过时,见上面最新一节) ## 一句话 今天做了两件事:**llm38 单请求上下文 32K → 256K**(已完成、已验收、已恢复), **MiniMax-H3 文生视频从零部署到出片 + 封了 HTTP API**(已出片、已验收,**服务当前在跑,环境未恢复**)。 ## 2026-08-19 16:30 更新:断连 13 小时后重连 - **50 步全质量已完成并验收**(6.4 MB · 5364 s · 107.3 s/步 · 峰值 9.1 G · 帧间差异 7.366 · 音频 −27.2 dB)。 两档都出片,详见 `minimax-h3/README.md` 验收记录。 - **断连原因是本地网络**:Mac 到 192.168.110.x 的路由被 VPN 隧道(`utun7` / `198.18.0.1`)劫持。 期间 `nc -z` 对**所有**端口都返回成功(连 9999 和只听 127.0.0.1 的 7860 也"通")—— **那是假阳性,别据此判断服务状态**。用 `ping` + `route -n get` 确认路径。 - **期间另有进程在这台机器上工作**:两个 H3 API 实例常驻(8010 → GPU1 13.0 G / 8011 → GPU2 7.7 G, 两者 `/health` 均 ok),`api.py` 被改过(新增 `PARTITION` 字段),新增 `restart-8010.sh`, `outputs/` 里有 6 个非本次生成的视频。 ## ★ 当前是用户指定的目标状态:只留 H3 ★ **2026-08-19 16:40 用户明确:「停止 llm38」「只留着 h3 即可」。** 所以下面这四个**停着是对的,不要"顺手"起回来**: | 服务 | 状态 | 说明 | |---|---|---| | `llm38`(8000) | **停** | 配置已是 256K 单请求上下文并验收过,需要时 `docker start llm38` | | `whisper`(8002) | **停** | `docker start whisper` | | PM2 `m3e`(7590) | **停** | `/usr/local/node/bin/pm2 start m3e` | | PM2 `sd-forge`(7860) | **停** | `/usr/local/node/bin/pm2 start sd-forge` | | **H3 API 8010 / 8011** | **运行中** | 唯一保留的负载,占 GPU1+GPU2 共 20.8 G | GPU0 与 GPU3 全空(各 22002 MiB)。恢复命令详见 `deploy-scripts/RESTORE-GPU.md`。 > ⚠️ 要恢复 llm38 时注意:它跑 256K 需**四卡各约 14.3 G**,而 GPU1 现被 8010 占着只剩 8.95 G。 > 要共存就用 `--parallel 1 --ctx-size 262144`(单请求仍 256K,并发降为 1,省 16 G), > 或先停一个 H3 实例。 ## ⚠️ 立刻要知道的三件事 1. **四个服务当前是停的,而这是用户要的状态**(见上一节),不是未完成的收尾。 需要时按 `deploy-scripts/RESTORE-GPU.md` 恢复,恢复后验 7590 / 7860 / 8000 / 8002 四个端口。 2. **`m3e` 和 `sd-forge` 由 PM2 托管**(God Daemon PID 1467)。`kill` 它们**没用**, 60 秒内会被拉回来;必须 `/usr/local/node/bin/pm2 stop m3e sd-forge`。恢复是 `pm2 start`。 pm2 不在默认 PATH 里,非交互 shell 下写全路径。 3. **一条 50 步全质量视频还在跑**(GPU2,22:22 启动,约 90 分钟,预计 23:50 左右完), H3 的 **API 服务也在跑**(GPU1,端口 8010)。恢复环境前先确认这两个结束/停掉。 ## 状态速查 | 东西 | 状态 | 位置 | |---|---|---| | llm38(Qwen3.8-27B) | **已停**。配置已改成 256K 单请求上下文,验收过 | `deploy-scripts/ctx256k.sh` | | llm38 旧容器 `llm38_prev` | 保留着(32K 配置),确认稳定后可 `docker rm` | `/` 只剩 29 G,别忘 | | whisper / m3e / sd-forge | **已停** | `deploy-scripts/RESTORE-GPU.md` | | MiniMax-H3 权重 34 GB | 下载完 + 字节级校验 `ALL GOOD` | `/data/models/minimax-h3/models/` | | H3 出片 | **成功**,已验收(见下) | `outputs/t2va_20260818-223136.mp4` | | H3 API | **在跑**,GPU1:8010 | `minimax-h3/API.md` | | 50 步全质量 | **进行中**,结果未验 | `logs/real-full50.log` | ## 今天最重要的一条结论 **`minimax-h3/README.md` 原第一节写「`bfloat16` → `float16`(致命)」是错的。 改成 fp16 才是这条线一直出不了片的原因。** 对照实验(唯一变量是 dtype,448×256×28f): | | fp32 | fp16 | |---|---|---| | 视频 | mean 140.9 **std 50.6** | mean 0 **std 0** | | 音频 | finite | NaN | | 结果 | **出片 ✅** | **纯黑 ❌** | 原因:模型按 bf16 训练(bf16 有 fp32 的动态范围),sm_75 没有 bf16, fp16 的 65504 上限装不下——`dit.blocks.0` 输入 absmax 89 就放大到 38176,50 层残差累积必爆。 **逐点修 fp16 是行不通的**,我试了四条弯路全部失败(`--fp32-accum` 无效、替换 forward 绕过换入、 转换处溢出、钳位到上限反而自造故障),细节和实测数据都在 `minimax-h3/README.md`。 `t2va.py` 的 `--dtype` 现在**默认 fp32**,保留 `fp16` 只为复现故障。 ## 验收数据(都是实测,没有推测) **H3 出片**:832×480 · 24fps · 124 帧 · 5.17 s · 7.5 MB · 像素 std 57.85 · **帧间差异 16.99(证明是动态视频)** · 音频 aac 32 kHz 立体声 mean −34.0 dB / rms 0.0199(**证明不是静音**)。 **性能**(fp32,单任务):全分辨率 Turbo 4 步约 **107 s/步**,峰值显存 **12.2 G**, 首次加载 238 s,page cache 命中后 12–20 s。fp16 是 36.8 s/步但出不了片。 **llm38 256K**:`n_ctx_slot = 262144`(模型原生上限,GGUF 元数据 `qwen35.context_length`), 150 K token 大海捞针 `truncated=0` 通过,显存 57.3 G,并发从 4 降到 2, 256 K 提示 prefill 约 4.5 分钟。 ## 下一步(按优先级) 1. **收尾**:等 50 步跑完 → 验收(`bash /tmp/verify-out.sh <文件>`,脚本在服务器 `/tmp`, 要长期用得挪到 `scripts/`)→ 补 `README.md` 验收记录 → **恢复四个服务并验端口**。 2. **决定 llm38 与 H3 的共存策略**。两者显存放不下(57.3 + 12.3 > 单卡余量)。 选项:H3 按需起停;或 llm38 降到 `--parallel 1 --ctx-size 262144`(保 256 K 单请求, 并发降为 1,省 16 G);或换卡。**这是产品决策,需要用户拍板。** 3. **安全**:`qwen.wudaima.com` 公网无鉴权可直调 27B;H3 API 也无鉴权。 SSH 22 经 FRP 映射到公网 6000 且允许密码登录、admin 在 sudo 组。**都没动,等授权。** 4. 可选:`t2va.py:47` 的 `max(6.0, ...)` 地板值在空闲显存 < 8 G 时会要一个不存在的额度, 应改成显式报错。串行跑绕开了,多负载共存时会咬人。 ## 踩过的坑(别重踩) - **退出码不可信**:原 `t2va.py` 只查 audio,全黑视频照样 `[done]` + 退出码 0。 已补 video 纯色检查,坏产物退出码 3。API 的 worker 也会验 std 和音频有限性。 - **诊断配置必须和故障配置逐项一致**:我用中文 prompt 诊断、英文默认 prompt 复现, 溢出点不同,白跑几轮。 - **prompt 必须从文件读**:`ssh … bash -c "…"` 三层 shell 会吃掉引号,传进去是空串, 报错是 `cannot reshape tensor of 0 elements`——看着像 transformers 的 bug,实际是输入为空。 - **`cmd1 && cmd2 &` 的 `&` 作用于整条链**:`cat > file && ... &` 会让文件写入和执行竞争, 我因此跑了一个 0 字节的脚本还以为它成功了。上传和启动要分两步。 - **`pgrep -f "gunicorn.*7590"` 会匹配到你自己那条 SSH 命令行**,照着 kill 会误杀。 用 `nvidia-smi --query-compute-apps=pid` 反查。 - **`ffmpeg -af volumedetect` 的输出容易被 grep 漏掉**,误报"无音频流"。 用 PyAV 直接解码算 rms 更可靠。 - **合成小张量的 probe 测不出真问题**:`probe-nf4-fp16.py` 验证「单层 NF4 fp16 不溢出」通过, 但推不出「50 层残差不溢出」。 ## 本地与服务器的对应 | 本地 | 服务器 | |---|---| | `deploy-scripts/` | `/home/admin/qwen38-deploy/` | | `llm-setup.md` | `/home/admin/llm-setup.md` | | `minimax-h3/scripts/`、`API.md`、`README.md` | `/data/models/minimax-h3/`(`run.sh`/`api.py`/`realrun.sh` 在顶层) | **服务器那份才是运行的那份**,两边会漂移。今天就因为只在服务器上修了导入、 本地那份没同步,重新生成脚本时把旧 bug 带了回去,白跑一轮。