# SKG AI 素材管线 - TK 二创验证

## 启动
- 后台启动（不弹 Terminal）：`./scripts/start-dev-background.sh`（通过 macOS launchd 后台托管；前端 4290 + 后端 4291，日志写入 `.logs/`）
- 后台停止：`./scripts/stop-dev-background.sh`
- 前端 dev：`cd web && npm run dev`（Next.js 16，端口 4290）
- 后端 dev：`cd api && uvicorn main:app --host 127.0.0.1 --port 4291`（FastAPI，端口 4291，重任务用）
- 注意：后端不要带 `--reload` 跑长下载 / 抽帧 / 音频任务；reload 会等待后台任务结束，导致 4291 端口占用但新请求卡住。

## 立项决策快索引
- 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解
- 风格：`04-Dark-Gallery-Ambient`（路径：`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md`）
- 第一冲刺：步骤 1-4（下载 / 拆轨 / 关键帧 / ASR+翻译）
- 当前产品方向（2026-05-20 再确认）：信息流广告快速复刻默认进入“三字段候选生成”工作流。主界面为“左侧素材输入列 + 右侧信息流复刻工作表”。用户粘贴 TK 链接或上传视频后点击“开始分析”，系统自动下载源视频；下载完成后并行启动两条路：音频文案路提取原音频文案/字幕，并分析讲话人、语速节奏、背景音乐/环境声/音效；视频视觉路自动抽取参考帧。源视频工作区右侧主体链路是“参考帧池 → 转换层 → 主体元素”：参考帧池竖向排列；转换层改为项目内生图对话智能体，用户把 1-3 张参考帧拖入同一个参考区，选择 GPT 套件或 Gemini 套件，点击“开始分析”后由模型识别参考图关键特征、生成中文摘要、英文 generation brief、特征 chip、追问和风险提示。GPT 套件表示 GPT 分析/对话 + `gpt-image-2` 生图；Gemini 套件表示 Gemini 分析/对话 + `gemini-3-pro-image-preview` 生图，默认不跨套件偷偷兜底。转换层对话只处理主体生图需求，允许在对话里修改形象锁定、卡通重构、创意复刻、自主描述、数量、服装统一、人物占比、保留/删除元素等；对话、模型套件和最终英文 prompt 写入 `Job.subject_agent` / `state.json`，不再把模型选择作为主状态存进浏览器本地记忆。点击生成后右侧主体元素区按每次生成的套图文件夹展示主体多视图，当前套图在最上层展开，其他套图顺位进入下方可滚动列表，同一方向允许保留多套。主体重构默认继承参考图里的性别、人种/肤色、年龄体态和角色气质这些广义特征，但生成同一个全新主体；形象锁定模式走同一可见主体参考重绘。后端会给每套视图注入同一份 pack bible，锁定脸部设定、发型、体态、服装类型、配色、材质、剪裁和配饰，并在保存时裁白边后允许放大主体到画布高度约 88-94%，避免一套图里每张衣服不同或人物太小。旧下方“相似主体 / 主体模板库”不再作为主路径。波形下方的画面胶片只是临时预览，点击只跳转原视频时间点，双击或拖进参考帧池才正式加入关键帧，已加入的胶片直接显示“已添加”。产品图上传后独立形成产品资产包，自动识别视角/结构/比例并补缺角度。分镜工作台按逐句时间轴默认只露“文案 / 场景一句话 / 人物+产品+动作”，产品素材池、批量控制、三字段、视频候选和高级区都必须可折叠；视频候选无内容时默认不占大面积，有候选时默认只显示迷你缩略条，展开后才显示 4-grid。单条默认生成 4 个视频候选，顶部支持整片批量生成候选；首尾帧、视觉规划、产品出现方式和旧 6 字段保留在“高级”抽屉与后端 quick-plan 自动展开中，不能再作为客户默认闸门。

## 部署事实
- 平台：VPS `76.13.31.179`（Ubuntu 24.04 / Docker Compose / Coolify Traefik）
- 发布状态：已部署并验证（2026-05-19，主体元素改为按套图文件夹分组展示，主体生成接口提交后立即返回 queued 占位并后台逐视角生成、逐张回填；转换层为真人重构 / 卡通重构 / 元素重构 / 自主描述四个入口，每个入口最多 3 张参考帧；拖入只加入参考队列，点击生成后固定生成全新 6 视图；胶片双击/拖拽加入参考帧池 + 胶片缓存复用 + 音频解析失败可重试，右侧三栏主体管线：竖向参考帧池 + 转换层 + 主体元素，参考帧缩略图保持小尺寸 9:16 比例 + hover 左侧紧凑预览 + 转换层多参考滚动，旧主体模板区移出主路径 + 逐句时间轴移到原版视频下方并支持双行文案 + 波形同框时间对齐画面胶片 + 胶片密度按钮上移波形顶部 + 去分隔线 + 胶片上下错落 + body 顶层原位大放大 + 隐藏源视频工作区音频解析摘要卡 + 隐藏工作区顶部状态提示条 + 三字段候选生成工作流 + 折叠紧凑候选区）；`https://marketing.skg.com` 已启用应用内登录页，未登录 API 返回 401，认证后首页 200；容器内 `/health` 返回 `ok:true`
- 最近部署验证（2026-05-19）：`fd794e3` 已推送并部署到 `/opt/skg-marketing-studio`；生产 `/health` 显示 `image=gpt-image-2`、`subject_image=gpt-image-2`、`image_request_timeout_seconds=60`、`image_base_url=https://ai.skg.com/ezlink/v1`。容器内最小文字生图探针在 20 秒限制下返回 `ReadTimeout`，说明当前阻塞点是 `https://ai.skg.com/ezlink/v1` 的 `gpt-image-2` 上游通道超时，服务端不会更换图片模型。
- 最近部署验证（2026-05-19）：`3756259` 已推送并部署到 `/opt/skg-marketing-studio`；生产 `/health` 显示 `image=gpt-image-2`、`image_fallbacks=['gemini-3-pro-image-preview']`、`subject_image_fallbacks=['gpt-image-2','gemini-3-pro-image-preview']`、短时熔断阈值 2 次 / 600 秒。线上真实探针确认 `gpt-image-2` 读超时后同次调用可自动兜底到 `gemini-3-pro-image-preview` 并返回图片；模拟探针确认连续 2 次主模型失败后第三次直接走 Gemini。
- 最近部署验证（2026-05-20）：`c245bff` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py` 和 `web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web 容器 Up、API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。容器内模型偏好探针确认转换层 `image_model_preference` 路由：`auto -> ['gpt-image-2','gemini-3-pro-image-preview']`、`gpt-image-2 -> ['gpt-image-2']`、`gemini-3-pro-image-preview -> ['gemini-3-pro-image-preview']`。
- 最近部署验证（2026-05-20）：`2366662` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过。容器内后处理探针确认白底小主体保存为 `1152x2048` 时有效主体高度占比从约 0.60 可放大到 `0.906`，主体 6 视图 prompt 已注入同一份 pack bible。
- 最近部署验证（2026-05-20）：`7acbfd5` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层主体提示词记忆和生图模型偏好改为按 `job.id` 隔离；有参考帧的 `reconstruction_mode=similar` 会先生成 source brief，再把参考帧作为 `/images/edits` 的 `image[]` 参考提交；自主描述空文本切到 `reconstruction_mode=same` 源形象锁定路径。
- 最近部署验证（2026-05-20）：`e10b1a6` 已推送并部署到 `/opt/skg-marketing-studio`；本地 `python3 -m py_compile api/main.py`、`web/npm run build` 通过，生产 Docker 重建后 `./scripts/verify-prod-docker.sh` 通过（web/API 容器 Up、`/login/` 200、缺失 `_next` 资源 404、未登录 `/api/health` 401、容器内 `api:health ok`）。转换层改为项目内生图对话智能体，新增 `Job.subject_agent` 和 `/subject-agent/analyze`、`/subject-agent/message`，GPT / Gemini 改为成套控制分析、对话和生图模型，数量与要求修改进入对话状态后再调用主体套图生成；Pydantic `model_bundle` protected namespace warning 已消除。
- 主站 / 前端：`https://marketing.skg.com`
- API / 后端：`https://marketing.skg.com/api`
- 代码仓库 / Gitea：`https://git.kang-kang.com/kangwan/20260512-skg-tk`
- 文档 / 解析：`docs/source-analysis.html`（项目内独立文档，不公开挂主应用路由）
- 管理后台：待定
- 服务器目录：`/opt/skg-marketing-studio`
- 生产启动：`docker compose -f docker-compose.prod.yml --env-file deploy/.env.production up -d --build`
- 生产架构：`web` 容器用 Nginx 承载 Next 静态导出；`/login/`、`/_next/`、`/assets/`、`/skg-logo-black.svg`、`/oasis-source/` 等登录页必需静态资源公开访问；未登录访问工作台跳转 `/login/`，`/api/` 通过 Nginx `auth_request` 校验 FastAPI 会话 Cookie 后反代到 `skg-marketing-api:4291`；Traefik 通过 `coolify` 外部网络接入 80/443
- Web 验收必须以生产 Docker 形态为准：前端是 `next export` 静态产物 + Nginx，不是 `next dev` / `next start`。任何 Web 改动部署后必须运行 `./scripts/verify-prod-docker.sh`，确认 `/login/`、`/_next/`、`/api/health`、本地 API 地址泄漏和 API 镜像 `.env` 污染检查通过；不能只用本地 `npm run build` 作为上线依据。
- 当前音频解析：`https://ai.skg.com/azure/v1` 的 `gpt-4o-transcribe` 当前返回 `DeploymentNotFound`，且官方 Azure OpenAI transcription 路径探测也未返回可用部署；生产临时复制本地成功策略，直接使用容器内 `faster-whisper tiny.en` 真实转写，关闭 Gemini 多模态音频兜底。拿到真实 Azure ASR deployment 名后再恢复 `ASR_REMOTE_ENABLED=true`。
- 持久化目录：服务器 `./data/jobs` 挂载到后端 `/data/jobs`；全局资源中心持久化在 `./data/asset_library`、`./data/prompt_library` 和 `./data/_trash`
- TikTok 下载登录态：公开视频默认不带 cookies 直接下载，生产环境变量必须显式保持 `YTDLP_COOKIES_FILE=`、`YTDLP_COOKIES_FROM_BROWSER=` 为空，防止容器读取不存在的浏览器 cookies。只有 TikTok 明确要求登录态时，才使用服务器私有 cookies 文件 `./secrets/tiktok_cookies.txt` 挂载到 API 容器 `/run/secrets/tiktok_cookies.txt` 并配置 `YTDLP_COOKIES_FILE=/run/secrets/tiktok_cookies.txt`；`yt-dlp` 会在任务结束时回写 cookies，因此不要把该挂载设为只读；不要使用云端浏览器读取方案，也不要把 cookies 入库。生产容器严禁使用 `YTDLP_COOKIES_FROM_BROWSER=chrome`。
- 登录凭证：用户名写下方快捷登录；密码明文备份只放服务器 `/root/skg-marketing-studio-login.txt`，生产环境变量 `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET` 只放服务器 `deploy/.env.production`
- 手动 `rsync` 到服务器时必须排除本机开发文件和真实生产 env：`.git`、`.memory`、`.logs`、`.pids`、`data`、`jobs`、`secrets`、`api/.env`、`api/.env.local`、`api/.env.production`、`deploy/.env.production`、`web/node_modules`、`web/.next`、`web/out`。不要把本地 `api/.env` 或 `deploy/.env.production` 覆盖到 `/opt/skg-marketing-studio`，否则会把开发 cookies / API 配置烤进生产镜像或清空生产登录与模型配置。

## 快捷登录
- 登录地址：`https://marketing.skg.com/login/`
- 用户名：`skg`
- 密码：见服务器 `/root/skg-marketing-studio-login.txt`（不入库）
- 说明：当前是生产入口应用内登录页；数据库密码、API Key、服务器 root 密码不要写这里

## 元数据回写清单
- 新增或变更公网地址后，必须同步更新 `.project.json.urls`
- 如果有网页后台登录：
  - 可直接入库：写 `.project.json.quick_login`
  - 不应入库：写 `.project.json.credentials` 引用
- 部署完成后，`RULES.md` 和 `.project.json` 必须同一次任务一起更新

## Git / 开发收口
- 工作看板全局规则适用于本项目：`/Users/kangwan/Projects/code/20260317-rules-dashboard/RULES.md`、`SCHEMA.md`、`rules/03-Git约定.md`、`rules/04-版本发布规则.md`
- 主分支：`main`
- 主远端：`origin` → `ssh://git@git.kang-kang.com:22222/kangwan/20260512-skg-tk.git`
- Gitea 网页仓库：`https://git.kang-kang.com/kangwan/20260512-skg-tk`
- 每次开发结束前必须执行并汇报 `git status -sb` 和变更范围
- 代码、规则、部署或元数据变更必须形成 `feat:`、`fix:`、`docs:`、`chore:`、`release:` 等人工语义 commit；`auto-save` 只算安全快照
- 能联网和鉴权时必须 `git push origin main`；如果不能推送，最终回复必须写清楚当前分支、领先/落后数量、最新未推送 commit 和失败原因

## 环境变量
- `LLM_BASE_URL` / `LLM_API_KEY`：OpenAI 兼容网关，用于翻译、文案改写、音频分析等文本/多模态理解模型调用
- `ASR_BASE_URL` / `ASR_API_KEY`：OpenAI Audio Transcriptions 兼容网关，用于上传 `audio.wav` 做真实转写；未配置 `ASR_API_KEY` 时复用 `LLM_API_KEY`，生产默认指向 `https://ai.skg.com/azure/v1`
- `ASR_MODEL`：OpenAI Audio Transcriptions 音频转写模型；微软通道使用 Azure OpenAI 部署名 `gpt-4o-transcribe`，如果 Azure 侧实际部署名不同必须同步改这里
- `ASR_LANGUAGE`：远端 ASR 的输入语言提示，默认 `en`；微软官方说明指定 ISO-639-1 语言可改善准确率和延迟。
- `ASR_REMOTE_ENABLED`：是否启用远端 OpenAI Audio Transcriptions；微软 ASR 验收时必须为 `true`。当前生产因 `https://ai.skg.com/azure/v1` 下 `gpt-4o-transcribe` 返回 `DeploymentNotFound`，临时设为 `false`，直接走容器内 `faster-whisper`，等真实 Azure deployment 名补齐后再恢复。
- `ASR_LOCAL_FALLBACK_ENABLED`：是否允许远端 ASR 失败后落到本机 / 容器内 ASR；当前生产为 `true`，复制本地成功路径的“本机真实转写”策略，云端用 CPU 版 `faster-whisper` 替代本机 Mac 的 `mlx_whisper`。
- `ASR_AUDIO_FALLBACK_ENABLED`：是否允许远端和本机 ASR 失败后落到多模态音频兜底；生产微软 ASR 验收设为 `false`，避免静默使用 Gemini 音频
- `FASTER_WHISPER_MODEL` / `FASTER_WHISPER_DEVICE` / `FASTER_WHISPER_COMPUTE_TYPE`：容器内本地 ASR 兜底，仅在 `ASR_LOCAL_FALLBACK_ENABLED=true` 时启用
- `ASR_FALLBACK_MODEL`：多模态音频兜底模型，仅在 `ASR_AUDIO_FALLBACK_ENABLED=true` 时用于兜底或音频画像，默认 `gemini-2.5-flash`；如果模型不能真实听到音频或返回疑似逐秒假字幕，后端必须拒绝写入时间轴
- `ASR_TIMEOUT_SECONDS`：远端 ASR / 翻译 / 音频分析单次请求超时；当前生产本地转写模式设为 45 秒，微软 ASR 重新启用时可按素材长度提高。
- `LOCAL_ASR_BIN` / `LOCAL_ASR_MODEL` / `LOCAL_ASR_TIMEOUT_SECONDS`：本机 ASR 兜底，默认使用 `/opt/homebrew/bin/mlx_whisper` + `mlx-community/whisper-tiny`，用于当前 SKG 网关 `/audio/transcriptions` 不可用时生成真实逐句时间轴
- `TRANSLATE_MODEL`：字幕翻译模型，默认 `gemini-2.5-flash`
- `GPT_TEXT_MODEL`：GPT 文本 / 视觉默认模型，默认 `gpt-4o`；用于兜底修正旧 Gemini 覆盖值
- `REWRITE_MODEL`：通用改写/分镜描述模型，默认 `gpt-4o`；如果旧环境仍写 `gemini-*`，后端会自动改用 `GPT_TEXT_MODEL`
- `VISION_MODEL`：关键帧画面理解模型，默认 `gpt-4o`；如果旧环境仍写 `gemini-*`，后端会自动改用 `GPT_TEXT_MODEL`
- `AUDIO_REWRITE_MODEL`：后续音频口播改写模型，默认跟随 `REWRITE_MODEL`；如果旧环境仍写 `gemini-*`，后端会自动改用 `REWRITE_MODEL`
- `AUDIO_PRODUCT_BRIEF`：音频口播改写时注入的 SKG 产品卖点
- `PRODUCT_VIEW_MODEL`：同一产品素材池的视角标注/自动识别模型；当前按项目要求强制使用 `gpt-image-2`
- `IMAGE_BASE_URL` / `IMAGE_API_KEY` / `IMAGE_MODEL`：OpenAI 兼容生图网关；当前所有生图入口主模型仍为 `gpt-image-2`
- `IMAGE_REQUEST_TIMEOUT_SECONDS`：单次图片网关请求超时，默认 60 秒；超时会直接把该视图标失败并继续下一张，避免主体 6 视图整包长时间无反馈
- `IMAGE_FALLBACK_ENABLED` / `IMAGE_FALLBACK_MODEL`：图片主模型故障兜底；当前允许在 `gpt-image-2` 超时、429、5xx 或网络错误时临时使用 `gemini-3-pro-image-preview`，400/401/403/404 和参数错误不兜底
- `IMAGE_CIRCUIT_FAILURE_THRESHOLD` / `IMAGE_CIRCUIT_COOLDOWN_SECONDS`：短时熔断配置，默认 `gpt-image-2` 连续 2 次上游类失败后 600 秒内直接走 Gemini 兜底；成功恢复后自动清空失败计数
- `GPT_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODEL` / `SUBJECT_ASSET_IMAGE_MODELS`：保留兼容旧环境变量名；主体 6 视图在转换层默认自动使用 `gpt-image-2`，同一套图内一旦触发 Gemini 兜底，后续视图沿用 Gemini，避免一张张等待主模型超时；用户显式选择 GPT 或 Gemini 时，`image_model_preference` 会让主体套图只走所选模型
- `AI_HTTP_PROXY` / `IMAGE_HTTP_PROXY`：可选的 AI 网关出站代理；本地 launchd 后台进程不一定继承 shell 的 `http_proxy/https_proxy`，如生图报 DNS / ConnectError，可在本地 `api/.env` 配置后重启后端。`/health` 只回传是否配置代理，不回传代理地址。
- `YTDLP_COOKIES_FILE` / `YTDLP_COOKIES_FROM_BROWSER`：可选 TikTok 下载登录态；生产云端固定使用 cookies 文件 `/run/secrets/tiktok_cookies.txt`（宿主机 `./secrets/tiktok_cookies.txt` 挂载进容器），本地开发可临时用浏览器 cookies。cookies 文件属于敏感登录态，只能放本机或服务器私有路径，不允许入库。
- `VOICE_PROVIDER`：配音通道，服务端固定使用 `azure_openai`；旧环境若写 `minimax` 会被忽略
- `AZURE_OPENAI_BASE_URL` / `AZURE_OPENAI_API_KEY`：微软 Azure OpenAI 协议配音网关；本地未单独配置 Key 时回退复用 `LLM_API_KEY`
- `AZURE_TTS_MODEL` / `AZURE_TTS_VOICE_ID` / `AZURE_TTS_VOICE_POOL` / `AZURE_TTS_PATH` / `AZURE_TTS_PATHS`：Azure OpenAI TTS 模型、默认音色、音色池和 OpenAI 协议语音路径；后端会按 `AZURE_TTS_PATHS` 依次尝试，便于区分路径不对和整条语音服务不可用
- `POE_API_KEY` / `VIDEO_API_KEY`：视频生成通道 Key，只能放本地环境变量
- `WEB_AUTH_USERNAME` / `WEB_AUTH_PASSWORD` / `WEB_AUTH_SESSION_SECRET`：生产网页登录和会话签名配置；密码和 session secret 只放服务器环境变量，不入库
- `FFMPEG_BIN` / `FFPROBE_BIN`：可选本地媒体二进制路径；本机 Homebrew ffmpeg 动态库损坏时，后端会自动跳过不可用的 PATH 版本并尝试本机静态 ffmpeg 备选，生产仍建议使用系统 ffmpeg/ffprobe
- 生产环境变量：服务器只使用 `deploy/.env.production`，模板为 `deploy/.env.production.example`；真实 Key 不入库
- 同步生产代码时必须排除服务器真实 `deploy/.env.production`，只同步 `deploy/.env.production.example`；网页登录密码、session secret、ASR/API Key 只保留在服务器环境文件和 `/root/skg-marketing-studio-login.txt`

## 规则
- 不允许编造不存在的部署域名、账号、密码
- 没有公网地址时，`.project.json.urls` 保持空数组
- 任何部署或域名变化，都要先改元数据，再视为任务完成
- 用户给到源码 / 下载包 / 参考实现时，默认优先按源码实现和复刻，不先自创“类似效果”；如果因安全、依赖、性能或部署限制必须改写，必须先说明差异和原因。
- 媒体素材交互为项目基底规则：任何图片、视频、抽帧、产品图、AI 生成图、首尾帧和视频候选缩略图，默认复用 `web/components/media-asset-tile.tsx`；必须支持鼠标停留顶层放大预览，可删除素材必须有删除按钮，预览不能被面板或滚动容器遮挡。

## 注意事项
- 项目内源码解析页：`docs/source-analysis.html`
- 源码解析页是给产品协作和需求描述用的独立 HTML，不接入 Next 应用路由
- 后续任何功能、节点职责、接口、数据模型或用户操作路径变更，都要同步更新 `docs/source-analysis.html` 的对应章节和变更记录
