Files
pod_trend_agent/graph/seed_shot.py
T
3218485270 f493bde8a9 POD 趋势感知 Agent:缓存热点模式 + 三图合成 + 热点去重/风格去重 + review 兜底
- 缓存热点批量流程(有采集缓存不触发 Google)
- 简报不足直接从采集缓存生成(轻量补齐)
- 三图合成(模特/印花/底图)+ 底图压缩 <2MB
- 热点去重→风格去重自动切换 + 不适合类目 review 兜底
- 透明背景(background=transparent)+ 提示词清洗(敏感词/背景描述)
- 任务前 basemap 校验 + 模板国家校验 + 模特任务级分配
2026-08-22 14:14:01 +08:00

104 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""种草图(Seed Shot)生成。
- 模板:configs/seed_shot_templates.yaml(可自定义,占位符 [商品名称]/[材质]/[模特特征])
- 模特特征:configs/model_features.yaml(可自定义,随机取一条)
- 生成:以 product 合成图(图1)为参考,img2img 生成 N 张种草图(保留衣服外观、换场景/模特)
- 占位替换:[商品名称]→cn_title(缺省回退 topic);[材质]→SPU.material[模特特征]→随机
"""
import random
from pathlib import Path
from typing import Any, Dict, List, Optional
import yaml
from graph.paths import project_root
def _load_yaml(rel: str) -> Dict[str, Any]:
for root in (project_root(),):
p = root / rel
if p.exists():
try:
return yaml.safe_load(p.read_text(encoding="utf-8")) or {}
except Exception as e: # noqa: BLE001
print(f"[seed_shot] 读取 {rel} 失败: {e}")
return {}
def load_templates() -> List[Dict[str, str]]:
"""种草图提示词模板列表(无配置时给内置兜底)。"""
data = _load_yaml("configs/seed_shot_templates.yaml")
tpls = data.get("seed_shot_templates") or []
if not tpls:
tpls = [{
"name": "default",
"prompt": (
"【最高优先级约束】请严格保留参考图1中模特所穿衣服的完整外观,包括其原有的颜色、"
"印花图案、版型款式、材质纹理与缝线细节,绝对禁止对衣服本身进行任何形式的修改、"
"重新设计、变色或改变图案。仅提取这件[商品名称],将其穿在一位[模特特征]的身上。"
"全身动态抓拍构图,行走在阳光斑驳的城市林荫道上,微微低头微笑,凸显[材质]的透气与百搭。"
"徕卡Q2摄影质感,高对比度色彩,35mm镜头,f/1.7大光圈,8k分辨率。"
),
}]
return [{"name": str(t.get("name", "default")), "prompt": str(t.get("prompt", ""))}
for t in tpls if t.get("prompt")]
def load_model_features() -> List[str]:
"""模特特征列表(无配置时给内置兜底)。"""
data = _load_yaml("configs/model_features.yaml")
feats = [str(f) for f in (data.get("model_features") or []) if str(f).strip()]
if not feats:
feats = ["20岁清新少女,素颜通透感", "25岁都市职场女性,干练气质"]
return feats
def load_style_features() -> List[str]:
"""服装风格列表(style_features.yaml,随机取一条替换 [服装风格];无配置时内置兜底)。"""
data = _load_yaml("configs/style_features.yaml")
feats = [str(f) for f in (data.get("style_features") or []) if str(f).strip()]
if not feats:
feats = ["极简基础款风格,干净纯粹,无过多繁复装饰",
"日系City Boy/Girl风,微宽松版型,注重舒适度与层次感"]
return feats
def render_prompt(template_prompt: str, cn_title: str, material: str, model_feature: str,
style_feature: str = "") -> str:
"""占位替换:[商品名称]/[材质]/[模特特征]/[服装风格]"""
out = template_prompt.replace("[商品名称]", (cn_title or "").strip() or "这件衣服")
out = out.replace("[材质]", (material or "").strip() or "面料")
out = out.replace("[模特特征]", (model_feature or "").strip() or "模特")
out = out.replace("[服装风格]", (style_feature or "").strip() or "日常休闲风")
return out
def generate_seed_shots(image_backend, base_image: str, cn_title: str, material: str,
count: int, out_dir: str, negative: str = "",
size: str = "1504x2000", prefix: str = "") -> List[str]:
"""生成 count 张种草图(img2img,图1=合成图)。返回产物路径列表。
size: 种草图统一 1504x2000。
prefix: 货号前缀(对应产品货号,命名 {prefix}_seedshot_{n}.png,不覆盖旧文件)。
占位符 [商品名称]/[材质]/[模特特征]/[服装风格] 均随机组合(模板/模特/服装风格各随机取一条)。"""
templates = load_templates()
features = load_model_features()
style_features = load_style_features()
out = Path(out_dir)
out.mkdir(parents=True, exist_ok=True)
paths: List[str] = []
for i in range(count):
tpl = random.choice(templates)
feat = random.choice(features)
style_feat = random.choice(style_features)
prompt = render_prompt(tpl["prompt"], cn_title, material, feat, style_feat)
out_path = str(out / f"{prefix}_seedshot_{i + 1:02d}.png" if prefix
else out / f"seed_shot_{i + 1:02d}.png")
try:
image_backend.print(prompt, base_image, out_path, negative, size=size)
paths.append(out_path)
print(f"[seed_shot] 已生成种草图 {i + 1}/{count}: {out_path}"
f"(模板={tpl['name']},模特={feat[:14]}…,风格={style_feat[:14]}…)")
except Exception as e: # noqa: BLE001
print(f"[seed_shot] 种草图 {i + 1} 生成失败: {e}")
return paths