Bridge 图像生成技能: gs2-sdxl (GS-2 SDXL) + gs1-flux (GS-1 FLUX)
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# Bridge VM 图像生成技能
|
||||
|
||||
给 9Webs 全部 Bridge 虚拟机使用的图像生成技能。
|
||||
|
||||
## 包含技能
|
||||
|
||||
- **gs2-sdxl** — GS-2 (192.168.9.116) Forge SDXL base 1.0 双实例 (:7861/:7862),快速出图
|
||||
- **gs1-flux** — GS-1 (192.168.9.113) ComfyUI FLUX.1-dev (GGUF Q5_K_S),高质量出图
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/skills && cd ~/.hermes/skills
|
||||
git clone http://gitea.9webs.online:3000/admin9webs/bridge-skills.git
|
||||
# 或仅复制所需技能目录
|
||||
cp -r bridge-skills/gs2-sdxl bridge-skills/gs1-flux ~/.hermes/skills/
|
||||
```
|
||||
|
||||
## 使用策略
|
||||
|
||||
- 快速草图/批量配图 → `gs2-sdxl` (steps 24-30, 约几秒/张)
|
||||
- 最终成品/复杂构图 → `gs1-flux` (steps 20, 约 100 秒/张)
|
||||
|
||||
> 部署日期: 2026-08-27,已在全部 23 台 Bridge 上安装验证。
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
name: gs1-flux
|
||||
description: 调用 GS-1 (192.168.9.113) ComfyUI FLUX.1-dev 图像生成 — txt2img/img2img/填充。GGUF Q5_K_S 模型,V100 双卡。触发:FLUX 出图、高质量图片、复杂构图、产品图、图生图、局部重绘、inpaint。
|
||||
version: 1.0.0
|
||||
author: 9Webs
|
||||
license: internal
|
||||
platforms: [linux]
|
||||
triggers:
|
||||
- FLUX
|
||||
- flux1
|
||||
- 高质量出图
|
||||
- ComfyUI
|
||||
- inpaint
|
||||
- 局部重绘
|
||||
- 图生图
|
||||
---
|
||||
|
||||
# GS-1 FLUX.1-dev 图像生成 (ComfyUI API)
|
||||
|
||||
用 GS-1 的 ComfyUI + FLUX.1-dev (GGUF Q5_K_S) 生成高质量图片。直连 `192.168.9.113:8188` 即可。
|
||||
|
||||
## 服务拓扑
|
||||
|
||||
| 项 | 值 |
|
||||
|----|----|
|
||||
| 节点 | GS-1 = 192.168.9.113 (2× Tesla V100-SXM2-16GB) |
|
||||
| ComfyUI | `http://192.168.9.113:8188` (v0.25.0) |
|
||||
| UNet | `flux1-dev-Q5_K_S.gguf` (GGUF, V100 无 bf16 用 Q5) |
|
||||
| 填充模型 | `flux1-fill-dev-Q5_K_S.gguf` (inpaint/局部重绘) |
|
||||
| CLIP | `t5-v1_1-xxl-encoder-Q5_K_M.gguf` + `clip_l.safetensors` (type=flux) |
|
||||
| VAE | `ae.safetensors` |
|
||||
|
||||
## 调用方式 (ComfyUI REST API)
|
||||
|
||||
1. **POST `/prompt`** 提交 API 格式工作流 (见 workflows/flux_txt2img.json),返回 `prompt_id`
|
||||
2. **轮询 `GET /history/{prompt_id}`** 直到 `status.completed == true`
|
||||
3. **取图 `GET /view?filename=<name>&type=output`** 下载 PNG
|
||||
|
||||
### 参考参数 (已实测)
|
||||
|
||||
- **steps 20** (18-28 均可;GGUF 量化,步数低会糊)
|
||||
- **cfg 1.0** (FLUX 用 cfg≈1 + guidance;标准 KSampler 下 cfg 1.0 即可)
|
||||
- **1024×1024** (FLUX 原生;V100 16GB 上限约 1024,超高会 OOM)
|
||||
- **sampler `euler` / scheduler `simple`**, seed 可固定
|
||||
- **耗时约 100-120 秒/张** (V100 无 flash-attention;首次调用含模型加载更慢)
|
||||
|
||||
### 脚本 (scripts/flux_txt2img.py)
|
||||
|
||||
```bash
|
||||
python3 ~/.hermes/skills/gs1-flux/scripts/flux_txt2img.py "prompt here" [--steps 20] [--seed -1]
|
||||
# 输出: /tmp/flux_out/flux_*.png
|
||||
```
|
||||
|
||||
## 健康检查
|
||||
|
||||
```bash
|
||||
curl -s http://192.168.9.113:8188/system_stats | head -c 200 # 返回 JSON = 正常
|
||||
curl -s http://192.168.9.113:8188/object_info/UnetLoaderGGUF | grep -o 'flux1[^"]*' # 模型在位
|
||||
curl -s http://192.168.9.113:8188/queue | python3 -c "import json,sys; d=json.load(sys.stdin); print('running:', len(d['queue_running']), 'pending:', len(d['queue_pending']))"
|
||||
```
|
||||
|
||||
## 排障
|
||||
|
||||
- **提交 400** → 工作流节点错误,读 `node_errors` 详情 (常见: CLIPTextEncodeFlux 需 clip_l/t5xxl/guidance 三个输入)
|
||||
- **长时间不出图** → 查队列 `queue` 是否堆积;单实例顺序执行,批量任务排队
|
||||
- **OOM** → 降到 768×768 或减少 batch
|
||||
- **换风格/复杂提示** → FLUX 对提示词敏感,直接写详细英文描述;负向提示基本不需要
|
||||
|
||||
## 服务端排障
|
||||
|
||||
```bash
|
||||
sshpass -p 'Tt123456!' ssh admin9webs@192.168.9.113 'systemctl --user status comfyui* --no-pager | head -20'
|
||||
```
|
||||
|
||||
## 陷阱
|
||||
|
||||
1. **Q5_K_S 量化**:图片质量略低于 fp8/bf16,但 V100 无 bf16 只能跑 GGUF;复杂文字/人脸注意检查。
|
||||
2. **每次约 2 分钟**:批量任务要轮询串行,不要并发提交同实例。
|
||||
3. **图片在 ComfyUI output 目录** (`~/ComfyUI/output/`),推荐客户端用 /view 下载,不依赖服务端目录。
|
||||
4. **Flux Fill** (flux1-fill-dev) 用于局部重绘/inpaint,需配合 mask 工作流。
|
||||
|
||||
## 相关
|
||||
|
||||
- SDXL 出图用 `gs2-sdxl` 技能 (GS-2 :7861/:7862,快但质量略低)
|
||||
- 配图策略: 快速草图用 SDXL,最终成品/复杂构图用 FLUX
|
||||
@@ -0,0 +1,35 @@
|
||||
import json, urllib.request, urllib.error, time
|
||||
BASE = 'http://192.168.9.113:8188'
|
||||
wf = {
|
||||
"1": {"class_type": "UnetLoaderGGUF", "inputs": {"unet_name": "flux1-dev-Q5_K_S.gguf"}},
|
||||
"2": {"class_type": "DualCLIPLoaderGGUF", "inputs": {"clip_name1": "t5-v1_1-xxl-encoder-Q5_K_M.gguf", "clip_name2": "clip_l.safetensors", "type": "flux"}},
|
||||
"3": {"class_type": "VAELoader", "inputs": {"vae_name": "ae.safetensors"}},
|
||||
"4": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["2", 0], "text": "a red apple on a wooden table, product photo, studio lighting"}},
|
||||
"5": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["2", 0], "text": "blurry, low quality"}},
|
||||
"6": {"class_type": "EmptyLatentImage", "inputs": {"width": 1024, "height": 1024, "batch_size": 1}},
|
||||
"7": {"class_type": "KSampler", "inputs": {"model": ["1", 0], "positive": ["4", 0], "negative": ["5", 0], "latent_image": ["6", 0], "seed": 12345, "steps": 20, "cfg": 1.0, "sampler_name": "euler", "scheduler": "simple", "denoise": 1.0}},
|
||||
"8": {"class_type": "VAEDecode", "inputs": {"samples": ["7", 0], "vae": ["3", 0]}},
|
||||
"9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "flux_test", "images": ["8", 0]}},
|
||||
}
|
||||
req = urllib.request.Request(BASE + '/prompt', data=json.dumps({"prompt": wf}).encode(), headers={'Content-Type': 'application/json'})
|
||||
try:
|
||||
r = json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
pid = r.get('prompt_id')
|
||||
print('prompt_id:', pid, '| node_errors:', r.get('node_errors'))
|
||||
if not pid: raise SystemExit(1)
|
||||
for i in range(60):
|
||||
time.sleep(5)
|
||||
try:
|
||||
h = json.loads(urllib.request.urlopen(BASE + '/history/' + pid, timeout=10).read())
|
||||
except Exception: continue
|
||||
if pid in h:
|
||||
st = h[pid].get('status', {})
|
||||
if st.get('completed') or st.get('status_str') == 'success':
|
||||
for nid, o in h[pid].get('outputs', {}).items():
|
||||
for img in o.get('images', []):
|
||||
print('出图:', img.get('filename'), '| 节点', nid)
|
||||
print('FLUX 生成成功!'); raise SystemExit(0)
|
||||
elif st.get('status_str') == 'error':
|
||||
print('FLUX 错误:', json.dumps(st)[:500]); raise SystemExit(1)
|
||||
except urllib.error.HTTPError as e:
|
||||
print('HTTP', e.code, e.read().decode()[:600])
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: gs2-sdxl
|
||||
description: 调用 GS-2 (192.168.9.116) Forge SDXL base 1.0 图像生成 API — txt2img/img2img,双实例 :7861/:7862 轮询。触发:生成图片/产品图/配图/场景图,SDXL 出图,文生图,图生图。
|
||||
---
|
||||
|
||||
# GS-2 SDXL 图像生成 (Forge API)
|
||||
|
||||
用 GS-2 的 SDXL base 1.0 生成图片。本机(任意节点)直连 `192.168.9.116` 即可,无需代理。
|
||||
|
||||
## 服务拓扑
|
||||
|
||||
| 项 | 值 |
|
||||
|----|----|
|
||||
| 节点 | GS-2 = 192.168.9.116 (2× RTX 3060 Laptop 12GB) |
|
||||
| 实例1 | `http://192.168.9.116:7861` (GPU0, systemd `forge-sdxl.service`) |
|
||||
| 实例2 | `http://192.168.9.116:7862` (GPU1, systemd `forge-sdxl-2.service`) |
|
||||
| 模型 | `sd_xl_base_1.0.safetensors` — **完整 SDXL base,不是 turbo!** + `sdxl_vae.safetensors` |
|
||||
| API | 标准 A1111/Forge: `/sdapi/v1/txt2img`, `/sdapi/v1/img2img`, `/sdapi/v1/sd-models` |
|
||||
|
||||
双实例轮询:优先 7861,失败/繁忙换 7862。
|
||||
|
||||
## 推荐参数 (SDXL base 完整版,2026-08-27 实测)
|
||||
|
||||
⚠️ 模型是完整 base(非 turbo),**steps 需要 24-30**,2 步会出糊图。
|
||||
|
||||
| 参数 | 值 |
|
||||
|------|----|
|
||||
| steps | 24-30 (出图约 10-25s@1024²) |
|
||||
| cfg_scale | 6-7.5 |
|
||||
| width/height | 768~1024 (SDXL 原生尺寸),默认 1024×1024 或 832×1216 |
|
||||
| sampler_name | `euler_a` 或 `dpmpp_2m_karras` |
|
||||
| negative_prompt | `lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, cropped, worst quality, low quality, jpeg artifacts, signature, watermark, username, blurry` |
|
||||
| 并发 | 单实例同时只处理 1 个请求 (12GB VRAM),批量任务轮询双实例 |
|
||||
|
||||
## 调用方式 (已验证)
|
||||
|
||||
### curl txt2img
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://192.168.9.116:7861/sdapi/v1/txt2img \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"prompt":"a cute corgi wearing sunglasses, studio photo","negative_prompt":"lowres, blurry, watermark","steps":24,"cfg_scale":7,"width":1024,"height":1024,"sampler_name":"euler_a","seed":-1}'
|
||||
```
|
||||
|
||||
### Python (urllib,无 curl 依赖)
|
||||
|
||||
```python
|
||||
import json, base64, urllib.request
|
||||
|
||||
def sdxl_txt2img(prompt, negative_prompt="", steps=24, cfg=7.0,
|
||||
w=1024, h=1024, sampler="euler_a", seed=-1, port=7861):
|
||||
req = urllib.request.Request(
|
||||
f"http://192.168.9.116:{port}/sdapi/v1/txt2img",
|
||||
data=json.dumps({"prompt": prompt, "negative_prompt": negative_prompt,
|
||||
"steps": steps, "cfg_scale": cfg, "width": w, "height": h,
|
||||
"sampler_name": sampler, "seed": seed}).encode(),
|
||||
headers={"Content-Type": "application/json"})
|
||||
with urllib.request.urlopen(req, timeout=180) as r:
|
||||
data = json.loads(r.read())
|
||||
return base64.b64decode(data["images"][0]) # PNG bytes
|
||||
|
||||
# 保存
|
||||
png = sdxl_txt2img("a red apple on a wooden table, product photo", port=7861)
|
||||
open("/tmp/apple.png", "wb").write(png)
|
||||
```
|
||||
|
||||
### img2img (重绘/换风格/局部重绘)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://192.168.9.116:7862/sdapi/v1/img2img \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"init_images":["<base64 PNG>"],"prompt":"same product, luxury studio background","denoising_strength":0.6,"steps":24,"cfg_scale":7,"width":1024,"height":1024,"sampler_name":"euler_a"}'
|
||||
```
|
||||
|
||||
要点:
|
||||
- `init_images` 传 **base64 编码的 PNG 字符串**(无 data: 前缀)
|
||||
- `denoising_strength` 0.5-0.7 重绘风格,0.3 以下基本保留原图,0.8+ 大改
|
||||
|
||||
## 健康检查
|
||||
|
||||
```bash
|
||||
# 模型列表 (返回 JSON 即服务正常)
|
||||
curl -s http://192.168.9.116:7861/sdapi/v1/sd-models
|
||||
# 实例间轮询: 7861 → 7862
|
||||
```
|
||||
|
||||
## 服务端排障 (需要时)
|
||||
|
||||
```bash
|
||||
sshpass -p 'Tt123456!' ssh admin9webs@192.168.9.116 \
|
||||
'export XDG_RUNTIME_DIR=/run/user/$(id -u); systemctl --user status forge-sdxl forge-sdxl-2 --no-pager | head -30'
|
||||
# 重启: systemctl --user restart forge-sdxl forge-sdxl-2
|
||||
```
|
||||
|
||||
## 陷阱
|
||||
|
||||
1. **是 base 模型不是 turbo**:steps 必须 24+,2-4 步出糊图/灰图。
|
||||
2. **双实例各绑一张卡**:单实例并发第 2 个请求会排队或 OOM,批量任务轮询两个端口。
|
||||
3. **12GB VRAM 上限**:1024×1024 稳定;超 1024 或加 ControlNet 可能 OOM,降 768。
|
||||
4. **seed=-1 随机**,固定 seed 可复现。
|
||||
5. 服务端 outputs 目录在 GS-2 (`~/stable-diffusion-webui-forge/outputs/`),但**推荐客户端解析 base64 直接存本地**,不依赖服务端目录。
|
||||
|
||||
## 相关
|
||||
|
||||
- 部署/版本排障细节见本地 `gpustack-cluster` 技能 (GS-2 Forge 双实例 systemd 章节)
|
||||
- FlairGS (AppServer-105 :7870) 也调这两个实例:FORGE_STEPS=24, CFG=7.5, denoise=0.70 (产品图场景生成)
|
||||
Reference in New Issue
Block a user