v89-v91 模板增强 + 图源映射 + 多模态提示词可配置化

- 图源映射统一:热点采集与 Pinterest 模式均走 config.product.mark_dirs 配置,按任务序号随机抽模特图/平铺图
- 商品产地固定:统一为「中国大陆」+「产地省份=广东省」(不再读站点/字典映射)
- 模板 SKU 字段检测:按建议售价同一套路检测 SKU分类/SKU数量/SKU数量单位,必填时填入单品/1/件
- 多模态分析提示词可配置:prompts/pinterest_analyze_system.md + user.md,支持国家覆盖,不丢文件回退内置
- 自定义图片模式:新增 pinterest_custom_load_node,图片数量硬校验,选品清单 ≤ 有效图片数
- 模板导出优化:写入前按货号末 3 位升序排序,不再产生空白 xlsx
- 修复 v90 project review 10 项(503 致命终止、线程安全、原子写入等)
This commit is contained in:
2026-08-28 16:24:50 +08:00
parent 2a96ec0870
commit 71a48e4ed5
40 changed files with 780 additions and 200 deletions
+64 -5
View File
@@ -379,11 +379,15 @@ def run_pipeline(countries, provider, max_seeds, log_q,
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=""):
oai=None, markup_percent=0.0, code_prefix="DG", template_path="",
custom_image_dir=""):
"""Pinterest 参考模式后台线程:独立于 Google Trends 采集链路。
各国独立种子词池 → LLM 搜索词(json_schema + 动态注入防重复)→ 爬图
→ LLM 分析图片 → 设计简报 → 产品生成(设计稿/主图/种草图/模板导出)。
custom_image_dir 非空 → 自定义图片模式:不搜索不采集,把该文件夹有效图片直接送多模态分析,
沿用 Pinterest 后续所有步骤;数量硬校验(有效图片数 ≥ 选品清单总数)由 run_pinterest_ref 执行。
"""
config = load_config()
config["seed_provider"] = provider
@@ -401,6 +405,9 @@ def run_pinterest_pipeline(countries, provider, log_q, spu_tasks=None, spu_count
p["markup_percent"] = markup_percent
if code_prefix:
p["code_prefix"] = code_prefix
# 自定义模式:把上传文件夹写入配置(run_pinterest_ref 据此进入 custom 图并校验)
if custom_image_dir:
config.setdefault("pinterest", {})["custom_image_dir"] = custom_image_dir
# 任务扩展(与 run_pipeline 一致):每个集合按自己数量复制 N 份
if spu_tasks:
tasks_list = []
@@ -425,7 +432,8 @@ def run_pinterest_pipeline(countries, provider, log_q, spu_tasks=None, spu_count
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)
state = run_pinterest_ref(c, config, config_root(), runtime_root(),
task_timestamp=task_ts, custom_image_dir=custom_image_dir)
items = state.get("product") or []
errs = state.get("errors") or []
log_q.put(("log", f"[{c}] Pinterest 参考完成:{len(items)} 个产品,兜底错误 {len(errs)}\n"))
@@ -538,6 +546,13 @@ class App(tk.Tk):
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.Radiobutton(top, text="自定义图片", value="custom",
variable=self.flow_var).pack(side="left", padx=2)
ttk.Button(top, text="📁 选图片文件夹…", command=self._choose_custom_dir).pack(side="left", padx=(8, 0))
self.custom_dir_var = tk.StringVar(value="")
self.custom_dir_name_var = tk.StringVar(value="")
ttk.Label(top, textvariable=self.custom_dir_name_var, anchor="w",
foreground="#185FA5").pack(side="left", padx=(6, 0))
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")
@@ -854,6 +869,24 @@ class App(tk.Tk):
).start()
# ---------- 运行 ----------
def _choose_custom_dir(self):
"""选择自定义模式图片文件夹,并立即统计有效图片数量(供数量校验提示)。"""
path = filedialog.askdirectory(title="选择自定义图片文件夹(直接送多模态分析)")
if not path:
return
try:
from graph.pinterest import list_valid_images
imgs = list_valid_images(path)
n = len(imgs)
except Exception: # noqa: BLE001
n = 0
self.custom_dir_var.set(path)
if n == 0:
self.custom_dir_name_var.set(f"已选: {Path(path).name}0 张有效图片!)")
else:
self.custom_dir_name_var.set(f"已选: {Path(path).name}{n} 张有效图片)")
self._log(f"[UI] 自定义图片文件夹已选择: {path}(有效图片 {n} 张,jpg/jpeg/png/webp\n")
def _choose_template(self):
"""选择自定义商品上传模板(.xlsx),后续 template_export 从该模板解析。
导入时校验「经营站点」是否与当前国家一致(如 JP → 日本站),不一致则导入失败。"""
@@ -1022,14 +1055,40 @@ class App(tk.Tk):
self._busy = True
self.run_btn.config(state="disabled", text="运行中…")
self.fetch_btn.config(state="disabled")
if self.flow_var.get() == "pinterest":
# Pinterest 参考模式:独立于 Google Trends 采集链路
flow = self.flow_var.get()
if flow in ("pinterest", "custom"):
# Pinterest 参考 / 自定义图片:独立于 Google Trends 采集链路
custom_dir = self.custom_dir_var.get().strip()
if flow == "custom":
# —— 自定义图片模式硬校验:必须先选文件夹;有效图片数 ≥ 选品清单总数 ——
if not custom_dir:
self._busy = False
self.run_btn.config(state="normal", text="运行")
self.fetch_btn.config(state="normal")
messagebox.showwarning("自定义图片模式", "请先点击「📁 选图片文件夹…」选择上传的图片文件夹")
return
try:
from graph.pinterest import list_valid_images, validate_custom_images
# 选品清单总数 = 每个任务按 count 扩展后的求和(与 run_pinterest_pipeline 一致)
expanded = sum(int(t.get("count") or 0) or spu_count or 1 for t in tasks)
ok, valid_n, msg = validate_custom_images(custom_dir, expanded)
except Exception as e: # noqa: BLE001
ok, valid_n, msg = False, 0, f"校验失败: {e}"
if not ok:
self._busy = False
self.run_btn.config(state="normal", text="运行")
self.fetch_btn.config(state="normal")
messagebox.showerror("自定义图片数量校验未通过", msg)
self._log("[UI] 自定义图片数量校验未通过,任务未运行: " + msg + "\n")
return
self._log(f"[UI] 自定义图片数量校验通过:有效图片 {valid_n} 张 ≥ 选品清单 {expanded}\n")
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()),
self.template_path_var.get().strip(),
custom_dir if flow == "custom" else ""),
daemon=True,
).start()
else: