# POD 热点抓取 Agent(LangGraph 工程化版) 自动抓取海外各国(US / GB / JP / AU / MX / DE / BR / SA / PL / ES / IT / CA)实时热点 + 风格趋势, 过滤侵权风险,产出可直接用于 AI 生图的印花设计提示词,并完成 **设计稿 → 三合一模特试穿 → OSS 图床 → 种草图 → 商品上传模板** 的完整 POD 生产链路。本版本用 **LangGraph** 重写,强调**工程化、节点兜底、可插拔**。 ## 两种运行模式 | 模式 | 入口 | 数据来源 | 适用 | |---|---|---|---| | **热点采集模式** | UI「热点采集」 | Google Trends(trending/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 数据填入商品上传模板 | 模板列名模糊匹配,多语言变体兼容 | ### 模式 B:Pinterest 参考模式 ``` 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']`、返回最小更新,整图继续往下走,单点故障不中断流水线。 ## 目录结构 ``` pod_trend_agent/ ├── cli.py # 命令行入口 ├── ui_app.py # 桌面 UI(Tkinter) ├── 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 种子词池(.yaml) │ ├── model_features.yaml # 模特特征库(按性别分组 female/male,种草图用) │ ├── seed_shot_templates.yaml # 种草图提示词模板 │ └── style_features.yaml ├── prompts// # ★ 每个国家不同的提示词单独文件夹 │ ├── system_prompt.md # 该国 LLM 系统提示(补充段,叠加到默认规则) │ └── aesthetics.yaml # 审美 hint + 风格-配色 extra 规则 + 额外黑名单 ├── 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 # 构建并编译 StateGraph(build_graph / build_pinterest_graph) ├── templates/ │ └── template_router.py # 商品上传模板路由(列名定位、数据写入) ├── scripts/ │ └── package.py # 一键打包脚本(PyInstaller → dist_vXX) ├── basemap/ # 平铺衣服底图(<款号>//) ├── material_library/ # 模特图(<品类>/) ├── db/spu_sku.db # SPU/SKU 数据库 ├── pinterest_scraper/ # Playwright 爬虫 + Chrome 登录态 └── output// # ★ 每个国家的产物独立文件夹 ├── design_briefs.json/md # 设计简报(含 image_prompt / composite_prompt) ├── designs/ # 纯印花设计稿 ├── product/ # 三合一合成图 + 已填写商品模板 _已填写.xlsx └── report.md # 各阶段统计 + 兜底错误记录 ``` ## Pinterest 参考模式详解 1. **种子词池**:`configs/pinterest/.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_level:safe/review/blocked)。blocked 拦截、review 加原创化魔改引导、safe 直接生成。 每张图由多模态模型判定 `suitable_for_print`(是否适合做印花),False 的简报在节点层被过滤丢弃, 且该图 md5 立即拉黑,不进入生成流程。 6. **并发生成**:简报推入 `PinterestPipeline`,后台线程池(`product.concurrency`,默认 5)逐条 生成 设计稿 → 三合一 → OSS 上传 → 种草图,边分析边生成,不等全部分析完。 每完成一个产品立即追加落盘到 `output///products_pending.jsonl`(中断/崩溃也不丢已 完成产品),全部完成后统一合并写模板。 ## 生成可靠性 / 错误处理 - **致命 503(图像服务不可用,如 "No available compatible accounts")**:端到端识别 (设计稿 compose → 三合一 product → 种草图 seed_shot),置位终止标志后**提前终止任务**—— 停止继续搜索/分析,`abort_unfinished` 丢弃未完成简报,**已完成落盘的产品保留**,直接合成模板导出。 - **图像 API 重试退避**(`graph/backends/openai_image_backend.py`):429 尊重网关 `Retry-After` 头,否则指数退避;5xx(500/502/503/504 超时)与网关空响应(JSON 解析失败)均退避 3/6/9s 重试、 最多 3 次,避免空响应/超时风暴触发限流。 - **yunfei / 标准 OpenAI 网关适配**:`compose.execution_mode` 与 `background` 参数默认不再传入—— yunfei 等网关不认识它们会返回空 body;`base_url` 需带 `/v1` 前缀(如 `https://img.yunfei.best/v1`),否则请求错误路径得到空响应。 - **模特分配**:`material_library/<品类>` 内的合格模特图(3:4 比例过滤)按**任务序号**独立随机, 同一 SPU 的多款不再共用同一张模特。每张种草图随机取不同模特特征。 ## 模特性别分组 `configs/model_features.yaml` 的模特特征按性别分组(`female` / `male`)。种草图生成时读取商品上传 模板的「类目」表头值: - 类目含「男」(如 `男士时尚>男装>男装T恤`)→ 固定从 **male** 组随机取模特 - 类目含「女」→ 固定从 **female** 组随机取模特 - 都不含 → 男女混合随机 ## 生图尺寸(读 config,不硬编码) | 图片类型 | 配置项 | 默认 | |---|---|---| | 纯印花设计稿 | `compose.design_size` | 1024x1024(1:1) | | 三合一合成图 | `compose.size` | 1536x2048(3:4) | | 种草图 | `seed_shot.size` | 1536x2048(3: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 行「款式来源」统一填「现货款」; - **商品产地**:国家简称映射正式名称(如「沙特」→「沙特阿拉伯」,`_COUNTRY_NAME_MAP` 可扩展); - **详情图文**:由全部主图 + 种草图组成(不再拼接 img_url_2); - 规格类型2 = `{size}*{color}`;币种=CNY;发货仓1~N 取模板顶头按「、」分隔,库存均 200; - 模板文件被占用(打开中)时自动换名 `_已填写_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 # 跑美国 + 真实 LLM(provider 已在 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 ``` ## 桌面 UI(Tkinter 轻量版) ```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)+ 上传阿里云 OSS,URL 写回 `*_url` 字段。 9. `seed_shot` 从三合一主图按颜色选取种草图(模特性别分组),上传 OSS,URL 写入模板「详情图文」。 10. `template_export` 把 SPU/SKU 数据填入商品上传模板,输出 `_已填写.xlsx`。 > 合规红线(最重要):版权/商标/真实人物/敏感内容一律拦截或降级;二创/重绘仍有风险, > 拿不准进"待复核"。各国侵权盲区不同(如英国 WWE/游戏/乐队、日本动漫 IP),放在 > `prompts//aesthetics.yaml` 的 `extra_blacklist` 按国家隔离。