新增 Pinterest 参考模式:独立于 Google Trends 的完整链路(12国种子词池 / LLM搜索词json_schema+防重复+已用词限100 / 并发爬图 / 多模态分析→原创简报 / 生图带爬取图参考图生图 / UI流程选择)

This commit is contained in:
2026-08-24 15:02:01 +08:00
parent 2144c36e60
commit 309d4a520c
30 changed files with 2076 additions and 23 deletions
+99 -9
View File
@@ -190,8 +190,12 @@ def _apply_provider_mode(config: dict, provider: str) -> None:
cp["backend"] = "openai" if on else "mock"
pp = config.setdefault("product", {})
pp["backend"] = "openai" if on else "mock"
# Pinterest 参考模式:LLM(搜索词/图片分析)提供商跟随模式开关
pin = config.setdefault("pinterest", {})
pin["provider"] = "openai" if on else "mock"
print(f"[UI] 模式开关: {'OpenAI(真 LLM + 真生图)' if on else 'Mock(演示)'} | "
f"llm_screen={ls['provider']} compose={cp['backend']} product={pp['backend']}")
f"llm_screen={ls['provider']} compose={cp['backend']} product={pp['backend']} "
f"pinterest={pin['provider']}")
def fetch_keywords(country, provider, max_seeds, log_q, oai=None):
@@ -374,6 +378,75 @@ def run_pipeline(countries, provider, max_seeds, log_q,
log_q.put(("log", f"[UI] 缓存打包失败: {e}\n"))
def run_pinterest_pipeline(countries, provider, log_q, spu_tasks=None, spu_count=0,
oai=None, markup_percent=0.0, code_prefix="DG", template_path=""):
"""Pinterest 参考模式后台线程:独立于 Google Trends 采集链路。
各国独立种子词池 → LLM 搜索词(json_schema + 动态注入防重复)→ 爬图
→ LLM 分析图片 → 设计简报 → 产品生成(设计稿/主图/种草图/模板导出)。
"""
config = load_config()
config["seed_provider"] = provider
apply_openai_cfg(config, oai)
_apply_provider_mode(config, provider) # 联动 pinterest.provider / compose / product backend
p = config.setdefault("product", {})
if template_path:
p["template_path"] = template_path
if spu_tasks:
p["spu_tasks"] = spu_tasks
p.pop("spu_code", None)
if spu_count:
p["spu_count"] = spu_count
if markup_percent:
p["markup_percent"] = markup_percent
if code_prefix:
p["code_prefix"] = code_prefix
# 任务扩展(与 run_pipeline 一致):每个集合按自己数量复制 N 份
if spu_tasks:
tasks_list = []
for t in spu_tasks:
n = int(t.get("count") or 0) or spu_count or 1
for _ in range(n):
tt = dict(t)
tt.pop("count", None)
tasks_list.append(tt)
p["spu_tasks"] = tasks_list
if spu_count:
p["spu_count"] = spu_count
# 简报数上限联动:设计数 ≥ 任务数(否则任务会复用简报)
pcfg = config.setdefault("pinterest", {})
pcfg["max_designs"] = max(int(pcfg.get("max_designs", 10) or 10), int(spu_count or 1))
old_stdout = sys.stdout
sys.stdout = StdoutRedirector(log_q)
results = {}
task_ts = time.strftime("%Y%m%d%H%M%S")
try:
from graph.agent import run_pinterest_ref
for c in countries:
log_q.put(("log", f"\n===== Pinterest 参考模式 {c}SPU 数量 {spu_count}=====\n"))
state = run_pinterest_ref(c, config, config_root(), runtime_root(), task_timestamp=task_ts)
items = state.get("product") or []
errs = state.get("errors") or []
log_q.put(("log", f"[{c}] Pinterest 参考完成:{len(items)} 个产品,兜底错误 {len(errs)}\n"))
results[c] = items
ts_dir = runtime_root() / "output" / countries[0] / task_ts
log_q.put(("log", f"\n✅ Pinterest 参考任务完成,产物文件夹:{ts_dir}\n"
f" (缓存/去重记录在 {runtime_root() / 'output' / countries[0]} 根目录,不进任务文件夹)\n"))
log_q.put(("done", results))
except Exception as e: # noqa: BLE001
import traceback
log_q.put(("log", f"运行失败: {e}\n{traceback.format_exc()}\n"))
log_q.put(("error", None))
finally:
sys.stdout = old_stdout
try:
for c in countries:
_pack_cache(c, log_q) # 运行完成 → 自动打包缓存/去重数据
except Exception as e: # noqa: BLE001
log_q.put(("log", f"[UI] 缓存打包失败: {e}\n"))
def _pack_cache(country: str, log_q=None) -> str:
"""运行/采集完成后,把该国缓存+去重数据打包成 zipoutput/cache_packs/<国>_<时间戳>.zip),
内含 design_briefs / used_designs / collected_keywords / products.json + .cache 关键缓存,
@@ -459,6 +532,12 @@ class App(tk.Tk):
variable=self.provider_var).pack(side="left", padx=2)
ttk.Radiobutton(top, text="Mock 演示", value="mock",
variable=self.provider_var).pack(side="left", padx=2)
ttk.Label(top, text=" 流程:").pack(side="left", padx=(14, 0))
self.flow_var = tk.StringVar(value="trends")
ttk.Radiobutton(top, text="热点采集", value="trends",
variable=self.flow_var).pack(side="left", padx=2)
ttk.Radiobutton(top, text="Pinterest 参考", value="pinterest",
variable=self.flow_var).pack(side="left", padx=2)
ttk.Label(top, text=" 种子数量:").pack(side="left", padx=(14, 0))
self.seed_var = tk.StringVar(value="24")
ttk.Entry(top, textvariable=self.seed_var, width=4).pack(side="left")
@@ -934,14 +1013,25 @@ class App(tk.Tk):
self._busy = True
self.run_btn.config(state="disabled", text="运行中…")
self.fetch_btn.config(state="disabled")
threading.Thread(
target=run_pipeline,
args=(countries, self.provider_var.get(), ms, self._q,
"", "", spu_count, tasks, self._oai_cfg(), markup,
self.code_prefix_var.get().strip() or "DG",
self.template_path_var.get().strip()),
daemon=True,
).start()
if self.flow_var.get() == "pinterest":
# Pinterest 参考模式:独立于 Google Trends 采集链路
threading.Thread(
target=run_pinterest_pipeline,
args=(countries, self.provider_var.get(), self._q,
tasks, spu_count, self._oai_cfg(), markup,
self.code_prefix_var.get().strip() or "DG",
self.template_path_var.get().strip()),
daemon=True,
).start()
else:
threading.Thread(
target=run_pipeline,
args=(countries, self.provider_var.get(), ms, self._q,
"", "", spu_count, tasks, self._oai_cfg(), markup,
self.code_prefix_var.get().strip() or "DG",
self.template_path_var.get().strip()),
daemon=True,
).start()
def _poll(self):
try: