From 923c810e3b8fea106a1ed3cb988f05bc3f36e4ab Mon Sep 17 00:00:00 2001 From: admin9webs Date: Fri, 10 Jul 2026 16:12:13 +0800 Subject: [PATCH] Add cross-border-ecommerce/flairgs-vision-api --- .../flairgs-vision-api/SKILL.md | 318 ++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 skills/cross-border-ecommerce/flairgs-vision-api/SKILL.md diff --git a/skills/cross-border-ecommerce/flairgs-vision-api/SKILL.md b/skills/cross-border-ecommerce/flairgs-vision-api/SKILL.md new file mode 100644 index 0000000..505e2bc --- /dev/null +++ b/skills/cross-border-ecommerce/flairgs-vision-api/SKILL.md @@ -0,0 +1,318 @@ +--- +name: flairgs-vision-api +title: FlairGS 视觉分析 API +description: Self-hosted VL (qwen3-vl:8b) + image processing API for 9Webs cross-border ecommerce — OCR, captcha, product analysis, IP screening, white-bg, txt2img. 关键:字段名是 base64 不是 image_base64。 +category: cross-border-ecommerce +tags: [flairgs, vision, vl, ocr, captcha, qwen-vl, image-processing, cdp-bridge] +--- + +# FlairGS 视觉分析 API + +自建 VL (Vision-Language) + 图像处理 API。GS3 运行 qwen3-vl:8b,通过 frp 隧道暴露给 SG5。 + +## 速查表 + +| 项目 | 值 | +|------|-----| +| **LAN URL (优先)** | `http://192.168.9.105:7870` | +| **FRP URL (新)** | `http://43.160.244.125:17870` (2026-07 上线) | +| **FRP URL (旧/回退)** | `http://frp.9webs.online:17870` | +| **AtomK 工具箱** | `http://192.168.9.105:20261` (Desktop CDP 可访问) | +| **连接策略** | FRP 直连优先(10s 超时),headless browser 可能不通需走 cron | +| **共享客户端** | `~/.hermes/scripts/flairgs_client.py` | +| **Auth Header** | `X-API-Key: 8Ax4TyZc66qrZgXK` | +| **源码** | GS3: `/home/admin9webs/flairgs_v3_server.py` | +| **VL 模型** | GS3: qwen3-vl:8b (127.0.0.1:11434) + GS4: qwen3-vl:8b (192.168.9.129:11434) | +| **Chat 模型** | GS1-35B + 本机 4B | +| **典型延迟** | ~7.5s (VL analyze) | + +## 端点 + +### GET /health + +```bash +curl -H "X-API-Key: 8Ax4TyZc66qrZgXK" http://frp.9webs.online:17870/health +``` +返回: `{"status": "ready", ...}` + +### POST /api/analyze — VL 图像分析(最常用) + +OCR、视觉问答、场景理解。**这是最主要端点。** + +> ⚠️ **致命陷阱:JSON 字段是 `"base64"`,不是 `"image_base64"`!** +> 用 `image_base64` 会静默失败——HTTP 200 但返回 `"No image data"`。 + +``` +POST /api/analyze +Content-Type: application/json +X-API-Key: 8Ax4TyZc66qrZgXK + +{ + "base64": "<纯base64,不带 data:image/png;base64, 前缀>", + "prompt": "可选,引导分析的文本提示", + "filename": "可选,默认 image.jpg" +} +``` + +返回: +```json +{ + "description": "分析结果(中文/英文)...", + "model": "GS3/qwen3-vl:8b", + "elapsed": 7.8 +} +``` + +**常用 prompt 模板:** +- **OCR**: `"只输出图片中的文字,不要任何其他内容。"` +- **验证码**: `"只输出验证码中的4个字符,不要任何其他内容。区分大小写。"` +- **UI 分析**: `"Describe all visible buttons and their positions."` +- **产品分析**: `"描述这个商品图片。类目?材质?风格?"` +- **IP 筛查**: `"图中是否包含卡通/动漫/品牌角色?列出所有 IP 风险。"` + +### POST /api/resize — 图片缩放 + +```json +{"base64": "...", "width": 800, "height": 600} +``` + +### POST /api/txt2img — 文生图 + +```json +{"base64": "...", "prompt": "生成提示词"} +``` + +### POST /api/white-bg — 白底处理(商品图标准化) + +```json +{"base64": "..."} +``` + +### POST /api/chat — LLM 对话 + +GS1-35B + 本机 4B 混合推理。 + +## 共享客户端 (推荐) + +所有脚本统一用 `~/.hermes/scripts/flairgs_client.py`,自动实现 LAN 优先 + FRP 回退: + +```python +from flairgs_client import analyze, health_check, get_active_url + +# 自动 LAN → FRP 回退 +result = analyze(b64, "描述这张图片") +print(result) + +# 健康检查 +for url, info in health_check().items(): + print(f"{url} → {info['status']}") + +# 获取当前活跃 URL +active = get_active_url() +``` + +**连接策略:** +1. 先尝试 `http://192.168.9.105:7870`(LAN 直连,3s 超时) +2. 失败自动回退 `http://43.160.244.125:17870`(FRP 隧道) +3. 局域网用户享受低延迟直连,外网用户无缝 FRP + +便捷封装也内置了: +- `ocr_captcha(b64)` — 验证码 OCR +- `describe_product(b64)` — 商品描述 +- `check_ip_risk(b64)` — IP 风险筛查 + +> 📖 实现细节、超时策略、扩展现有脚本的侵入模式:`references/client-internals.md` + +## 标准集成模式:CDP 截图 → FlairGS 分析 + +最常见的组合:Cloud Bridge CDP 截图 + FlairGS VL 分析。使用共享客户端,自动 LAN/FRP 回退。 + +```python +import json, urllib.request +from flairgs_client import analyze as flairgs_analyze + +# === Bridge 认证 === +BRIDGE_KEY = "Bing2026Cao$$$" +BRIDGE_URL = "http://127.0.0.1:9228" +SLOT = "desktop-mqjqto79" +BH = { + "Authorization": f"Bearer {BRIDGE_KEY}", + "Content-Type": "application/json", + "X-Desktop-Id": SLOT, +} + +def bridge_req(path, data=None): + url = f"{BRIDGE_URL}{path}" + body = json.dumps(data).encode() if data else None + r = urllib.request.Request(url, data=body, method="POST") + for k, v in BH.items(): r.add_header(k, v) + return json.loads(urllib.request.urlopen(r, timeout=60).read()) + +# 1. Attach CDP +att = bridge_req("/cdp/attach", {}) +tid = att["targetId"] +att2 = bridge_req("/cdp/send", { + "method": "Target.attachToTarget", + "params": {"targetId": tid, "flatten": True} +}) +sid = att2["result"]["sessionId"] + +# 2. 截图 +ss = bridge_req("/cdp/send", { + "method": "Page.captureScreenshot", + "params": {"format": "png"}, + "sessionId": sid +}) +b64 = ss["result"]["data"] + +# 3. FlairGS 分析 (LAN → FRP 自动回退) +desc = flairgs_analyze(b64, "描述这个页面上的所有按钮和功能") +print(desc) +``` + +## Desktop CDP 内网工具箱模式(2026-07 新增) + +Bridge headless 浏览器到不了 192.168.9.x,但 Desktop CDP Chrome 可以(同内网): + +```python +# 1. Attach to Desktop CDP target +att = bridge_req("/cdp/attach", {}) # X-Desktop-Id header + +# 2. 交互工具箱页面 (evaluate 可用) +ev("document.querySelector('textarea')['value']='prompt here'") +ev("document.querySelectorAll('button')[7].click()") # "🎨 生成" + +# 3. 提取生成的图片 +ev("var img=document.querySelectorAll('img')[1]; var c=document.createElement('canvas'); c.width=img.naturalWidth; c.height=img.naturalHeight; c.getContext('2d').drawImage(img,0,0); window.__CAPTURED_IMG=c.toDataURL('image/jpeg',0.9).split(',')[1]") + +# 4. 图片在本地磁盘: /home/ubuntu/output/text2img_*.jpg +``` + +**关键发现**:工具箱生成的图片存在 Bridge 服务器的 `/home/ubuntu/output/` 目录下,说明工具箱和 Bridge 共享文件系统。可直接用 Python 读取本地文件后调用 VL API。 + +```javascript +(async function(){ + var urls = ["http://192.168.9.105:7870", "http://43.160.244.125:17870"]; + var key = "8Ax4TyZc66qrZgXK"; + var b64 = "iVBORw0KGgo..."; // 纯 base64 + // LAN first, FRP fallback + for (var url of urls) { + try { + var r = await fetch(url + "/api/analyze", { + method: "POST", + headers: {"X-API-Key": key, "Content-Type": "application/json"}, + body: JSON.stringify({base64: b64, prompt: "你的问题"}), + signal: AbortSignal.timeout(url.includes("105") ? 3000 : 120000) + }); + return await r.text(); + } catch(e) { continue; } + } + return "All URLs failed"; +})() +``` + +## Desktop CDP 交互模式(2026-07 实测) + +Desktop CDP Chrome 在用户 Windows 机器上(192.168.9.x 内网),可直接操控工具箱 Web UI: + +```javascript +// 获取按钮列表(while 循环兼容 CDP 表达式过滤器) +var b=document.querySelectorAll('button'); var i=b.length; var r=[]; +while(i--) { r.push(i+':'+b[i].innerText.substring(0,30)); } +JSON.stringify(r) +// → ["15:确认转换","14:取消",...,"7:🎨 生成",...,"1:🔍 一键分析","0:加载"] + +// 填 textarea + 生成图 +var ta=document.querySelector('textarea'); +ta.value='prompt'; ta.dispatchEvent(new Event("input",{bubbles:true})); +document.querySelectorAll('button')[7].click(); // 🎨 生成 + +// 一键 VL 分析 +document.querySelectorAll('button')[1].click(); // 🔍 一键分析 + +// 提取生成的图片为 base64 +var img=document.querySelectorAll('img')[1]; // 索引依页面而定 +var c=document.createElement('canvas'); +c.width=img.naturalWidth; c.height=img.naturalHeight; +c.getContext('2d').drawImage(img,0,0); +window.__CAPTURED_IMG=c.toDataURL('image/jpeg',0.9).split(',')[1]; +``` + +**限制**: CDP 仅支持 sync XHR(`XMLHttpRequest`,不可设 timeout),不支持 `fetch()`。VL API 跨域调用会失败(`NetworkError`),但工具箱页面内 `/api/analyze` 同源调用可用——`lastDescription` 全局变量存储分析结果。 + +## 陷阱大全 + +1. ⚠️ **字段名是 `base64`,不是 `image_base64`** — 用错静默返回 `"No image data"` HTTP 200。`~/.hermes/scripts/` 里多个脚本仍有此 bug。 + +2. **Base64 必须纯的** — 去掉 `data:image/png;base64,` 前缀。用 `dataUrl.split(",")[1]` 剥离。 + +3. **Multipart/form-data 会导致 500 crash** — 必须用 `Content-Type: application/json` + JSON body。 + +4. **`/api/load-url` 不可靠** — 可能超时。推荐本地下载后传 base64。 + +5. **frp 隧道可能截断大 payload** — 如果 SG5 突然报错但 GS3 本地正常,检查 frp。 + +6. **timeout** — 默认 60s 足够(VL ~7.5s)。大图或复杂 prompt 设 timeout=120。 + +7. **CORS** — 浏览器跨域会阻止。用服务端 Python 调用。 + +8. 🔴 **图片最小 32×32 像素** — qwen3-vl:8b 要求图片 ≥ 32×32,否则 Ollama 直接 500 panic。调用前校验尺寸!典型错误日志:`height:1 or width:1 < factor:32`。 + +9. 🔴 **VL 后端 503(2026-07)** — FRP 转发成功但 `127.0.0.1:7870/api/analyze` 返回 `503 Service Unavailable`。表示 VL 服务器进程挂了或端口未监听。检查 192.168.9.105 上 `systemctl --user status flairgs.service`。 + +10. 🔴 **alicdn 图片需 PIL convert('RGB')(2026-07)** — 从 alicdn 下载的产品图片(800x800 WebP/JPEG)直接传 base64 会导致 qwen3-vl 返回空描述。必须先用 `Image.open().convert('RGB')` 转换再 `save(format='JPEG')`。小 PNG 测试图不受影响。 + +10. **`get_active_url()` 每次调用都做 health check` + +9. **`get_active_url()` 每次调用都做 health check** — 开销小(3s 超时),但不要在热循环里调用。脚本里一般只在构造 URL 时调用一次。缓存策略见 `references/client-internals.md`。 + +## 服务器管理 + +- **API 端口**: 192.168.9.105: `7870`(LAN: `192.168.9.105:7870`, FRP: `43.160.244.125:17870`) +- **重启**: GS3 上 `systemctl --user restart flairgs.service` +- **日志**: `journalctl --user -u flairgs.service -f` +- **本地测试**(GS3 绕过 frp): + ```bash + curl -X POST http://localhost:7870/api/analyze \ + -H "Content-Type: application/json" \ + -H "X-API-Key: 8Ax4TyZc66qrZgXK" \ + -d '{"base64":"你的base64"}' + ``` +- **LAN 测试**(局域网内其他机器): + ```bash + curl -X POST http://192.168.9.105:7870/api/analyze \ + -H "Content-Type: application/json" \ + -H "X-API-Key: 8Ax4TyZc66qrZgXK" \ + -d '{"base64":"你的base64"}' + ``` + +## 已有脚本参考 + +`~/.hermes/scripts/` 中的参考脚本(**全部已升级 LAN 优先路由**): + +| 脚本 | 用途 | 字段名 | 路由 | 状态 | +|------|------|--------|:--:|:--:| +| `dxm_try2.py` | 验证码 OCR + CDP 登录 | `base64` | `_fga_url()` | ✅ | +| `dxm_captcha.py` | 验证码提取 + OCR | `base64` | `_fga_url()` | ✅ | +| `dxm_ss.py` | CDP 截图 + 分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_client.py` | **共享客户端(本模块)** | `base64` | 内置 LAN/FRP | ✅ | +| `flairgs_v2.py` | CDP 截图 + VL 分析 + 重试 | `base64` | `_fga_url()` | ✅ | +| `flairgs_full.py` | 163 邮箱截图 + 分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_analyze.py` | 独立 FlairGS 分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_163.py` | 163 工具栏分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_test.py` | 多格式探测 | `base64` | `_fga_url()` | ✅ | +| `flairgs_img.py` | 直接图片分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_diag.py` | 诊断探测 | `base64` | `_fga_url()` | ✅ | +| `flairgs_raw.py` | 多格式实验 | `base64` | `_fga_url()` | ✅ | +| `flairgs_multi.py` | 多格式提交 | `base64` | `_fga_url()` | ✅ | +| `flairgs_inbox.py` | 收件箱截图分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_inbox2.py` | 收件箱截图分析 v2 | `base64` | `_fga_url()` | ✅ | +| `flairgs_prompt.py` | Prompt 变体测试 | `base64` | `_fga_url()` | ✅ | +| `flairgs_final.py` | 最终集成方案 | `base64` | `_fga_url()` | ✅ | +| `flairgs_form.py` | 多表单格式 | `base64` | `_fga_url()` | ✅ | +| `flairgs_go.py` | 快速调用 | `base64` | `_fga_url()` | ✅ | +| `flairgs_oa.py` | OA 集成 | `base64` | `_fga_url()` | ✅ | +| `flairgs_q.py` | 快速分析 | `base64` | `_fga_url()` | ✅ | +| `flairgs_ollama.py` | Ollama 直连测试 | `base64` | `_fga_url()` | ✅ | + +> **Legacy 模式说明**: 现有脚本使用 `from flairgs_client import get_active_url as _fga_url` + `_fga_url() + "/api/analyze"` 模式——这是最小侵入式改动,保留了原有请求构建逻辑。新脚本推荐直接用 `analyze(b64, prompt)`。