模板导出增强 + 模特性别分组 + 三合一提示词精简

1) 模板导出:识别「基码表-胸围」填 sku.bust(多个胸围列都填);申报价格模糊匹配多列统一按加价后价格填写;详情图文不再拼接 img_url_2;SPU 款式来源统一填「现货款」;商品产地国家简称映射(沙特→沙特阿拉伯)
2) 模特性别分组:model_features 按男女分组,按模板类目含男/女固定取对应性别模特(含 Pinterest 模式 pipeline)
3) 三合一提示词:去掉 DESIGN CONTENT 四要素描述(设计已由设计稿提供)
4) 生图尺寸:全部改为读 config 不再硬编码(设计图 compose.design_size / 合成图 compose.size / 种草图 seed_shot.size)
This commit is contained in:
2026-08-26 18:05:11 +08:00
parent e317547b8b
commit b7f429db89
93 changed files with 2708 additions and 583 deletions
+68 -52
View File
@@ -1,7 +1,8 @@
"""Pinterest 参考模式节点 1/3LLM 生成搜索词(pinterest_search)。
"""Pinterest 参考模式节点 1/3按需生成单个搜索词(pinterest_search)。
流程:国家 Pinterest 种子词池 → LLM 生成搜索词(json_schema 结构化 + 动态注入已用词防重复)
→ 全局过滤(已用/黑名单/不适合T恤/去重)→ 持久化已用词
按需搜索:每次只生成 1 个搜索词(LLM json_schema + 动态注入已用词防重复)
带短袖/印花设计引导,保证搜索词适合短袖 T 恤印花
不在此处持久化已用词 —— 只有爬取成功(真正用掉)才标记已用(见 pinterest_scrape)。
兜底链:LLM json_schema → json_object → 解析失败/调用失败 → 回退种子词池随机抽样。
带 with_fallback:任何异常都不中断,返回空列表由下游跳过。
@@ -15,7 +16,6 @@ from graph.pinterest import (
load_used_terms,
merge_used,
sample_seeds,
save_used_terms,
)
from graph.validate import with_fallback
@@ -32,72 +32,88 @@ def pinterest_search_node(state: Dict[str, Any]) -> Dict[str, Any]:
return {"pinterest_search_terms": [], "errors": errors}
provider = str(pcfg.get("provider") or "openai").strip().lower()
want = int(pcfg.get("search_terms_per_run", 10))
search_mode = str(pcfg.get("search_mode") or "direct").strip().lower()
want = int(pcfg.get("search_terms_per_run", 1)) # 每次搜索词数量
seed_sample = int(pcfg.get("seed_sample", 40))
max_used_in_prompt = int(pcfg.get("max_used_terms_in_prompt", 100))
blacklist = config.get("blacklist") or []
# 1) 种子词池(随机抽样)+ 已用搜索词
# 1) 种子词池 + 已用搜索词 + 本轮已尝试词(防同轮重复,不持久化)
seeds = sample_seeds(country, seed_sample)
used = load_used_terms(output_dir, country)
attempted = [str(t).strip() for t in (state.get("pinterest_attempted") or []) if str(t).strip()]
rounds = int(state.get("pinterest_rounds") or 0) + 1
if not seeds:
print(f"[pinterest_search] {country} 无种子词,跳过搜索词生成")
return {"pinterest_search_terms": [], "errors": errors}
return {"pinterest_search_terms": [], "pinterest_rounds": rounds, "errors": errors}
# 2) LLM 生成(json_schema + 动态注入已用词)
# 已用词只取最近 N 个(默认 100)注入提示词,防 token 超限;过滤仍用全量。
used_llm = used[-max_used_in_prompt:] if max_used_in_prompt > 0 else []
# 2) 生成搜索词:种子词不再由 LLM 给出,直接由内置国家种子词库随机抽取(优先未用过),
# 追加 " t-shirt design"(保证 Pinterest 返回真正的 T 恤印花图);llm 模式保留兼容
terms: List[str] = []
llm = None
if provider != "static":
try:
llm = get_backend(provider)
if hasattr(llm, "bind_config"):
llm.bind_config(config.get("llm_screen") or {})
if provider not in ("mock",) and not getattr(llm, "has_key", False):
print(f"[pinterest_search] {provider} 未配置 API key,降级 mock")
llm = get_backend("mock")
except Exception as e: # noqa: BLE001
print(f"[pinterest_search] LLM 初始化失败: {e}")
llm = None
if search_mode == "direct":
from graph.pinterest import load_pinterest_seeds
pool = load_pinterest_seeds(country)
used_set = {str(u).strip().lower() for u in merge_used(used, attempted)}
fresh = [s for s in pool if s.lower() not in used_set]
if not fresh:
fresh = pool # 库内词全部用过 → 允许复用(词库有限)
terms = [f"{s} t-shirt design" if "t-shirt design" not in s.lower() else s
for s in random.sample(fresh, min(want, len(fresh)))]
print(f"[pinterest_search] direct 模式:国家种子词库随机抽 {len(terms)} 个 + t-shirt design{country}")
else:
used_llm = merge_used(used, attempted)
if max_used_in_prompt > 0:
used_llm = used_llm[-max_used_in_prompt:]
llm = None
if provider != "static":
try:
llm = get_backend(provider)
if hasattr(llm, "bind_config"):
llm.bind_config(config.get("llm_screen") or {})
if provider not in ("mock",) and not getattr(llm, "has_key", False):
print(f"[pinterest_search] {provider} 未配置 API key,降级 mock")
llm = get_backend("mock")
except Exception as e: # noqa: BLE001
print(f"[pinterest_search] LLM 初始化失败: {e}")
llm = None
if llm is not None and hasattr(llm, "generate_pinterest_terms"):
try:
ctx = {"country": country, "seeds": seeds, "used_terms": used_llm, "count": want}
res = llm.generate_pinterest_terms(ctx)
terms = [str(t).strip() for t in (res.get("search_terms") or []) if str(t).strip()]
print(f"[pinterest_search] LLM 生成搜索词 {len(terms)} 个({country},已用词注入 {len(used_llm)}/{len(used)}")
except Exception as e: # noqa: BLE001
print(f"[pinterest_search] LLM 生成失败,回退种子词池: {e}")
terms = []
if llm is not None and hasattr(llm, "generate_pinterest_terms"):
try:
ctx = {"country": country, "seeds": seeds, "used_terms": used_llm, "count": want}
res = llm.generate_pinterest_terms(ctx)
terms = [str(t).strip() for t in (res.get("search_terms") or []) if str(t).strip()]
print(f"[pinterest_search] LLM 生成搜索词 {len(terms)} 个({country},已用词注入 {len(used_llm)}")
except Exception as e: # noqa: BLE001
print(f"[pinterest_search] LLM 生成失败,回退种子词池: {e}")
terms = []
# 3) 兜底:LLM 无结果 → 种子词池随机抽样
if not terms:
terms = random.sample(seeds, min(want, len(seeds))) if seeds else []
print(f"[pinterest_search] 兜底:从种子词池取 {len(terms)}")
# 3) 兜底:LLM 无结果 → 种子词池随机抽样
if not terms:
terms = [f"{s} t-shirt design" if "t-shirt design" not in s.lower() else s
for s in random.sample(seeds, min(want, len(seeds)))]
print(f"[pinterest_search] 兜底:从种子词池取 {len(terms)}")
# 4) 全局过滤(已用/黑名单/不适合T恤/去重)
filtered = filter_search_terms(terms, used, blacklist)
if len(filtered) < want and seeds:
# 不足时用种子词池补充(同样过滤),保证数量
extra = filter_search_terms(seeds, merge_used(used, filtered), blacklist)
for t in extra:
if len(filtered) >= want:
break
filtered.append(t)
# 5) 持久化已用词
new_used = merge_used(used, filtered)
save_used_terms(output_dir, country, new_used)
# 4) 全局过滤(已用/本轮已尝试/黑名单/不适合T恤/去重)——注意:不在此处持久化已用词
# direct 模式:抽样时已避开已用词(库内词有限,全部用过后允许复用),不再额外过滤
if search_mode == "direct":
filtered = terms
else:
filtered = filter_search_terms(terms, merge_used(used, attempted), blacklist)
if not filtered and seeds:
# 生成词全被过滤 → 从种子词池补充(同样过滤)
extra = filter_search_terms(seeds, merge_used(used, attempted), blacklist)
filtered = extra[:want]
stats = dict(state.get("stats") or {})
stats["pinterest_search"] = {
"provider": provider,
"round": rounds,
"generated": len(terms),
"filtered": len(filtered),
"used_total": len(new_used),
"used_total": len(used),
}
print(f"[pinterest_search] 搜索词 {len(filtered)} 个(已用累计 {len(new_used)}: "
f"{', '.join(filtered[:6])}{'...' if len(filtered) > 6 else ''}")
print(f"[pinterest_search] {rounds}搜索词 {len(filtered)} 个(已用累计 {len(used)}: "
f"{', '.join(filtered[:3])}{'...' if len(filtered) > 3 else ''}")
return {"pinterest_search_terms": filtered, "stats": stats, "errors": errors}
return {"pinterest_search_terms": filtered, "pinterest_rounds": rounds,
"stats": stats, "errors": errors}