From f6fa3ea6530de95c579af6440f19ab1a61abd97a Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 4 Oct 2026 23:12:00 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(#2184):=20VLM=20=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E8=A7=A3=E6=9E=90+=E6=89=A9=E5=B1=95=E5=A4=96?= =?UTF-8?q?=E8=B2=8C=E5=B1=9E=E6=80=A7,=E4=BF=AE=E5=A4=8D=E4=BF=A1?= =?UTF-8?q?=E4=BB=BB=E9=93=BE=E7=AC=AC4=E5=A4=84=E6=96=AD=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题根因:#2177 XML重构后VLM prompt契约改为顶层标签, 人物信息不再是属性。worker的_xml_to_product只读product属性, VLM输出被忽略,所有人物判断走默认"无人像", 导致:preheat跳过→现场t2i跳过→真人图直传Seedance→400 portrait_intercept→降级纯t2v(无参考图→人物相似度仅30-40%) 修复: 1. _xml_to_product解析顶层标签,拼装portrait_prompt供信任链使用 2. 扩展VLM 标签新增外貌属性:hair/skin_tone/face_shape/outfit(原6属性→10属性) 提供足够细节(60-100字外貌描述)给Seedream t2i生成还原度高的AI人像 3. 同步更新example为新属性格式 4. 兼容旧schema:product属性仍有portrait_prompt时优先使用 5. 无product标签但时也构造结果,不丢人物信息 --- apps/worker/worker_app/tasks/viral_video.py | 66 ++++++++++++++++++++- packages/application/viral_video/prompts.py | 4 +- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/apps/worker/worker_app/tasks/viral_video.py b/apps/worker/worker_app/tasks/viral_video.py index 8097c73bf..d0395d9c7 100644 --- a/apps/worker/worker_app/tasks/viral_video.py +++ b/apps/worker/worker_app/tasks/viral_video.py @@ -404,6 +404,50 @@ def _analyze_single_image( product_nodes = [n for n in nodes if n["tag"] == "product"] scene = xp.text_of(raw_text, "scene") or "通用" mood = xp.text_of(raw_text, "mood") or "" + # #2184: #2177 XML 重构后人物信息放在顶层 , + # 不再是 的 portrait_prompt 属性。需从顶层 people 标签提取并拼装 portrait_prompt。 + portrait_prompt = "无人像" + try: + people_node = xp.find_first(raw_text, "people") + if people_node: + _pa = people_node.get("attrs") or {} + _has_person = xp.attr_bool(_pa.get("has_person"), False) + if _has_person: + _gender = _pa.get("gender", "无法判断") or "无法判断" + _age = _pa.get("age_range", "无法判断") or "无法判断" + _hair = _pa.get("hair", "无法判断") or "无法判断" + _skin = _pa.get("skin_tone", "无法判断") or "无法判断" + _face = _pa.get("face_shape", "无法判断") or "无法判断" + _outfit = _pa.get("outfit", "无法判断") or "无法判断" + _pose = _pa.get("pose", "无法判断") or "无法判断" + _expr = _pa.get("expression", "无法判断") or "无法判断" + _count = xp.attr_int(_pa.get("count"), 1) + _parts = [] + if _gender != "无法判断": + _g = _gender + ("性" if not _gender.endswith("性") else "") + _parts.append(_g) + if _age != "无法判断": + _parts.append(_age) + _parts.append("人物") + if _hair != "无法判断": + _parts.append(_hair) + if _skin != "无法判断": + _parts.append(f"{_skin}肤色") + if _face != "无法判断": + _parts.append(f"{_face}脸型") + if _outfit != "无法判断": + _parts.append(f"身着{_outfit}") + if _pose != "无法判断": + _parts.append(f"姿态{_pose}") + if _expr != "无法判断": + _parts.append(f"表情{_expr}") + portrait_prompt = ",".join(_parts) + logger.info( + "[爆款视频] 图片 #%d 解析: count=%d gender=%s age=%s hair=%s skin=%s face=%s outfit=%s pose=%s expr=%s → %s", + idx, _count, _gender, _age, _hair, _skin, _face, _outfit, _pose, _expr, portrait_prompt, + ) + except Exception as _pe: + logger.warning("[爆款视频] 图片 #%d 解析标签异常: %s,回退无人像", idx, _pe) for p in product_nodes: a = p["attrs"] text_on_pkg = a.get("text_on_package", "") @@ -419,6 +463,10 @@ def _analyze_single_image( appearance = a.get("appearance", "") or "无法判断" packaging = a.get("packaging", "") or "无法判断" summary = a.get("summary", "") or f"{brand} {name}" + # 优先取 product 属性上的 portrait_prompt(兼容旧schema),否则用顶层 解析结果 + _pp_from_attr = a.get("portrait_prompt", "") + if _pp_from_attr and _pp_from_attr != "无人像": + portrait_prompt = _pp_from_attr return { "name": name, "brand": brand, @@ -429,10 +477,26 @@ def _analyze_single_image( "key_features": feat_list or [features] if features else ["无法判断"], "scene": scene, "mood": mood, - "portrait_prompt": a.get("portrait_prompt", "无人像"), + "portrait_prompt": portrait_prompt, "summary": summary, "_source": "xml", } + # 没有 product 标签但有 也要能取到人物描述(兜底) + if portrait_prompt != "无人像": + return { + "name": "未识别", + "brand": "无法判断", + "category": "无法判断", + "appearance": "无法判断", + "packaging": "无法判断", + "text_on_package": [], + "key_features": ["无法判断"], + "scene": scene, + "mood": mood, + "portrait_prompt": portrait_prompt, + "summary": "未识别", + "_source": "xml_no_product", + } return _vision_fallback(idx, "no_product_tag") def _normalize(raw, source: str) -> dict: diff --git a/packages/application/viral_video/prompts.py b/packages/application/viral_video/prompts.py index 51a19151d..9307abe17 100644 --- a/packages/application/viral_video/prompts.py +++ b/packages/application/viral_video/prompts.py @@ -50,7 +50,7 @@ _IMAGE_ANALYSIS_SYSTEM = f"""你是电商商品视觉分析师,负责从商品 请严格按下面的标签格式输出,标签名一个都不能改,不要输出任何解释,不要用代码块: 下面每个产品用一个 标签,属性 name 是产品名、features 是外观特征、position 是 main 或 secondary、image_index 是第几张图(从0开始)。 下面每个主要颜色用一个 标签,属性 hex 是色值、name 是颜色名、coverage 是占比小数。 - 用一个标签,属性 has_person、count、gender、age_range、pose、expression 分别描述人物情况。 + 用一个标签,属性 has_person、count、gender、age_range、hair(发型发色)、skin_tone(肤色)、face_shape(脸型)、outfit(穿着)、pose(姿态)、expression(表情)分别描述人物外貌。有人物时属性尽量具体(如hair="黑色长直发"、outfit="白色衬衫"),无人像时除has_person=false外其他填"无法判断"。 标签写画面整体情绪氛围。 下面每处可见文字用一个 标签,属性 text 是文字内容、position 是位置。 标签写场景描述。 @@ -73,7 +73,7 @@ _IMAGE_ANALYSIS_EXAMPLE = """ - + 干净、实用 -- 2.54.0 From cb911f5eba5d698d4a7c43d2a222c370e3f9eb0c Mon Sep 17 00:00:00 2001 From: CI Bot Date: Sun, 4 Oct 2026 15:23:27 +0000 Subject: [PATCH 2/2] style: auto-format with black + isort + ruff + prettier [skip ci-format-check] --- apps/worker/worker_app/tasks/viral_video.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/worker/worker_app/tasks/viral_video.py b/apps/worker/worker_app/tasks/viral_video.py index d0395d9c7..ffc75cff2 100644 --- a/apps/worker/worker_app/tasks/viral_video.py +++ b/apps/worker/worker_app/tasks/viral_video.py @@ -444,7 +444,17 @@ def _analyze_single_image( portrait_prompt = ",".join(_parts) logger.info( "[爆款视频] 图片 #%d 解析: count=%d gender=%s age=%s hair=%s skin=%s face=%s outfit=%s pose=%s expr=%s → %s", - idx, _count, _gender, _age, _hair, _skin, _face, _outfit, _pose, _expr, portrait_prompt, + idx, + _count, + _gender, + _age, + _hair, + _skin, + _face, + _outfit, + _pose, + _expr, + portrait_prompt, ) except Exception as _pe: logger.warning("[爆款视频] 图片 #%d 解析标签异常: %s,回退无人像", idx, _pe) -- 2.54.0