# api.py 的坑:同时传首尾帧和参考图,`references` 会静默吃掉 `keyframes` **背景**:用户问"H3 怎么把首尾帧和人物设定图一次性都给模型",对照 DiffSynth-Studio 官方 `MiniMaxH3Pipeline.__call__` 源码查清楚了——不是我们代码的锅,是这个 pipeline 本身的设计: `keyframes`(FL2VA,首尾帧)和 `references`(Ref2VA,参考图)在打包序列这一层是**互斥**的, `MiniMax H3Unit_PackedSequenceBuilder` 里 `if ref_blocks is not None` 走 ref2va 分支, 否则才用 keyframes——**只要 `references` 非空,`keyframes` 会被直接忽略,不报错,静默丢弃**。 官方倒是有个 `H3-Context-IR`(真实存在,查到了官方 API 文档),专门负责编排"首帧/尾帧/身份参考图/ 参考视频各自扮演什么角色"这种复杂多模态输入,但官方原话明确写了"不包含在开源发布里"——只有 调用 MiniMax 付费云端 API 才能用,本地部署这条路走不通。 ## 更麻烦的:我们自己的 `api.py` 没有任何防护,而且已经真的踩过 `worker()` 里无条件把 `keyframes`(来自 `first_frame_url`/`last_frame_url`)和 `references` (来自 `reference_image_urls`/`reference_video_urls`)**一起**传给 `pipe()`,`GenReq` 这个 pydantic 模型也完全没有互斥校验——用户完全可以同时填首尾帧 URL 和参考图 URL 提交,系统照单全收, 但底层会按 references 非空走 ref2va 打包,首尾帧完全不起作用,**没有任何报错或警告**。 查了今天真实提交的任务 `ed3663dcda7f`:请求里 `reference_image_urls` 是空数组 `[]`。 prompt 文本里写的"女子、捕快面部由@图片3、@图片5锁定"这类 `@图片N` 写法是照抄官方 Context-IR 的约定,但我们本地这条链路根本不认识这个约定——那两张"设定图"从来没真的作为 图片数据挂载给模型,纯粹是 prompt 里的文字,模型只是把这几个字当普通文字理解,没有对应的 图片输入。以后写 prompt 不要用这种 `@图片N` 的写法指望它会自动关联图片,除非真的把对应 URL 填进 `reference_image_urls`。 ## 要做的事 1. **给 `GenReq` 加一条 `model_validator`**:如果 `(first_frame_url or last_frame_url)` 和 `(reference_image_urls or reference_video_urls)` 同时非空,直接 422 拒绝,错误信息写清楚 "FL2VA 首尾帧和 Ref2VA 参考图/参考视频不能同时传,pipeline 层会静默丢弃 keyframes"—— 参考 `ROWS_LIMIT` 那条校验的写法(`api.py` 里已有先例),不是新模式。 2. **h3-console 前端也要同步**:现在"首尾帧生视频"(fl2va tab)和"多参考图生视频"(ref2va tab) 是两个独立表单,UI 层面本来就不会让用户在同一次提交里同时填两种,这条主要是防后端被绕过 (比如有人直接调 API 不走 console),前端不用大改,但可以在 fl2va tab 的 hint 里加一句 "这个模式不支持额外挂参考图做身份锁定,要锁身份得改用 Ref2VA 模式",减少以后再犯同样的 prompt 写法误解。 3. **给用户的实际建议**(不是代码改动,是使用方式):像"延长视频+保持人物身份"这种场景, 本地开源版做不到"首尾帧强约束 + 身份参考图"同时要,只能二选一改用纯 Ref2VA:不设 `first_frame_url`/`last_frame_url`,把"原视频最后一帧截图 + 女子设定图 + 捕快设定图" 都塞进 `reference_image_urls`(最多 9 张),需要运镜连贯性的话再加 `reference_video_urls` (2026-08-20 新加的字段,还没在真实 GPU 上跑过一次,见 `verify-video-reference-on-gpu.md`)。 代价是失去"第一帧/最后一帧精确锁定"这个强约束,换成"多张参考图做柔性引导",不是等价替换。 ## 优先级 不着急——这是个"用错了会静默出错"的边界情况,不是现在正在发生的故障。但比起线上突然出现 "明明传了首尾帧却不起作用"的困惑,加一条清晰的 422 报错成本很低(半小时内能写完,参考 `ROWS_LIMIT` 校验抄一遍就行),建议跟 `quality-tuning-plan.md` 里的实测一起顺手做掉, 不用单独排期。