# POD 热点抓取 Agent（LangGraph 工程化版）

自动抓取海外各国（US / GB / JP / AU）实时热点 + 风格趋势，过滤侵权风险，产出可直接用于
AI 生图的印花设计提示词。本版本用 **LangGraph** 重写，强调**工程化、节点兜底、可插拔**。

## 架构：LangGraph 状态图

```
START → seed → fetch → filter → score → screen → prompt_build → compose → END
```

| 节点 | 职责 | 可插拔点 / 兜底 |
|---|---|---|
| `seed` | 动态生成种子词（trending 派生 + 历史热点 + 月份/节日 → LLM） | 策略可插拔（`graph/seeds/`）；LLM 失败回退静态种子 |
| `fetch` | 抓取热点（多数据源汇总） | 数据源可插拔（`graph/sources/`）；单源失败不影响其它源 |
| `filter` | 黑名单 + 真实人物 + 设计相关性三级过滤 | 各级独立 try，单级失败不影响其它级 |
| `score` | 归一化 + 跨源融合 + 综合分 | 纯逻辑，空数据返回空 |
| `screen` | LLM 合规筛查 + 结构化四要素 | LLM 后端可插拔（`graph/llms/`）；调用失败降级 Mock |
| `prompt_build` | 用固定模板装配 image/wearable/composite 提示词 | 四要素缺失时动态推导兜底 |
| `compose` | 写 `output/<country>/` 产物；可选印图 | 图像后端可插拔（`graph/backends/`）；无底图只导出 |

**每个节点都用 `graph/validate.with_fallback` 包裹**：任何未预料异常都被捕获、记入
`state['errors']`、返回最小更新，整图继续往下走，单点故障不中断流水线。

## 目录结构

```
pod_trend_agent/
├── cli.py                 # 命令行入口（替代旧 run/screen/compose）
├── config.yaml            # 全局配置（数据源、权重、黑名单、LLM、模板、compose）
├── configs/countries/     # 各国专属数据源种子（US|GB|JP|AU.yaml）
├── prompts/<country>/     # ★ 每个国家不同的提示词单独文件夹
│   ├── 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       # 固定提示词模板（按国家区分，30×38cm 上限，文字敏感规则）
│   ├── classify.py        # 热点分类
│   ├── sources/           # 数据源可插拔（google_trends / pinterest + 注册表）
│   ├── llms/              # LLM 后端可插拔（mock / openai_compat + 注册表）
│   ├── backends/          # 图像后端可插拔（openai）
│   ├── seeds/             # 种子词策略可插拔（static / dynamic + holidays 月份节日）
│   ├── nodes/             # 7 个流水线节点（含 seed）
│   └── agent.py           # 构建并编译 StateGraph，run_country()
└── output/<country>/      # ★ 每个国家的产物独立文件夹
    ├── design_briefs.json #   含 国家/热点词/类别/风险/设计稿提示词/完整印图提示词
    ├── design_briefs.md
    ├── composite_prompts.json/md   # 封装提示词包（印到底图用）
    └── report.md          # 各阶段统计 + 兜底错误记录
```

## 可插拔指南

- **加数据源**：在 `graph/sources/` 新建类继承 `DataSource` 实现 `fetch()`，在
  `graph/sources/__init__.py` 的 `SOURCES` 登记；在 `config.yaml` 的 `sources:` 加入。
- **加 LLM 后端**：在 `graph/llms/` 新建类继承 `LLMBackend` 实现 `screen()`，在
  `LLM_BACKENDS` 登记；`llm_screen.provider` 选择（openai/deepseek/qwen/moonshot 走 openai_compat）。
- **改种子词策略**：`seed_provider` 选 `static`（仅 yaml）/ `mock`（规则生成，零成本）/ `openai_compat`（真 LLM）。
  动态策略收集 trending 派生 + 上轮 safe 历史热点 + 月份/临近节日上下文，由 LLM 后端生成种子词，
  与 yaml 静态种子合并后注入 `style.seeds` / `related.seed_keywords`（`config.yaml` 的 `seed_provider_cfg` 限量）。
- **加国家**：建 `configs/countries/<CODE>.yaml`（种子词）与 `prompts/<CODE>/`（提示词），
  在 `config.yaml` 的 `countries:` 加入即可，无需改代码。
- **加图像后端**：在 `graph/backends/` 实现 `ImageBackend`，`compose.backend` 配置后
  用 `cli.py -c <国家> --base 底图.png` 印图。

## 用法

```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
```

## 桌面 UI（Tkinter 轻量版）

```bash
# 开发模式直接跑
python ui_app.py

# 无 GUI 自检（验证环境/核心链路，结果写入运行根 self_test_result.txt）
python ui_app.py --self-test

# 打包 exe（单文件、无控制台；数据文件打进 _MEIPASS，output/.cache 写在 exe 旁）
.venv/Scripts/pyinstaller -F -w --name PODTrendAgent \
  --add-data "config.yaml;." --add-data "configs;configs" --add-data "prompts;prompts" \
  --hidden-import pytrends --hidden-import pytrends.request ui_app.py
# 产物：dist/PODTrendAgent.exe
```

UI 功能：国家多选、LLM 后端选择（mock/static/openai_compat...）、种子上限、**SPU/颜色选品**、运行（后台线程 + 实时日志）、
结果表格（国家/热点/类别/风险/分数，双击看完整提示词）、打开产物目录。
打包后首次运行自动在 exe 旁创建 `output/` 与 `.cache/`，并生成一份**可编辑的默认配置**
（`config.yaml` / `configs/` / `prompts/`）；exe 运行时**优先读 exe 旁这份配置**（改了就生效），
exe 内置的作为兜底默认。想恢复默认：删掉 exe 旁对应文件即可。

## 产品图生成（product）

热点提示词 → SPU/颜色选品 → basemap 底图 → 印花图 → 模特试穿合成图，产物在 `output/<country>/product/`。

```bash
# 查看 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
```

- 数据关系：`SPU.code`=款号，`SKU.code`=`款号-颜色编码`（如 DG004-BL01）；底图在 `basemap/<款号>/<SKU.code>/`，模特图在 `material_library/<品类>/`。
- 缺底图/模特图时对应步骤自动跳过并提示；`product.backend` 留空则仅存底图。
- **商品上传模板自动导出**：生成产品图后，从 db 读该 SPU/SKU 信息（材质/成分/图案/领型/面料/克重/印花类型/尺码表/边长/包装重量…），经 `templates/template_router.py` 的路由函数填入商品上传模板，
  输出 `<SKU>_已填写.xlsx` 到 `output/<国家>/product/`。模板已自包含在项目 `templates/`（`config.product.template_path`，可改为自由上传）。
  - 数据从模板第 6 行（数据区空行）开始填，SPU 与各尺码 SKU 紧邻；
  - SKC货号：SPU 行=SPU.code、SKU 行=SKU.code；SKU货号暂不填；
  - 规格类型2 = `{size}*{color}`；币种=CNY；发货仓1~N 取模板顶头按「、」分隔，库存均 200；
  - 商品轮播图列名中/英/日变体均可模糊匹配；SPU 行轮播图1=生成首图，SKU 行轮播图2~5=db `img_url_2~5`（无则回退生成图）；
  - 模板文件被占用（打开中）时自动换名 `<SKU>_已填写_N.xlsx`，不中断导出；
  - **多颜色 + 模式**：`sku_code` 支持逗号分隔多颜色（如 `DG015-VT01,DG015-DARK HEATHER`）；`spu_per_color`（true=每颜色一个 SPU 块 / false=单 SPU 挂多颜色变体）；CLI 用 `--single-spu` 切单 SPU 模式，UI 用「颜色多选 + 模板模式」。
- UI 打开时**默认加载勾选国家的热点缓存**（`output/<国家>/design_briefs.json`）直接展示，点「运行」刷新最新。

## 数据流要点

0. `seed`（动态种子词）：收集 trending 派生（去噪 + 人名 + 风险护栏过滤）、上轮 safe 历史热点、
   当前月份/临近节日，由 LLM 后端（mock 规则 / 真 LLM）生成动态种子词，与 yaml 静态种子合并注入抓取配置。
1. `fetch` 抓 `gt_trending`（国家 RSS 实时榜）+ `gt_style`/`gt_related`（风格/行业种子关联词，种子来自 seed 节点）。
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. `product`（可选）SPU/颜色选品 → basemap 底图 → 印花图 → 模特试穿合成（`output/<country>/product/`）。
7. `compose` 写各国 `output/<country>/`；给底图则调图像后端印图。

> 合规红线（最重要）：版权/商标/真实人物/敏感内容一律拦截或降级；二创/重绘仍有风险，
> 拿不准进"待复核"。各国侵权盲区不同（如英国 WWE/游戏/乐队、日本动漫 IP），放在
> `prompts/<country>/aesthetics.yaml` 的 `extra_blacklist` 按国家隔离。
