Files
pod_trend_agent/README.md
T
3218485270 71a48e4ed5 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 致命终止、线程安全、原子写入等)
2026-08-28 16:24:50 +08:00

313 lines
21 KiB
Markdown
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.
# POD 热点抓取 AgentLangGraph 工程化版)
自动抓取海外各国(US / GB / JP / AU / MX / DE / BR / SA / PL / ES / IT / CA)实时热点 + 风格趋势,
过滤侵权风险,产出可直接用于 AI 生图的印花设计提示词,并完成 **设计稿 → 三合一模特试穿 → OSS 图床 →
种草图 → 商品上传模板** 的完整 POD 生产链路。本版本用 **LangGraph** 重写,强调**工程化、节点兜底、可插拔**。
## 两种运行模式
| 模式 | 入口 | 数据来源 | 适用 |
|---|---|---|---|
| **热点采集模式** | UI「热点采集」 | Google Trendstrending/style/related | 抓国家实时热点词,LLM 筛出可印花设计 |
| **Pinterest 参考模式** | UI「Pinterest 参考模式」 | Pinterest 爬图 + LLM 多模态分析 | 从真实图片提炼原创印花设计,侵权风险更低 |
## 架构:LangGraph 状态图
### 模式 A:热点采集模式
```
START → seed → fetch → filter → score → screen → prompt_build → compose → product
→ oss_upload → seed_shot → template_export → END
```
| 节点 | 职责 | 可插拔点 / 兜底 |
|---|---|---|
| `seed` | 动态生成种子词(trending 派生 + 历史热点 + 月份/节日 → LLM) | 策略可插拔(`graph/seeds/`);LLM 失败回退静态种子 |
| `fetch` | 抓取热点(Google Trends 多数据源汇总) | 数据源可插拔(`graph/sources/`);单源失败不影响其它源 |
| `filter` | 黑名单 + 真实人物 + 设计相关性三级过滤 | 各级独立 try,单级失败不影响其它级 |
| `score` | 归一化 + 跨源融合 + 综合分 | 纯逻辑,空数据返回空 |
| `screen` | LLM 合规筛查 + 结构化四要素 | LLM 后端可插拔(`graph/llms/`);调用失败降级 Mock |
| `prompt_build` | 用固定模板装配 image/wearable/composite 提示词 | 四要素缺失时动态推导兜底 |
| `compose` | 生成纯印花设计稿(白底,可直接打印) | 图像后端可插拔(`graph/backends/`);无底图只导出 |
| `product` | SPU/颜色选品 → 底图 → 三图合成(图1模特+图2设计稿+图3底图) | 缺底图/模特图自动跳过 |
| `oss_upload` | 压缩(3:4 / ≥1340×1785 / <2MB+ 上传阿里云 OSS | 可关闭(`oss.enabled=false` |
| `seed_shot` | 从三合一主图按颜色选取种草图 + 上传 | 按 `seed_shot.count` 随机选取 |
| `template_export` | 把 SPU/SKU 数据填入商品上传模板 | 模板列名模糊匹配,多语言变体兼容 |
### 模式 BPinterest 参考模式
```
START → pinterest_init → pinterest_search → pinterest_scrape → pinterest_analyze
→ [pinterest_route 循环:简报不足 → 回 pinterest_search]
→ pinterest_finalize(排空简报池,后台并发生成 设计→三合一→OSS→种草图)
→ template_export → END
```
| 节点 | 职责 |
|---|---|
| `pinterest_init` | 创建简报池 + 并发生成流水线(`graph/pinterest_pipeline.py` |
| `pinterest_search` | 按需生成搜索词(direct=种子词直接拼「 t-shirt design」;llm=LLM 按需生成,json_schema+防重复) |
| `pinterest_scrape` | 用 Playwright 爬取 Pinterest 图片(共享 Chrome 登录态,并发=1) |
| `pinterest_analyze` | 从图池取图 → 多并发 LLM 多模态分析 → 原创设计简报(侵权检测 + 原创化引导) |
| `pinterest_route` | 简报不足 → 图池有未消费图 → 继续分析;图池不足 → 新一轮搜索;轮次耗尽 → 结束 |
| `pinterest_finalize` | 排空简报池,后台线程池并发生成(设计→三合一→OSS→种草图),合并产品 |
**图池机制**:爬取的图片注册到 `image_pool.json`(路径+md5+搜索词);分析过的图片 md5 一律拉黑
`used_images.json`,合适/不合适都拉黑);图池有未消费图片 → 直接分析(不搜索);不足 → 新一轮搜索。
每个简报带 `image_index`/`source_md5` 全局 id 校验,保证简报、设计稿、三合一套图对应同一张图。
分析前对图片做文件完整性校验(`PIL.verify()` 过滤损坏/截断图),避免把坏 base64 发给多模态网关
导致 400 拒收;分析失败(无有效图片/网关 400)时直接放弃该产品、跳过后续流程(**不做**纯文本降级、
**不做** mock 兜底),由路由自动取新图继续。
**并发控制**`config.yaml``pinterest:`):`analyze_concurrency`(分析线程数)、`analyze_batch`
(每轮取图数,0=自动补齐让 pipeline 队列满并发)、`max_designs`(每搜索词简报上限)、
`scrape_concurrency`(爬图并发,必须=1)、`err400_limit`400 超限放弃当前种子词)。
**每个节点都用 `graph/validate.with_fallback` 包裹**:任何未预料异常都被捕获、记入
`state['errors']`、返回最小更新,整图继续往下走,单点故障不中断流水线。致命图像服务错误(503 /
"No available compatible accounts")除外——`with_fallback` 不再静默吞掉,会同步标记流水线终止,
让路由短路到 `pinterest_finalize → template_export` 提前收尾合成模板。
### 模式 B-2:自定义图片模式(Custom)
无需采集/搜索图片,把用户本地图片文件夹里的**有效图片**直接送进多模态分析,沿用 Pinterest
后续所有步骤(分析 → 设计稿 → 三合一 → OSS → 种草图 → 模板导出)。
```
START → pinterest_init → pinterest_custom_load → pinterest_analyze
→ [pinterest_custom_route 循环:图池有未消费图 → 继续分析;图池空/简报达标 → 结束]
→ pinterest_finalize → template_export → END
```
| 节点 | 职责 |
|---|---|
| `pinterest_custom_load` | 读取 `pinterest.custom_image_dir` 文件夹 → 统计有效图片 → **数量硬校验** → 每次重建图池 + 清空已消费拉黑 |
**启用方式**(两种任选):
- 配置 `config.yaml``pinterest.mode: "custom"` 并填 `pinterest.custom_image_dir: "<文件夹路径>"`
- 或直接在 UI 选择「**自定义图片**」流程 →「📁 选图片文件夹…」选择文件夹后运行(自动写入配置)。
**数量硬校验(必过)**
- 必须先选定上传的文件夹(UI 未选会弹窗报错,任务不启动);
- 文件夹内所有有效图片(`jpg/jpeg/png/webp`,递归)数量必须 `> 0`
- **选品清单总数(`product.spu_tasks` 展开后)不得大于有效图片数**。不满足则在
UI 弹窗 / `run_pinterest_ref` 提前抛错 / `custom_load` 节点记录错误,三重拦截,任务不进入分析。
**行为差异**:自定义模式每次运行把图池重建为该文件夹的图片集(唯一图源)并清空已消费 md5 拉黑,
保证用户每次重新上传/选择的所有有效图片都会被重新多模态分析;分析批上限按「选品清单总数」截断,
避免对多余图片浪费配额。`pinterest_custom_route` 只在 wait / analyze / done 间流转,从不 search。
## 目录结构
```
pod_trend_agent/
├── cli.py # 命令行入口
├── ui_app.py # 桌面 UITkinter
├── config.yaml # 全局配置(数据源、权重、黑名单、LLM、模板、compose/product/oss/seed_shot
├── configs/
│ ├── countries/ # 各国 Google Trends 种子词(US|GB|JP|AU|MX|DE|BR|SA|PL|ES|IT|CA.yaml
│ ├── pinterest/ # 各国 Pinterest 种子词池(<CC>.yaml
│ ├── model_features.yaml # 模特特征库(按性别分组 female/male,种草图用)
│ ├── seed_shot_templates.yaml # 种草图提示词模板
│ └── style_features.yaml
├── prompts/<country>/ # ★ 每个国家不同的提示词单独文件夹
│ ├── system_prompt.md # 该国 LLM 系统提示(补充段,叠加到默认规则)
│ └── aesthetics.yaml # 审美 hint + 风格-配色 extra 规则 + 额外黑名单
├── prompts/pinterest_analyze_system.md # 多模态分析系统提示词(Pinterest/自定义模式),可配置
├── prompts/pinterest_analyze_user.md # 多模态分析用户提示词,可配置
├── graph/
│ ├── state.py # AgentState(共享状态)
│ ├── validate.py # with_fallback 兜底 + 数据校验
│ ├── loader.py # 配置/提示词加载与合并
│ ├── scoring.py # 归一化/融合/过滤(纯逻辑)
│ ├── style_rules.py # 动态风格/配色推导
│ ├── templates.py # 固定提示词模板(按国家区分,文字敏感规则)
│ ├── classify.py # 热点分类
│ ├── pinterest.py # Pinterest 图池/黑名单/压缩/MD5 去重
│ ├── pinterest_pipeline.py # 简报池 + 并发生成流水线(Pinterest 模式核心)
│ ├── product.py / product_batch.py # 三合一合成 / 批量
│ ├── seed_shot.py # 种草图选取 + 模特特征(性别分组)
│ ├── template_export.py # 商品上传模板导出(列名模糊匹配)
│ ├── oss_upload.py # 图片压缩 + 阿里云 OSS 上传
│ ├── sources/ # 数据源可插拔(google_trends + 注册表)
│ ├── llms/ # LLM 后端可插拔(mock / openai_compat + 注册表)
│ ├── backends/ # 图像后端可插拔(openai / mock
│ ├── seeds/ # 种子词策略可插拔(static / dynamic + holidays
│ ├── nodes/ # 流水线节点(seed/fetch/.../pinterest_*/product/oss/seed_shot/template_export
│ └── agent.py # 构建并编译 StateGraphbuild_graph / build_pinterest_graph
├── templates/
│ └── template_router.py # 商品上传模板路由(列名定位、数据写入)
├── scripts/
│ └── package.py # 一键打包脚本(PyInstaller → dist_vXX
├── basemap/ # 平铺衣服底图(<款号>/<SKU.code>/
├── material_library/ # 模特图(<品类>/
├── db/spu_sku.db # SPU/SKU 数据库
├── pinterest_scraper/ # Playwright 爬虫 + Chrome 登录态
└── output/<country>/ # ★ 每个国家的产物独立文件夹
├── design_briefs.json/md # 设计简报(含 image_prompt / composite_prompt
├── designs/ # 纯印花设计稿
├── product/ # 三合一合成图 + 已填写商品模板 <SKU>_已填写.xlsx
└── report.md # 各阶段统计 + 兜底错误记录
```
## Pinterest 参考模式详解
1. **种子词池**`configs/pinterest/<CC>.yaml` 存各国风格化种子词(含节日词),偏向印花设计。
2. **按需搜索**:每次只生成 1 个搜索词(`search_terms_per_run=1`),自动追加「 t-shirt design」后缀
(保证爬到的图是真实印花艺术,而非生活方式/风景图);爬取成功才标记已用,失败不拉黑。
3. **爬图**Playwright 打开 Pinterest 搜索页截图采集(共享 `.chrome_session` 登录态,并发强制=1)。
4. **图池 + MD5 去重**:图片注册 `image_pool.json`;分析过的 md5 一律拉黑(`used_images.json`);
设计稿生成后全局 MD5 去重(`.cache/global_design_md5.json`),跨国家生效。
5. **多模态分析**:大图先压缩(max 1024px / 1.5MB)再送 LLM,产出原创设计简报(motif/风格/配色/构图 +
risk_levelsafe/review/blocked)。blocked 拦截、review 加原创化魔改引导、safe 直接生成。
每张图由多模态模型判定 `suitable_for_print`(是否适合做印花),False 的简报在节点层被过滤丢弃,
且该图 md5 立即拉黑,不进入生成流程。
6. **并发生成**:简报推入 `PinterestPipeline`,后台线程池(`product.concurrency`,默认 5)逐条
生成 设计稿 → 三合一 → OSS 上传 → 种草图,边分析边生成,不等全部分析完。
每完成一个产品立即追加落盘到 `output/<country>/<ts>/products_pending.jsonl`(中断/崩溃也不丢已
完成产品),全部完成后统一合并写模板。
## 多模态分析提示词(可配置)
Pinterest 参考模式与自定义图片模式的多模态分析提示词已**外部化为配置**,无需改代码即可调优:
- `prompts/pinterest_analyze_system.md`——多模态分析**系统提示词**(POD T 恤设计分析师角色 +
禁止抄袭/违禁项/输出 JSON 结构等规则);
- `prompts/pinterest_analyze_user.md`——**用户提示词**"Analyze the attached image and produce
one ORIGINAL T-shirt print design brief...")。
**加载优先级**`graph/llms/openai_compat_backend.py`):
1. `prompts/<国家>/pinterest_analyze_*.md`(国家专属覆盖);
2. `prompts/pinterest_analyze_*.md`(全局配置,先查运行根 exe 旁再查内置);
3. 内置默认常量(兜底,向前兼容)。
直接编辑这两份文件即可自定义分析与生图引导规则;`pinterest_analyze_system.md` 里可调整
`image_prompt` 二段式结构 / `negative_prompt` / `suitable_for_print` 判定标准等约束。
## 生成可靠性 / 错误处理
- **致命 503(图像服务不可用,如 "No available compatible accounts"**:端到端识别
(设计稿 compose → 三合一 product → 种草图 seed_shot),置位终止标志后**提前终止任务**——
停止继续搜索/分析,`abort_unfinished` 丢弃未完成简报,**已完成落盘的产品保留**,直接合成模板导出。
- **图像 API 重试退避**`graph/backends/openai_image_backend.py`):429 尊重网关 `Retry-After`
头,否则指数退避;5xx500/502/503/504 超时)与网关空响应(JSON 解析失败)均退避 3/6/9s 重试、
最多 3 次,避免空响应/超时风暴触发限流。
- **yunfei / 标准 OpenAI 网关适配**`compose.execution_mode``background` 参数默认不再传入——
yunfei 等网关不认识它们会返回空 body;`base_url` 需带 `/v1` 前缀(如
`https://img.yunfei.best/v1`),否则请求错误路径得到空响应。
- **图源映射(可配置)**`config.product.mark_dirs` 定义 SPU.mark → `material_library` 下的
`model_dir`(模特图)/ `flat_dir`(平铺图)两个图源文件夹及对应合成提示词。热点采集与 Pinterest
两种模式统一走该映射:在「有图的文件夹」间按**任务序号**独立随机抽图,抽到模特图用模特三图提示词
(图1模特+图2印花+图3底图),抽到平铺图用平铺三图提示词(图1平铺实拍+图2印花+图3底图)。
同一 SPU 的多款不再共用同一张图;某类目录无图则只用另一类。每张种草图随机取不同模特特征。
## 模特性别分组
`configs/model_features.yaml` 的模特特征按性别分组(`female` / `male`)。种草图生成时读取商品上传
模板的「类目」表头值:
- 类目含「男」(如 `男士时尚>男装>男装T恤`)→ 固定从 **male** 组随机取模特
- 类目含「女」→ 固定从 **female** 组随机取模特
- 都不含 → 男女混合随机
## 生图尺寸(读 config,不硬编码)
| 图片类型 | 配置项 | 默认 |
|---|---|---|
| 纯印花设计稿 | `compose.design_size` | 1024x10241:1 |
| 三合一合成图 | `compose.size` | 1536x20483:4 |
| 种草图 | `seed_shot.size` | 1536x20483:4 |
## 商品上传模板导出
生成产品图后,从 db 读该 SPU/SKU 信息(材质/成分/图案/领型/面料/克重/印花类型/尺码表/边长/包装重量…),
`templates/template_router.py` 的路由函数填入商品上传模板,输出 `{模板原文件名}_已填写.xlsx`
`output/<国家>/product/`。模板已自包含在项目 `templates/``config.product.template_path`,可改为自由上传)。
- 数据从模板第 6 行(数据区空行)开始填,SPU 与各尺码 SKU 紧邻;
- **写入前按货号最后 3 位升序排序**:`template_export` 在批量填模板前取货号(`img_code`,前端
`code_prefix` 前缀命名的货号)末 3 位数字从小到大排序再顺序插入,保证 SPU/SKU 块按货号有序,
不受后台并发生成顺序影响;
- **不再产生空白 xlsx**:修复了旧版本探测模板可写性时误建空文件的 bug,product 目录只保留填好
的有效文件,文件名 = 模板原文件名 + `_已填写`(非固定「商品上传」);
- **列名模糊匹配**:模板列名按名称匹配(非固定行列),支持中/英/日变体;
- **胸围识别**:识别「基码表-胸围(cm)」「胸围全围(cm)」等含「胸围」的列,全部填 `sku.bust` 值;
- **申报价格**:模糊匹配所有含「申报价格」的列,统一按加价后价格填写(`price × (1+markup_percent%)`);
- **款式来源**:SPU 行「款式来源」统一填「现货款」;
- **商品产地**:所有国家统一填「中国大陆」;新增「产地省份」精确匹配列,统一填「广东省」(不再读站点/做字典映射);
- **SKU 分类 / SKU 数量 / SKU 数量单位**:按「建议售价」同一套路检测——关键词模糊匹配定位列,读取列头下一行备注判断是否必填(含「非必填」才跳过),检测到且为必填时统一填固定值:`SKU分类=单品``SKU数量=1``SKU数量单位=件`
- **详情图文**:由全部主图 + 种草图组成(不再拼接 img_url_2);
- 规格类型2 = `{size}*{color}`;币种=CNY;发货仓1~N 取模板顶头按「、」分隔,库存均 200;
- 模板文件被占用(打开中)时自动换名 `<SKU>_已填写_N.xlsx`,不中断导出;
- **多颜色 + 模式**`sku_code` 支持逗号分隔多颜色;`spu_per_color`true=每颜色一个 SPU 块 /
false=单 SPU 挂多颜色变体);CLI 用 `--single-spu` 切单 SPU 模式,UI 用「颜色多选 + 模板模式」。
## 用法
```bash
# 跑全部国家(热点采集模式,默认 US GB JP AU)
python cli.py
# 只跑英国,Mock 兜底 LLM
python cli.py -c GB --provider mock
# 跑美国 + 真实 LLMprovider 已在 config.llm_screen 配好时可不传)
python cli.py -c US
# 提供平铺衣服底图,把设计印上去(需 config.compose.backend + api_key
python cli.py -c US --base path/to/flatlay.png
# 查看 db 可选款号 / 某款颜色
python cli.py --list-spus
python cli.py --list-colors DG004
# 指定款号+颜色跑流水线(product.backend=mock 占位 / openai 真生图需 compose.api_key
python cli.py -c GB --spu DG004 --sku DG004-BL01
```
## 桌面 UITkinter 轻量版)
```bash
# 开发模式直接跑
python ui_app.py
# 无 GUI 自检(验证环境/核心链路,结果写入运行根 self_test_result.txt
python ui_app.py --self-test
```
UI 功能:国家多选、**流程选择(热点采集 / Pinterest 参考模式 / 自定义图片模式)**、LLM 后端选择、种子上限、
**SPU/颜色选品**、运行(后台线程 + 实时日志)、结果表格(双击看完整提示词)、打开产物目录。
## 打包
```bash
# 一键打包(自动取下一个版本号)
python scripts/package.py
# 指定版本号
python scripts/package.py 84
```
产物:`dist_vXX/PODTrendAgent.exe`。打包流程:PyInstaller 构建(不跑自检)→ 同步数据目录
config.yaml / configs / prompts / templates / basemap / material_library / db / output / logs)→
复制当前 Chrome 登录态到 exe 同目录(`pinterest_scraper/.chrome_session`)→ 同步上一个版本的 `.cache`
exe 运行时优先读 exe 旁这份配置(改了就生效),exe 内置的作为兜底默认。
## 数据流要点
0. `seed`(动态种子词):收集 trending 派生(去噪 + 人名 + 风险护栏过滤)、上轮 safe 历史热点、
当前月份/临近节日,由 LLM 后端(mock 规则 / 真 LLM)生成动态种子词,与 yaml 静态种子合并注入抓取配置。
1. `fetch``gt_trending`(国家 RSS 实时榜)+ `gt_style`/`gt_related`(风格/行业种子关联词)。
2. `filter` 三级过滤:合规黑名单(全局 + 各国 `extra_blacklist`)→ 真实人物 → 泛新闻/科技/赛事词。
3. `score` 按 (source,kind) 分组 min-max 归一化、跨源融合加权重。
4. `screen` LLM 合规筛查,产出 risk_level / 四要素;`keep_review=false` 时丢弃待复核。
5. `prompt_build` 用四要素 + 固定模板装配 image/wearable/composite 提示词(结构一致、可复用 img2img)。
6. `compose` 生成纯印花设计稿(白底,可直接打印),product 取第一张作为图2 复用。
7. `product` SPU/颜色选品 → basemap 底图 → 三图合成(图1模特 + 图2设计稿 + 图3底图)→ 模板。
8. `oss_upload` 压缩(3:4 / ≥1340×1785 / <2MB+ 上传阿里云 OSSURL 写回 `*_url` 字段。
9. `seed_shot` 从三合一主图按颜色选取种草图(模特性别分组),上传 OSS,URL 写入模板「详情图文」。
10. `template_export` 把 SPU/SKU 数据填入商品上传模板,输出 `<SKU>_已填写.xlsx`
> 合规红线(最重要):版权/商标/真实人物/敏感内容一律拦截或降级;二创/重绘仍有风险,
> 拿不准进"待复核"。各国侵权盲区不同(如英国 WWE/游戏/乐队、日本动漫 IP),放在
> `prompts/<country>/aesthetics.yaml` 的 `extra_blacklist` 按国家隔离。