12 KiB
name, title, description, category, tags
| name | title | description | category | tags | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| flairgs-vision-api | FlairGS 视觉分析 API | 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。 | cross-border-ecommerce |
|
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
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"
}
返回:
{
"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 — 图片缩放
{"base64": "...", "width": 800, "height": 600}
POST /api/txt2img — 文生图
{"base64": "...", "prompt": "生成提示词"}
POST /api/white-bg — 白底处理(商品图标准化)
{"base64": "..."}
POST /api/chat — LLM 对话
GS1-35B + 本机 4B 混合推理。
共享客户端 (推荐)
所有脚本统一用 ~/.hermes/scripts/flairgs_client.py,自动实现 LAN 优先 + FRP 回退:
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()
连接策略:
- 先尝试
http://192.168.9.105:7870(LAN 直连,3s 超时) - 失败自动回退
http://43.160.244.125:17870(FRP 隧道) - 局域网用户享受低延迟直连,外网用户无缝 FRP
便捷封装也内置了:
ocr_captcha(b64)— 验证码 OCRdescribe_product(b64)— 商品描述check_ip_risk(b64)— IP 风险筛查
📖 实现细节、超时策略、扩展现有脚本的侵入模式:
references/client-internals.md
标准集成模式:CDP 截图 → FlairGS 分析
最常见的组合:Cloud Bridge CDP 截图 + FlairGS VL 分析。使用共享客户端,自动 LAN/FRP 回退。
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 可以(同内网):
# 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。
(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:
// 获取按钮列表(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 全局变量存储分析结果。
陷阱大全
-
⚠️ 字段名是
base64,不是image_base64— 用错静默返回"No image data"HTTP 200。~/.hermes/scripts/里多个脚本仍有此 bug。 -
Base64 必须纯的 — 去掉
data:image/png;base64,前缀。用dataUrl.split(",")[1]剥离。 -
Multipart/form-data 会导致 500 crash — 必须用
Content-Type: application/json+ JSON body。 -
/api/load-url不可靠 — 可能超时。推荐本地下载后传 base64。 -
frp 隧道可能截断大 payload — 如果 SG5 突然报错但 GS3 本地正常,检查 frp。
-
timeout — 默认 60s 足够(VL ~7.5s)。大图或复杂 prompt 设 timeout=120。
-
CORS — 浏览器跨域会阻止。用服务端 Python 调用。
-
🔴 图片最小 32×32 像素 — qwen3-vl:8b 要求图片 ≥ 32×32,否则 Ollama 直接 500 panic。调用前校验尺寸!典型错误日志:
height:1 or width:1 < factor:32。 -
🔴 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。 -
🔴 alicdn 图片需 PIL convert('RGB')(2026-07) — 从 alicdn 下载的产品图片(800x800 WebP/JPEG)直接传 base64 会导致 qwen3-vl 返回空描述。必须先用
Image.open().convert('RGB')转换再save(format='JPEG')。小 PNG 测试图不受影响。 -
**
get_active_url()每次调用都做 health check` -
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):
curl -X POST http://localhost:7870/api/analyze \ -H "Content-Type: application/json" \ -H "X-API-Key: 8Ax4TyZc66qrZgXK" \ -d '{"base64":"你的base64"}' - LAN 测试(局域网内其他机器):
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)。