diff --git a/pipeline_platform/platform_ability.py b/pipeline_platform/platform_ability.py index 9f999c2..dbba64e 100644 --- a/pipeline_platform/platform_ability.py +++ b/pipeline_platform/platform_ability.py @@ -578,6 +578,19 @@ async def _h_apply_llm_config(sor, params, ctx): if not models: return "spec.models 为空——文档里没有可配置的模型?" + # 能力一致性守卫前置(2026-09-06 wan3.0 二次教训):必须在 caps 计算/模板 + # 生成之前跑——上一版放在模型循环里,纠正 t2v→r2v 时模板已按 t2v 生成, + # profile_ids 无 r2v 键,模型更新的 IF 空值保护把旧 t2v 模板原样保留 + # (实测:template_notes 有纠正记录、库里 cap 仍是 t2v)。 + cap_notes = [] + for m in models: + _fix, _warn = _capability_guard( + m.get("capability") or "", m.get("description") or "") + if _fix: + m["capability"] = _fix + if _warn: + cap_notes.append("%s: %s" % (m.get("vendor_model_id") or "?", _warn)) + # 能力分类校验(2026-09-05 用户定夺的规则):模型能力必须是字典已登记的 # 分类;不存在则拒绝落库——先加能力分类(appcodes_kv llm_capability, # 含种子/提示词/端点注释四处同步),再配模型。防 LLM 静默塞进近似分类。 @@ -672,6 +685,7 @@ async def _h_apply_llm_config(sor, params, ctx): profile_ids = {} tpl_errors = [] tpl_notes = [] + tpl_notes.extend(cap_notes) # 能力守卫纠正/告警先进 notes(前置阶段产生) caps = sorted(set((m.get("capability") or "t2t") for m in models)) for cap in caps: # 名称过滤必须加(2026-09-05 e2e 实测 bug):查询/下载步骤模板同库同协议 @@ -744,15 +758,6 @@ async def _h_apply_llm_config(sor, params, ctx): cap = m.get("capability") or "t2t" sync_mode = "async" if str(m.get("sync_mode") or "").strip() == "async" else "sync" desc = (m.get("description", "") or "").strip() - # 能力一致性守卫(2026-09-06 wan3.0 实测):描述模态声明 vs capability - # 铁证矛盾时代码级纠正——提取 LLM 照示例误判,提示词铁律不保证遵守 - cap_fix, cap_warn = _capability_guard(cap, desc) - if cap_fix: - tpl_notes.append("%s: %s" % (vmid, cap_warn)) - cap = cap_fix - m["capability"] = cap_fix - elif cap_warn: - tpl_notes.append("%s: %s" % (vmid, cap_warn)) # 出处标注(2026-09-05 用户规则):模型注册表描述字段必须保存文档 URL if doc_url and ("[出处:" + doc_url + "]") not in desc: desc = (desc + " " if desc else "") + "[出处:%s]" % doc_url @@ -787,13 +792,15 @@ async def _h_apply_llm_config(sor, params, ctx): # query_profile_ids 同理要 IF 保护(2026-09-06 实测 bug):本次规格没带 # async_steps 时空串覆盖会把模型已挂好的查询模板清空 → 异步测试报 # 「未登记查询步骤模板」。空值=没提取到,不是「要清除」。 + # capability 必须写回(2026-09-06 wan3.0 二次教训):守卫纠正 t2v→r2v + # 后模型行能力不更新=纠正只改了模板没改注册表,运行时仍按旧能力路由 new_pid = profile_ids.get(cap, "") await sor.sqlExe( - "UPDATE llm_model SET description=${d}$, " + "UPDATE llm_model SET description=${d}$, capability=${cap}$, " "sync_mode=${sm}$, query_profile_ids=IF(${q}$='', query_profile_ids, ${q}$), " "profile_id=IF(${p}$='', profile_id, ${p}$), updated_at=NOW() " "WHERE id=${i}$", - {"d": desc, "sm": sync_mode, "q": query_ids_json, + {"d": desc, "cap": cap, "sm": sync_mode, "q": query_ids_json, "p": new_pid, "i": mid}) await sor.sqlExe("COMMIT", {}) updated.append(vmid)