Files
cmshoppe/docs/troubleshooting.md

306 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 常见问题排查
> 本文只记录可复用的本地排障步骤。T-538 后用户数据默认位于 `data/`,涉及 `data/config.json`、`data/config/ai_models.json`、`data/config/cmhub.json`、`data/cmshopee.db`、`data/chrome_user_data_dir/`、`data/images/` 时,默认它们是本机敏感或业务数据,必须保持 gitignore,不把真实密码、API Key、Cookie、token 写入文档、日志或提交。
## 启动时报 “AI 模型 category 必须是 text 或 image”
### 现象
运行 `python main.py` / `python -m app` 启动 GUI 时,弹出或输出:
```text
AI 模型 category 必须是 text 或 image
```
### 原因
`data/config/ai_models.json` 是本地 AI 模型清单,里面每个模型都必须有合法的 `category`:
- `text`:标题生成模型,会出现在“标题大模型”下拉。
- `image`:封面生成模型,会出现在“图片大模型”下拉。
早期或手工维护过的 `data/config/ai_models.json` 可能缺少 `category`,或者填了中文、空值、旧字段,导致 `appconfig.load_ai_models_config()` 严格校验失败,GUI 启动被阻断。
### 不要这样做
- 不要删除 `data/config/ai_models.json` 来“重置”,否则会丢失本地明文 API Key 和模型配置。
- 不要把 `data/config/ai_models.json` 提交到 git。
- 不要把完整文件内容贴到聊天、文档或日志里;该文件含本地明文 API Key。
### 推荐修复
先只查看非敏感字段,确认哪些模型缺少 `category`:
```powershell
python -c "import json; p='data/config/ai_models.json'; data=json.load(open(p,encoding='utf-8')); [print({'index': i+1, 'name': m.get('name'), 'category': m.get('category'), 'api_type': m.get('api_type'), 'enabled': m.get('enabled'), 'has_api_key': bool(m.get('api_key'))}) for i,m in enumerate(data.get('models', []))]"
```
然后手工编辑 `data/config/ai_models.json`,只补这些字段:
```json
{
"name": "示例文本模型",
"category": "text",
"enabled": true
}
```
```json
{
"name": "示例图片模型",
"category": "image",
"enabled": true
}
```
如果要用脚本批量修复,只按模型名或 `api_type` 推断 `category`,并保留原有 `url`、`model`、`api_key`、`extra_body` 等字段。示例:
```powershell
python -c "import json; p='data/config/ai_models.json'; data=json.load(open(p,encoding='utf-8')); mapping={'你的文本模型名':'text','你的图片模型名':'image'}; changed=False
for m in data.get('models', []):
name=m.get('name')
if not m.get('category') and name in mapping:
m['category']=mapping[name]; changed=True
if m.get('enabled') is None:
m['enabled']=True; changed=True
open(p,'w',encoding='utf-8').write(json.dumps(data,ensure_ascii=False,indent=2)+'\n')
print('updated' if changed else 'unchanged')"
```
### 验证
验证时只输出模型名、类别、是否启用、是否存在 Key,不输出 Key 本身:
```powershell
python -c "import os,sys; sys.path.insert(0, os.getcwd()); from app import appconfig; print([(m['name'], m['category'], m['enabled'], m['api_key_set']) for m in appconfig.list_ai_models()])"
```
确认 `data/config/ai_models.json` 仍被忽略:
```powershell
git status --short --ignored data/config/ai_models.json
```
正常输出应包含:
```text
!! data/config/ai_models.json
```
最后重新启动:
```powershell
python main.py
```
## ⑤ 设置里测试连接提示 “HTTP 404”
### 现象
在⑤设置里填写文本大模型信息后,点击「测试连接」,结果显示:
```text
HTTP 404
```
### 原因
HTTP 404 表示服务端已响应,但当前请求 URL 路径不存在。旧代码会把「网址」字段原样作为 POST 地址;如果只填 OpenAI-compatible base URL,例如:
```text
https://api.vectorengine.ai/v1
```
程序会直接 POST 到 `/v1`,部分服务商会返回 404。当前代码已兼容 base URL:
- `api_type=chat` 或 `auto`:`/v1`、`/api/v1` 会自动补成 `/chat/completions`。
- `api_type=images_edits`:`/v1`、`/api/v1` 会自动补成 `/images/edits`。
- 已经填写完整 endpoint(如 `/v1/chat/completions`)时保持不变。
### 推荐修复
更新到包含该修复的代码后,保留当前配置即可;也可以手工把「网址」改成完整 endpoint:
```text
https://api.vectorengine.ai/v1/chat/completions
```
如果仍然 404,优先检查服务商文档要求的 endpoint 路径和 `api_type`,不要把 `data/config/ai_models.json` 或 API Key 贴到日志、文档或聊天里。
### 非敏感检查
只输出非密钥字段确认 URL 形态:
```powershell
python -c "import json; from urllib.parse import urlsplit,urlunsplit; p='data/config/ai_models.json'; data=json.load(open(p,encoding='utf-8')); [print({'name':m.get('name'),'category':m.get('category'),'api_type':m.get('api_type'),'url_without_query':urlunsplit((urlsplit(m.get('url','')).scheme,urlsplit(m.get('url','')).netloc,urlsplit(m.get('url','')).path,'','')),'has_api_key':bool(m.get('api_key'))}) for m in data.get('models', [])]"
```
## ⑤ cmhub「刷新别名 / 测试连接」提示 notfound / 404
### 现象
在⑤设置切到 cmhub 后端,手动填入 Base URL、API Key,点「刷新别名」或「测试连接」,提示 `notfound`(即 HTTP 404)。
### 原因
先明确:**cmshopee 的请求本身是达标的**——`GET {Base URL}/api/v1/models`,头 `Authorization: Bearer <key>`,与对接文档《cmhub-接口对接文档-桌面端生文生图》§4.4 和 cmhub 服务端路由 `path("v1/models", ModelsView)` 完全一致。404 表示服务端已响应但该路径不存在,原因在请求之外,通常是两类:
1. **Base URL 填了多余路径(手动填入最常见)**:Base URL 只能填**网关根**,例如 `https://cmhub.example.com`。T-530 后程序会在保存和请求前规整 Base URL,填成 `https://host/api/v1` 也会按 `https://host` 拼出 `https://host/api/v1/models`;仍建议只填网关根,便于人工排查。
2. **连的那台 cmhub 实例没有该路由**:`ModelsView` 在 cmhub 仓库代码里有,但**运行中的实例**若未重新部署/重启,`/api/v1/models`(较新接口)会 404,而 `/api/v1/balance`(较老接口)可能正常。
### 隔离方法(一条 curl 定位)
```bash
curl -i "https://<你的BaseURL>/api/v1/models" -H "Authorization: Bearer sk_cmhub_xxx"
curl -i "https://<你的BaseURL>/api/v1/balance" -H "Authorization: Bearer sk_cmhub_xxx"
```
- 两个都 404 → **Base URL 填错**,改成网关根(不带 `/api`、`/api/v1`)。
- balance 通、models 404 → **cmhub 实例未更新**,让部署方重启/重部署带上 `ModelsView`。
- models 通 → cmshopee 侧别的问题,回代码排查。
> 不要把 API Key 贴进日志、文档或聊天;curl 里的 `sk_cmhub_xxx` 是占位。
### 当前处理(T-530 后)
T-530 已实现:保存和请求前都会把 Base URL 规整为网关根,去掉多余路径、查询串和片段;HTTP 404 会映射为 `not_found`,显示“cmhub 接口不存在,请检查 Base URL 或该实例是否已部署 /api/v1/models”,不再透传 `notfound` 这类生涩原文。
## ②AI生成提示“当前筛选结果没有可生成任务”
### 现象
在②AI生成模块选中某个批次,列表里看到多条“失败”记录,点击「开始生成」后,状态栏提示:
```text
当前筛选结果没有待生成或生成失败可重试任务;请先在①导入采集完成旧数据采集
```
旧版本可能提示:
```text
当前筛选结果没有可生成任务
```
### 原因
②AI生成只能处理两类任务:
- 已完成①采集的待生成任务:底层通常是 `stage=collected`。
- AI 生成阶段失败、可以重试的任务:例如生成标题或生成封面失败。
如果列表里的“失败”其实是①采集失败,任务还停在 `stage=imported/status=failed`,没有旧标题和旧封面,②无法直接生成。2026-07-06 的实测问题就是这种情况:本地最新批次 5 条任务都是 `stage=imported/status=failed/collect_attempts>0/generate_attempts=0`,属于采集失败,不是 AI 生成失败。
如果失败来自③更新shopee,任务通常已有新标题/新封面,但这是更新阶段失败,也不应回到②自动重新生成。
### 当前处理
当前代码已把②AI生成列表的失败状态细分为:
- `采集失败`:需要先回①重新采集旧标题/旧封面。
- `生成失败`:可以在②当前筛选范围内再次点击「开始生成」重试。
- `更新失败`:应去③更新shopee重试或重置更新状态,不会被②误当成生成任务。
②「开始生成」使用统一的 `ai.is_generatable_task()` 判断可生成范围,GUI、Worker 和底层 `generate_batch()` 口径一致。
### 推荐处理
1. 如果②状态列显示“采集失败”:回到①导入采集,确认对应账号 Chrome 已启动且已登录,再重新采集旧标题/旧封面。
2. 如果②状态列显示“生成失败”:留在②,确认 cmhub Base URL、API Key、别名和点数正常后,直接点击「开始生成」重试当前筛选结果。
3. 如果②状态列显示“更新失败”:到③更新shopee处理;需要重复更新时使用③的重置更新状态入口,不要在②重新生成。
排查时不要把 `data/cmshopee.db`、`data/config/cmhub.json`、`data/config/ai_models.json` 或任何 API Key、密码、Cookie 发到聊天、文档或提交里。
## AI生成:标题成功但图片生成失败,且看不到原因
当前代码会为 ② AI生成写两层日志:
- 页面右下「AI生成运行日志」显示最近一次 `run_type=generate` 的逐条事件,例如 `phase=cover step=cover_request result=failed detail=...`。
- 本地 `data/logs/cmshopee.log` 保存脱敏后的 traceback、任务 id、alias、item_id、phase 和 step,用于判断卡在模型配置、封面请求、图片解析、保存文件还是写库。
排查顺序:先看 ② 页面运行日志里的 `phase` / `step` / `detail`;如果只看到简短错误,再查看本地 `data/logs/cmshopee.log`。不要把 `data/config/ai_models.json` 或 API Key 发到聊天、文档或提交里。
## ④启动登录重复打开 Chrome
现象:在④账号管理中选中同一个账号,第一次点击「启动登录」会打开该账号 Chrome;不关闭该 Chrome 的情况下再次点击「启动登录」,旧版本会再打开一个 Chrome 窗口,而不是复用已打开的账号窗口创建/激活 tab。
旧原因:原代码路径是 `AccountsTab.launch_login()` → `accounts.launch_for_login()` → `chrome.launch_chrome()` → `subprocess.Popen()`,每次点击都会直接启动进程;已有的 `chrome.is_running(debug_port)` 没有接入启动入口,`login_statuses[alias]="已启动"` 也只是界面状态,不会阻止下一次点击。
已修复:T-105b 已把④「启动登录」改成幂等流程。先检查该账号 `debug_port` 是否已有 CDP 响应;已响应时不再 `Popen`,而是复用现有 Chrome/CDP 并打开或激活 `https://<region_host>/portal/` 登录/卖家中心 tab;未响应时才新启动 Chrome。该修复不自动登录、不填密码、不绕过验证码,也不改变①/③预检“不自动启动 Chrome”的规则。
验证方式:同一账号第一次点击「启动登录」应显示“Chrome 已启动,请人工登录”;保持该账号 Chrome 不关闭,再点一次应显示“该账号 Chrome 已打开,已复用现有窗口”,且不会新增 Chrome 进程。
## 打包版窗口左上角显示不全,标题栏不可拖动
### 现象
在 Windows 10 虚拟机或小分辨率环境运行打包后的 `dist/cmshopee/cmshopee.exe` / `release/蝦皮圈優化助手<版本>.zip` 后,主窗口出现在屏幕左上角,左边和标题栏显示不全,看不到「蝦皮圈優化助手 v<版本>」标题,导致无法用鼠标拖动窗口到中间。
### 原因
修复前旧逻辑只设置了固定窗口大小:
```python
self.resize(1180, 760)
```
没有按当前屏幕可用区域做以下处理:
- 限制初始尺寸不超过屏幕可用区域。
- 居中显示。
- 保证窗口 frame/title bar 完整落在屏幕内。
因此在虚拟机、小分辨率、高 DPI、任务栏占用较多或多显示器切换环境下,Qt/Windows 默认放置窗口时可能让标题栏或左边缘落到屏幕外。PyInstaller 打包不是根因,只是打包版更常在目标虚拟机里暴露该问题。
### 临时处理
如果已经遇到窗口无法拖动:
1. 按 `Alt + Space`。
2. 按 `M` 选择移动。
3. 用方向键把窗口移回屏幕中间,再按回车。
也可以尝试 `Win + 方向键` 贴靠/恢复窗口,或先调高虚拟机分辨率。
### 当前修复(T-541 后)
T-541 已在主窗口启动时读取 `QApplication.primaryScreen().availableGeometry()`:
- 大屏保持接近 `1180x760`。
- 小屏自动缩小到可用区域内。
- `resize()` 后按可用区域居中 `move()`。
- 确保窗口 frame 左上角不小于可用区域左上角,标题栏完整可见、可拖动。
- 不保存坏的历史窗口坐标,避免下次继续打开到屏幕外。
修复后需要在普通桌面和 Windows 10 虚拟机/小分辨率环境各启动一次打包版,确认标题栏完整可见。
## ② cmhub 图片下载很慢(几十秒~几分钟),而 curl 只需几秒
### 现象
② 生图时,日志显示 `cmhub 已返回 image_url` 后,本地「下载完成」耗时几十秒甚至上百秒;用 curl 下载同一个 `image_url`(如 `http://<ip>:8080/generated/images/...png`)只需几秒。
### 原因
图片是从 cmhub 的**媒体服务**(常见形如 `http://<公网IP>:8080/...`,明文 HTTP、非标准端口)下载,与生成 API(HTTPS 域名)是不同端点。慢的常见根因两类:
1. **系统代理**(最常见):`requests` 默认读 `HTTP(S)_PROXY`/`ALL_PROXY` 环境变量,会把明文 HTTP 到 `:8080` 的下载塞进代理;慢/不支持非标端口的代理转发会让下载卡顿,而干净窗口的 curl 直连很快。
2. **并发争抢**:多图并发时,同步生成的长连接 + 并发下载挤同一主机,单条下载被拖慢(T-545 已限制生图/下载并发上限 5;T-546 已加共享连接池缓解)。
### 定位(一条命令区分代理 vs 并发)
```powershell
# curl 直连基准
curl -o NUL -w "curl %{time_total}s`n" "<image_url>"
# python 关掉代理直连
py -3.10 -c "import requests,time; s=requests.Session(); s.trust_env=False; t=time.time(); r=s.get('<image_url>'); print('no-proxy', len(r.content), round(time.time()-t,1),'s')"
```
若 `no-proxy` 明显变快 → 就是代理。
### 修复(T-546 后)
cmshopee 的 cmhub 请求默认**绕过系统代理**(`ai.cmhub.use_system_proxy` 默认 `false`),并用共享 `requests.Session` + 连接池减少握手/连接饥饿。
- 若你的机器**必须走代理**才能上网,编辑 `data/config.json` 把 `ai.cmhub.use_system_proxy` 改为 `true`(注意:慢代理仍可能拖累图片下载)。
- 若下载仍慢且 curl 也慢,则是 cmhub **媒体服务器本身慢**(如 Django 直接服媒体、单线程),属服务端问题,需在 cmhub 侧用 nginx/对象存储服 `/generated/images/`。
- 调试期可临时把 ⑤「图片并发数」设 1 复测单张,排除并发因素。