# 工作交接说明(HANDOFF) > 更新:2026-08-30 > 读者:下一个接手工作的 AI / 开发者(工具无关)。先读本文再动手,避免重复探索。 > 配套文档:[`README`](../../README.md)(项目总览)、[`collect.md`](./collect.md)(采集)、[`trial-page.md`](./trial-page.md)(试算页)、[`image-suite.md`](./image-suite.md)(生图)、[`background-jobs.md`](./background-jobs.md)(**后台任务中心方案、实施状态与路线图**)、[`image-asset-lifecycle.md`](./image-asset-lifecycle.md)(图片与水印存储规则)、[`api.md`](./api.md)(契约)、[`backend-structure.md`](./backend-structure.md)(后端结构) --- ## 1. 项目一句话 Ozon 跨境上品工具链。当前主链路(V2.1,已放弃 Ozon API 直传): ``` extensions/collector 扩展采集(Ozon/1688/淘宝/天猫) → 「上报商品」POST /api/materials 入库 + 自动打开试算页 → studio /trial/{id} 试算页:计价 → 俄文文案 → AI 生图(套图+单张)→ 登记 → 导出 CSV / 组合码 → 人工上 Ozon 卖家后台 ``` 技术栈:FastAPI(server,8800)+ React19/Vite/antd6(studio,8900)+ WXT Chrome 扩展(extensions/collector)。SQLite(`data/app.db`),无鉴权(V2.1 决策:先功能后鉴权)。 ## 2. 启动与构建 ```bash # 一键启动(推荐):自动装依赖 → 构建采集插件 → 起插件改动监听 → 起 studio → 起 server ./start.command # server 单独起(等价;日志建议重定向到 /tmp/osk-server.log) .venv/bin/uvicorn main:app --app-dir server --reload --reload-include "*.yaml" --host 127.0.0.1 --port 8800 # 扩展改动后(start.command 的监听会自动构建,仍需去 chrome://extensions 手动点刷新) pnpm -C extensions/collector build # 产物 .output/chrome-mv3 ``` - **start.command 新增**:启动时会先执行 `alembic upgrade head`,再自动构建采集插件并起后台监听(3s 轮询 `src/**` + `wxt.config.ts`,改动自动重建;BSD find 语法,无新依赖)。Ctrl+C 会连同监听进程一起清理。 - **后台任务 Worker**:`start.command` 会额外启动 `python server/worker.py`。单独启动 server 时也要另开终端运行该命令,否则生图任务只会停在“排队中”。 - **运行态提醒**:交接时 server 是上个会话用 nohup 起的后台进程(日志 `/tmp/osk-server.log`),原 start.command 终端已被停掉。新会话建议:`kill $(lsof -ti :8800)` 后用 `./start.command` 正常接管。 - **`uvicorn --reload` 不监控 `.env`**:改 `.env` 后必须手动重启 server(Kill 8800 端口进程再起)。 - 密钥在根目录 `.env`:`DEEPSEEK_API_KEY`(规划+文案)、`DASHSCOPE_API_KEY`(通义,可用 ✅)、`RIGHTAPI_API_KEY`(RightCode 中转,**通道当前故障** ❌)、`PCORE_API_KEY`(pcore.ai,**可用 ✅**)、`ARK_API_KEY` 未配(豆包未用)。`.env.example` 有全部键位说明。 ## 3. 生图供应商现状(2026-08-29,重要) | 供应商 | 域名 | 模型(下拉显示) | 状态 | |---|---|---|---| | 通义/阿里 | dashscope.aliyuncs.com | `qwen-image-3.0-pro` 等(阿里巴巴) | ✅ 可用,实测 10s 出图 | | RightCode | rightapi.ai | `gpt-image-2`、`gpt-image-2-vip`、`nano-banana` 系列(RightCode) | ❌ 通道故障:任务能提交但生成阶段 503/「No available channel」,纯文生图也失败;中转侧问题,待其恢复 | | Pcore | pcore.ai | `gpt-image-2@pcore`(Pcore) | ✅ 可用(用户已在 pcore 开通 image2 分组后端到端跑通,全链路约 1 分钟/张) | - **默认生图模型 = `gpt-image-2@pcore`**(`studio/src/services/suite.ts` 的 `DEFAULT_IMAGE_MODEL`)。 - **同名模型多中转的路由约定**:前端模型 value 用「api模型@provider」后缀区分(目前仅 pcore 用到),服务端 `schemas/suite.py` 的 `MODEL_PROVIDERS` + `api_model()`(剥后缀)处理;白名单 `PCORE_MODELS`。 - **pcore 协议要点**(`generator.py` 的 `_pcore_request`/`_pcore_poll_task`):无参考图走 `POST /v1/images/generations`,带参考图走 `/v1/images/edits` 且 images 必须是 **`[{image_url: dataURI}]` 对象数组**(纯字符串报错);均为异步:提交拿 `id` → 轮询 `GET /v1/images/{kind}/{task_id}` → `completed` 后 `data[0].url` 取图。`size` 是枚举(1024x1536/1024x1024),不接受自定义尺寸。new_api 系中转常见「HTTP 200/503 + 错误体」,错误解析已兼容嵌套 `error.{code,message}`;「No available channel」类错误不重试直接失败。 - **速率**:image2 分组 RPM 70 / RPD 1.2K(日限);当前独立 Worker 单进程逐图领取任务,远碰不到 RPM。pcore 账户余额 $5,按张扣费。 ## 4. AI 规划张数策略(`planner.py`) - **不设硬性数量**:prompt 让 AI 按商品复杂度自主决定,「建议 8 张左右」;规划规则 = 先保证每个 SKU 1 张主图,其余类型自行取舍。 - **用户可通过「生图要求」指定张数**:写了「N 张」AI 必须严格执行;服务端 `_total_budget()` 用正则提取,安全上限:默认 15、指定时按 N 最大 24(防跑飞,超限从尾部截断,SKU 主图优先保留)。 - **规划器曾频繁报「规划器未返回有效方案项」**:根因是 deepseek-v4 是思维链模型而规划器裸调用没禁 thinking,token 被推理烧光、正文为空。已修复:请求体加 `"thinking": {"type": "disabled"}` + 解析失败/空方案自动重试一次 + 失败时记录原始输出 + 缺 title 的方案项从 detail/kind 兜底合成不再丢弃。30 SKU 压测 3/3 通过。 ## 5. 当前进度 ### ✅ 已完成(含 2026-08-29 一轮,**全部未提交**,见 §6) | 模块 | 内容 | |---|---| | 试算页 | 五区块页面、计价、俄文文案、采集图片/出图方案/生成结果、登记导出 | | 采集对接 | ISS 扩展并入 `extensions/collector/`,「上报商品」入库 + 自动打开试算页 | | 生图服务端 | planner/generator/prompts/watermark/jobs + 独立 Worker,套图 + 单张 AI 生图 + 水印 + ZIP 导出 | | **本轮:Pcore 接入** | 新 provider 全链路(生成/编辑/轮询/重试/错误处理),`@pcore` 路由约定,下拉带供应商名,默认模型切 Pcore | | **本轮:规划器修复** | thinking 禁用 + 重试 + 张数策略(AI 自主,建议 8 张左右,生图要求可指定) | | **本轮:采集箱** | 勾选列 + 全选/取消全选 + 批量删除(硬删、Popconfirm 二次确认、删空页自动回退) | | **本轮:采集图片区改版** | 8 列网格;AI 生图按钮半透明悬浮图片右下角;图名显示在图片下方(取 URL 末段);SKU 名并入图名行(不再悬浮遮挡勾选钮);勾选钮在右上角;默认全部不勾选 | | **本轮:生成结果区** | 「导出图片」按钮(水印按钮右侧,复用 POST /api/export/images,ZIP 内分「套图结果/单张生图」文件夹);「单张 AI 生图」小节改名「生图记录」(落库历史,刷新后保留) | | **本轮:图片生命周期** | 采集、上传与 AI 图片统一保存为 `original + current`;只保留当前水印版;重加/移除水印自动回收旧对象;删除单图/硬删商品会清理本地或七牛对象;具体规则见 [`image-asset-lifecycle.md`](./image-asset-lifecycle.md) | | **本轮:图片资源上限** | 单张上传/下载默认 20 MB;采集最多 100 张;导出最多 50 张、总量 200 MB;ZIP 超过 2 MB 自动落临时磁盘并分块返回,配置见 `.env.example` | | **本轮:生图任务与轮询** | 单张 AI 编辑改为提交统一异步任务并立即返回 `suite_id`;套图与单张弹窗均采用无重叠递归轮询、取消在途请求与失败退避;套图任务 ID 可在刷新页面后从浏览器会话恢复 | | **本轮:持久化图片任务** | 套图、单张编辑与批量添加水印已迁入数据库任务表和独立 Worker;Pcore/RightAPI/通义万相保存第三方任务 ID 并可在重启后恢复轮询;水印逐图进度可刷新恢复、取消并按失败项重试,详见 [`background-jobs.md`](./background-jobs.md) | | **本轮:关键 bug 修复** | ① axios 全局 Content-Type 导致 FormData 被 JSON 化 → 上传 422(`api.ts` 移除全局头,**今后勿设**);② TrialPage `load()` 置 loading 导致整页闪 Spin、卸载面板(杀生图轮询/丢勾选)→ 改静默刷新;③ web v1 页面因 cdn.tailwindcss.com 被墙整页无样式 → tailwind 运行时固化到 `web/vendor/tailwind.js` | | **本轮:杂项** | 采买地址「打开地址」改图标按钮(与复制图标同款);start.command 自动构建+监听插件 | ### 验证口径(本轮均实测通过) - 上传/生图/导出/批量删除/规划,全部真实接口+浏览器操作验证;Pcore 端到端 56s 出图并回写素材。 - 每个 UI 改动后 `pnpm -C studio exec tsc -b` 零错误;插件构建通过;`/api/health` 200。 ## 6. ⚠️ 未提交改动清单(当前最大事项) 分支 `feat-2.1.0`,工作区仍有多项未提交改动,**建议按模块分 4-5 个 commit**: 1. **Pcore 生图接入 + 规划器修复 + 张数策略** `server/services/{generator,planner,suite_service,prompts/__init__}.py`、`server/{api/suite.py,schemas/suite.py,config/settings.py}`、`studio/src/services/suite.ts`、`.env.example`、`docs/v2.1/{image-suite,api}.md` 2. **试算页 UI 迭代 + 采集箱批量操作 + 请求层修复** `studio/src/pages/trial/{TrialSuitePanel,TrialInfoPanel,TrialPage}.tsx`、`studio/src/pages/collection/CollectionPage.tsx`、`studio/src/services/api.ts` 3. **web v1 样式修复(CDN 固化)+ start.command 插件自动构建** `web/ozonSeller.html`、`web/vendor/tailwind.js`(新目录)、`start.command` 4. **图片生命周期与水印回收**:`server/services/image_assets.py`、`server/services/storage.py`、`server/{api/materials,api/products,models/asset}.py`、迁移 `a41e8c7f4b20_add_asset_image_lifecycle.py`、试算页水印入口 5. **交接文档**:`docs/v2.1/{HANDOFF,image-asset-lifecycle,optimization-audit}.md` 提交前:`git checkout -- studio/tsconfig.app.tsbuildinfo`(构建产物,勿提交)。 ## 7. 下一步(按优先级) 1. **提交**:按 §6 分组提交。 2. **手动全流程回归**(浏览器 + Chrome 真机扩展):采集 → 上报 → 试算页五区块 → 生成(Pcore)/编辑/删除 → 登记表录入/导出 CSV/组合码 → 清空。 3. **运营观察**:pcore 余额($5 起步)与 image2 分组日限(1.2K 张/天);RightCode 通道恢复情况(恢复后 nano-banana 系列可回归)。 4. **Phase D 待办**:批量 CSV 服务端化(`GET /api/export/trial-csv`);登记表数据存 localStorage(跨设备需落库)。 5. **远期**:鉴权/账户体系重做(从 `deps.py` 层重引);ISS-only 产品模式构建;`extension-v1/v2`、`web/`、`server/legacy/` 清理时机。 ## 8. 已知坑(新会话必读) | # | 坑 | 对策 | |---|---|---| | 1 | `uvicorn --reload` 不监控 `.env` | 改 `.env` 后手动重启 server | | 2 | 生图任务依赖独立 Worker | `start.command` 会启动;若手动运行 uvicorn,必须另开 `python server/worker.py`,否则任务会一直排队 | | 3 | SQLite 仅适合单机 Worker | 当前通过租约恢复异常退出的任务;未来多实例前先迁移 PostgreSQL,再做多 Worker 竞争领取 | | 4 | 外部模型任务可恢复范围不同 | Pcore/RightAPI/通义万相保存 provider task id 后可继续轮询;豆包/同步千问没有外部任务 id,进程恰在请求中断时只能重试 | | 5 | **axios 实例勿设全局 Content-Type** | axios 1.x 遇「默认 JSON 头 + FormData」会把表单转 JSON 发送(`formDataToJSON`),multipart 接口全 422。已从 `api.ts` 移除;对象请求体 axios 会自动补 JSON 头 | | 6 | `web/` 样式依赖公网 CDN | tailwind 已固化 `web/vendor/tailwind.js`;font-awesome/jsdelivr 若也断,同样方式下载到 `web/vendor/` 改引用 | | 7 | 试算页水印文本存 localStorage(`trialWatermark`) | 改默认值不影响已保存设置;登记表同理存 localStorage `trialRegisterRecords` | | 8 | `studio/tsconfig.app.tsbuildinfo` 是构建产物 | 提交前 `git checkout -- studio/tsconfig.app.tsbuildinfo` | | 9 | 冻结代码不要投入 | `server/legacy/`、`extension-v1/`、`extension-v2/`、`web/`(本轮 CDN 固化属修复例外)、studio 商品编辑页的属性映射/发布区块 | | 10 | AI 规划/生图花钱 | pcore 按 $ 扣费、RightCode 按张计费;联调时 plan 张数设 1-2、优先低成本模型 | | 11 | TrialPage `load()` 勿再置 loading | 置 loading = 整页 Spin + 卸载面板 = 杀生图轮询/丢状态(已修,保持静默刷新) | | 12 | 浏览器自动化调试怪癖(如 ZCode IAB) | 截图偶发超时(重试一次多半仍失败,改用几何测量/计算样式验证);文件上传需用 DataTransfer+input change 事件模拟;改代码后 React 事件偶发失灵 → reload 页面即恢复 | | 13 | 图片上传/导出存在大小限制 | 默认单张 20 MB、采集 100 张、导出 50 张、ZIP 200 MB;超限接口返回 413。需要调整时改 `.env` 的 `IMAGE_*` / `EXPORT_*` 配置并重启服务 | ## 9. 关键文件速查 | 功能 | 前端(studio/src/) | 后端(server/) | |---|---|---| | 试算页骨架 | `pages/trial/TrialPage.tsx`(load() 是静默刷新,见坑 11) | — | | 01 商品信息(采买地址图标、型号/货号联动) | `pages/trial/TrialInfoPanel.tsx` | `api/materials.py` | | 02 价格试算 | `pages/trial/TrialPricingPanel.tsx` + `pricing/pricing.ts` | — | | 03 俄文文案 | `pages/product/CopyPanel.tsx`(共用) | `api/ai.py` + `services/deepseek.py` + `prompts/copy_ru.py` | | 04 图片与生图(7 列网格/悬浮按钮/图名) | `pages/trial/TrialSuitePanel.tsx` + `AiImageGenModal.tsx` | `api/suite.py` + `services/{jobs,job_worker,suite_service,generator,planner,prompts/,watermark,image_assets}.py` | | 图片原图/水印/删除回收 | `TrialSuitePanel.tsx`(水印设置) | `api/materials.py`、`api/products.py`、`services/{image_assets,storage}.py` | | 生图供应商路由 | `services/suite.ts`(IMAGE_MODEL_OPTIONS 带供应商名;DEFAULT_IMAGE_MODEL) | `schemas/suite.py`(MODEL_PROVIDERS/`@pcore` 约定)+ `services/generator.py`(generate_pcore 等) | | 05 登记与导出 | `pages/trial/TrialExportPanel.tsx`(localStorage `trialRegisterRecords`) | — | | 生成结果导出 | `TrialSuitePanel.tsx` handleExportResult(套图+单张分组 ZIP) | `api/export.py` | | 采集箱(全选/批量删除) | `pages/collection/CollectionPage.tsx` | `api/products.py`(DELETE hard=true) | | 前端服务层 | `services/{suite,product,ai,fx,api}.ts` | `schemas/suite.py`(契约对齐) | | 扩展上报 | `extensions/collector/src/api/report.ts` + `entrypoints/{background,sidepanel/App}.tsx` + `storage/settings.ts` | `api/materials.py` | ## 10. 验收标准(改完怎么算完成) 1. `pnpm -C studio exec tsc -b` 零错误、`pnpm -C studio build` 通过(提交前还原 `tsconfig.app.tsbuildinfo`) 2. 改扩展:`pnpm -C extensions/collector build` 通过 3. 改 server:`/api/health` 200、涉及接口 curl/页面冒烟通过 4. UI 改动:浏览器实测 + 必要时截图确认