@clawhub-baobaodawang-creater-221d0b34d3
Autonomous pipeline manager that orchestrates the entire development workflow. You are the leader of this process.
--- name: Agents Orchestrator description: Autonomous pipeline manager that orchestrates the entire development workflow. You are the leader of this process. --- # AgentsOrchestrator Agent Personality You are **AgentsOrchestrator**, the autonomous pipeline manager who runs complete development workflows from specification to production-ready implementation. You coordinate multiple specialist agents and ensure quality through continuous dev-QA loops. ## 🧠 Your Identity & Memory - **Role**: Autonomous workflow pipeline manager and quality orchestrator - **Personality**: Systematic, quality-focused, persistent, process-driven - **Memory**: You remember pipeline patterns, bottlenecks, and what leads to successful delivery ## 🎯 Your Core Mission - Manage full workflow: PM → ArchitectUX → [Dev ↔ QA Loop] → Integration - Task-by-task validation: Each task must pass QA before proceeding - Automatic retry logic: Failed tasks loop back to dev with specific feedback (max 3 retries) - Autonomous operation with single initial command ## 🚨 Critical Rules - No shortcuts: Every task must pass QA validation - Evidence required: All decisions based on actual agent outputs - Retry limits: Maximum 3 attempts per task before escalation - Clear handoffs: Each agent gets complete context and specific instructions ## 🔄 Workflow Phases 1. Phase 1: Project Analysis & Planning (spawn project-manager-senior) 2. Phase 2: Technical Architecture (spawn ArchitectUX) 3. Phase 3: Development-QA Continuous Loop (Dev agent ↔ EvidenceQA loop per task) 4. Phase 4: Final Integration & Validation (spawn testing-reality-checker) ## 📋 Status Reporting Track and report: current phase, task completion %, QA status, retry counts, blockers ## 🚀 Launch Command "Please spawn an agents-orchestrator to execute complete development pipeline for project-specs/[project]-setup.md"
ComfyUI 图像生成工坊 — 用自然语言描述需求,自动生成高质量 AI 图片。支持 SDXL/Flux 多模型、风格模板自动匹配、批量生成、质量评分。说「画一张图」即可触发。
---
name: visual-muse
description: "ComfyUI 图像生成工坊 — 用自然语言描述需求,自动生成高质量 AI 图片。支持 SDXL/Flux 多模型、风格模板自动匹配、批量生成、质量评分。说「画一张图」即可触发。"
version: 1.0.0
metadata: { "openclaw": { "emoji": "🎨", "requires": { "bins": ["python3", "curl"], "env": ["COMFYUI_API_URL"] }, "primaryEnv": "COMFYUI_API_URL" } }
---
# Visual Muse — AI 视觉缪斯
通过自然语言对话生成 AI 图片的完整技能包。
## 它能做什么
- 你说中文需求 → 自动生成英文 prompt → 调用本地 ComfyUI → 出图
- 自动匹配风格模板(电影感/动漫/写实/概念艺术/水彩)
- 支持批量生成 + 质量评分 + 迭代优化
- 记录你的审美偏好,越用越懂你
## 前置条件
1. macOS(Apple Silicon)或 Linux + NVIDIA GPU
2. ComfyUI 已安装并运行(API 端口 8188)
3. 至少一个 SDXL checkpoint 模型
## 快速开始
安装后执行 setup 脚本:
```bash
bash ~/.openclaw/workspace/skills/creative-workshop/scripts/setup.sh
```
它会检查 ComfyUI 是否安装、模型是否就位,并引导你完成配置。
## 使用方式
直接对 OpenClaw 说:
- "画一张赛博朋克猫在雨夜街道"
- "动漫风格,女孩在樱花树下"
- "写实照片,城市天际线黄昏"
- "出4张不同风格的猫"
## 包含的子技能
| 技能 | 功能 |
|------|------|
| prompt-agent | 中文→英文prompt,自动匹配风格模板 |
| workflow-agent | 选择模型和参数,组装 ComfyUI 工作流 |
| render-agent | 调用 ComfyUI API 执行生成 |
| critic-agent | 多维评分 + 问题诊断 + 改进建议 |
| memory-agent | 记录审美偏好,越用越准 |
## 支持的模型
开箱即用支持任何 SDXL checkpoint,推荐:
- DreamShaper XL(万能型)
- Juggernaut XL(电影写实)
- Animagine XL(动漫)
## 配置
环境变量 `COMFYUI_API_URL` 默认值 `http://host.docker.internal:8188`(Docker 环境)或 `http://127.0.0.1:8188`(本地环境)。
## 图片输出位置
生成的图片保存在 ComfyUI 的 output 目录下的 `creative_workshop/` 子文件夹。
FILE:README.md
# 🎨 Visual Muse — ComfyUI AI 视觉缪斯
## 效果展示(Visual Muse 示例)
|  |  |  |
|---|---|---|
| 画一张赛博朋克风,霓虹雨夜中的未来少女特写 | 画一张赛博朋克风,绿色霓虹与兜帽黑客少女 | 画一张赛博朋克风,雨夜街头的耳机少女 |
|  |  | |
|---|---|---|
| 画一张高饱和动漫风,霓虹机甲耳机少女 | 画一张宫崎骏风格的猫在花田里奔跑 | |
> 用自然语言对话生成 AI 图片的 OpenClaw 技能包
## 效果演示
对 OpenClaw 说:
```
画一张宫崎骏风格的猫在花田里奔跑
```
Agent 自动完成:中文理解 → 英文 prompt → 风格模板匹配 → ComfyUI 渲染 → 质量评分 → 输出图片
## 特性
- 🗣️ **自然语言驱动**:说中文就能出图,不需要懂 prompt 工程
- 🎭 **风格模板自动匹配**:电影感 / 动漫 / 写实 / 概念艺术 / 水彩
- 🔄 **多模型智能切换**:根据风格自动选择最合适的 checkpoint
- 📊 **质量评分**:自动评估构图、色彩、风格、细节、氛围
- 🧠 **偏好记忆**:记住你喜欢什么风格,越用越准
- 📋 **运行追踪**:每次生成的完整链路都有记录,方便复盘和优化
## 系统要求
- OpenClaw(最新版本)
- ComfyUI(本地运行,API 端口 8188)
- macOS Apple Silicon(推荐 16GB+ 内存)或 Linux + NVIDIA GPU
- 至少一个 SDXL checkpoint 模型(约 6.5GB)
## 安装
```bash
clawhub install visual-muse
```
安装后运行 setup 向导:
```bash
bash ~/.openclaw/workspace/skills/visual-muse/scripts/setup.sh
```
向导会帮你检查 ComfyUI 连接、模型状态,并初始化工作文件。
## 手动安装
如果不通过 ClawHub:
```bash
# 克隆到 workspace skills 目录
cd ~/.openclaw/workspace/skills/
git clone https://github.com/你的用户名/visual-muse.git
# 运行 setup
bash visual-muse/scripts/setup.sh
```
## 推荐模型
| 模型 | 风格 | 下载 |
|------|------|------|
| SDXL Base 1.0 | 基础通用 | [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0) |
| DreamShaper XL | 万能型 | [HuggingFace](https://huggingface.co/Lykon/DreamShaper) |
| Juggernaut XL | 电影写实 | [HuggingFace](https://huggingface.co/RunDiffusion/Juggernaut-XL-v9) |
| Animagine XL 3.1 | 动漫 | [HuggingFace](https://huggingface.co/cagliostrolab/animagine-xl-3.1) |
## 风格关键词
| 你说的 | 匹配风格 | 使用模型 |
|--------|----------|----------|
| 电影感、大片、cinematic | cinematic | DreamShaper XL |
| 动漫、二次元、anime | anime | Animagine XL |
| 写实、照片、photo | photorealistic | Juggernaut XL |
| 概念、插画、concept | concept_art | DreamShaper XL |
| 水彩、水墨 | watercolor | DreamShaper XL |
## 目录结构
```
visual-muse/
├── SKILL.md # 主技能定义(ClawHub 入口)
├── README.md # 本文件
├── scripts/
│ └── setup.sh # 安装向导
├── skills/
│ ├── prompt-agent/SKILL.md # 中文→英文 prompt
│ ├── workflow-agent/SKILL.md # 工作流组装
│ ├── render-agent/SKILL.md # ComfyUI 渲染执行
│ ├── critic-agent/SKILL.md # 质量评分
│ └── memory-agent/SKILL.md # 偏好记忆
├── tools/
│ ├── comfyui-client.py # ComfyUI API 客户端
│ └── run-tracker.py # 运行追踪
├── workflows/
│ └── sdxl_basic.json # SDXL 基础工作流模板
└── resources/
├── prompt-templates.json # 风格模板库
└── preferences.json # 偏好档案初始模板
```
## 工作原理
```
你说中文需求
↓
Prompt Agent(匹配风格模板 → 生成英文 prompt)
↓
Workflow Agent(选择 checkpoint → 填入工作流模板)
↓
Render Agent(调用 ComfyUI API → 等待生成)
↓
Critic Agent(评分 → 推荐最佳 → 给出改进建议)
↓
Memory Agent(记录你的偏好 → 下次更准)
```
## 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| COMFYUI_API_URL | http://127.0.0.1:8188 | ComfyUI API 地址 |
Docker 环境下通常设为 `http://host.docker.internal:8188`
## 常见问题
### ComfyUI 连不上
确保 ComfyUI 正在运行。macOS 用户可以安装 [ComfyUI Desktop](https://www.comfy.org/download) 或命令行启动。
### 出图很慢
M1/M2 芯片约 30-60 秒一张,M3/M4/M5 约 10-30 秒。首次加载模型会额外花 10-20 秒。
### 模型不存在
agent 会自动回退到 SDXL Base。确保至少下载了一个 checkpoint 到 ComfyUI 的 models/checkpoints/ 目录。
### 风格没变化
检查 prompt-templates.json 是否在 workspace 根目录,并确认 agent 能读取到。
## License
MIT-0
## 作者
Built with OpenClaw + ComfyUI + Claude
## 踩坑指南
首次使用遇到问题?查看 [TROUBLESHOOTING.md](./TROUBLESHOOTING.md),覆盖常见问题:黑图修复、Skill调度、Token优化、记忆系统、模型选择建议。
## 模型热切换
Visual Muse 支持一行命令在不同 LLM 之间切换 painter 模型:
```bash
bash scripts/switch-painter-model.sh gemini # ~$0.005/次(推荐日常)
bash scripts/switch-painter-model.sh gpt-nano # ~$0.003/次(最便宜)
bash scripts/switch-painter-model.sh claude # ~$0.07/次(最强)
bash scripts/switch-painter-model.sh claude-haiku # ~$0.01/次
```
所有模型通过 Ofox (ofox.ai) 统一调用,无需分别注册。ComfyUI 本地推理完全免费,花钱的只是 LLM 理解需求的那一轮调用。
FILE:TROUBLESHOOTING.md
# Visual Muse 踩坑指南
## 问题1:生成图片全黑(5KB或更小)
**根因**:SDXL的VAE在fp16精度下有bug,MPS设备尤其严重。
**修复**:启动ComfyUI时加 `--fp32-vae`:
```bash
python main.py --listen 0.0.0.0 --port 8188 --highvram --fp32-vae
```
⚠️ 参数是 `--fp32-vae`,不是 `--force-fp32-vae`(后者不存在会报错)。
如果仍有黑图,额外下载修复版VAE:
```bash
curl -L -o models/vae/sdxl_vae_fp16fix.safetensors \
"https://hf-mirror.com/madebyollin/sdxl-vae-fp16-fix/resolve/main/sdxl_vae.safetensors"
```
## 问题2:Agent不按SKILL.md执行,自己写Python脚本
**根因**:模型工具调用能力不足(DeepSeek等常见),Skill过于复杂时模型会绕过。
**修复**:
1. 精简Skill为单轮调用,封装成一个bash脚本
2. SKILL.md明确写"禁止自己写Python脚本"
3. 使用Claude Sonnet替代DeepSeek处理出图任务
| 指标 | DeepSeek自写脚本 | Claude按Skill走 |
|------|-----------------|----------------|
| 黑图率 | ~75% | 0% |
| 耗时 | 30-45分钟 | 1-2分钟 |
| Skill遵守 | 否 | 是 |
## 问题3:Token消耗过高
**根因**:无contextTokens限制 + safeguard压缩模式 + workspace大文件注入。
**修复**:
```bash
openclaw config set agents.defaults.contextTokens 32000
openclaw config set agents.defaults.compaction.mode default
openclaw config set agents.defaults.compaction.reserveTokens 4000
openclaw config set tools.profile messaging
```
⚠️ 字段名是 `contextTokens` 不是 `maxContextTokens`。`compaction.mode` 只支持 `default` 和 `safeguard`,不支持 `full`。
清理workspace和session:
```bash
mkdir -p ~/.openclaw/workspace/archive
mv ~/.openclaw/workspace/*.log ~/.openclaw/workspace/archive/ 2>/dev/null
cp -r ~/.openclaw/agents/main/sessions ~/.openclaw/agents/main/sessions.bak
rm -rf ~/.openclaw/agents/main/sessions/*
docker restart openclaw-gateway
```
优化效果:从$0.37/次降到$0.07/次(出图),$0.003/次(聊天)。
## 问题4:LanceDB记忆系统崩溃
**症状**:`Cannot find module '@lancedb/lancedb'`
**临时修复**:
```bash
docker exec openclaw-gateway npm install @lancedb/lancedb
docker restart openclaw-gateway
```
ARM Docker容器下会反复出现,建议长期切换到MEMORY.md文件记忆。
## 问题5:图片在容器内找不到
**临时方案**:
```bash
docker cp openclaw-gateway:/home/node/ai-outputs/ ~/ai-studio/outputs/latest/
```
**永久方案**:docker-compose添加映射:
```yaml
volumes:
- HOME/ai-studio/outputs:/home/node/ai-outputs
```
## 问题6:Impact Pack未加载
**修复**:
```bash
source ~/ai-studio/comfyui-venv/bin/activate
cd ~/ai-studio/comfyui/custom_nodes/ComfyUI-Impact-Pack
pip install -r requirements.txt
python install.py
```
## 推荐模型配置
| 场景 | 模型 | 成本 |
|------|------|------|
| 出图+复杂任务 | Claude Sonnet 4.6 | ~$0.07/次 |
| 日常聊天 | DeepSeek V3.2 | ~$0.001/次 |
| 分析任务 | Claude Haiku 4.5 | ~$0.01/次 |
通过Ofox等API聚合平台接入Claude,避免封号风险。
## 问题8:Agent说"我没有生成图像的能力"(SOUL.md优先级问题)
**现象**:明明 agentDir 里的 SOUL.md 写了出图指令,但 agent 说"我是文字/代码型 AI,不是画图工具",推荐用户去 Midjourney。
**根因**:OpenClaw 给每个 agent 自动创建 `workspace-{agentId}/` 目录,里面的 SOUL.md **优先级高于** agentDir 里的。如果 workspace-painter/SOUL.md 是通用人格模板,agent 就不知道自己是画师。
**诊断方法**:
```bash
# 看 agent 实际读取的是哪个 SOUL.md
docker exec openclaw-gateway cat /home/node/.openclaw/workspace-painter/SOUL.md
# 对比 agentDir 里的
docker exec openclaw-gateway cat /home/node/.openclaw/agents/painter/agent/SOUL.md
```
**修复**:
```bash
# 用正确的 SOUL.md 覆盖 workspace 里的
cp /home/node/.openclaw/agents/painter/agent/SOUL.md \
/home/node/.openclaw/workspace-painter/SOUL.md
# 清理垃圾文件
rm -f /home/node/.openclaw/workspace-painter/*.svg
rm -f /home/node/.openclaw/workspace-painter/*.py
rm -f /home/node/.openclaw/workspace-painter/cyberpunk_cat.*
docker restart openclaw-gateway
```
⚠️ **重要**:以后修改 SOUL.md 必须同时更新两个位置(agentDir 和 workspace-painter),否则改了等于没改。
## 问题9:Agent连续生成不停(Gemini等模型)
**现象**:让画一张图,agent 生成完后自动继续生成变体,停不下来。
**根因**:部分模型(如Gemini)倾向于"主动服务",把"画一张"理解成"画到满意为止"。
**修复**:在 SOUL.md 末尾添加数量规则:
```
## 数量规则(严格遵守)
- 默认只生成1张图,生成完立即停止
- 不要自动生成变体
- 只有用户明确说「再来一张」「多几张」时才生成更多
```
## 问题10:模型切换后出图失败
**现象**:切换模型后 agent 行为异常,不调用 paint-dispatch.sh。
**修复**:
```bash
# 切换模型一行命令
bash ~/openclaw/switch-painter-model.sh gemini # 便宜
bash ~/openclaw/switch-painter-model.sh claude # 最强
bash ~/openclaw/switch-painter-model.sh gpt-nano # 最便宜
# 如果新模型不听话,切回 Claude
bash ~/openclaw/switch-painter-model.sh claude
```
FILE:archive/v1.2-skills/critic-agent/SKILL.md
---
name: critic-agent
description: "对生成的图像进行多维质量评分、问题诊断和改进建议。当图像生成完成需要评估质量时触发。"
metadata: { "openclaw": { "emoji": "🔍" } }
---
# Critic Agent(视觉评审)
评价图像质量,选出最佳候选。
## 评审方式
如果可以直接查看图片文件(通过多模态能力),则基于实际图像评分。
如果无法直接查看,则基于 prompt 内容和生成参数进行推断评分,并标注"推断评分"。
## 评分维度(1-10 分)
- **构图**:主体位置、画面平衡、视觉引导
- **色彩和光影**:色调和谐度、光源合理性、明暗对比
- **风格贴合度**:是否符合用户要求的风格方向
- **细节质量**:手部、面部、文字、边缘是否有缺陷
- **整体氛围**:情绪传达是否到位
## 输出格式
```json
{
"scores": {"composition": 0, "color_lighting": 0, "style_match": 0, "detail_quality": 0, "mood": 0},
"total": 0,
"visual_review": true,
"issues": ["具体问题1"],
"defect_types": ["STYLE_MISMATCH"],
"suggestions": ["可执行的改进建议1"],
"keep": true,
"best_candidate": "文件名(多图时)",
"reasoning": "选择理由(一句话)"
}
```
## 缺陷分类诊断(低于 7 分时必须输出)
- `STYLE_MISMATCH`:风格不匹配(要求写实却偏动漫)
- `SUBJECT_MISSING`:主体缺失或不明显
- `COMPOSITION_BAD`:构图问题(主体太小、居中失败、裁切不当)
- `DETAIL_LACKING`:细节不足(模糊、低纹理、塑料感)
- `COLOR_OFF`:色彩偏差(暖冷方向不符合需求)
- `ANATOMY_ERROR`:人体结构错误(手指、面部、肢体)
## 修复建议对应表
- `STYLE_MISMATCH` → 更换 checkpoint,或提高风格关键词权重(1.3-1.5)
- `SUBJECT_MISSING` → 主体关键词提升到 `(subject:1.4)`,并减少背景干扰词
- `COMPOSITION_BAD` → 添加构图词:`centered, rule of thirds, close-up, medium shot`
- `DETAIL_LACKING` → steps 提升到 30-40,补充 `masterpiece, best quality, high detail`
- `COLOR_OFF` → 添加明确色彩词:`warm golden tone` / `cool cyan tone`
- `ANATOMY_ERROR` → negative 增加:`bad hands, extra fingers, deformed limbs, bad face`
## 自动重试机制(低于 6 分触发)
当 `total < 6` 时:
1. 输出 `defect_types` + `suggestions` 给 Prompt Agent
2. Prompt Agent 按建议重写 prompt
3. Render Agent 重新生成
4. 最多重试 2 次(避免无限循环)
重试状态建议写入运行记录,例如:
```bash
python3 /home/node/.openclaw/workspace/tools/run-tracker.py update --run-id <RUN_ID> --data '{"status":"retrying","critic_result":{...},"retry_count":1}'
```
## 工作规则
1. `issues` 只列可观察到的具体问题(如"左手有6根手指"),不要写空泛评价
2. `suggestions` 必须可执行(如"提高 CFG 到 8.0"、"negative 增加 extra fingers")
3. 多图对比时,给出排名和最佳候选
4. `total` = 五项平均分
5. `keep=false` 当总分低于 5 或存在严重缺陷
6. 评分完成后更新运行记录:
```bash
python3 /home/node/.openclaw/workspace/tools/run-tracker.py update --run-id <RUN_ID> --data '{"critic_result":{...},"status":"reviewed"}'
```
FILE:archive/v1.2-skills/memory-agent/SKILL.md
---
name: memory-agent
description: "维护用户审美偏好与创作历史,为其他 Agent 提供可复用的风格参考。当开始新任务或用户表达喜好时触发。"
metadata: { "openclaw": { "emoji": "🧠" } }
---
# Memory Agent
管理创作偏好和历史。
## 存储文件
`/home/node/.openclaw/workspace/preferences.json`
## 读取偏好
新任务开始时,读取 preferences.json 并输出摘要提供给 Prompt Agent 和 Critic Agent:
```bash
cat /home/node/.openclaw/workspace/preferences.json
```
## 写入偏好
当用户表达喜好("我喜欢这个风格"、"太卡通了不要")时,用 Python 更新文件:
```bash
python3 -c "
import json, sys
with open('/home/node/.openclaw/workspace/preferences.json','r') as f: data=json.load(f)
# 在此处修改 data 的对应字段
with open('/home/node/.openclaw/workspace/preferences.json','w') as f: json.dump(data,f,ensure_ascii=False,indent=2)
"
```
## 数据结构
```json
{
"profile": {
"liked_styles": [],
"disliked_styles": [],
"preferred_composition": [],
"preferred_tones": []
},
"successful_patterns": [],
"model_recipes": [],
"history": []
}
```
## 规则
1. 追加写入,不覆盖历史。
2. 相同偏好合并去重。
3. 输出摘要时分两层:"最近偏好"(最近5条)+ "长期偏好"(高频项)。
4. 不记录任何密钥、账号、凭据。
FILE:archive/v1.2-skills/prompt-agent/SKILL.md
---
name: prompt-agent
description: "将中文创意需求转换为 SDXL 或 Flux 可用的高质量英文图像提示词。当用户要求生成图片、画一张图、出图、AI绘画时触发。"
metadata: { "openclaw": { "emoji": "🎨" } }
---
# Prompt Agent
将用户的中文需求转成可执行的英文 prompt。
## 第一步:读取风格模板库
```bash
cat /home/node/.openclaw/workspace/prompt-templates.json
```
根据用户需求匹配最合适的风格模板。匹配规则:
- 用户说"电影感/电影风" → cinematic
- 用户说"动漫/二次元/卡通" → anime
- 用户说"写实/照片/真实" → photorealistic
- 用户说"概念艺术/概念图" → concept_art
- 用户说"水彩" → watercolor
- 用户说"油画" → oil_painting
- 用户说"赛博朋克" → cyberpunk
- 用户说"奇幻/魔幻" → fantasy
- 用户说"复古/昭和/怀旧" → vintage
- 用户没指定风格 → cinematic(默认)
## 第二步:结构化拆解(6维)
收到需求后,先拆解为以下 6 个维度,并分别产出英文关键词:
- `subject`:画面中心主体(人物/动物/物体)
- `environment`:场景地点(街道、森林、室内等)
- `style`:画风、年代感、材质质感
- `lighting`:时间与光线(晨光、霓虹夜景、逆光等)
- `camera`:景别、角度、镜头(close-up, wide shot, low angle, 35mm)
- `mood`:氛围与情绪(nostalgic, tense, dreamy, warm)
组合顺序:`subject -> environment -> style -> lighting -> camera -> mood`。
## 第三步:权重控制规范
- 用户强调元素(如“重点是XXX”)必须加权:`(keyword:1.4)` 到 `(keyword:1.5)`
- 重要但非核心元素:`(keyword:1.2)` 到 `(keyword:1.3)`
- 需要弱化元素:`(keyword:0.7)` 到 `(keyword:0.9)`
- SDXL 使用关键词+权重格式;Flux 使用自然语言段落,但仍可对核心词做轻量加权。
## 第四步:负向 prompt 模板库
先写通用排除,再拼接风格专用排除。
- 通用排除(必须包含):
`bad anatomy, bad hands, blurry, watermark, text, logo, deformed`
- 写实风格额外排除:
`cartoon, anime, illustration, painting`
- 动漫风格额外排除:
`photorealistic, photo, 3d render`
- 复古风格额外排除:
`modern, digital, clean, sharp`
## 第五步:用户意图确认机制
当需求模糊时先确认,再出最终 prompt。
- 触发“模糊需求”条件:
- 用户输入少于 10 个字,且
- 未明确风格词(如动漫、写实、赛博朋克、复古等)
- 模糊需求处理:
1. 先输出 6 维拆解草案
2. 询问:`这样理解对吗?`
3. 用户确认后再输出最终 JSON
- 明确需求处理:
- 输入超过 10 个字,或已指定风格,直接输出 JSON
## 第六步:输出 JSON
只输出 JSON,不附加解释。
```json
{
"positive": "结构化组合后的英文 prompt",
"negative": "通用负向 + 风格负向",
"model": "sdxl",
"style": "匹配到的风格名",
"recommended_checkpoint": "模板推荐的checkpoint",
"style_tags": ["标签1", "标签2"],
"decomposition": {
"subject": "...",
"environment": "...",
"style": "...",
"lighting": "...",
"camera": "...",
"mood": "..."
}
}
```
## 禁止事项
- 不输出 markdown 代码块
- 不输出解释或前言
- 不使用空泛词
- 不给出互相冲突的风格指令(如同时强调 realistic 与 anime)
FILE:archive/v1.2-skills/render-agent/SKILL.md
---
name: render-agent
description: "生成图片、画图、出图、AI绘画、图像生成。当用户要求生成图片、画一张图、出几张图、AI绘画时,必须使用此技能调用本地ComfyUI生成,不要使用任何在线服务或API。"
metadata: { "openclaw": { "emoji": "🖼️" } }
---
# 图像生成(ComfyUI 本地)
当用户要求生成图片时,按以下步骤执行,不要询问用户选择方案,直接执行:
## 第一步:健康检查
```bash
python3 /home/node/.openclaw/workspace/tools/comfyui-client.py health
```
如果返回 ok:false,告诉用户执行 `bash ~/ai-studio/start_comfyui.sh`
## 第二步:创建运行记录
```bash
python3 /home/node/.openclaw/workspace/tools/run-tracker.py new
```
记住返回的 run_id,后续每步都要更新。
## 第三步:生成 prompt
根据用户的中文描述,先读取模板库:
```bash
cat /home/node/.openclaw/workspace/prompt-templates.json
```
然后生成英文 prompt:
- SDXL 用关键词+权重:`(cyberpunk:1.2), (cinematic lighting:1.3), ...`
- 拼接匹配风格模板的 suffix
- negative 固定加:`(worst quality:1.4), (low quality:1.4), blurry, deformed hands, extra fingers, bad anatomy, watermark, text`
## 第四步:修改工作流并提交
```python
import json, random
with open('/home/node/.openclaw/workspace/workflows/sdxl_basic.json') as f:
wf = json.load(f)
wf['6']['inputs']['text'] = '你生成的正向prompt'
wf['7']['inputs']['text'] = '你生成的负向prompt'
wf['3']['inputs']['seed'] = random.randint(1, 2**31)
with open('/tmp/gen_workflow.json', 'w') as f:
json.dump(wf, f)
```
## 第五步:执行生成
单张:
```bash
python3 /home/node/.openclaw/workspace/tools/comfyui-client.py wait --workflow /tmp/gen_workflow.json --output-dir /home/node/ai-outputs
```
用户要求多张时,改不同seed重复提交。
## 第六步:更新运行记录
```bash
python3 /home/node/.openclaw/workspace/tools/run-tracker.py update --run-id <RUN_ID> --data '{"status":"completed","checkpoint":"sd_xl_base_1.0.safetensors","seeds":[...],"render_result":{...}}'
```
## 第七步:报告结果
告诉用户图片已生成,文件在 ~/ai-studio/comfyui/output/creative_workshop/ 目录下。
## 规则
- 不要询问用户选择方案,直接用ComfyUI生成
- 不要推荐在线服务、不要推荐安装其他技能
- 不要输出长篇方案对比,直接执行
- 每次生成都要创建运行记录
## 黑图检测与重试
生成完成后检查图片文件大小:
- 文件小于 50KB → 判定为黑图/损坏图
- 自动重试:换一个随机seed重新生成
- 最多重试3次
- 如果3次都失败,报告错误并建议用户重启ComfyUI
检测命令示例:
```bash
FILE_SIZE=$(stat -f%z "$IMAGE_PATH" 2>/dev/null || stat -c%s "$IMAGE_PATH" 2>/dev/null)
if [ "$FILE_SIZE" -lt 50000 ]; then
echo "黑图检测:文件仅FILE_SIZE字节,判定为损坏,准备重试"
fi
```
FILE:archive/v1.2-skills/workflow-agent/SKILL.md
---
name: workflow-agent
description: "选择并改写 ComfyUI 工作流模板,输出可直接提交到 ComfyUI API 的完整 JSON。当需要准备渲染任务、选择模型、调整参数时触发。"
metadata: { "openclaw": { "emoji": "⚙️" } }
---
# Workflow Agent
管理和组装 ComfyUI 工作流。
## 模板位置
`/home/node/.openclaw/workspace/workflows/`
## 工作流程
1. 根据 Prompt Agent 的输出选择模板(如 `sdxl_basic.json`)。
2. 用 Python `json` 模块读取模板,替换以下字段:
- 节点 `6` 的 `inputs.text` → 正向 prompt
- 节点 `7` 的 `inputs.text` → 反向 prompt
- 节点 `3` 的 `inputs.seed` → 随机整数
- 节点 `3` 的 `inputs.steps` → 按复杂度动态设置
- 节点 `3` 的 `inputs.cfg` → 按风格动态设置
- 节点 `5` 的 `inputs.width` / `inputs.height` → 按画面类型动态设置
3. 输出替换后的完整 JSON(不附加解释文本)。
## 模型选择规则
- 默认写实/通用:`sd_xl_base_1.0.safetensors`
- 快速实验:`flux1-schnell-Q5_K_S.gguf`(需要 Flux 专用工作流)
- 用户未指定时用 SDXL
## 参数精细化规则
### 1) CFG(按风格)
- 写实/照片:`4-6`(更自然)
- 动漫/插画:`7-9`(更风格化)
- 概念艺术:`6-8`
- 未知风格默认:`7.0`
### 2) Steps(按内容复杂度)
- 简单场景(单主体)→ `20`
- 复杂场景(多人物 + 背景)→ `30`
- 精细细节(特写、珠宝、建筑)→ `35-40`
### 3) 分辨率(按画面类型)
- 人物特写/肖像:`768x1024`(竖版)
- 风景/全景:`1024x768`(横版)
- 默认/不确定:`1024x1024`
## 参数默认值(兜底)
| 参数 | 默认值 |
|------|--------|
| steps | 25 |
| cfg | 7.0 |
| width | 1024 |
| height | 1024 |
| sampler | euler |
| scheduler | normal |
## 禁止事项
- 不修改节点连接关系(只改 inputs 值)
- 数值字段不得写成字符串(seed 必须是 int,cfg 必须是 float)
FILE:resources/SOUL.md
你是 Visual Muse(视觉缪斯),一个专业的 AI 画师。
你的唯一职责:接收用户的中文图片描述,通过 ComfyUI 生成高质量图片。
## 出图流程(严格遵守,不得跳过任何步骤)
当用户要求画图时,执行以下步骤:
### 第一步:生成参数 JSON
根据用户描述,在心里构思以下 JSON 参数(不需要输出给用户看):
- **positive**:英文正向提示词。以 `masterpiece, best quality, 8k` 开头,然后是具体描述。使用关键词+权重格式,如 `(cinematic lighting:1.3), (cyberpunk:1.2)`
- **negative**:英文负向提示词。固定为 `lowres, bad anatomy, bad hands, blurry, worst quality, watermark, signature, text, extra fingers, deformed`
- **workflow**:工作流文件名
- 默认用 `sdxl_basic.json`
- 用户要求高清大图时用 `sdxl_hires.json`
- **checkpoint**:模型选择
- 动漫/二次元风格 → `animagine-xl`(如果可用)或 `dreamshaper-xl`
- 写实/电影感 → `juggernaut-xl`(如果可用)或 `dreamshaper-xl`
- 默认/不确定 → `dreamshaper-xl`
- **seed**:设为 -1(随机)
### 第二步:调用 paint-dispatch.sh
用 bash 执行以下命令(把参数填入 JSON):
```bash
echo '{"positive":"你构思的英文prompt","negative":"lowres, bad anatomy, bad hands, blurry, worst quality, watermark, signature, text, extra fingers, deformed","workflow":"sdxl_basic.json","checkpoint":"dreamshaper-xl","seed":-1}' | bash /home/node/.openclaw/workspace/tools/paint-dispatch.sh
```
### 第三步:检查结果
- 看输出里有没有 `SUCCESS` 和 `FILE:` 行
- 如果有,提取文件路径,检查文件大小(SIZE 应该 > 100KB,否则是黑图)
- 如果成功,告诉用户"图片已生成"并提供 seed 信息
- 如果失败,告诉用户错误原因
## 严禁事项
- **禁止自己写 Python/JavaScript 脚本来调 ComfyUI API**。已有 paint-dispatch.sh,直接用。
- **禁止用 Python 生成 SVG/Canvas/PIL 图片来代替 ComfyUI 出图**。
- **禁止创建子会话(session)**。
- **禁止修改 workspace 里的任何文件**。
- **禁止安装任何 pip/npm 包**。
## 对话风格
- 用中文回复用户
- 简洁友好,不需要解释技术细节
- 如果用户的描述太模糊,可以简单追问一下风格偏好(写实/动漫/电影感)
- 出图成功后,可以简单描述图片内容,并提供 seed 方便用户复现
## 数量规则(严格遵守)
- 默认只生成1张图,生成完立即停止
- 不要自动生成变体、不要自动生成第二张、不要「再来一张不同风格的」
- 生成完成后简短报告结果(图片+seed),然后等用户下一步指令
- 只有用户明确说「再来一张」「多几张」「出4张」时才生成更多
- 违反此规则等于浪费用户的钱
FILE:resources/preferences.json
{
"profile": {
"liked_styles": [],
"disliked_styles": [],
"preferred_composition": [],
"preferred_tones": []
},
"successful_patterns": [],
"model_recipes": [],
"history": []
}
FILE:resources/prompt-templates.json
{
"cinematic": {
"name": "电影感",
"suffix": ", cinematic lighting, film grain, shallow depth of field, anamorphic lens flare, color grading, dramatic shadows, 8k, masterpiece",
"negative_extra": "overexposed, underexposed, flat lighting",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
},
"anime": {
"name": "动漫",
"suffix": ", anime style, cel shading, vibrant colors, detailed eyes, clean lines, studio ghibli influence, makoto shinkai lighting",
"negative_extra": "realistic, photographic, 3d render, western cartoon",
"recommended_checkpoint": "animagine_xl_v3.1.safetensors"
},
"photorealistic": {
"name": "照片写实",
"suffix": ", photorealistic, RAW photo, 8k uhd, DSLR, high quality, film grain, Fujifilm XT3, sharp focus, natural lighting",
"negative_extra": "painting, drawing, illustration, cartoon, anime, cgi",
"recommended_checkpoint": "juggernaut_xl_v9.safetensors"
},
"concept_art": {
"name": "概念艺术",
"suffix": ", concept art, digital painting, trending on artstation, highly detailed, illustration, matte painting",
"negative_extra": "photographic, realistic, blurry, amateur",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
},
"watercolor": {
"name": "水彩",
"suffix": ", watercolor painting, soft edges, paper texture, artistic, flowing colors, traditional media, delicate brushstrokes",
"negative_extra": "photographic, digital, sharp edges, 3d, oversaturated",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
},
"oil_painting": {
"name": "油画",
"suffix": ", oil painting, thick brushstrokes, canvas texture, rich colors, classical art, impasto technique, museum quality",
"negative_extra": "photographic, digital art, smooth, flat colors",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
},
"cyberpunk": {
"name": "赛博朋克",
"suffix": ", cyberpunk, neon lights, dark atmosphere, rain, reflections, holographic, futuristic city, blade runner style, high contrast",
"negative_extra": "bright, cheerful, pastoral, natural, daylight",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
},
"fantasy": {
"name": "奇幻",
"suffix": ", fantasy art, magical atmosphere, ethereal lighting, enchanted, mystical, epic composition, detailed environment",
"negative_extra": "modern, urban, photographic, mundane",
"recommended_checkpoint": "dreamshaper_xl.safetensors"
}
}
FILE:scripts/setup.sh
#!/usr/bin/env bash
set -euo pipefail
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; BLUE='\033[0;34m'; NC='\033[0m'
log() { echo -e "BLUE[信息]NC $1"; }
ok() { echo -e "GREEN[完成]NC $1"; }
warn() { echo -e "YELLOW[提示]NC $1"; }
err() { echo -e "RED[错误]NC $1" >&2; }
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
AI_STUDIO_DIR="$HOME/ai-studio"
COMFY_DIR="$AI_STUDIO_DIR/comfyui"
VENV_DIR="$AI_STUDIO_DIR/comfyui-venv"
MODELS_DIR="$COMFY_DIR/models"
WORKSPACE_DIR="-$HOME/.openclaw/workspace"
COMFYUI_URL="-http://127.0.0.1:8188"
echo
echo -e "GREEN╔════════════════════════════════════════════╗NC"
echo -e "GREEN║ 🎨 Visual Muse 一键安装向导 ║NC"
echo -e "GREEN║ ComfyUI + 模型 + Agent 全自动配置 ║NC"
echo -e "GREEN╚════════════════════════════════════════════╝NC"
echo
# ============================================================
# 第一步:前置检查
# ============================================================
log "检查前置条件..."
for cmd in python3 curl git; do
if ! command -v "$cmd" >/dev/null 2>&1; then
err "缺少 $cmd,请先安装"
exit 1
fi
done
ok "python3 / curl / git 已就绪"
# 检测系统
OS="$(uname -s)"
ARCH="$(uname -m)"
if [[ "$OS" == "Darwin" && "$ARCH" == "arm64" ]]; then
ok "检测到 macOS Apple Silicon — 将使用 MPS 加速"
PLATFORM="macos_arm"
elif [[ "$OS" == "Linux" ]]; then
if command -v nvidia-smi >/dev/null 2>&1; then
ok "检测到 Linux + NVIDIA GPU — 将使用 CUDA 加速"
PLATFORM="linux_nvidia"
else
warn "检测到 Linux 但未发现 NVIDIA GPU — 将使用 CPU 模式(很慢)"
PLATFORM="linux_cpu"
fi
else
warn "未识别的平台 $OS/$ARCH — 将尝试通用安装"
PLATFORM="generic"
fi
# ============================================================
# 第二步:安装 ComfyUI
# ============================================================
echo
log "=== 安装 ComfyUI ==="
if [ -d "$COMFY_DIR/.git" ]; then
warn "ComfyUI 已存在于 $COMFY_DIR,跳过克隆"
else
log "克隆 ComfyUI..."
mkdir -p "$AI_STUDIO_DIR"
git clone --depth 1 https://github.com/comfyanonymous/ComfyUI.git "$COMFY_DIR" || \
git clone --depth 1 https://ghfast.top/https://github.com/comfyanonymous/ComfyUI.git "$COMFY_DIR" || {
err "ComfyUI 克隆失败,请检查网络后重试"
exit 1
}
ok "ComfyUI 克隆完成"
fi
# 创建虚拟环境
if [ ! -d "$VENV_DIR" ]; then
log "创建 Python 虚拟环境..."
python3 -m venv "$VENV_DIR"
ok "虚拟环境创建完成"
else
warn "虚拟环境已存在,跳过"
fi
source "$VENV_DIR/bin/activate"
log "安装 PyTorch 和 ComfyUI 依赖(这一步需要几分钟)..."
pip install --upgrade pip setuptools wheel -q
pip install torch torchvision torchaudio -q
pip install -r "$COMFY_DIR/requirements.txt" -q
ok "PyTorch 和 ComfyUI 依赖安装完成"
# 安装 ComfyUI-Manager
if [ ! -d "$COMFY_DIR/custom_nodes/ComfyUI-Manager/.git" ]; then
log "安装 ComfyUI-Manager..."
mkdir -p "$COMFY_DIR/custom_nodes"
git clone --depth 1 https://github.com/ltdrdata/ComfyUI-Manager.git "$COMFY_DIR/custom_nodes/ComfyUI-Manager" || \
git clone --depth 1 https://ghfast.top/https://github.com/ltdrdata/ComfyUI-Manager.git "$COMFY_DIR/custom_nodes/ComfyUI-Manager" || \
warn "ComfyUI-Manager 安装失败,不影响核心功能"
ok "ComfyUI-Manager 安装完成"
else
warn "ComfyUI-Manager 已存在,跳过"
fi
# 创建模型目录
mkdir -p "$MODELS_DIR/checkpoints" "$MODELS_DIR/vae" "$MODELS_DIR/clip" "$MODELS_DIR/loras" "$MODELS_DIR/controlnet"
# 创建符号链接
if [ ! -L "$AI_STUDIO_DIR/models" ]; then
if [ ! -e "$AI_STUDIO_DIR/models" ]; then
ln -s "$MODELS_DIR" "$AI_STUDIO_DIR/models"
fi
fi
# 创建启动脚本
mkdir -p "$AI_STUDIO_DIR/outputs"
cat > "$AI_STUDIO_DIR/start_comfyui.sh" << 'STARTEOF'
#!/usr/bin/env bash
set -euo pipefail
source "$HOME/ai-studio/comfyui-venv/bin/activate"
cd "$HOME/ai-studio/comfyui"
echo "[信息] 启动 ComfyUI(listen 0.0.0.0:8188 --highvram --fp32-vae)..."
echo "[信息] 浏览器访问 http://127.0.0.1:8188"
echo "[信息] 按 Ctrl+C 停止"
python main.py --listen 0.0.0.0 --port 8188 --highvram --fp32-vae
STARTEOF
chmod +x "$AI_STUDIO_DIR/start_comfyui.sh"
ok "启动脚本已生成"
# ============================================================
# 第三步:下载模型
# ============================================================
echo
log "=== 下载模型(约 7GB,支持断点续传)==="
download() {
local url="$1" output="$2" name="$3"
if [ -s "$output" ]; then
local size; size="$(du -h "$output" | awk '{print $1}')"
warn "$name 已存在($size),跳过"
return 0
fi
log "下载 $name..."
curl -L -C - --progress-bar -o "$output" "$url" || \
curl -L -C - --progress-bar -o "$output" "$(echo "$url" | sed 's|https://huggingface.co|https://hf-mirror.com|')" || {
warn "$name 下载失败,可稍后手动下载"
rm -f "$output"
return 0
}
ok "$name 下载完成"
}
download \
"https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors" \
"$MODELS_DIR/checkpoints/sd_xl_base_1.0.safetensors" \
"SDXL Base 1.0(6.5GB)"
download \
"https://huggingface.co/stabilityai/sdxl-vae/resolve/main/sdxl_vae.safetensors" \
"$MODELS_DIR/vae/sdxl_vae.safetensors" \
"SDXL VAE(335MB)"
download \
"https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/clip_l.safetensors" \
"$MODELS_DIR/clip/clip_l.safetensors" \
"CLIP L(250MB)"
# ============================================================
# 第四步:复制 skill 工具和模板
# ============================================================
echo
log "=== 配置 Agent 工具和模板 ==="
mkdir -p "$WORKSPACE_DIR/tools" "$WORKSPACE_DIR/workflows" "$WORKSPACE_DIR/runtime" "$WORKSPACE_DIR/runs"
for f in comfyui-client.py run-tracker.py; do
if [ ! -f "$WORKSPACE_DIR/tools/$f" ] && [ -f "$SKILL_DIR/tools/$f" ]; then
cp "$SKILL_DIR/tools/$f" "$WORKSPACE_DIR/tools/$f"
ok "已部署工具:$f"
else
warn "工具已存在或源文件不存在:$f"
fi
done
if [ ! -f "$WORKSPACE_DIR/workflows/sdxl_basic.json" ] && [ -f "$SKILL_DIR/workflows/sdxl_basic.json" ]; then
cp "$SKILL_DIR/workflows/sdxl_basic.json" "$WORKSPACE_DIR/workflows/sdxl_basic.json"
ok "已部署工作流模板"
fi
if [ ! -f "$WORKSPACE_DIR/prompt-templates.json" ] && [ -f "$SKILL_DIR/resources/prompt-templates.json" ]; then
cp "$SKILL_DIR/resources/prompt-templates.json" "$WORKSPACE_DIR/prompt-templates.json"
ok "已部署 prompt 模板库"
fi
if [ ! -f "$WORKSPACE_DIR/preferences.json" ] && [ -f "$SKILL_DIR/resources/preferences.json" ]; then
cp "$SKILL_DIR/resources/preferences.json" "$WORKSPACE_DIR/preferences.json"
ok "已初始化偏好档案"
fi
# ============================================================
# 第五步:验证
# ============================================================
echo
log "=== 最终验证 ==="
PASS=0; FAIL=0
check() {
if [ -f "$1" ] || [ -d "$1" ]; then
ok "$2"; PASS=$((PASS+1))
else
err "$2 — 缺失"; FAIL=$((FAIL+1))
fi
}
check "$COMFY_DIR/main.py" "ComfyUI 安装"
check "$VENV_DIR/bin/python" "Python 虚拟环境"
check "$MODELS_DIR/checkpoints/sd_xl_base_1.0.safetensors" "SDXL 模型"
check "$WORKSPACE_DIR/tools/comfyui-client.py" "API 客户端"
check "$WORKSPACE_DIR/workflows/sdxl_basic.json" "工作流模板"
check "$WORKSPACE_DIR/prompt-templates.json" "Prompt 模板库"
echo
echo -e "GREEN╔════════════════════════════════════════════╗NC"
if [ "$FAIL" -eq 0 ]; then
echo -e "GREEN║ ✅ 安装完成!$PASS/$PASS 项检查通过 ║NC"
else
echo -e "YELLOW║ ⚠️ 安装部分完成($PASS 通过 / $FAIL 失败) ║NC"
fi
echo -e "GREEN╚════════════════════════════════════════════╝NC"
echo
echo "接下来:"
echo
echo " 1) 启动 ComfyUI(新开终端窗口):"
echo " bash ~/ai-studio/start_comfyui.sh"
echo
echo " 2) 对 OpenClaw 说:"
echo " 「画一张赛博朋克猫在雨夜街道」"
echo
echo " 图片输出在:~/ai-studio/comfyui/output/creative_workshop/"
echo
FILE:scripts/switch-painter-model.sh
#!/bin/bash
# 宿主机快捷入口:切换 painter 模型并重启
# 用法: bash ~/openclaw/switch-painter-model.sh gemini
if [ -z "$1" ]; then
echo "用法: bash ~/openclaw/switch-painter-model.sh [模型名]"
echo "可用模型: gemini gemini-pro gemini-lite gpt gpt-nano gpt-pro claude claude-haiku cheap best"
exit 0
fi
echo "切换 painter 模型到: $1"
docker exec openclaw-gateway bash /home/node/.openclaw/workspace-painter/switch-model.sh "$1"
echo ""
echo "重启 openclaw-gateway..."
docker restart openclaw-gateway
sleep 15
echo ""
echo "=== 验证 ==="
docker exec openclaw-gateway python3 - <<'PY'
import json
cfg=json.load(open('/home/node/.openclaw/openclaw.json'))
items=cfg.get('agents',{}).get('items', cfg.get('agents',{}).get('list', []))
for a in items:
if a.get('id')=='painter':
print(f"当前 painter 模型: {a.get('model')}")
break
PY
docker ps | grep openclaw-gateway | grep -q healthy && echo "网关状态: healthy" || echo "网关状态: 等待中..."
echo ""
echo "✅ 完成!去 Telegram @visual_muse00bot 测试吧"
FILE:skills/generate-image/SKILL.md
# generate-image
用户请求画图时触发。
只做一次执行:先写英文 prompt,再调用脚本。
```bash
bash /home/node/.openclaw/workspace/tools/quick-generate.sh "<positive>" "<negative>"
```
Prompt 规则:
- positive: masterpiece, best quality, ultra-detailed, 8k, [主体], [风格], [场景], [光影]
- negative: lowres, bad anatomy, bad hands, blurry, worst quality, watermark, signature, text, ugly, deformed
风格词:
- anime style, cel shading, vibrant colors, anime screencap
- photorealistic, RAW photo, film grain, shallow depth of field
- showa era aesthetic, retro atmosphere, 1980s japan, vintage photography, neon signs
- cyberpunk, neon lights, rain, holographic, futuristic city
- watercolor painting, soft edges, color bleeding, artistic
高清版:
```bash
bash /home/node/.openclaw/workspace/tools/quick-generate.sh "<positive>" "<negative>" sdxl_hires.json
```
脚本会返回文件路径与 seed;用 message 工具发给用户(附简短中文标题+seed)。
规则:
- 单轮执行,不要多轮追问
- 不要手写 Python
- 不要手动 curl ComfyUI
- 不要额外检查(脚本已内置)
- 遵守系统与平台安全边界
FILE:skills/image-review/SKILL.md
# image-review
用户说评价、改进、优化图片时触发。
## 流程
### Step 1: 检查最近图片
```bash
ls -lt /home/node/ai-outputs/ | head -5
```
### Step 2: 评估(5分制)
构图、色彩、细节、风格一致性、整体印象。
### Step 3: 改进建议
评分<3.5时建议:prompt调整、换checkpoint、用hires workflow。
### Step 4: 记录
```bash
python /home/node/.openclaw/workspace/tools/run-tracker.py --log "评估摘要"
```
FILE:tools/comfyui-client.py
#!/usr/bin/env python3
"""ComfyUI API 客户端(仅使用 Python 标准库)。"""
from __future__ import annotations
import argparse
import copy
import json
import os
import random
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from typing import Any
DEFAULT_API_URL = "http://host.docker.internal:8188"
DEFAULT_TIMEOUT = 30
def eprint(message: str) -> None:
print(message, file=sys.stderr)
def api_url() -> str:
return os.environ.get("COMFYUI_API_URL", DEFAULT_API_URL).rstrip("/")
def http_json(method: str, path: str, payload: dict[str, Any] | None = None, timeout: int = DEFAULT_TIMEOUT) -> Any:
url = f"{api_url()}{path}"
data = None
headers = {}
if payload is not None:
data = json.dumps(payload).encode("utf-8")
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url=url, data=data, method=method, headers=headers)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read()
if not body:
return {}
return json.loads(body.decode("utf-8"))
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"HTTP {exc.code} {exc.reason}: {detail}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"网络请求失败: {exc}") from exc
def http_download(path: str, output_path: str, timeout: int = DEFAULT_TIMEOUT) -> None:
url = f"{api_url()}{path}"
req = urllib.request.Request(url=url, method="GET")
try:
with urllib.request.urlopen(req, timeout=timeout) as resp, open(output_path, "wb") as f:
f.write(resp.read())
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"下载失败 HTTP {exc.code} {exc.reason}: {detail}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"下载失败: {exc}") from exc
def load_workflow(path: str) -> dict[str, Any]:
with open(path, "r", encoding="utf-8") as f:
workflow = json.load(f)
if not isinstance(workflow, dict):
raise RuntimeError("workflow JSON 顶层必须是对象")
return workflow
def submit_workflow(workflow: dict[str, Any]) -> str:
result = http_json("POST", "/prompt", {"prompt": workflow})
prompt_id = result.get("prompt_id")
if not prompt_id:
raise RuntimeError(f"提交成功但未返回 prompt_id: {result}")
return str(prompt_id)
def query_status(prompt_id: str) -> dict[str, Any]:
history = http_json("GET", f"/history/{prompt_id}")
item = history.get(prompt_id)
completed = bool(item and item.get("status", {}).get("completed", False))
return {
"prompt_id": prompt_id,
"completed": completed,
"raw": item if item else history,
}
def extract_image_items(history_item: dict[str, Any]) -> list[dict[str, str]]:
outputs = history_item.get("outputs", {})
items: list[dict[str, str]] = []
for node_id, node_output in outputs.items():
images = node_output.get("images", [])
for image in images:
filename = image.get("filename")
subfolder = image.get("subfolder", "")
image_type = image.get("type", "output")
if filename:
items.append(
{
"node_id": str(node_id),
"filename": str(filename),
"subfolder": str(subfolder),
"type": str(image_type),
}
)
return items
def download_outputs(history_item: dict[str, Any], output_dir: str, prefix: str) -> list[str]:
os.makedirs(output_dir, exist_ok=True)
image_items = extract_image_items(history_item)
saved_files: list[str] = []
for idx, item in enumerate(image_items, start=1):
params = urllib.parse.urlencode(
{
"filename": item["filename"],
"subfolder": item["subfolder"],
"type": item["type"],
}
)
ext = os.path.splitext(item["filename"])[1] or ".png"
save_name = f"{prefix}_{idx}{ext}"
save_path = os.path.abspath(os.path.join(output_dir, save_name))
http_download(f"/view?{params}", save_path)
saved_files.append(save_path)
return saved_files
def wait_for_completion(prompt_id: str, poll_interval: float, timeout_sec: int) -> dict[str, Any]:
deadline = time.time() + timeout_sec
while time.time() < deadline:
history = http_json("GET", f"/history/{prompt_id}")
item = history.get(prompt_id)
if item and item.get("status", {}).get("completed", False):
return item
time.sleep(poll_interval)
raise RuntimeError(f"等待超时,prompt_id={prompt_id}")
def update_seed(workflow: dict[str, Any], seed: int) -> dict[str, Any]:
new_workflow = copy.deepcopy(workflow)
updated = False
for node in new_workflow.values():
if isinstance(node, dict) and node.get("class_type") == "KSampler":
inputs = node.get("inputs", {})
if isinstance(inputs, dict):
inputs["seed"] = int(seed)
updated = True
if not updated:
raise RuntimeError("workflow 中未找到 KSampler 节点,无法设置 seed")
return new_workflow
def cmd_health(_: argparse.Namespace) -> int:
try:
stats = http_json("GET", "/system_stats")
print(
json.dumps(
{
"ok": True,
"api_url": api_url(),
"system_stats": stats,
},
ensure_ascii=False,
)
)
return 0
except Exception as exc: # noqa: BLE001
eprint(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
return 1
def cmd_submit(args: argparse.Namespace) -> int:
try:
workflow = load_workflow(args.workflow)
prompt_id = submit_workflow(workflow)
print(json.dumps({"ok": True, "prompt_id": prompt_id}, ensure_ascii=False))
return 0
except Exception as exc: # noqa: BLE001
eprint(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
return 1
def cmd_status(args: argparse.Namespace) -> int:
try:
status = query_status(args.prompt_id)
status["ok"] = True
print(json.dumps(status, ensure_ascii=False))
return 0
except Exception as exc: # noqa: BLE001
eprint(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
return 1
def cmd_wait(args: argparse.Namespace) -> int:
try:
workflow = load_workflow(args.workflow)
prompt_id = submit_workflow(workflow)
history_item = wait_for_completion(prompt_id, args.poll_interval, args.timeout)
files = download_outputs(history_item, args.output_dir, prefix=f"{prompt_id}")
print(
json.dumps(
{
"ok": True,
"prompt_id": prompt_id,
"completed": True,
"output_files": files,
},
ensure_ascii=False,
)
)
return 0
except Exception as exc: # noqa: BLE001
eprint(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
return 1
def cmd_batch(args: argparse.Namespace) -> int:
try:
base_workflow = load_workflow(args.workflow)
start_seed = args.seed_start if args.seed_start is not None else random.randint(1, 2**31 - 1)
submissions: list[dict[str, Any]] = []
for i in range(args.count):
seed = start_seed + i
wf = update_seed(base_workflow, seed)
prompt_id = submit_workflow(wf)
submissions.append({"prompt_id": prompt_id, "seed": seed})
results: list[dict[str, Any]] = []
for item in submissions:
prompt_id = item["prompt_id"]
history_item = wait_for_completion(prompt_id, args.poll_interval, args.timeout)
files = download_outputs(history_item, args.output_dir, prefix=f"{prompt_id}_{item['seed']}")
results.append(
{
"prompt_id": prompt_id,
"seed": item["seed"],
"output_files": files,
}
)
print(json.dumps({"ok": True, "count": args.count, "results": results}, ensure_ascii=False))
return 0
except Exception as exc: # noqa: BLE001
eprint(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False))
return 1
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="ComfyUI API 客户端")
sub = parser.add_subparsers(dest="command", required=True)
p_health = sub.add_parser("health", help="检查 ComfyUI 是否在线")
p_health.set_defaults(func=cmd_health)
p_submit = sub.add_parser("submit", help="提交 workflow 并返回 prompt_id")
p_submit.add_argument("--workflow", required=True, help="workflow JSON 文件路径")
p_submit.set_defaults(func=cmd_submit)
p_status = sub.add_parser("status", help="查询 prompt_id 状态")
p_status.add_argument("--prompt-id", required=True, help="任务 prompt_id")
p_status.set_defaults(func=cmd_status)
p_wait = sub.add_parser("wait", help="提交 workflow 并等待完成")
p_wait.add_argument("--workflow", required=True, help="workflow JSON 文件路径")
p_wait.add_argument("--output-dir", required=True, help="下载结果的输出目录")
p_wait.add_argument("--poll-interval", type=float, default=2.0, help="轮询间隔(秒)")
p_wait.add_argument("--timeout", type=int, default=900, help="超时时间(秒)")
p_wait.set_defaults(func=cmd_wait)
p_batch = sub.add_parser("batch", help="批量提交同一 workflow 的不同 seed 变体")
p_batch.add_argument("--workflow", required=True, help="workflow JSON 文件路径")
p_batch.add_argument("--count", type=int, default=4, help="批量数量")
p_batch.add_argument("--output-dir", required=True, help="下载结果的输出目录")
p_batch.add_argument("--seed-start", type=int, default=None, help="起始 seed")
p_batch.add_argument("--poll-interval", type=float, default=2.0, help="轮询间隔(秒)")
p_batch.add_argument("--timeout", type=int, default=900, help="每个任务超时时间(秒)")
p_batch.set_defaults(func=cmd_batch)
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
return args.func(args)
if __name__ == "__main__":
raise SystemExit(main())
FILE:tools/paint-dispatch.sh
#!/bin/bash
# paint-dispatch.sh — 接收 JSON 输入,调用 ComfyUI 出图
# 用法:echo "JSON" | bash paint-dispatch.sh
# 或:bash paint-dispatch.sh "JSON字符串"
set -euo pipefail
# 读取 JSON 输入(从参数或 stdin)
if [ -n "-" ]; then
JSON_INPUT="$1"
else
JSON_INPUT=$(cat)
fi
if [ -z "-" ]; then
echo "ERROR: 没有收到 JSON 输入"
exit 1
fi
# 解析 JSON
POSITIVE=$(echo "$JSON_INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('positive',''))")
NEGATIVE=$(echo "$JSON_INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('negative','lowres, bad anatomy, bad hands, blurry, worst quality, watermark, signature, text'))")
WORKFLOW=$(echo "$JSON_INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('workflow','sdxl_basic.json'))")
CHECKPOINT=$(echo "$JSON_INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('checkpoint',''))")
SEED=$(echo "$JSON_INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('seed',-1))")
OUTPUT_DIR="/home/node/ai-outputs"
WORKDIR="/home/node/.openclaw/workspace"
# workflow 白名单
case "$WORKFLOW" in
sdxl_basic.json|sdxl_hires.json) ;;
*)
echo "WARNING: workflow=$WORKFLOW 非法,回退到 sdxl_basic.json"
WORKFLOW="sdxl_basic.json"
;;
esac
if [ -z "$POSITIVE" ]; then
echo "ERROR: positive 为空"
exit 1
fi
# 如果 seed 是 -1,生成随机值
if [ "$SEED" = "-1" ]; then
SEED=$((RANDOM * RANDOM))
fi
# Step 1: 检查 ComfyUI
if ! python3 "$WORKDIR/tools/comfyui-client.py" health >/tmp/comfy_health.json 2>/tmp/comfy_health.err; then
echo "ERROR: ComfyUI 未运行或不可达"
cat /tmp/comfy_health.err || true
exit 1
fi
# Step 1.5: 校验 checkpoint 是否可用,不可用则回退到 ComfyUI 当前可用的第一个模型
if [ -n "$CHECKPOINT" ]; then
AVAILABLE_CKPT=$(
curl -s --connect-timeout 5 http://host.docker.internal:8188/object_info/CheckpointLoaderSimple \
| python3 -c "import json,sys; d=json.load(sys.stdin); arr=d.get('CheckpointLoaderSimple',{}).get('input',{}).get('required',{}).get('ckpt_name',[[]])[0]; print(','.join(arr) if isinstance(arr,list) else '')" \
2>/dev/null || true
)
if [ -n "$AVAILABLE_CKPT" ]; then
export CHECKPOINT AVAILABLE_CKPT
CKPT_OK=$(
python3 - <<'PY'
import os
ckpt = os.environ.get("CHECKPOINT", "")
avail = [x for x in os.environ.get("AVAILABLE_CKPT", "").split(",") if x]
print("1" if ckpt in avail else "0")
PY
)
if [ "$CKPT_OK" != "1" ]; then
FALLBACK_CKPT=$(echo "$AVAILABLE_CKPT" | cut -d',' -f1)
echo "WARNING: checkpoint '$CHECKPOINT' 不可用,回退为 '$FALLBACK_CKPT'"
CHECKPOINT="$FALLBACK_CKPT"
fi
fi
fi
# Step 2: 创建输出目录与临时 workflow
mkdir -p "$OUTPUT_DIR"
TEMPLATE_WORKFLOW="$WORKDIR/workflows/$WORKFLOW"
if [ ! -f "$TEMPLATE_WORKFLOW" ]; then
echo "ERROR: workflow 不存在: $TEMPLATE_WORKFLOW"
exit 1
fi
TMP_WORKFLOW="/tmp/paint-workflow-$$.json"
export TEMPLATE_WORKFLOW TMP_WORKFLOW POSITIVE NEGATIVE CHECKPOINT SEED
python3 - <<'PY'
import json, os
src=os.environ['TEMPLATE_WORKFLOW']
out=os.environ['TMP_WORKFLOW']
pos=os.environ['POSITIVE']
neg=os.environ['NEGATIVE']
ckpt=os.environ.get('CHECKPOINT','').strip()
seed=int(os.environ['SEED'])
wf=json.load(open(src, 'r', encoding='utf-8'))
# 按 KSampler 链路精确替换正负提示词
for node in wf.values():
if isinstance(node, dict) and node.get('class_type') == 'KSampler':
ins=node.get('inputs', {})
ins['seed']=seed
pos_ref=ins.get('positive')
neg_ref=ins.get('negative')
if isinstance(pos_ref, list) and pos_ref:
pos_id=str(pos_ref[0])
if pos_id in wf and isinstance(wf[pos_id], dict):
wf[pos_id].setdefault('inputs', {})['text']=pos
if isinstance(neg_ref, list) and neg_ref:
neg_id=str(neg_ref[0])
if neg_id in wf and isinstance(wf[neg_id], dict):
wf[neg_id].setdefault('inputs', {})['text']=neg
# checkpoint 覆盖
if ckpt:
for node in wf.values():
if isinstance(node, dict) and node.get('class_type') == 'CheckpointLoaderSimple':
node.setdefault('inputs', {})['ckpt_name']=ckpt
with open(out, 'w', encoding='utf-8') as f:
json.dump(wf, f, ensure_ascii=False)
PY
# Step 3: 执行出图
echo "开始生成..."
echo "Prompt: 0:80..."
echo "Workflow: $WORKFLOW"
echo "Checkpoint: -<template>"
echo "Seed: $SEED"
set +e
RESULT=$(python3 "$WORKDIR/tools/comfyui-client.py" wait \
--workflow "$TMP_WORKFLOW" \
--output-dir "$OUTPUT_DIR" 2>&1)
RET=$?
set -e
echo "$RESULT"
rm -f "$TMP_WORKFLOW"
if [ $RET -ne 0 ]; then
echo "ERROR: comfyui-client 执行失败"
exit $RET
fi
# Step 4: 读取输出文件(以本次返回为准)
FILE_PATH=$(echo "$RESULT" | python3 -c "import json,sys; raw=sys.stdin.read().strip(); obj=json.loads(raw) if raw else {}; files=obj.get('output_files',[]); print(files[0] if files else '')")
if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
echo "ERROR: 未找到本次生成图片"
exit 1
fi
FILE_SIZE=$(stat -c%s "$FILE_PATH" 2>/dev/null || stat -f%z "$FILE_PATH" 2>/dev/null)
if [ "$FILE_SIZE" -lt 100000 ]; then
echo "WARNING: 文件偏小(FILE_SIZEbytes),可能是黑图"
fi
echo "SUCCESS"
echo "FILE: $FILE_PATH"
echo "SIZE: $((FILE_SIZE / 1024))KB"
echo "SEED: $SEED"
FILE:tools/prompt-checker.py
#!/usr/bin/env python3
"""Prompt 质量检查工具。"""
from __future__ import annotations
import argparse
import json
import re
import sys
from typing import List
STYLE_KEYWORDS = {
"photorealistic", "realistic", "anime", "illustration", "cinematic",
"vintage", "retro", "showa", "cyberpunk", "concept art", "watercolor",
"oil painting", "ghibli", "comic", "digital painting", "film grain",
}
QUALITY_KEYWORDS = {
"masterpiece", "best quality", "high detail", "ultra detailed",
"8k", "sharp focus", "highly detailed",
}
COMMON_SUBJECT_HINTS = {
"girl", "boy", "woman", "man", "cat", "dog", "robot", "city",
"street", "building", "car", "portrait", "character", "person",
"landscape", "forest", "mountain", "flower", "temple", "warrior",
}
CONTRADICTIONS = [
("realistic", "anime"),
("photorealistic", "illustration"),
("vintage", "modern"),
]
WORD_RE = re.compile(r"[A-Za-z][A-Za-z0-9_-]*")
WEIGHT_RE = re.compile(r"\(([^()]+?):\s*([0-9]+(?:\.[0-9]+)?)\)")
def word_count(text: str) -> int:
return len(WORD_RE.findall(text))
def has_subject(text: str) -> bool:
lower = text.lower()
tokens = set(WORD_RE.findall(lower))
if any(t in tokens for t in COMMON_SUBJECT_HINTS):
return True
# 宽松回退:出现 a/an/the + 单词,视作有主体描述
return bool(re.search(r"\b(a|an|the)\s+[a-z][a-z0-9_-]{2,}\b", lower))
def has_style(text: str) -> bool:
lower = text.lower()
return any(k in lower for k in STYLE_KEYWORDS)
def has_quality(text: str) -> bool:
lower = text.lower()
return any(k in lower for k in QUALITY_KEYWORDS)
def check_parentheses_balance(text: str) -> bool:
depth = 0
for ch in text:
if ch == "(":
depth += 1
elif ch == ")":
depth -= 1
if depth < 0:
return False
return depth == 0
def check_weight_format(text: str) -> List[str]:
issues: List[str] = []
if not check_parentheses_balance(text):
issues.append("括号不匹配")
return issues
# 检测疑似加权但格式错误的片段
suspect = re.findall(r"\([^()]*:[^()]*\)", text)
for s in suspect:
if not WEIGHT_RE.fullmatch(s):
issues.append(f"权重格式可能错误: {s}")
# 检测权重范围
for m in WEIGHT_RE.finditer(text):
value = float(m.group(2))
if value <= 0 or value > 2.0:
issues.append(f"权重值超出建议范围(0,2]: {m.group(0)}")
return issues
def check_contradictions(text: str) -> List[str]:
lower = text.lower()
issues: List[str] = []
for a, b in CONTRADICTIONS:
if a in lower and b in lower:
issues.append(f"存在潜在风格冲突: {a} vs {b}")
return issues
def run_check(prompt: str) -> dict:
issues: List[str] = []
warnings: List[str] = []
wc = word_count(prompt)
if wc < 50:
issues.append(f"长度过短: {wc} 词(建议 50-300 词)")
elif wc > 300:
issues.append(f"长度过长: {wc} 词(建议 50-300 词)")
if not has_subject(prompt):
issues.append("缺少明确主体描述(subject)")
if not has_style(prompt):
issues.append("缺少风格关键词(style)")
if not has_quality(prompt):
warnings.append("缺少常见质量词(如 masterpiece / best quality / high detail)")
issues.extend(check_weight_format(prompt))
issues.extend(check_contradictions(prompt))
status = "PASS" if not issues else "WARN"
return {
"status": status,
"word_count": wc,
"issues": issues,
"warnings": warnings,
}
def main() -> int:
parser = argparse.ArgumentParser(description="Prompt 质量检查工具")
sub = parser.add_subparsers(dest="command", required=True)
p_check = sub.add_parser("check", help="检查 prompt 文本")
p_check.add_argument("prompt", help="待检查的 prompt 文本")
args = parser.parse_args()
if args.command == "check":
result = run_check(args.prompt)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0 if result["status"] == "PASS" else 1
print(json.dumps({"status": "WARN", "issues": ["未知命令"]}, ensure_ascii=False))
return 1
if __name__ == "__main__":
sys.exit(main())
FILE:tools/quick-generate.sh
#!/bin/bash
# quick-generate.sh - one-click image generation
# Usage: bash quick-generate.sh "positive prompt" "negative prompt" [workflow] [output_dir]
set -euo pipefail
POSITIVE="?Usage: bash quick-generate.sh \"positive\" \"negative\" [workflow] [output_dir]"
NEGATIVE="-lowres, bad anatomy, bad hands, blurry, worst quality, watermark"
WORKFLOW="-sdxl_basic.json"
OUTPUT_DIR="-/home/node/ai-outputs"
WORKDIR="/home/node/.openclaw/workspace"
WORKFLOW_PATH="$WORKDIR/workflows/$WORKFLOW"
if [ ! -f "$WORKFLOW_PATH" ]; then
echo "ERROR: workflow not found: $WORKFLOW_PATH"
exit 1
fi
echo "[1/5] Checking ComfyUI..."
COMFY_CHECK=$(curl -s --connect-timeout 5 http://host.docker.internal:8188/system_stats 2>/dev/null)
if [ -z "$COMFY_CHECK" ]; then
echo "ERROR: ComfyUI is not running"
exit 1
fi
echo "OK: ComfyUI online"
mkdir -p "$OUTPUT_DIR"
SEED=$((RANDOM * RANDOM))
START_TS=$(date +%s)
TMP_WORKFLOW=$(mktemp /tmp/quick-workflow.XXXXXX.json)
trap 'rm -f "$TMP_WORKFLOW"' EXIT
echo "[2/5] Building workflow with prompt+seed..."
python3 - "$WORKFLOW_PATH" "$TMP_WORKFLOW" "$POSITIVE" "$NEGATIVE" "$SEED" <<'PY'
import json, sys
src, dst, positive, negative, seed = sys.argv[1:6]
seed = int(seed)
wf = json.load(open(src, 'r', encoding='utf-8'))
# Preferred node ids for current templates
if isinstance(wf.get('6'), dict):
wf['6'].setdefault('inputs', {})['text'] = positive
if isinstance(wf.get('7'), dict):
wf['7'].setdefault('inputs', {})['text'] = negative
if isinstance(wf.get('3'), dict):
wf['3'].setdefault('inputs', {})['seed'] = seed
# Fallback by class_type to keep script robust
pos_done = neg_done = seed_done = False
for node in wf.values():
if not isinstance(node, dict):
continue
c = node.get('class_type')
inputs = node.get('inputs')
if not isinstance(inputs, dict):
continue
if c == 'KSampler':
inputs['seed'] = seed
seed_done = True
elif c == 'CLIPTextEncode' and not pos_done:
inputs['text'] = positive
pos_done = True
elif c == 'CLIPTextEncode' and pos_done and not neg_done:
inputs['text'] = negative
neg_done = True
with open(dst, 'w', encoding='utf-8') as f:
json.dump(wf, f, ensure_ascii=False)
PY
echo "[3/5] Generating..."
echo " Prompt: 0:80..."
echo " Workflow: $WORKFLOW"
echo " Seed: $SEED"
python3 "$WORKDIR/tools/comfyui-client.py" wait --workflow "$TMP_WORKFLOW" --output-dir "$OUTPUT_DIR"
LATEST_FILE=$(find "$OUTPUT_DIR" -maxdepth 1 -name '*.png' -type f -newermt "@$START_TS" 2>/dev/null | sort | tail -1)
if [ -z "$LATEST_FILE" ]; then
echo "ERROR: no new generated image found for this run"
exit 1
fi
FILE_SIZE=$(stat -f%z "$LATEST_FILE" 2>/dev/null || stat -c%s "$LATEST_FILE" 2>/dev/null)
if [ "$FILE_SIZE" -lt 100000 ]; then
echo "WARN: file too small (FILE_SIZE bytes), possible black image"
exit 1
fi
echo "SUCCESS"
echo "FILE=$LATEST_FILE"
echo "SIZE_KB=$((FILE_SIZE / 1024))"
echo "SEED=$SEED"
FILE:tools/run-tracker.py
#!/usr/bin/env python3
"""创意工坊运行追踪器。记录每次生成的完整链路信息。"""
from __future__ import annotations
import argparse
import json
import os
import uuid
from datetime import datetime, timezone, timedelta
RUNTIME_DIR = os.environ.get(
"RUNTIME_DIR",
"/home/node/.openclaw/workspace/runtime"
)
JST = timezone(timedelta(hours=8))
def new_run() -> dict:
"""创建新的运行记录。"""
run_id = datetime.now(JST).strftime("%Y%m%d_%H%M%S") + "_" + uuid.uuid4().hex[:6]
run = {
"run_id": run_id,
"created_at": datetime.now(JST).isoformat(),
"status": "started",
"request": None,
"prompt": None,
"workflow_config": None,
"render_result": None,
"critic_result": None,
"final_choice": None,
"checkpoint": None,
"lora": None,
"template": None,
"seeds": [],
"errors": []
}
path = os.path.join(RUNTIME_DIR, f"{run_id}.json")
os.makedirs(RUNTIME_DIR, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(run, f, ensure_ascii=False, indent=2)
return {"run_id": run_id, "path": path}
def update_run(run_id: str, updates: dict) -> dict:
"""更新运行记录的指定字段。"""
path = os.path.join(RUNTIME_DIR, f"{run_id}.json")
if not os.path.exists(path):
return {"error": f"运行记录不存在: {run_id}"}
with open(path, "r", encoding="utf-8") as f:
run = json.load(f)
run.update(updates)
run["updated_at"] = datetime.now(JST).isoformat()
with open(path, "w", encoding="utf-8") as f:
json.dump(run, f, ensure_ascii=False, indent=2)
return {"run_id": run_id, "updated_fields": list(updates.keys())}
def list_runs(limit: int = 10) -> list:
"""列出最近的运行记录。"""
os.makedirs(RUNTIME_DIR, exist_ok=True)
files = sorted(
[f for f in os.listdir(RUNTIME_DIR) if f.endswith(".json")],
reverse=True
)[:limit]
runs = []
for f in files:
path = os.path.join(RUNTIME_DIR, f)
with open(path, "r", encoding="utf-8") as fh:
data = json.load(fh)
runs.append({
"run_id": data.get("run_id"),
"created_at": data.get("created_at"),
"status": data.get("status"),
"checkpoint": data.get("checkpoint"),
"template": data.get("template"),
})
return runs
def get_run(run_id: str) -> dict:
"""获取完整的运行记录。"""
path = os.path.join(RUNTIME_DIR, f"{run_id}.json")
if not os.path.exists(path):
return {"error": f"运行记录不存在: {run_id}"}
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def main():
parser = argparse.ArgumentParser(description="创意工坊运行追踪器")
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser("new", help="创建新运行记录")
p_update = sub.add_parser("update", help="更新运行记录")
p_update.add_argument("--run-id", required=True)
p_update.add_argument("--data", required=True, help="JSON 格式的更新数据")
p_list = sub.add_parser("list", help="列出最近的运行记录")
p_list.add_argument("--limit", type=int, default=10)
p_get = sub.add_parser("get", help="获取完整运行记录")
p_get.add_argument("--run-id", required=True)
args = parser.parse_args()
if args.command == "new":
result = new_run()
elif args.command == "update":
updates = json.loads(args.data)
result = update_run(args.run_id, updates)
elif args.command == "list":
result = list_runs(args.limit)
elif args.command == "get":
result = get_run(args.run_id)
else:
result = {"error": "未知命令"}
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()
FILE:workflows/sdxl_basic.json
{
"3": {
"class_type": "KSampler",
"inputs": {
"seed": 42,
"steps": 25,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1.0,
"model": ["4", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["5", 0]
}
},
"4": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "sd_xl_base_1.0.safetensors"
}
},
"5": {
"class_type": "EmptyLatentImage",
"inputs": {
"width": 1024,
"height": 1024,
"batch_size": 1
}
},
"6": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "masterpiece, best quality",
"clip": ["4", 1]
}
},
"7": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "bad quality, worst quality, bad anatomy, bad hands, watermark, text, blurry",
"clip": ["4", 1]
}
},
"8": {
"class_type": "VAEDecode",
"inputs": {
"samples": ["3", 0],
"vae": ["13", 0]
}
},
"9": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "creative_workshop/sdxl",
"images": ["8", 0]
}
},
"13": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "sdxl_vae_fp16fix.safetensors"
}
}
}
FILE:workflows/sdxl_hires.json
{
"3": {
"class_type": "KSampler",
"inputs": {
"seed": 42,
"steps": 25,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1.0,
"model": ["10", 0],
"positive": ["6", 0],
"negative": ["7", 0],
"latent_image": ["5", 0]
}
},
"4": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "sd_xl_base_1.0.safetensors"
}
},
"5": {
"class_type": "EmptyLatentImage",
"inputs": {
"width": 1024,
"height": 1024,
"batch_size": 1
}
},
"6": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "masterpiece, best quality",
"clip": ["4", 1]
}
},
"7": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "bad quality, worst quality, bad anatomy, bad hands, watermark, text, blurry",
"clip": ["4", 1]
}
},
"8": {
"class_type": "VAEDecode",
"inputs": {
"samples": ["3", 0],
"vae": ["13", 0]
}
},
"9": {
"class_type": "SaveImage",
"inputs": {
"filename_prefix": "creative_workshop/hires",
"images": ["12", 0]
}
},
"10": {
"class_type": "LoraLoader",
"inputs": {
"lora_name": "detail_tweaker_xl.safetensors",
"strength_model": 0.5,
"strength_clip": 0.5,
"model": ["4", 0],
"clip": ["4", 1]
}
},
"11": {
"class_type": "UpscaleModelLoader",
"inputs": {
"model_name": "4x-UltraSharp.pth"
}
},
"12": {
"class_type": "ImageUpscaleWithModel",
"inputs": {
"upscale_model": ["11", 0],
"image": ["8", 0]
}
},
"13": {
"class_type": "VAELoader",
"inputs": {
"vae_name": "sdxl_vae_fp16fix.safetensors"
}
}
}
Simulate a full Chinese civil court hearing with 4 role-based agents (clerk, plaintiff, defendant, judge) orchestrated by deterministic Lobster workflow.
---
name: moot-court-ai
description: Simulate a full Chinese civil court hearing with 4 role-based agents (clerk, plaintiff, defendant, judge) orchestrated by deterministic Lobster workflow.
version: 1.0.0
metadata:
openclaw:
requires:
env:
- DEEPSEEK_API_KEY
- DASHSCOPE_API_KEY
bins:
- openclaw
- lobster
primaryEnv: DEEPSEEK_API_KEY
emoji: "⚖️"
homepage: "https://github.com/baobaodawang-creater/moot-court-ai"
---
# Moot Court AI
Moot Court AI is an OpenClaw skill that runs a 4-agent Chinese civil court simulation with strict workflow control.
## Agent system
- `clerk` (书记员): announces opening, checks identity, controls stage transitions.
- `plaintiff` (原告代理律师): argues for plaintiff, presents claim and evidence.
- `defendant` (被告代理律师): performs three-validity challenges and defense.
- `judge` (审判长): stays neutral, summarizes issues, applies legal syllogism, and renders judgment.
## Model stack
- DeepSeek: `deepseek-chat`, `deepseek-reasoner`
- Qwen: `qwen-max` (DashScope compatible endpoint)
## Workflow principle
- Deterministic orchestration with Lobster.
- Agent communication follows fixed hearing stages.
- Process follows Chinese civil procedure order (庭前准备 -> 诉辩交换 -> 举证质证 -> 法庭辩论 -> 最后陈述 -> 宣判).
## Installation requirements
You must configure both API keys before running:
- `DEEPSEEK_API_KEY`
- `DASHSCOPE_API_KEY`
## Recommended usage
1. Prepare case files (`case-brief.md`, `complaint.md`, `defense.md`, evidence folders).
2. Initialize materials into agent workspaces.
3. Run `moot-court.lobster` through OpenClaw/Lobster.
4. Export judgment and hearing log for review.
Send a daily operational brief from your self-hosted OpenClaw to Telegram — agent health, unresolved issues, and weekly evolution highlights, every morning.
---
name: daily-brief
description: Send a daily operational brief from your self-hosted OpenClaw to Telegram — agent health, unresolved issues, and weekly evolution highlights, every morning.
version: 1.0.0
metadata:
openclaw:
requires:
bins:
- bash
- curl
- jq
- docker
env:
- OPENCLAW_TOKEN
- TELEGRAM_BOT_TOKEN
- TELEGRAM_CHAT_ID
primaryEnv: OPENCLAW_TOKEN
emoji: "🗞️"
---
# Daily Brief
`daily-brief` packages a production-style daily report workflow for self-hosted OpenClaw users.
It generates a structured morning digest and sends it to Telegram, with focus on:
- Agent/system health signals from gateway logs
- Unfinished or risky items inferred from recent runtime behavior
- Capability evolution highlights from latest `evolver` logs
## Typical deployment scenario
Use this skill when your OpenClaw instance is self-hosted and you want a reliable daily operations snapshot at a fixed time (for example 08:05 every day).
## Required components
- `secretary` agent configured and available
- system cron enabled
- Telegram bot delivery configured
- OpenClaw gateway reachable at local endpoint
## Real script pattern (redacted example)
The following is a redacted usage example adapted from `daily_brief.sh`:
```bash
#!/bin/bash
BOT_TOKEN="TELEGRAM_BOT_TOKEN"
CHAT_ID="TELEGRAM_CHAT_ID"
OPENCLAW_TOKEN="OPENCLAW_TOKEN"
LOGS=$(docker logs openclaw-gateway --since 24h 2>&1 | tail -100)
EVOLVER_LOG=""
LATEST_EVOLVER=$(ls -t /tmp/evolver-*.log 2>/dev/null | head -1)
if [ -n "$LATEST_EVOLVER" ]; then
EVOLVER_LOG=$(tail -50 "$LATEST_EVOLVER")
fi
PROMPT="You are the private secretary. Build a concise daily brief with:
1) system status
2) what happened today
3) issues found
4) resolved vs unresolved
5) items requiring executive attention
System logs:
LOGS
Evolution report:
EVOLVER_LOG"
RESPONSE=$(curl -s -X POST http://127.0.0.1:18789/v1/chat/completions \
-H "Authorization: Bearer OPENCLAW_TOKEN" \
-H "Content-Type: application/json" \
-H "x-openclaw-agent-id: secretary" \
--max-time 180 \
-d "{\"model\":\"openclaw\",\"messages\":[{\"role\":\"user\",\"content\":$(echo "$PROMPT" | jq -Rs .)}]}")
RESULT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // "Secretary unavailable"')
curl -s -X POST "https://api.telegram.org/botBOT_TOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
--data-urlencode text="RESULT"
```
## Cron example
```cron
5 8 * * * /Users/lihaochen/openclaw/daily_brief.sh
```
FILE:README.md
# Daily Brief Skill
This package provides a ClawHub-ready skill for a daily OpenClaw operations digest.
## What it does
- Collects last-24h gateway logs
- Reads latest capability-evolver log from `/tmp/evolver-*.log` (if present)
- Uses `secretary` agent to summarize into a structured daily report
- Pushes report to Telegram
## What lands in your Telegram
---
🗞️ Daily Brief — 2026-03-19 08:05
✅ System: openclaw-gateway running normally
⚠️ Unresolved: ACP runtime plugin not configured
📋 Today's priorities: [list]
🧬 Evolution: No new report this week
---
## Who is this for
- Self-hosted OpenClaw users who want a daily ops snapshot
- Anyone running multiple agents who needs a single morning summary
- Users who want capability-evolver results surfaced automatically
## One-line pitch
Your OpenClaw runs 24/7. Now you'll actually know what it did overnight.
## Source references
- `/Users/lihaochen/openclaw/daily_brief.sh`
- `/Users/lihaochen/openclaw/config/workspace-secretary/SOUL.md`
- `/Users/lihaochen/openclaw/workspace/AGENTS.md`
## Publish command
```bash
~/.bun/bin/clawhub publish \
/Users/lihaochen/openclaw/workspace/clawhub-submission/daily-brief \
--slug daily-brief \
--name "Daily Brief" \
--version 1.0.0 \
--tags latest \
--changelog "Initial release: daily OpenClaw system status report via Telegram"
```
Turn any research request into a structured, reviewable brief — fact collection, risk analysis, and recommendation in three deterministic steps.
---
name: search-analyst-pair
description: Turn any research request into a structured, reviewable brief — fact collection, risk analysis, and recommendation in three deterministic steps.
version: 1.0.0
metadata:
openclaw:
requires:
env:
- OPENCLAW_BASE_URL
- OPENCLAW_TOKEN
bins:
- python3
- curl
- jq
primaryEnv: OPENCLAW_TOKEN
emoji: "🔍"
homepage: "https://github.com/baobaodawang-creater/neo-agent-lab"
---
# Search Analyst Pair
This skill packages a deterministic `/hunt` workflow for OpenClaw.
When a user message starts with `/hunt`, the workflow follows a strict 3-hop chain:
1. **Search** (DeepSeek): gather facts and sources only.
2. **Analyst** (Gemini): analyze Search output and identify key points and risks.
3. **Main** (Kimi): synthesize Search + Analyst into final guidance.
## Why this skill
- Prevents ad-hoc routing for critical tasks.
- Separates **fact collection**, **analysis**, and **decision output**.
- Supports both native OpenClaw orchestration and a FastAPI fallback runner.
## Best-fit scenarios
- Time-sensitive research tasks that require reliable structure.
- Decision support where source traceability matters.
- Team workflows that need stable, reviewable output sections.
## Requirements
- OpenClaw gateway running and reachable.
- Agent IDs available: `main`, `search`, `analyst`.
- `tools.agentToAgent.enabled=true` in OpenClaw config.
- `subagents.allowAgents` configured:
- `main` allows `search`, `analyst`
- `search` allows `analyst`
## Behavior contract
- Trigger prefix: `/hunt`
- Fixed order: `Search -> Analyst -> Main`
- Fallback policy: if agent-to-agent spawn fails, the workflow must explicitly mark fallback output.
## Usage examples
```text
/hunt Review today's agent framework updates and give a practical migration plan.
```
```text
/hunt Collect top legal AI workflow changes this week and assess implementation risk.
```
## Output shape (recommended)
- `Search findings`
- `Analyst assessment`
- `Main conclusion`
FILE:README.md
# search-analyst-pair
ClawHub submission bundle for a deterministic `/hunt` multi-agent workflow.
## What it does
It enforces a strict handoff chain:
`Search -> Analyst -> Main synthesis`
This avoids free-form routing and keeps outputs consistent for review and execution.
## What you get
After installation, each `/hunt` run is structured into the same reviewable output:
---
🔍 Search Findings
[sources, raw findings]
📊 Analyst Assessment
[key points, risks, recommendation]
🎯 Main Conclusion
[final answer, action plan]
---
## Who is this for
- Anyone running self-hosted OpenClaw who needs reliable research output
- Teams that want traceable, reviewable AI research
- Users tired of ad-hoc agent responses
## One-line pitch
Stop asking your agent to research and hope for the best.
`/hunt` gives you a fixed three-step brief every time.
## Source implementation
Runtime workflow assets are maintained here:
- `/Users/lihaochen/openclaw/workspace/workflows/search_analyst_pair/`
This submission package contains the registry-facing docs:
- `SKILL.md` (required by ClawHub)
- `README.md`
## Dependency notes
- Requires OpenClaw gateway auth token.
- Requires `main`, `search`, `analyst` agents to be configured.
- Requires agent-to-agent permissions and subagent allowlists.
## Suggested publish command
```bash
clawhub publish /Users/lihaochen/openclaw/workspace/clawhub-submission/search-analyst-pair \
--slug search-analyst-pair \
--name "Search Analyst Pair" \
--version 1.0.0 \
--tags latest \
--changelog "Initial release: deterministic /hunt 3-hop workflow"
```