dressgen-admin-tool.md 7.81 KB

人衣图生成 admin 工具 — 现状与待办

独立于主线换装跳舞直播间任务序列(T1-T39,见 dev-plan*.md)之外的一个 standalone 工具:admin-dressgen.html(新建任务)+ admin-dressgenlist.html(任务列表)+ admin-dressgendetail.html(任务 详情)。输入人物定妆照(全身/脸部 × 正/侧/背,传几张算几张,至少 1 张)+ 1 张新衣服图,依次生成这位人物穿新衣服的全身正/侧/背 3 张图。 2026-09-03 当天从头做到这里,记录一下现状,避免下次接着做时重新踩一遍 已经踩过的坑。


1. 已完成

  • 异步任务队列(不是同步阻塞接口——最早的同步版本真的把前端代理 拖到 502 超时过一次):POST /api/admin/dressgen 立即返回 status:"queued" 的任务行,runDressGenViews()(server.js)在响应 发出后 fire-and-forget 依次(不并发)跑 3 个视角,每一步都写回 dressGenRecords(dress-gen.json),前端轮询 GET /api/admin/dressgen/:id 看进度。
  • 重试:POST /api/admin/dressgen/:id/retry(整体重试,只重跑没成 功的视角)、POST /api/admin/dressgen/:id/retry/:view(单张重试, 可选带 {prompt} 覆盖那一张的 prompt)——都复用任务自己存的图片/ prompt,不用重新上传、不用重新填 prompt。两个重试接口都额外接受可选 {provider} 覆盖,手动把某个任务/某一张换供应商重跑。
  • 两个供应商都已接入并验证成功:
    • GPT Image 2(万界方舟):https://maas-openapi.wanjiedata.com /api/v1/images/edits,model:"GPT Image 2"(带空格,写错会被 WAF 拦 410 或报 unauthorized),rejectUnauthorized:false(万界这个 host 的证书链在这台机器上验证不过,-k),300s 超时,60-180秒/张。
    • Seedream v4(wavespeed.ai 的 bytedance/seedream-v4/edit):上传 POST /api/v3/media/upload/binary → 提交 POST /api/v3/{model} → 轮询 GET /api/v3/predictions/{id}/result,约 30 秒/张,$0.027/次, 无 TLS 证书问题、无内容审核。.env 已配置 WAVESPEED_API_KEY。
    • 默认行为(2026-09-03 当天二次确认的产品决策,覆盖了更早的"不做 自动切换"那版):新任务默认用 image2;某一视角 image2 失败(不 区分技术故障还是审核拒绝)→ 自动改用 wavespeed 重试一次,成功的话 该视角 viewProvider[view] 记为 "wavespeed",前端详情页会显示 "实际使用: Seedream v4"。手动重试接口仍可传 {provider} 强制指定。 这条决策的取舍写在 server.js 里 WAVESPEED_API_KEY 声明上方的 注释里,包含为什么会改主意——下次改这块之前先看那段注释。
  • 17 个自动化测试全过(dressgen-generate.test.js:任务创建即时 返回、轮询到终态、per-view 自定义 prompt、单视角失败不拖累其它两个、 整体/单视角重试(含 provider 覆盖)、非法路径防护、wavespeed 供应商 路径、image2→wavespeed 自动降级等)。
  • 前端拆成三个页面(原来是"新建+列表"揉在一张页面里,"详情"按钮在 宽表格右侧、窄窗口下需要横向滚动才能看到,容易被当成"消失了"):
    • admin-dressgen.html:新建任务表单,提交后跳到该任务的详情页。
    • admin-dressgenlist.html:任务列表,操作列(详情/重试/删除)用 position: sticky; right: 0 钉住,横向滚动时始终可见;所有缩略图 (人物参考图/衣服图/生成结果)点击可放大预览(一个简单的 lightbox,点哪张放大哪张,点遮罩关闭)。
    • admin-dressgendetail.html:单任务详情(原 admin-dressgen-task. html 改名),生成结果图也支持点击放大,每张视角显示"实际使用"的 供应商。
    • 其它 admin 页面导航栏的"人衣图生成"链接都改指向 admin-dressgenlist.html(列表作为默认落地页,列表页右上角 "+新建任务" 去创建页)。

2. 这个工具跟直播间生产流水线是什么关系——已有结论(2026-09-04)

一度以为 dressgen 是"先出人衣图、再生成跳舞视频"两段式流程里的 第一段,需要接进 characters/garments 库和 generateDanceVideo 流水线。实测下来不需要——详见 docs/todo/dressup-dance-room-mvcp.md M.6.1:4 组真实 H3 Console 对比 测试显示,人物身份稳不稳,关键是"参考图张数够不够",不是"要不要先合 成换装图";直接给一步法喂人物的多张原始参考图(不用先换装)+ 1 张衣服图,效果跟先用 dressgen 合成换装图再喂效果相当,路径还更简单。

结论:dressgen 就停在"独立小工具"这个定位——管理员想看"这件衣服穿 这个人物大概什么样"时用一下,不接入生产流水线。 之前"存为新人物" 那条待办也就不用做了(人物库要做的是"支持多张原始参考图",不是"接 dressgen 的合成图当定妆照",两者是不同的改动,后者已确认不需要)。

3. 待办(更新,2026-09-04)

  • [x] "存为新人物"按钮 —— 已确认不需要,见上一节。
  • [ ] 背景不统一:同一任务里正/侧/背三张图背景经常不一致(比如侧 面保留了人物参考图原本的暖色调拱廊庭院,正/背却换成了纯白影棚)。 已经在 defaultDressgenPrompt() 里加了明确指令(背景/地面材质/ 光照都要跟人物参考图一致,三视角统一),这版 prompt 改动还没 实测,下次跑 dressgen 任务时留意一下效果,不够的话再考虑做一张 统一的背景参考图传进去(用户 2026-09-04 提的备选方案)。

4. 已完成(第二批,2026-09-03 当天晚些时候)

  • 任务僵死检测:server.js 的 resumeDressgenTasks(),进程启动时 (resumeGenerations()/resumeDressupGenerations() 旁边)扫一遍 dressGenRecords,把仍停在 queued/generating 的视角标记为 failed(错误信息"进程重启,任务丢失,请点重试",前端详情页会照常 显示),并按 T8 generationJobs 同款逻辑重算整条任务的 status。 没有可恢复的 in-flight 请求 id(image2/wavespeed 调用是 runDressGenViews 里直接 await 的 HTTP 往返,进程一退什么都不剩), 所以是标失败而不是真的续跑——用户点"重试"即可,跟真实生成失败的 体验一致。测试:dressgen-generate.test.js 里专门起了一个独立 cwd/mock(mock 故意不响应,制造一个真实卡在 generating 的视角), SIGKILL 模拟崩溃,重新起一个指向同一份 dress-gen.json 的进程, 断言恢复后的状态。
  • 列表分页:GET /api/admin/dressgen 现在接受 ?page=&pageSize= (默认 pageSize=20,上限 100),按 createdAt 倒序(最新的在前) 切片,返回 {items, total, page, pageSize}(之前是裸数组,前端自己 .reverse()——这是个破坏性的响应格式变更,前端和测试已同步改掉)。 admin-dressgenlist.html 加了"上一页/下一页",操作列还是 sticky, 互不冲突。

5. 相关文件

  • server.js:IMAGE2_*/WAVESPEED_* 常量、generateDressImage/ generateDressImageViaImage2/generateDressImageViaWavespeed/ runDressGenViews(含自动降级分支)/dressGenImageAbsPaths、 /api/admin/dressgen* 路由
  • dressup-store.js:dressGenRecords
  • dress-gen.json:数据文件
  • admin-dressgen.html(新建)/ admin-dressgenlist.html(列表)/ admin-dressgendetail.html(详情):前端
  • dressgen-generate.test.js:测试
  • .env:IMAGE2_API_KEY / IMAGE2_API_BASE / WAVESPEED_API_KEY / WAVESPEED_API_BASE