Add cross-border-ecommerce/flairgs-vision-api

This commit is contained in:
2026-07-10 16:12:13 +08:00
parent 539bcff6d0
commit 923c810e3b
@@ -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 后端 5032026-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)`。