# POD 趋势感知 Agent · 对接文档

> 面向开发者/维护者的适配与扩展指南。覆盖：项目架构、**如何适配新国家**、核心流程、缓存机制、合规红线、常见问题与打包发布。
> 适用版本：dist_v67 / 源码 `pod_trend_agent/`

---

## 1. 项目概览

**目标**：为 POD（Print on Demand）出海业务自动捕捉海外热点 → 生成可打印的 T 恤印花设计 → 三图合成模特图 → 多语言标题 → 导出商品上传模板。

**技术栈**：Python 3.13 · LangGraph（7 节点流水线）· tkinter 桌面 UI · requests · Pillow · PyInstaller 打包

**两条运行路径**：

| 路径 | 触发条件 | 行为 |
|---|---|---|
| **缓存热点模式**（日常） | `output/<国>/collected_keywords.json` 存在 | 直接用采集缓存分配热点；简报不足时**从采集缓存自动生成简报**（轻量补齐），**绝不触发 Google** |
| **完整流水线**（首次） | 完全无缓存 | `seed → fetch → filter → score → screen → prompt_build → compose → product → seed_shot → template_export → oss_upload` |

---

## 2. 目录结构

```
pod_trend_agent/
├── ui_app.py                  # 桌面 UI：选品/采集/运行/模板/结果表格
├── cli.py                     # 命令行入口
├── config.yaml                # 全局配置（countries/sources/llm_screen/prompt_templates/compose/oss/product…）
├── configs/countries/<CC>.yaml# ★每国独立配置：种子词/related 种子/采集限额
├── prompts/<CC>/              # ★每国提示词：system_prompt.md + aesthetics.yaml
├── graph/
│   ├── agent.py               # LangGraph 流水线编排（START→seed→fetch→…→END）
│   ├── nodes/                 # 各节点实现（seed/fetch/filter/score/screen/prompt/compose/product/seed_shot/template/oss）
│   ├── product_batch.py       # 缓存热点批量流程：分配/三级去重/review 兜底/轻量补齐
│   ├── backends/              # 图像后端：openai_image_backend / mock_image_backend
│   ├── llms/                  # LLM 后端：openai_compat_backend（多模态标题）/ mock_backend
│   ├── seeds/                 # 种子策略：dynamic_strategy（统一池+加权随机）/ static / holidays
│   ├── sources/               # 数据源注册表：google_trends（pytrends，429 兜底）/ pinterest
│   ├── product.py             # 底图/模特查找（IMG_EXTS 全格式）、SKU 逻辑
│   ├── style_rules.py         # 风格/配色/构图规则 + sanitize_image_prompt + review 魔改引导
│   ├── paths.py               # config_root / project_root / runtime_root 路径解析
│   └── validate.py            # 校验 + with_fallback 兜底装饰器
├── templates/                 # 商品上传模板 + template_router.py（模板读写）
├── basemap/<款号>/<颜色>/     # 平铺底图（jpg/png…）
├── material_library/<品类>/   # 模特图（webp/jpg/png/avif…）
├── db/                        # 商品数据库（sqlite）
└── dist_vNN/                  # PyInstaller 打包产物（含全部数据）
```

---

## 3. ★适配新国家（核心章节）

以新增 **DE（德国）** 为例，完整步骤：

### 3.1 国家代码入列

`config.yaml`：
```yaml
countries:
  - US
  - GB
  - JP
  - AU
  - MX
  - DE        # ← 新增
```

### 3.2 每国配置 `configs/countries/DE.yaml`

```yaml
# 德国专属数据源配置
trending:
  enabled: true
  limit: 40            # Google Trends 抓取上限

style:
  enabled: true
  seeds:               # ★印花风格种子词（重点，越多越好，20~30 个）
    - lederhosen
    - alpine
    - berlin street art
    - bauhaus pattern
    - autobahn
    - ...

related:
  enabled: true
  seed_keywords:
    - ...

exclude_topics: []     # 黑名单热点（已用过/明确不分配的词，可选）
```

**种子词选词红线**（写配置时注意）：
- ❌ 会被 `review 兜底 _UNSUITABLE` 剔除的类目：`nails / recipes / lottery / weather / results / prices / jobs / mortgage / election…`（见 `graph/product_batch.py`）
- ❌ 品牌/版权/真实人物：`nike / disney / margot robbie / prison break…`
- ❌ 图像策略敏感词：`skull / skeleton / blood / gun / demon…`（生成时会被 `sanitize_image_prompt` 替换，主题会变味）
- ✅ 用各国**本土流行 + 全球趋势**的印花主题（2026 参考：retro vintage / 极简 typography / 自然户外 / 手绘涂鸦 / Y2K / coquette / western / 幽默 quote / 星座塔罗 / 植物线稿）

### 3.3 每国提示词 `prompts/DE/`

- **`system_prompt.md`**：screen 阶段 LLM 的角色/输出规则（风险判定、motif/art_style/color_palette/composition 四要素），**必须包含 "Return JSON…" 字样**（阿里云 MaaS 的 `response_format=json_object` 硬性要求）。
- **`aesthetics.yaml`**：
  ```yaml
  extra_style_rules: [...]     # 该国风格倾向（配色/构图/审美）
  extra_blacklist: [...]       # 该国合规红线词（德国：纳粹符号等）
  ```

### 3.4 节假日 `graph/seeds/holidays.py`

`_HOLIDAYS_BY_COUNTRY` 增加国家节日（名称/月/日/规则/窗口），节日种子词自动带权重 3.0 进入统一池：
```python
"DE": [
    ("Oktoberfest", 9, 21, "third_saturday", 30),   # 啤酒节（9 月）
    ("Weihnachten", 12, 25, "fixed", 25),           # 圣诞节
    ...
]
```

### 3.5 国家模板站点校验 `ui_app.py`

`_check_template_site` 的 `country_sites` 字典加国家→站点关键词：
```python
country_sites = {
    ...
    "DE": ("德国站", "德国"),
}
```
（模板「模版」sheet 第 1 行定位「经营站点」列 → 第 2 行站点名 → 与所选国家不匹配则**导入失败**，提示重新导入）

### 3.6 图像提示词模板 `config.yaml → prompt_templates`

`image_prompt` / `wearable_prompt` / `composite_prompt` 各加国家版本（`{motif}` 等占位符保持不变），注意：
- **提示词中不得出现任何背景描述**（"isolated on … background"、"no background scene"）——透明背景只由 `background="transparent"` 参数控制（官方要求）
- 不含安全规则段（"no politics / no religion / no hate / no violence / no sexual"——这些词本身会触发图像 API 内容政策拦截）

### 3.7 验证清单

```bash
python cli.py -c DE --provider mock          # mock 跑通全流程（不花钱）
python -c "from graph.seeds.dynamic_strategy import DynamicStrategy; ..."  # 验证种子池
# UI 打开 → 切 DE → 采集 → 运行
```

---

## 4. 核心流程详解

### 4.1 完整流水线（LangGraph 7 节点）

```
START → seed(种子词) → fetch(采集) → filter(过滤) → score(打分) → screen(风险筛选+简报)
      → prompt_build(提示词) → compose(设计稿) → product(合成/标题) → seed_shot(种草图)
      → template_export(模板) → oss_upload(压缩上传) → END
```
每个节点均套 `with_fallback` 兜底（失败不中断整体流程）。

### 4.2 缓存热点模式（日常入口，`ui_app` / `run_product_batch`）

```
判断：output/<国>/ 有 collected_keywords.json 或 design_briefs.json → 缓存模式
  → load 简报（简报不足 → _rebuild_briefs_from_cache 从采集缓存生成，不碰 Google）
  → assign_hotspots 分配（三级去重 + review 兜底）
  → product 节点批量处理（并发 ≤5，每 SPU 一线程）
  → 模板导出
```

### 4.3 热点分配与去重（`assign_hotspots`）

| 层级 | 逻辑 |
|---|---|
| ① 热点去重 | 本批优先不同 topic（热点1-风格1、热点2-风格1…） |
| ② 风格去重 | 热点用完后自动切：热点1-风格2、热点2-风格2…（同 topic 不同 art_style） |
| ③ 兜底 | 剩余未用指纹（低分热点也可）；**历史已用 (topic, style) 指纹绝不重复出** |
| review 兜底 | `_UNSUITABLE` 正则剔除不适合类目（美甲/食谱/彩票/天气/比分…），日志提示 |

### 4.4 种子词机制（`dynamic_strategy.py`）

- **统一池**：静态 style + 静态 related + 月份主题 + 节日 + LLM 动态 → 全部合并去重（跨类型同词权重累加）
- **权重**：节日 3.0 > 月份主题 2.0 > 静态/动态 1.0（加权随机抽取）
- **每次随机取**（加权不重复）；**用完全用**（池 ≤ 需要数时全部使用，不随机限量）
- 每国独立配置（`configs/countries/<CC>.yaml`），种子缓存 `.cache/seeds/`（按 国家+provider+日期，防 Google 429）

---

## 5. 缓存文件

| 文件 | 位置 | 作用 |
|---|---|---|
| `collected_keywords.json` | `output/<国>/` | 采集热点池（关键词列表） |
| `design_briefs.json` | `output/<国>/` | 简报池（topic/motif/risk/score/image_prompt…） |
| `used_designs.json` | `output/<国>/` | 已用 (topic, art_style) 指纹（成功生成才写入） |
| `.cache/seeds/` | 项目根 | 种子词日缓存（防 429 重采集） |
| `.cache/llm_screen/` | 项目根 | LLM 筛查缓存 |

> 注意：**失败产品不写 used_designs**（合成/设计失败的，热点下次仍可用）。
> 已用过不要的热点：直接从 `collected_keywords.json` / `design_briefs.json` 物理删除即可（如 8-22 的 18 个 GB 热点处理方式）。

---

## 6. 合规红线（最重要）

1. **review 判定**：screen 阶段 LLM 判定疑似商标/版权/真实人物 → `risk_level=review`；openai 模式下生成设计时**自动注入原创化魔改引导**（`REVIEW_REBRAND_HINT`：只做风格参考、禁止复刻 logo/商标/角色、换名换细节）
2. **提示词清洗** `sanitize_image_prompt`（生成设计前必过）：
   - 删除一切背景描述（透明背景由参数控制）
   - 删除内容策略触发段（`any text must be safe: no politics/religion/hate/violence/sexual…`）
   - 21+ 个敏感词安全替换（skull→smiley mascot、gun→star…）
3. **各国红线**：美国慎政治/宗教；德国慎纳粹符号（`aesthetics.yaml extra_blacklist` 扩展）；中东整体保守；真实人物一律不进
4. **review 兜底 `_UNSUITABLE`**：不适合类目自动剔除（分配源头拦截）

---

## 7. 图像生成与合成

### 7.1 设计稿生成（`compose` / `product` 的 `generate`）
- **透明背景**：`background="transparent"` 参数（compose/product 配置均已设）；**提示词不得提背景**
- 并发：compose 并发生成（`compose.design_workers` 默认 5）；product 阶段每 SPU 一线程，并发上限 5
- 底图/模特/设计 >2MB → 自动压缩到 <2MB 再上传网关（`_shrink_blob_to_2mb`）

### 7.2 三图合成（`product` 的 `print`，mark=1）
- 上传顺序：**图1 模特图 → 图2 印花设计稿 → 图3 衣服平铺底图**（与提示词角色一一对应）
- 图3 只提取衣服本身底色与面料材质（忽略平铺图背景/桌面/场景）
- 合成失败 → 退避重试 3 次（5/20/40s）；仍失败 → 跳过该产品（不生成标题、不写模板）
- 网关处理约 2-6 分钟属正常（重负载 img2img）

---

## 8. 模型与网络配置（`config.yaml`）

```yaml
llm_screen:          # LLM（screen/种子/标题共用）
  model: "qwen3.7-..."    # ★标题生成需视觉模型（qwen-vl 系列）；纯文本模型会 400
  base_url: "https://...maas.aliyuncs.com/compatible-mode/v1"
  api_key: "sk-..."       # 或环境变量 LLM_API_KEY

compose:             # 设计稿图像网关
  backend: "openai"
  model: "gpt-image-2"    # 支持 background=transparent
  base_url: "https://api.tofastcode.xyz"
  background: "transparent"

product:
  backend: "mock"         # openai / mock
  background: "transparent"
  concurrency: 5          # 合成并发上限（默认 min(任务数,5)）
```

**代理策略**：模型调用（LLM + 图像）一律直连（`NO_PROXY` + 环境变量级 `no_proxy=*`，用户挂 VPN 也不走代理）；**Google 采集（pytrends）走系统代理**（需要 VPN）。

---

## 9. 打包发布（每版流程）

```bash
# 1. 打包（不跑 self-test：会撞 Google 429，浪费 7-10 分钟）
pyinstaller -y PODTrendAgent.spec --distpath dist_vNN --workpath build_cleanNN

# 2. 补齐 dist 数据（必须，否则 exe 缺资源）
cp -r templates db basemap material_library configs prompts .cache dist_vNN/
cp config.yaml dist_vNN/

# 3. 拷贝最近缓存（output/<国>/*.json），避免 exe 首次运行重新采集
# 4. 验证：find_first_model_folder 能查到模特（material_library 拷贝不完整是常见坑）
```

---

## 10. 常见问题（FAQ）

| 现象 | 根因 | 解决 |
|---|---|---|
| 有缓存还去采集（Google 429 刷屏） | UI 判断只看 design_briefs，忽略 collected | v60+ 已修：有 collected 即缓存模式，绝不触发 seed/fetch |
| Google 429 / SSL EOF | 限流 | 换 VPN 节点；已采集后运行走缓存不再碰 Google |
| 三图合成全部超时 | 并发过高压垮网关 / 底图过大 | 并发上限 5 + 输入图压缩 <2MB + 退避重试 |
| 生成设计全是商标侵权 | review 简报未魔改 | v57+ 自动注入 REVIEW_REBRAND_HINT |
| 内容政策拦截频繁 | 提示词含敏感词/安全规则段/背景描述 | sanitize_image_prompt 自动清理 |
| 设计稿不透明（RGB 白底） | product 后端未配 background | `product.background: transparent` |
| 标题 400 | 模型不支持图片输入（非视觉模型） | 换 qwen-vl 系列视觉模型 |
| 模特找不到 | material_library 拷贝不全 / avif 不识别 | 全量拷贝；IMG_EXTS 已含 avif/gif/tiff |
| 生成的设计 6 个重复 | compose 只生成 safe 简报的设计 | v48+ 为全部简报（含 review）生成设计 |

---

## 11. 开发环境

```bash
python -m venv .venv
.venv/Scripts/pip install -r requirements.txt   # 或 uv sync（uv.lock 已锁定）
# 环境变量：LLM_API_KEY / LLM_BASE_URL / OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET
```

**对接新国家一句话总结**：`configs/countries/<CC>.yaml`（种子词）+ `prompts/<CC>/`（提示词）+ `holidays.py`（节日）+ `config.yaml countries 列表` + UI 国家下拉 + 模板站点映射 → mock 跑通 → 真 LLM 验证。
